PDA

View Full Version : API C++ - Custom Animation exporter


Vacrial
04-08-2009, 05:13 PM
Hi Everybody,
First of all sorry for my poor english.
I'd created a custom file format for my 3D model. Based uppon Xml files stored in a zip.
I have no problem to export vertices, uv, normals. But i have a big problem with animation.
I am desesperate.

Here is how i extract bones :


JointNode
SkeletonExtractor::extractSkeletonHierarchy(const MDagPath jointPath,
uint32 depth) const
{
for (uint32 i = 0; i < depth; ++i)
cout << " ";
cout << jointPath.partialPathName().asChar() << endl;
JointNode currentJointNode;
currentJointNode.name = jointPath.partialPathName().asChar();
MFnTransform transform(jointPath);
currentJointNode.localTransform = transform.transformation().asMatrix();
currentJointNode.worldTransform = jointPath.inclusiveMatrix();
for (uint32 i = 0; i < jointPath.childCount(); ++i)
{
if (jointPath.child(i).hasFn(MFn::kJoint))
{
MDagPath childPath;
childPath.set(jointPath);
childPath.push(jointPath.child(i));
if (!shouldJointBeExtracted(childPath))
continue; // prune tree
JointNode childJointNode = extractSkeletonHierarchy(childPath,
depth + 1);
currentJointNode.children.push_back(childJointNode);
}
else
{
for (uint32 j = 0; j <= depth; ++j)
cout << " ";
cout << "unusual joint-child "
<< jointPath.partialPathName().asChar()
<< ".child(" << i << ") ignored" << std::endl;
}
}
return currentJointNode;
}
// extracts the skeleton. Note, that there might be several root-nodes.
// furthermore: if there are references (two joints pointing to the same
// node), they will be "separated" now (ie. there will be 2 different
// entries).
Skeleton
SkeletonExtractor::ExtractSkeleton() const
{
cout << std::endl << " Extracting Skeleton" << std::endl;
Skeleton skeleton;
for (MItDag dagIt(MItDag::kDepthFirst, MFn::kJoint, &status);
!dagIt.isDone();
dagIt.next())
{
MDagPath jointPath;
dagIt.getPath(jointPath);
if (shouldJointBeExtracted(jointPath))
skeleton.rootNodes.push_back(
extractSkeletonHierarchy(jointPath, 2));
// children have already been treated by extractSkeletonHierarchy,
// or are intentionally ignored
dagIt.prune();
}
return skeleton;
}


Here is how i extract skinning data :


SkinningData
MeshExtractor::extractSkinningData(const MDagPath mayaOutputMeshPath) const
{
MFnMesh mayaOutputMesh(mayaOutputMeshPath, &status);
// first find skinclusters.
// if any of those skinclusters influence our mesh, extract the weights
for (MItDependencyNodes depNodeIt(MFn::kSkinClusterFilter);
!depNodeIt.isDone();
depNodeIt.next())
{
MObject depNodeObject = depNodeIt.item();
MFnSkinCluster mayaSkinCluster(depNodeObject, &status);
CheckStatus("couldn't acces skinCluster");
// normally we should cycle through all outputConnections,
// and look, if it's our mesh.
// the following line is just shorter
uint32 shapeIndex =
mayaSkinCluster.indexForOutputShape(mayaOutputMeshPath.node(),
&status);
if (!status)
continue; // our mesh is not an output for this skincluster
// note, that we only get here once (there's a "return"
// at the end of the scope).
cout << " skincluster found" << std::endl;
SkinningData skinningData;
// - get the names of the influence-objects (joints)
MDagPathArray influenceObjectPaths;
mayaSkinCluster.influenceObjects(influenceObjectPaths, &status);
CheckStatus("couldn't get influenceObjects");
for (uint32 i = 0; i < influenceObjectPaths.length(); ++i)
{
cout << "merde " << influenceObjectPaths[i].partialPathName().asChar() << "\r\n";
skinningData.jointNames.push_back(
influenceObjectPaths[i].partialPathName().asChar());
}
// - get the influences

MObject mayaInputObject =
mayaSkinCluster.inputShapeAtIndex(shapeIndex, &status);
// numVertices() should be the same for input and output shape.
skinningData.influences.resize(mayaOutputMesh.numVertices());
// iterate over all points (= components in Maya) and get the
// influences (= [boneIndex, weight])
uint32 pointCounter = 0;
for (MItGeometry geometryIt(mayaInputObject);
!geometryIt.isDone();
geometryIt.next(), ++pointCounter)
{
MObject mayaComponent = geometryIt.component();
MFloatArray mayaWeightArray;
uint32 numInfluences; // is going to be discarded
mayaSkinCluster.getWeights(mayaOutputMeshPath,
mayaComponent,
mayaWeightArray,
numInfluences);
// put them into a more convenient format
for (uint32 j = 0; j < mayaWeightArray.length(); ++j)
{
// I know: we are working with floats, but Maya has a
// function to eliminate weights, that are too small
if (mayaWeightArray[j] != 0)
{
Influence influence;
influence.boneIndex = j;
influence.weight = mayaWeightArray[j];
skinningData.influences[pointCounter]
.push_back(influence);
}
}
}
return skinningData;
}
// no skinCluster for this mesh
// a virgin SkinningData represents perfectly an un-influenced mesh.
return SkinningData();
} // extractSkinningData


