Vertex Animation
The Cube demo shows the construction of a mesh from scratch and the animation of a couple of vertices.The logical structure is as follows:
A Mesh
consists of one or more Groups of faces.
(each Group can be manipulated
as an entity,
consists of a set of faces, and is defined by a vertex list.)
Faces are not constructed separately from the Group to which they belong.
d3drm->CreateMesh ( &mesh );
mesh->AddGroup(24, 6, 4, vertorder, &group);
24 vertices, to be organized as 6 faces of 4 vertices each, and referred to (temporarily) via the data member (of type D3DRMGROUPINDEX) named 'group'. This is not a pointer, but an index (like 0, 1, 2 etc.) which indicates which group within the mesh is being referred to at any given time. The first created group will be called 0.
The 'vertorder' array provides a layer of indirection so that the order of vertices' presentation can be re-mapped onto the order in which the SetVertices command consumes them (maps them into the group.) It's not clear to me at this time who would need this, though the text says that modelers often deliver vertices in jumbled order.
mesh->SetVertices( group, 0, 24, vertexlist );
Pretty obvious. All the interesting structure, if any, is in the typedef of the vertex structure, which is
typedef struct _D3DRMVERTEX
{
D3DVECTOR position;
D3DVECTOR normal;
D3DVALUE tu, tv;
D3DCOLOR color;
} D3DRMVERTEX;
tu and tv specify what portion of (location within) a texture map is
to be applied to this vertex. Substantial distortion can obviously occur
if we mis-assign these values.
Some macro magic on page 300 leads to the creation of the cube. Note that it sets color to 0 and saves you from a little bit of typing.
static D3DRMVERTEX vertexlist[] =
{
// left face
VERTEX( 0,0,0, -1,0,0, 0,1), // vertex
0, with texture coordinates (0,1)
VERTEX(0,0,1, -1,0,0, 0,0), //
vertex 1, with texture coordinates (0,0) etc.
VERTEX(0,1,1, -1,0,0, 1,0), // all normals
for this surface point in -x direction (no Gouraud corner trickery)
VERTEX(0,1,0, -1,0,0, 1,1),
etc. Comments in the text indicate vertices 0, 6, 8, 14, 18 and 20 as targets for animation to come.
Query 13.1. How many vertices does it take to define a cube? How many are being defined here? What is going on with respect to the word 'shared'?
ANSWER:
The mesh in this example doesn't share vertices. However it seems to me that 'vertorder' would be a means of sharing vertices, if any indices were repeated in that list. Highly subject to off-by-one errors,though. It's strange that D3D doesn't seem to be oriented toward sharing of vertices.
/ANSWER
The warping example
The following text explains pages 300 through 310 of the text. (The text actually explains itself pretty well; I write out these lecture notes to make sure I understand it myself.)
------------
The mesh is then told to translate and scale itself, and put into a frame for rotational purposes. A static structure of type CallbackData is declared and named cbdata, for communicating with a callback function named UpdateCube(). That function is then bound to the mesh's frame using the AddMoveCallback method. Whenever the frame is moved, UpdateCube() will be called.
void CubeWin::UpdateCube(LPDIRECT3DRMFRAME frame, void* p, D3DVALUE)
// First parameter is obviously the frame whose motion triggers this
callback
// Second parameter p points to the callback data which will receive
the internal handle 'data'
// I don't know why the type D3DVALUE is provided as the third parameter.
{
CallbackData* data=(Callbackdata*)p;
static const D3DVALUE lim=D3DVALUE(5);
static D3DVALUE control; // This
STATIC is going to carry the integral of 'inc' to tell when to reverse
the morph
static D3DVALUE inc=D3DVALUE(.25); // This STATIC
is gonna toggle to make the verts walk back, eventually
static D3DRMVERTEX vert[24]; // a place to park
the verts for workin' on them.
// Now we grab the vertices into the above temp array, and doctor SOME of them.
data->mesh->GetVertices( data->group, 0, 24, vert ); // grab 'em
vert[0].position.x+=inc;
vert[0].position.y+=inc;
vert[0].position.z+=inc;
vert[6].position.x+=inc;
vert[6].position.y+=inc;
vert[6].position.z+=inc;
etc.
data->mesh->SetVertices( data->group, 0, 24, vert); // put 'em back.
control += inc; // The integration step
if (control>lim || control < -lim)
inc=-inc;
// reversing direction of warping
static UINT delay;
if (++delay<20)
return; // 19 out of 20 calls
to this callback end right here.
delay=0; // the 20th falls through to spin the cube
a little bit
LPDIRECT3DRMFRAME scene;
frame->GetScene ( &scene );
D3DVECTOR spinvect;
D3DRMVectorRandom( &spinvect );
// point the spin axis off somewhere
D3DVALUE spin=D3DDivide (rand()%50+1, 400); // pick some small angle
between 1/400 and 50/400=1/8 of PI.
frame -> SetRotation( scene, spinvect.x, spinvect.y, spinvect.z, spin
);
The rest of this chapter repeats the exercise for two groups, and animates the color. Let's go do something more interesting.
Morphing is done by making sure that two meshes have the same number of vertices. The morph is generally only meaningful if the two meshes have the same topology and general shape (e. g. dog face to human face. Both have noses, eyes, etc.)
Morphing is based on .MRF files which contain at least two meshes which are named with single lowercase letters beginning with 'a'. The code just interpolates.... boring. Let's go look at some potentially more interesting stuff, in the 3D Max book.
3D Max and Meshes - Chapter 5
This chapter teaches how to control lines and form basic surfaces with them.
Basic splines ("space curves") consist of a series of segments. Max automatically decides how many segments to use, based on how many curves you put into a line. If shapes such as squares are produced, each edge is a single segment and vertex slope continuity is not preserved (obviously).
Why use spline segments for straight lines? Because it gives you a uniform means of interpolating along straight and curved lines, e. g. for surface-filling algorithms.
You can edit vertices by using a Bezier 'tangent handle' control.
Forming 2d surfaces is done by using the CrossInsert capability to insert two vertices into two lines which cross while still working in a 'flat' drawing. Then you can raise this intersection in the z direction, and both curves adapt.
There is an implicit tiling associated with any closed loop in such a mesh. For instance if four lines named a, b, c and d are cross connected to form a quadrilateral, how is the surface constructed? That is, how many of the original control points are used? The answer is that it doesn't matter how many control points are used to define the border. These spline curves can be sampled at any point as needed for a polygonal approximation of the surface. We tesselate (divide into polygons) when we want to run on realtime hardware, then use Gouraud or Phong shading to disguise the fact.
Mesh Modeling Tools - Chapter 6
Thoughts about Object-Level Operations on Spline Meshes.
NOTE: "Spline" is not officially applicable to Hermite and Bezier curves, but people use it anyway and so will I.
Now, how do we go beyond basic vertex oriented editing and get some "high level functionality?" Consider for instance the cube with a twist, in Figure 6.3. There are going to be two different representations of this object:
a) A procedural "how it was done" record of the successive transformations
to the primives; and
b) A data representation of the resulting surface, exactly as-is.
The latter is not always stored, unless you export it. But it must be generated for rendering purposes.
The procedural rep is based on a stack structure, actually a linear history. Let's think about how to twist a cube. Originally the cube was stored as twelve splines which (initially) consisted of single segments, with some identification (cross-links) between certain vertices. Name the edges of the top of the cube as a, b, c, d with the endpoints referred to as a1, a2; b1, b2 etc. Thus a2 and b1 are crosslinked, etc.
The vertical edges will be referred to as c, d, e, f, where c1 is crosslinked with a1 and d2, etc.
The bottom edges are g, h, i and j, where g1 is crosslinked to c2.
Now let's think about applying a distortion to the space containing these points. If we just shrank the space pyramidally, we'd transform the upper vertices and the corresponding edges would obviously track. But what if we twisted space with a shear-twist (that is, the amount of rotation depends on the z value.) The upper and lower faces would rotate with respect to one another, but the vertical edges might remain straight. This "string art" approach would yield a wasp-waisted "spindle" shape but no beautiful helically curved edges.
Such transformations are analogous to a VRML construct where an interpolator's output drives another node's input. We need to be able to denote a primary interpolative axis, and then specify what operation we are distributing along this axis (scaling, rotation, etc.) In fact we don't even have to do the distributed operation in a linear fashion (but that's the simplest to understand.) Consider for instance the idea of a z^2 rotation, whereby the amount of rotation varies as the square of z.
So - back to the story. Pretend each vertical edge already had ten control vertices imbedded in it. If we were to specify that the z axis is our interpolative axis - it would be easy to generate a new 'point cloud' of appropriately rotated vertices. We'd be rotating all the points in both the top and the sides, but the tops' excess vertices wouldn't change the straightness of the edges. The vertical edges' new vertices would, however, be spiraling and would thus reshape the vertical edges.
But if the vertical edges are single segments, how is the code smart enough to break them into more and more segments, to generate the helical edges? It's the 'frog in the frying pan' problem. Small changes might lull you into staying with single segment splines and getting the spindle effect. Even if you preallocated ten vertices per edge, a sufficiently large cumulative rotation might mean that you'd be asking nine segments to span too much curvature. (Remember that a cubic parametric function of length parameter t can have at most two inflections. Thus you can't wrap a single segment around and around ... plot x versus t to see why.)
The answer is in the sequential instructions on page 158. You are instructed to go back into the stack and increase the amount of detail in the box, so that the spiral can properly interpolate.
Extrusion and Lathing.
Extrusion is pretty simple, because you just copy the profile and cross-link its corresponding points. Lathing is just extrusion with rotation rather than translation, and an additional closure operation.
Constructive Solid Geometry ("Compound Objects")
Consider subtracting a cylinder from a sphere (or three of them, to make a bowling ball.) How do you deal with the data structure to get a resulting mesh? It's not easy to describe, and involves a good deal of logic.
Chapter 7: Patch Modeling
Mesh models are essentially "string webs" without the twist-control capabilities afforded by the 4 x 4 (x 3) geometry matrices of the Hermite or Bezier patch.
Chapter 8: Exploring NURBS
An excursion into Foley & Van Dam (pages 490-504) to discuss B-splines and Non-uniform Rational B-Splines (NURBS). Simply put, NURBS give you more precise control over shape than Hermite/Bezier surfaces, but you have to learn a bit more.
Consider a Bezier spline, with its non-overlapping sets of control points. P1..4 control the first segment, P5..8 the second, etc. But P3 and P5 have a certain necessary relationship (figure 11.21) if you want slope continuity - they have to be collinear with P4. their distances may vary, which affects the stiffness of the curve on that side of P4.
An alternative would be to construct a set of "fenceposts" or control points P0.....Pm for m >=3. For m=3 we have 4 fenceposts; we would get three fence segments (thinking like a farmer, here...) but if we're planning to use the first and last control points to determine the slope of the line at its beginning and end, we will need two "isolated" posts so there would only be one actual segment in the line (between P1 and P2); by convention it is denoted Q3 for the rightmost control point (P3) which affects its shape.
It is traditional to use a continuously varying parameter t rather than repeatedly starting over (0..1) in each segment. For a segment Qi, the parameter t falls between ti and ti+1 for 3<=i+m. The join points between segments are called knots; for instance, Q3 joins Q4 at the knot t4. From figure 11.22 we can see that the control points don't actually fall ON the curve, but the knots obviously do.
Nonrational doesn't mean "irrational", but just that no ratios of polynomials are used in weighting functions (i. e. no denominator function.) Rational splines are more powerful and messy.
Uniform means the knots are evenly spaced; for instance t3=0, t4=1, t5=2 etc. The B-spline Geometry vector for a segment consists of the four control points for that segment. Since this is a moving definition, each control point except the ones at the beginning and end) affects four segments. The generative principle is very similar to that for the Hermite and Bezier curves, relating to a T-vector, a basis matrix and the geometry vector (page 493.)
Got nice continuity, but it won't 'hit' ("interpolate") your control points in general. You can force it to do so, by repeating control points.
Nonuniform just lets us slide the knots along to non-integral values of t. You can have multiple knots at the same value, which means we have to define the multiplicity of the knot. Blending functions consist of interpolations against lower order blending functions, where 'order' means fourth-order for cubic.
NonUniform Rational B-Splines NURBS allow a denominator term W(t) to divide each of the other weight functions. This works out to mean that NURBS are naturals for homogenous coordinates, and are invariant under perspective projection. Thus you can just project the control points and use a NURB in image space to get the same pixels you would have gotten if you had drawn them all in object space, then projected.
<Still gotta talk about the NURBS in Chapter 8 of MaxBook.)
Rational
Max allows splines to be manipulated via
To the syllabus
To lecture 12
To lecture 14