PDA

View Full Version : Key frame animation exporter


Vacrial
04-16-2009, 10:28 AM
Hi all,

Iím writing a Xna engine. I had wrote an exporter for maya to create a custom format for my 3D models. It exports skins and its bone matrices at each keyframe. But when i try to render a mesh with Xna i only get a crazy warped mesh.

I am using Robert Bateman tutorials for accessing Transform Data :

http://nccastaff.bournemouth.ac.uk/jmacey/RobTheBloke/www/research/maya/mfntransform.htm

but i don't understand how i can compute theses matrix together (JointOrient, Rotate, Translate, Scale and Parent Scale) and concatenate each bone matrix with its parent bone matrix in order to get final vertex position with the formula :

FinalVertex = (VecTransform(Vertex, Matrix1) * Weight1) + (VecTransform(Vertex, Matrix2) * Weight2)...

thx you for your help

Valentina

Robert Bateman
04-16-2009, 01:22 PM
but i don't understand how i can compute theses matrix together (JointOrient, Rotate, Translate, Scale and Parent Scale) and concatenate each bone matrix with its parent bone matrix in order to get final vertex position with the formula :

FinalVertex = (VecTransform(Vertex, Matrix1) * Weight1) + (VecTransform(Vertex, Matrix2) * Weight2)...


// world space matrix for the mesh in the bind pose.
// get using MDagPath::inclusiveMatrix() (on the meshes transform!)
Matrix meshBindPoseWorld;

// the world space inverse of the joint in the bind pose.
// call the mel command gotoBindPose(), extract the matrix from the joint using
// MDagPath::inclusiveMatrixInverse()
Matrix inverseBindPoseWorld;

// the current world space joint transform. This is what you'll need to compute
// in your engine.
// You can get it in maya using MDagPath::inclusiveMatrix
Matrix currentWorld;


When you have those 3 matrices, you can combine them like so:

Matrix final = currentWorld * inverseBindPoseWorld * meshBindPoseWorld

Do that for all bones and you have the matrices you need. To explain what that does:

* First you transform the local space vertices by the matrix meshBindPoseWorld. This transforms the mesh data into world space.
* The multiplication by inverseBindPoseWorld converts the world space mesh data into data that is local to the joint (in it's bind pose).
* The multiplcation by currentWorld puts the data

The only confusing part is how you actually get the animation data to be able to compute the currentWorld matrices for all of the joints. To do this, you first need to compute the local space matrices for the joints using the translate/rotate/scale data (+ any pivots, orients or shearing values you choose to support). There are basically 3 ways to get the key frame data to compute the local space TM's:

1. extract the values from MFnTransform.
2. export the animation curves + all transform info.
3. decompose the transform matrices manually (i.e. get values from MDagPath::inclusiveMatrix).

1 has some advantages, but can be overly cumbersome. If you go this route, you'll need to have a look at the docs for MFnTransform and MFnIkJoint to copy the matrix computation (i.e. how to handle the pivots, orients etc). You can to some extent ignore some of those terms (as my notes do) since it's only really joint orients and the occasional pivot you'll typically see.

2. Is somewhat error prone, since transforms may be animated by constraints/expressions etc. This may lead to a few problems since you will not have a full complement of F-Curves available, so you'll also need to replicate the constraints and IK systems of maya. This is normally why sampling each key ends up being fairly useful.

3. This is normally the simplest method since it's fairly trivial to get local space matrices from the dagpaths, and by doing the decomposition yourself you remove any of the complex terms (i.e. pivots etc). The matrixDecomp plug-in that ships with maya can be extremely useful here. It's fairly easy to do:


// some psudeo code for the export process
void translator::writer()
{
// store every change in this
MDgModifier mod;

// create a matrix decomp node for each joint to extract.

// connect each local matrix attribute of the joint to the matrixDecom nodes.

// create and connect nodes
mod.doIt();

for( time =0; time<=maxTime;++time)
{
currentTime time;

// extract local space TRS data from the matrix decomp nodes
}

// undo scene changes
mod.undoIt();
}

Vacrial
04-16-2009, 03:16 PM
thank you so much to take all this time to help me.

It's horreful : you are very very smart. I am on this problem since one month, working night and day with no hope and you, in few minutes you seams to be able to do that easily.


I you come one day in russia or france i will kiss you !!

Before trying, some others questions :)

First i have to extract meshBindPoseWorld

i will do that like this :


MGlobal::commandExecute("gotoBindPose();");

for (MItDag dagIt(MItDag::kDepthFirst, MFn::kMesh, &status);
!dagIt.isDone();
dagIt.next())
{
MDagPath currentMayaMeshPath;
if (mesh.isIntermediateObject())
continue;
meshBindPoseWorld = currentMayaMeshPath.inclusiveMatrix();;
}


After this i extract skeleton and your inverseBindPoseWorld