And here is how i extract animation :


Animation
AnimationExtractor::ExtractAnimation() const
{
const MTime initialTime = MAnimControl::currentTime();
cout << std::endl << " Extracting Animation\r\n";
Animation result;
result.fps = getFps();
std::vector<MDagPath> animatedJointPaths = getAnimatedJoints();

if (animatedJointPaths.size() == 0)
return result;
MTime min;
min.setValue(100000);
MTime max;
max.setValue(-1);
for (uint32 i = 0; i < animatedJointPaths.size(); ++i)
{
Track frames;
MDagPath jointPath = animatedJointPaths[i];
cout << "Joint : " << jointPath.partialPathName().asChar() << "\r\n";

std::set<MTime> jointTimes = extractKeyFrameTimes(jointPath);

//save empty transformation frames.
for (std::set<MTime>::const_iterator it = jointTimes.begin();
it != jointTimes.end();
++it)
{
cout << "" << (*it).value() << "\r\n";
KeyFrame frame;
frame.time = (*it);
if (frame.time < min)
min = frame.time;
if (frame.time > max)
max = frame.time;
MAnimControl::setCurrentTime(frame.time);
MFnTransform transform(jointPath);
frame.transformation = transform.transformation().asMatrix();
frames.push_back(frame);
}
result.tracks[jointPath.partialPathName().asChar()] = frames;
}

result.FrameNumber = (max.value() - min.value())+1;
MAnimControl::setCurrentTime(initialTime);
cout << endl;
cout << " extracted " << result.tracks.size() << " tracks" << endl;
cout << " with " << result.FrameNumber
<< " key-times (might be incorrect: could be +1.)" << endl;
return result;
}


Is this the good way ?

I put my mmlexport visual studio 2008 express project here :
http://msmvps.com/cfs-file.ashx/__key/CommunityServer.Blogs.Components.WeblogFiles/valentin.Arcane.MML/MMLExporter.zip

and a sample of the MML file (Model Markup Language) here :
http://msmvps.com/cfs-file.ashx/__key/CommunityServer.Blogs.Components.WeblogFiles/valentin.Arcane.MML/testMesh.zip

thx you so much for the time you can take to help me.

Robert Bateman
04-09-2009, 10:43 AM
Is this the good way ?

There aren't too many ways of doing this in the API, so if it works, yes.

Vacrial
04-09-2009, 01:27 PM
i don't know if it works :(
The render i made on Xna is ugly.

the generated matrix seems to be good but i dont know what can i do with the two matrix i extract from the transform node for a bone :

transform.transformation().asMatrix();
and
jointPath.inclusiveMatrix();

And what can i do with the transform matrix for each key frame :

transform.transformation().asMatrix();

By "what can i do" i mean, witch matrix multiplication i have to make :

bone.CurrentMatrix = boneParent.CurrentMatrix * bone.WorldTransform * bone.InterpolateFrameMatrix ?

CGTalk Moderation
04-09-2009, 01:27 PM
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.