Hi,
First, some background info :
- Morph node == smooth interpolation between 2 or more shapes sharing the same appearance
- OBJ : a static (non animated) format
- Morph + OBJ == smoothed KeyFrame animation (every OBJ beeing a Key position)
As I’ve been privately asked to explain the animation processus used in Jack Flowers, here it is :
-
step 1 : create a “key frame” setup, for example, 6 to 8 poses for a character walking, load each pose as BranchGroup with the OBJLoader
-
step 2 : use a Switch node to group these poses, the Switch will only display one pose at a time, which will help us for calculating the animation timing sequence :
-
step 3 : calculate an appropriate timing sequence, and test it with the Switch node. for example, if the poses are time-linear, just test how much time has passed between the begining of the animation and the current frame, and find which node corresponds
float sumTime;
public void step(float deltaTime){
this.sumTime += deltaTime;
}
float singleFrameDuration = 0.2; // in seconds
public int getAnimationSequence(){
return (int) (sumTime / singleFrameDuration); // this has to be tweaked to find the appropriate timing (I use an enhanced version based on a array of timings)
}
- step 4 : convert the Switch node to a Morph node ! Add the same poses to this node.
The morph node needs :
-an array of GeometryArray (corresponding to every pose)
-an appearance (should be the same for every pose)
(a recursive/tree inspection of the BranchGroup looking for a Shape3D was enough for me to find the geometry and appearance)
once the MorphNode is loaded, the last part is - for each frame - to select the two poses surrounding the current position, and the amount of these required.
public float getAnimationSequence(){
return (sumTime / singleFrameDuration); // now we return a float value
}
The morph node uses an array of weights for every pose, a zero weight for a pose makes it ignored (an optimization to avoid a lot of multiplies by 0)
So, the last part is to update this array of weights, with zeroes for every weight except for the two poses surrounding the current animation morph. These two remaining values can be easily computed from the float returned by getAnimationSequence :
float pose = getAnimationSequence();
int pose1 = (int) pose;
int pose2 = (int) (pose +1);
float pose1Weight = 1- (pose-pose1);
float pose2Weight = 1- (pose1Weight);
weights[pose1] = pose1Weight;
weights[pose2] = pose2Weight;
// zero the other weights ....
// and update the morph node
morphNode.setWeights(weights);
Of course, you have to take special care of loops (from last pose to first one) which is not shown here, and of course encapsulate everything into a MorphedAnimation object to avoid thinking of it again next time you’ll need an animation…
I hope some people here will find this information useful !
Lilian