[XNA] Keyframe animation without skin

Started by
3 comments, last by Crow-knee 14 years, 8 months ago
Hi All, I have been trying to load a .x file that has been exported from Blender. It is a mob of blocks which have been keyframe animated - no skinning, no bones. Is there a way to load in the model and get at the animation data? I have found that when I load it with the standard XNA model loader, it lists a heap of bones, but these don't seem to relate to keyframe transformations. Am I going to have to bite the bullet and write my own pipeline loader stuff? I have been trying to avoid that... Any info would be greatly appreciated. Steele.
Advertisement
Doh. The lack of replies is not a good sign.
I guess I will have to delve into pipeline stuff.
Wish me luck!
If I understand your post correctly you are saying that your X file consists of a hierarchy of D3DXFRAMES and D3DXMESHCONTAINERS, with *NO* SkinWeights property underneath any of the "Mesh" frames? Additionally you have one or more AnimationSets with Animation {frameName} and some AnimationKeys (4x4 matrices, quat rotations, translations, etc).

The SDK gives an example of just such an X file (c.f. Animations, Working With) and shows how to load the keyframe animations to move the frames around. Of course if none of your frame's influence the frame that ultimately holds the mesh (i.e. the vertices), then "animating" them won't do you much good. In this example from the SDK the animation moves the CubeFrame frame along the X-axis in 9 time steps. Since the CubeFrame frame is hierarchically "over" the CubeMesh frame, animating the parent affects the child (the mesh itself).

Frame CubeFrame
{
FrameTransformMatrix
{
1.000000, 0.000000, 0.000000, 0.000000,
0.000000, 1.000000, 0.000000, 0.000000,
0.000000, 0.000000, 1.000000, 0.000000,
0.000000, 0.000000, 0.000000, 1.000000;;
}
{CubeMesh} // You could have the mesh inline, but this
// uses an object reference instead.
}

AnimationSet AnimationSet0
{
Animation Animation0
{
{CubeFrame} // Use the frame containing the cube.
AnimationKey
{
2; // Position keys
9; // 9 keys
10; 3; -100.000000, 0.000000, 0.000000;;,
20; 3; -75.000000, 0.000000, 0.000000;;,
30; 3; -50.000000, 0.000000, 0.000000;;,
40; 3; -25.500000, 0.000000, 0.000000;;,
50; 3; 0.000000, 0.000000, 0.000000;;,
60; 3; 25.500000, 0.000000, 0.000000;;,
70; 3; 50.000000, 0.000000, 0.000000;;,
80; 3; 75.500000, 0.000000, 0.000000;;,
90; 3; 100.000000, 0.000000, 0.000000;;;
}
}
}
So the way the code for loading this is done in C++ DirectX9.0 may or may not help you with XNA/managed directx.

The X file is a hierarchical scene graph. This is how *most* of them work. They have two types of nodes: frames (transformation matrix) and meshcontainers (geometry). After that usually a list of animation sets is found. Each animation set is a list of frames to be animated and animation keys with keyframe data and time values.

You overload the Microsoft provided allocation hierarchy and tell it how to load/allocate Frame nodes and Geometry nodes. Then you call D3DXLoadMeshHierarchyFromX() and the framework calls back to your overloaded frame/geometry allocator methods when it hits one of these node types while parsing the X file.

That same function also takes a reference to a D3DXANIMATIONCONTROLLER, which will get automatically filled in with the animation sets and keyframe data.

You might need to physically open the X file that blender generates and visually verify that *EVERY* frame name referenced in *ALL* animation sets exist under the hierarchical scene graph part of the file, otherwise the framework usually barfs in strange ways.

Once D3DXLoadMeshHierarchyFromX() completes without errors, if the animation controller is valid you can do things like AdvanceTime() on the controller.

Now if this doesn't seem to work if the mesh containers are missing skin info because the animation controller doesn't get setup correctly, what you might try is this:

1. Pass a null animation controller to D3DXLoadMeshHierarchyFromX(), to prevent the framework from going down the "skinned" mesh path.
2. Call D3DXCreateAnimationController() to make your own empty controller.
3. Call D3DXFrameRegisterNamedMatrices() with the pointer to the frame root of the scene hierarchy you previously loaded from step #1 and the empty animation controller. This should add all the frames from your HSG to the animation controller.
4. Call D3DXCreateKeyframedAnimationSet() to build an empty animation set for one of your animation sets in your X file. Provide the name of the animation set here, for example "Idle" or "Attack".
5. Read in all the Scale,Rotate,Translate keyframe data under each "Animation" block for this animation set from your X file into user-allocated arrays (time, value pairs organized by frameName being animated).
6. Call RegisterAnimationSRTKeys() on the empty animation set and provide references to all your user-allocated S,R,T data, along with the name of the frame to be animated.
7. Now register the created animation set to the animation controller by calling RegisterAnimationSet() on animation controller object with new KeyframedAnimationSet object as input.
8. Now I *think* the final trick is to indicate where you want the animation mixer to place it's output: For each matrix you registered above, maybe call AnimationController::RegisterAnimationOutput() and provide the name of each of your frames to be animated as well as the address of it's transformation matrix in the hierarchical scene graph? But maybe this was already done by step #3?

Well, I'm not really sure about "rolling" your own animation mixer using the D3DX api because I never tried it, but this stuff is my best guess here, maybe its better than nothing.
Thanks for the info Steve.
Since XNA is built on DX9, this might be a nicer approach (for my understanding anyway!). I will have a play with these functions and see what I can leverage.
I think part of the problem I am having is art-pipeline related to. Something just isn't converting properly.
Can anyone recommend a good Blender to .x (or fbx or some other easy loading XNA format)?

This topic is closed to new replies.

Advertisement