Mesh Deformation


Introduction
Deformation of an objects geometry (point displacement) is typically handled by an Effect Module. It is important that you perform your deformation at the correct point in the Animation Pipeline otherwise your module will either have no affect on the mesh or it will behave in a way that the user does not expect. For that reason you should only perform point displacement in response to the P_POST_POINT_DISPLACE() AN. Don't let the "POST" in the name throw you, there is a reason for it that will be apparent in future versions of the messiahAPI, for now just trust that this is the correct place to perform point displacement.
Targets
Before our Effect Module can displace an object's points the question has to be asked "which objects do we displace?". The answer is that each Effect Module should displace the points of each of its targets (assuming of course that the purpose of the Effect Module is to displace points). An Effect Module can have any number of targets from 1 to N; you'll never need to worry about having at least one target because messiah enforces this condition for you.
Take the following image for example. In this case you can see that the Bone_Deform Effect has two targets: "sphere" and "quad". That means that the Bone_Deform is supposed to deform both of those objects.
EffectsListDiagram.gif
From the user's perspective they will be applying your Effect Module to each of the targets they assign to it. This means that you will need to find each of the targets and displace their points accordingly. This process is fairly simple, we'll use fxEffectTargetFirst() to find the first target of the Effect Module, then we'll use fxEffectTargetNext() to find each successive target until there are no more.
Example:

FXobject target;        FXeffect effect;

// ...

// get first target of the effect
target = fxEffectTargetFirst( effect, FX_NOFLAG );

// displace the points on the target 
// and cycle through the rest of the targets
while( target )
{
        // displace the points
        // ...

        // get next target (if any)
        target = fxEffectTargetNext( effect, target, FX_NOFLAG );
}