JointNode
SkeletonExtractor::extractSkeletonHierarchy(const MDagPath jointPath,
uTriangle2 depth) const
{
//object were i store data
JointNode currentJointNode;
//here your matrix Robert
currentJointNode.inverseBindPoseWorld = jointPath.inclusiveMatrixInverse();
for (unsigned int i = 0; i < jointPath.childCount(); ++i)
{
if (jointPath.child(i).hasFn(MFn::kJoint))
{
MDagPath childPath;
childPath.set(jointPath);
childPath.push(jointPath.child(i));
//recursive call
JointNode childJointNode = extractSkeletonHierarchy(childPath,
depth + 1);
currentJointNode.children.push_back(childJointNode);
}
}
return currentJointNode;
}


To finish on currentWorld . For that, i can extract local space matrice from dag path (third option) i can use your method explained here : http://www.highend2d.com/boards/lofiversion/index.php/t216498.html ?
But i will see the The matrixDecomp plug-in.


You think this is a good way ? Because before that, i try to do the same way as some existing exporter : cvxexporter, your m2 exporter, floriant cgexporter and no one use the same methods :(


At the end, in my C# code my formula will be :

FinalVertex = (VecTransform(Vertex, Matrix final 1) * Weight1) + (VecTransform(Vertex, Matrix final 2) * Weight2)...

?

thank you so much once again. That 's good to see that there some people like you can can help.

Robert Bateman
04-16-2009, 05:00 PM
For that, i can extract local space matrice from dag path (third option) i can use your method explained here : http://www.highend2d.com/boards/lof...hp/t216498.html (http://www.highend2d.com/boards/lofiversion/index.php/t216498.html) ?

Use the node. The method i posted there has a bug in it (though will work for 99% of situations).


You think this is a good way ? Because before that, i try to do the same way as some existing exporter : cvxexporter, your m2 exporter, floriant cgexporter and no one use the same methods :(

It looks fine. Test it though. The reason everyone does it differently is because it keeps changing. gotoBindPose() is the best bet (they changed how bind poses work in maya 2008 yet again).


At the end, in my C# code my formula will be :

FinalVertex = (VecTransform(Vertex, Matrix final 1) * Weight1) + (VecTransform(Vertex, Matrix final 2) * Weight2)...

That's fine too.

Vacrial
04-17-2009, 06:49 AM
i am implementing your system and i have two question

witch value i have to take to extract skeleton ? (i want my format to be "skeleton animable" and "keyframe animable").

my models can have more than one mesh. And animations matrix can affect one or more meshes, so in the formula :

Matrix final = currentWorld * inverseBindPoseWorld * meshBindPoseWorld

how can i use a "contextual" meshBindPoseWorld ?

thank you

Robert Bateman
04-17-2009, 10:06 AM
your engine will compute worldLocal. You'll need to compute the rest when applying the skinning to each mesh. i.e. Have a 2nd array of matrices just used for skinning.

Vacrial
04-17-2009, 10:28 AM
Sorry but i don't understand.
My engine will only compute the formula

FinalVertex = (VecTransform(Vertex, Matrix final 1) * Weight1) + (VecTransform(Vertex, Matrix final 2) * Weight2)...

where "Matrix final {0-n}" is extracted from the serialized model file.

So, if i have a model composed by a lot of mesh. And a skinning that apply on all this mesh. How can i apply this formula :

Matrix final = currentWorld * inverseBindPoseWorld * meshBindPoseWorld

because the Matrix Final is used to compute vertices used by more than one mesh.
For information my format contains a lot of files zipped together.
Each animation is specified inside a xml file :


<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<Storyboard>
<MatrixAnimation Target="joint1" TargetProperty="Matrix" KeyFrameNumber="10" Duration="22">
<MatrixKeyFrame KeyTime="1" Scale="1,1,1" Translate="0,0,0" Rotate="0,0,0,0"/>
<MatrixKeyFrame KeyTime="5" Scale="1,3,1" Translate="0,0,0" Rotate="0,0,0,0"/>
<MatrixKeyFrame KeyTime="6" Scale="1,1,1" Translate="0,0,0" Rotate="0,0,0,0"/>
<MatrixKeyFrame KeyTime="10" Scale="3,1,1" Translate="0,0,0" Rotate="0,0,0,0"/>
<MatrixKeyFrame KeyTime="11" Scale="1,1,1" Translate="0,0,0" Rotate="0,0,0,0"/>
<MatrixKeyFrame KeyTime="15" Scale="1,1,3" Translate="0,0,0" Rotate="0,0,0,0"/>
<MatrixKeyFrame KeyTime="16" Scale="1,1,1" Translate="0,0,0" Rotate="0,0,0,0"/>
<MatrixKeyFrame KeyTime="18" Scale="1,3,1" Translate="0,0,0" Rotate="0,0,0,0"/>
<MatrixKeyFrame KeyTime="20" Scale="3,3,1" Translate="0,0,0" Rotate="0,0,0,0"/>
<MatrixKeyFrame KeyTime="22" Scale="3,3,3" Translate="0,0,0" Rotate="0,0,0,0"/>
</MatrixAnimation>
<MatrixAnimation Target="joint2" TargetProperty="Matrix" KeyFrameNumber="10" Duration="22">
<MatrixKeyFrame KeyTime="1" Scale="1,1,1" Translate="0,4.643963,0" Rotate="0,0,0,0"/>
<MatrixKeyFrame KeyTime="5" Scale="1,0.333333,1" Translate="0,4.643963,0" Rotate="0,0,0,0"/>
<MatrixKeyFrame KeyTime="6" Scale="1,1,1" Translate="0,4.643963,0" Rotate="0,0,0,0"/>
<MatrixKeyFrame KeyTime="10" Scale="0.333333,1,1" Translate="0,13.931889,0" Rotate="0,0,0,0"/>
<MatrixKeyFrame KeyTime="11" Scale="1,1,1" Translate="0,4.643963,0" Rotate="0,0,0,0"/>
<MatrixKeyFrame KeyTime="15" Scale="1,1,0.333333" Translate="0,4.643963,0" Rotate="0,0,0,0"/>
<MatrixKeyFrame KeyTime="16" Scale="1,1,1" Translate="0,4.643963,0" Rotate="0,0,0,0"/>
<MatrixKeyFrame KeyTime="18" Scale="1,0.333333,1" Translate="0,4.643963,0" Rotate="0,0,0,0"/>
<MatrixKeyFrame KeyTime="20" Scale="0.333333,0.333333,1" Translate="0,13.931889,0" Rotate="0,0,0,0"/>
<MatrixKeyFrame KeyTime="22" Scale="0.333333,0.333333,0.333333" Translate="0,13.931889,0" Rotate="0,0,0,0"/>
</MatrixAnimation>
</Storyboard>


Instead of "Scale", "Translate", "Rotate" attribute it can be "Value" with a serialized matrix.
Here my Joint1 can manipulate more than one mesh no ? But "Scale", "Translate", "Rotate" have been computed from only one meshBindPoseWorld.

Robert Bateman
04-17-2009, 11:01 AM
Instead of "Scale", "Translate", "Rotate" attribute it can be "Value" with a serialized matrix.
Here my Joint1 can manipulate more than one mesh no ? But "Scale", "Translate", "Rotate" have been computed from only one meshBindPoseWorld.

there will be a meshBindPoseWorld for each mesh. You must calculate the skinning matrices in this equation

FinalVertex = (VecTransform(Vertex, Matrix final 1) * Weight1) + (VecTransform(Vertex, Matrix final 2) * Weight2)...
....for each mesh.

Vacrial
04-17-2009, 01:16 PM
The only thing i am affraid of is that if a vertex is shared by n meshes in my model

it will compute n times :

FinalVertex = (VecTransform(Vertex, Matrix final 1) * Weight1) + (VecTransform(Vertex, Matrix final 2) * Weight2)...


and The FinalVertex value will depends on the last mesh used ?

i hope it will not make some crazy form.

How can i know witch meshes a bone depends on ?

thx you

Vacrial
04-17-2009, 02:15 PM
i am stupid, i use it before

i guess it's


mayaSkinCluster.indexForOutputShape(mayaOutputMeshPath.node(),
&status);

Vacrial
04-27-2009, 08:55 AM
no sucess:(

this is today the second month i am working on it night and day.
I am so sade to have no little sucess.
So desesperate :(

I am sure i am close to the solution but the render failed
All my projects are stopped by this problem

Transform matrix seams to export well, i see good transformation keyframe with good scale, translate, rotate values

but in the render only the translation transformation seems to work. I am working on a rectangular shape with 4 verticals bones.

When i try scale, or rotate it is a disaster. I think this is a hierarchical joint computing problem but i am not good enough to understand the problem.

The matrix gived to the shader are computed like this :


this.BonesMatrix[bone.Id] = -bone.Pivot * (bone.Parent != null ? this.BonesMatrix[bone.Parent.Id] : Matrix.Identity) * bone.Matrix;



The thing named Pivot is the Robert's inverseBindPoseWorld * meshBindPoseWorld
the thing named Matrix is the scale * rotate * translate extracted from the current keyframe (interpolated). The BonesMatrix array is sended to the shader.

And the ouput position is computed like this :
tv = mul(input.Position, Bones[input.BoneIndices.x]);

v += mul(tv, input.BoneWeights.x);

tv = mul(input.Position, Bones[input.BoneIndices.y]);

v .BoneIndices.w]);

v += mul(tv, input.BoneWeights.w);



// Skin the vertex position.+= mul(tv, input.BoneWeights.y);

tv = mul(input.Position, Bones[input.BoneIndices.z]);

v += mul(tv, input.BoneWeights.z);

tv = mul(input.Position, Bones[input



output.Position = mul(mul(mul(v, World), View), Projection);



Is someone see the big mistake i make ? i am sure that it is a very litle (and stupid) error but there are so possibilities that i can't found one ...

if I find, the solution, I swear to create a complete tutorial to prevent other persons to lose several months of their life

CGTalk Moderation
04-27-2009, 08:55 AM
This thread has been automatically closed as it remained inactive for 12 months. If you wish to continue the discussion, please create a new thread in the appropriate forum.