That was fairly painless wasn't it?
Target Enumeration
There is another method of finding all of the targets of an Effect Module and doing something to/with them: Target Enumeration. For this method we will create a function that messiah will call once for each target of an Effect Module, each time passing it the FXobject of the target. This type of function is called an Enumeration Callback and is declared/defined using the Callback Signature Macro: FX_EFFECTSCAN(). We will refer to any function declared/defined by FX_EFFECTSCAN() as a scan_func(), although the name you give to the Callback will probably be different. The body of this Callback will just be what we had inside of the while loop above, not including the call to fxEffectTargetNext() (remember, our Callback gets called once for each target, so there's no need to manually get the next one). That leaves us with simply displacing the points of the target.
Example:

FX_EFFECTSCAN( MyScanFunc, void, data )
// expands to: FXintf MyScanFunc( FXeffect effect, FXtool tool, FXobject target, void *data )
{
        // displace the points  
        // ...

        return FX_TRUE;
}
Clearly a little explanation is in order regarding that void and data in FX_EFFECTSCAN(). All of the Enumeration Callbacks allow you to pass yourself some data that you create. Put another way, when you start the enumeration, you can pass some data that messiah will in turn pass to your Enumeration Callback each time that it is called. The reason for this is efficency. For example lets say that there is some value that only needs to be calculated once, independent of which target is currently being deformed. It would be very inefficent for us to calculate it in our scan_func() because we would be calculating it once for each target. Instead if we could calculate it once, then pass it to our scan_func() we would keep things efficent and have that value available to us in our scan_func().
We are able to do this by passing the address of our value to fxEffectTargetScan() when we start the enumeration process. That address will in turn be passed to our scan_func(). As far as messiah is concerned this address is a FXvoid *, so we would have to cast it to whatever data type it really is inside of our scan_func(). However if you look at what FX_EFFECTSCAN() expands to you will notice that the second argument becomes the data type for the last argument in the scan_func(), and the thrid argument becomes the name of that variable. You will also notice that this becomes a pointer to that data type. Ok, Ok, in this example it's still a void *, but by changing void in FX_EFFECTSCAN() to some other data type the pointer gets cast to that type for us.
A very typical use for this is to pass the Effect Module's state data to the scan_func(). We get the state data by calling fxObjectGetTypeData() ( see Data Attachment for more info ), we don't want to do this each time our scan_func() gets called because the address of this data doesn't change between calls, so why add the overhead? Instead we'll get the address of the state data before starting the target enumeration, then pass that address to fxEffectTargetScan() to start the enumeration.
Example:

// get state data from the effect
MyEffectData *med = fxObjectGetTypeData( effect, FX_NOFLAG );

// start the target enumeration 
// with med being passed to each call of our callback
fxEffectTargetScan( effect, &MyScanFunc, med, FX_NOFLAG );

// ...

// Our target enumeration callback ( scan_func() )
FX_EFFECTSCAN( MyScanFunc, MyEffectData, theData )
{
        // now theData is a MyEffectData * that points to
        // our effect's state data, neat huh?

        // displace points
        // ...

        return FX_TRUE;
}

Getting a Point's Position
To get the position of a point (or vertex) in an object's mesh you will need to use the fxMeshPoint() function. You will identify the point that you are interested in by a 0 based ID number called the point ID. There are two possible positions you can get for each point, one is the un-deformed position of the point (the position of the point as it was modelled), the other is the deformed position of the point at the current state in processing. Remember that Effect Modules are processed in the order they appear in the interface, therefore the deformed position of a point will be as a result of only those Effect Modules that precede yours. The position of the un-deformed points is in object space, and the position of the deformed points is in world space. This is for convienence as the majority of the time you would need to convert the deformed points to world space anyway.
Setting a Point's Position
To set the position of a point you will need a few functions. First you need to tell messiah that you are about to deform some points (and again this should only be done in response to P_POST_POINT_DISPLACE() ), you'll do this with a call to fxDisplaceBegin(). Next you'll use fxDisplacePoint() to displace the point(s) by passing a new world position. Finally you'll tell messiah that you're done deforming points by calling fxDisplaceEnd().
Example:

FXobject target;        FXeffect effect;
FXvecd   cur_pos;

// ...

// get the first target of the effect
target = fxEffectTargetFirst( effect, FX_NOFLAG );

// get the number of points in the mesh
fxMeshNumPoints( target, &num_points, FX_NOFLAG );

// tell messiah that we're going to start displacing points on this object
fxDisplaceBegin( target, FX_NOFLAG );

// loop through all of the points, displacing them in Y by the current time value
// (exciting effect, huh?)
for( i = 0; i < num_points; i++ )
{
        // get the point's current position
        fxMeshPoint( target, i, cur_pos, FX_NOFLAG );

        // add the current time to the Y value of the current position
        cur_pos[ypos] += NOW;

        // now set the point's position to this adjusted value
        fxDisplacePoint( target, i, cur_pos[xpos], cur_pos[ypos], cur_pos[zpos] );
}

// tell messiah that we're done displacing points
fxDisplaceEnd( target, FX_NOFLAG );
Point Enumeration
Just like there are two ways to cycle through all of the targets of an Effect Module, there are also two ways to cycle through all of the points of an object. That's right, we can use a Enumeration Callback here too. In this case we will use the Callback Signature Macro: FX_DISPLACESCAN() to create our Callback. Just like we called any Callback created with FX_EFFECTSCAN() a scan_func(), we will call any Callback created with FX_DISPLACESCAN() a disp_func(). Also, just like the scan_func(), we can pass data to our disp_func() when we start the enumeration process.
Each time our disp_func() gets called it will get a pointer to a FX_DisplacePoint structure. This structure contains a lot of the information you might be interested in regarding the current point. For example it contains the FXobject of the object the point belongs to (objID), the index of the point (ID), the positions of the point both deformed and un-deformed (cur_pos and file_pos respectively) and so on. One of the elements (new_pos) is used to set the new position of the current point. There's no need to call fxDisplaceBegin() or fxDisplaceEnd(), and likewise there's no need to call fxDisplacePoint(); just set new_pos.
Example:

// remember, NOW is actually a call to fxFrameCalcGet()
// so we don't want to call this for each point in the mesh
FXdouble time = NOW;    

// start enumerating points for displacement, passing the current time
fxDisplaceScan( target, FX_NULLID, FX_WEIGHT_DONTCREATE, &MyDisplaceScan, &time, FX_NOFLAG );

// ...

FX_DISPLACESCAN( MyDisplaceScan, FXdouble, now )
// expands to: FXint MyDisplaceScan(FX_DisplacePoint *p, FXdouble *now)
{
        // copy the current position to the new position
        FX_VEC3COPY( p->new_pos, p->cur_pos );

        // same affect as the previous example, offset Y based on the time
        p->new_pos[ypos] += *now;

        return FX_TRUE;
}
This method of displacing points is usually a lot cleaner than manually cycling through each of the target's points. We can also take advantage of Weight Tools very easily with this method (we'll get to weights in a moment). The disadvantage to the disp_func() is that you get called once for each point in the target whether you want to displace it or not. If you know, for example, that your effect is only going to be modifying a few select points in a mesh, then it is probably much more efficent to use the manual approach.
Weight Tools
A weight tool is a tool that the user can manipulate to define weight values for each of the points in a mesh. Your Effect Module can take advantage of these weights for example to allow the user to limit the affects of your displacement to only the areas they want. Presently there is only one type of weight tool in messiah, the MetaEffector, however more types will be added and in future releases of the messiahAPI you will have the ability to create your own type of weight tools.
A weight tool will calculate weights for each of the points in the object being displaced when you start a Point Enumeration. There are a few different ways that the weights can be computed. The first is by the position of the weight tool relative to the target mesh in Setup Mode. In the case of the MetaEffector, which generates a field around it, the point weights will be calculated based on the strength of the field at each of the points when the weight tool and the target are in their rest positions (i.e. Setup Mode). If we want to use this type of calculation we will use the FX_WEIGHT_SETUP() symbol (you'll see where in a moment). The next method is to calculate the weights based on the position of the weight tool relative to the target mesh outside of Setup Mode. This method allows the animator to animate the weights by animating the weight tool relative to the deformed mesh. We identify this method as FX_WEIGHT_ANIMATE(). The final method is a combination of the two previous methods. In this case the weights are taken by the position of the weight tool outside of Setup Mode, relative to the target mesh inside of Setup Mode. This may sound a little odd, but it's actually pretty handy. It may not always be convienent for the animator to animate the weights with respect to the deformed mesh, instead with this method they can animate them with respect to the un-deformed mesh. No surprise here, this method is called FX_WEIGHT_COMBO().
Now it's time to put weights to work. The first thing we'll need to do is tell messiah we want to use weights for a displacement, and which weight tool to use to calculate those weights. By telling messiah that we want to use weights we are also telling it which method of calculation to use (you can always allow the user to select the method through your Effect Module's interface).
Example:

FXobject weightTool, target;

// ...

// start point enumeration, this time using weights
fxDisplaceScan( target, weightTool, FX_WEIGHT_SETUP, &MyDisplaceScan, &time, FX_NOFLAG );
Now that we've told messiah that we want weight information, we need to do something with it in our disp_func(). The value that messiah computes for the weight of the point is stored in weight of the FX_DisplacePoint sturcture. But before we use that weight we'll need to verifiy that weights are supposed to be used (this is especially important if your Effect Module allows the user to turn weights on or off). The flags element of the FX_DisplacePoint structure will tell us if weights are supposed to be used by checking if the FX_DISPFLAG_WEIGHT() bit is set. If that bit is set then we can safely use the weight value in the structure, otherwise we should ignore it.
Example:

FX_DISPLACESCAN( MyDisplaceScan, FXdouble, now )
{
        // copy the current position to the new position
        FX_VEC3COPY( p->new_pos, p->cur_pos );

        // same boring affect as before
        p->new_pos[ypos] += *now;
        
        // if weights are to be used, then scale the displacement by the weight
        if( p->flags & FX_DISPFLAG_WEIGHT )
                FX_VEC3SCALE1( p->new_pos, p->weight );

        return FX_TRUE;
}


© 2003 pmG WorldWide, LLC.


www.projectmessiah.com

groups.yahoo.com/pmGmessiah

Last Updated on Thu Jul 10 04:49:36 2003