skeletal conversion now writes out joints and joint keyframes

This commit is contained in:
gered 2011-04-28 23:22:13 -04:00
parent a0513ab83c
commit f5da1d16b2
6 changed files with 203 additions and 20 deletions

View file

@ -94,6 +94,7 @@
<ClInclude Include="src\assimputils\utils.h" />
<ClInclude Include="src\convert\convert.h" />
<ClInclude Include="src\convert\mesh.h" />
<ClInclude Include="src\convert\meshjoint.h" />
<ClInclude Include="src\convert\meshmaterial.h" />
<ClInclude Include="src\convert\meshtriangle.h" />
<ClInclude Include="src\convert\submesh.h" />

View file

@ -68,5 +68,8 @@
<ClInclude Include="src\convert\submesh.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\convert\meshjoint.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>

View file

@ -8,6 +8,7 @@
#include "meshmaterial.h"
#include "meshtriangle.h"
#include "submesh.h"
#include "meshjoint.h"
void WriteMeshHeader(FILE *fp);
void WriteVertices(const AssimpVertices &vertices, FILE *fp);
@ -16,5 +17,8 @@ void WriteTexCoords(const AssimpVertices &texCoords, FILE *fp);
void WriteMaterials(const std::vector<MeshMaterial> &materials, FILE *fp);
void WriteTriangles(const std::vector<MeshTriangle> &triangles, FILE *fp);
void WriteSubMeshes(const std::vector<SubMesh> &subMeshes, FILE *fp);
void WriteJoints(const std::vector<MeshJoint> &joints, FILE *fp);
void WriteJointToVertexMap(const std::vector<uint32_t> &vertexToJointMap, FILE *fp);
void WriteJointKeyFrames(const std::vector<JointKeyFrames> &jointKeyFrames, FILE *fp);
#endif

View file

@ -0,0 +1,26 @@
#ifndef __CONVERT_MESHJOINT_H_INCLUDED__
#define __CONVERT_MESHJOINT_H_INCLUDED__
#include <stdint.h>
#include <string>
#include <aiVector3D.h>
#include <aiQuaternion.h>
#include <vector>
struct MeshJoint
{
std::string name;
std::string parentName;
aiVector3D position;
aiQuaternion rotation;
};
struct MeshJointKeyFrame
{
aiVector3D position;
aiQuaternion rotation;
};
typedef std::vector<MeshJointKeyFrame> JointKeyFrames;
#endif

View file

@ -198,3 +198,105 @@ void WriteSubMeshes(const std::vector<SubMesh> &subMeshes, FILE *fp)
fwrite(&m->numTriangles, 4, 1, fp);
}
}
void WriteJoints(const std::vector<MeshJoint> &joints, FILE *fp)
{
uint32_t count = joints.size();
if (count == 0)
return;
uint32_t size = 4 + // count
(
(sizeof(float) * 3) + // position (x, y, z)
(sizeof(float) * 4) // rotation (x, y, z, w)
) * count;
// add up all the variable length joint names and parent joint names
for (uint32_t i = 0; i < count; ++i)
{
size += joints[i].name.length() + 1; // include null terminator
size += joints[i].name.length() + 1; // ditto
}
fputs("JNT", fp);
fwrite(&size, 4, 1, fp);
fwrite(&count, 4, 1, fp);
for (uint32_t i = 0; i < count; ++i)
{
const MeshJoint *j = &joints[i];
fwrite(j->name.c_str(), j->name.length(), 1, fp);
char c = '\0';
fwrite(&c, 1, 1, fp);
// haven't tried, but i have a feeling fwrite() won't like passing a length of 0 to be written
if (j->parentName.length() > 0)
fwrite(j->parentName.c_str(), j->parentName.length(), 1, fp);
fwrite(&c, 1, 1, fp);
fwrite(&j->position.x, sizeof(float), 1, fp);
fwrite(&j->position.y, sizeof(float), 1, fp);
fwrite(&j->position.z, sizeof(float), 1, fp);
fwrite(&j->rotation.x, sizeof(float), 1, fp);
fwrite(&j->rotation.y, sizeof(float), 1, fp);
fwrite(&j->rotation.z, sizeof(float), 1, fp);
fwrite(&j->rotation.w, sizeof(float), 1, fp);
}
}
void WriteJointToVertexMap(const std::vector<uint32_t> &vertexToJointMap, FILE *fp)
{
uint32_t count = vertexToJointMap.size();
if (count == 0)
return;
uint32_t size = 4 + // count
(4 * count); // num vertices
fputs("JTV", fp);
fwrite(&size, 4, 1, fp);
fwrite(&count, 4, 1, fp);
for (uint32_t i = 0; i < count; ++i)
{
uint32_t jointIndex = vertexToJointMap[i];
fwrite(&jointIndex, 4, 1, fp);
}
}
void WriteJointKeyFrames(const std::vector<JointKeyFrames> &jointKeyFrames, FILE *fp)
{
uint32_t numJoints = jointKeyFrames.size();
if (numJoints == 0)
return;
uint32_t numFrames = jointKeyFrames[0].size();
uint32_t size = 4 + // num frames
(
(
(sizeof(float) * 3) + // position (x, y, z)
(sizeof(float) * 4) // rotation (x, y, z, w)
) * numJoints // for each joint
) * numFrames; // for each frame
fputs("JKF", fp);
fwrite(&size, 4, 1, fp);
fwrite(&numFrames, 4, 1, fp);
for (uint32_t i = 0; i < numJoints; ++i)
{
for (uint32_t j = 0; j < numFrames; ++j)
{
const MeshJointKeyFrame *frame = &jointKeyFrames[i][j];
fwrite(&frame->position.x, sizeof(float), 1, fp);
fwrite(&frame->position.y, sizeof(float), 1, fp);
fwrite(&frame->position.z, sizeof(float), 1, fp);
fwrite(&frame->rotation.x, sizeof(float), 1, fp);
fwrite(&frame->rotation.y, sizeof(float), 1, fp);
fwrite(&frame->rotation.z, sizeof(float), 1, fp);
fwrite(&frame->rotation.w, sizeof(float), 1, fp);
}
}
}

View file

@ -69,29 +69,76 @@ void ConvertSkeletalAnimated(const std::string &outfile, const aiScene *scene)
}
WriteSubMeshes(subMeshes, fp);
// joints
// foreach joint:
// - name
// - parentindex (or name?)
// - position (vec3d)
// - rotation (vec3d or quat?)
// TODO: cheating for now, but according to the ASSIMP doc's, there could be
// models which have nodes that are not used by any bones, but still
// affect the skeleton pose, because child nodes *are* used by bones
// and so, any parent node transformations will apply and need to be
// carried down the chain. None of the test models I'm using have this
// type of node structure, so I'm ignoring it for now :)
// joints to vertices mapping
// foreach vertex:
// - jointindex
// - weight
// collect basic skeleton/joint info
std::vector<MeshJoint> joints;
std::vector<uint32_t> vertexToJointMap;
vertexToJointMap.resize(vertices.size());
for (unsigned int i = 0; i < scene->mNumMeshes; ++i)
{
aiMesh *mesh = scene->mMeshes[i];
for (unsigned int j = 0; j < mesh->mNumBones; ++j)
{
aiBone *bone = mesh->mBones[j];
aiNode *node = scene->mRootNode->FindNode(bone->mName);
aiNode *parentNode = node->mParent;
// joint keyframes
// foreach keyframe:
// foreach joint:
// - position (vec3d)
// - rotation (vec3d or quat?)
MeshJoint joint;
joint.name = std::string(bone->mName.data, bone->mName.length);
if (strcmp(parentNode->mName.data, "$dummy_root") == 0)
joint.parentName = "";
else
joint.parentName = std::string(parentNode->mName.data, parentNode->mName.length);
node->mTransformation.DecomposeNoScaling(joint.rotation, joint.position);
// animation sequences
// foreach sequence:
// - name
// - start frame index
// - end frame index
// assumption: sum of all bone->mNumWeights is always == vertices.size()
for (unsigned int k = 0; k < bone->mNumWeights; ++k)
{
aiVertexWeight *weight = &bone->mWeights[k];
unsigned int index = vertexIndicesMap[i][weight->mVertexId];
vertexToJointMap[index] = j;
}
joints.push_back(joint);
}
}
WriteJoints(joints, fp);
WriteJointToVertexMap(vertexToJointMap, fp);
// TODO: this collection process is dumb at the moment. assumes each bone
// has the same number of frames as all the other ones. also, every
// test model i've used so far puts all the frames into a single
// animation (usually without a name even). so this is, for now, only
// looking at the first aiAnimation object. ALSO, because i'm REALLY
// lazy, the animations aiNodeAnim objects are assumed to be in the
// same order as the bones were collected in :)
// collect joint keyframes
std::vector<JointKeyFrames> meshFrames;
aiAnimation *animation = scene->mAnimations[0];
for (unsigned int i = 0; i < animation->mNumChannels; ++i)
{
aiNodeAnim *nodeAnim = animation->mChannels[i];
JointKeyFrames jointFrames;
for (unsigned int j = 0; j < nodeAnim->mNumPositionKeys; ++j)
{
// TODO: should really get the times too
MeshJointKeyFrame frame;
frame.position = nodeAnim->mPositionKeys[j].mValue;
frame.rotation = nodeAnim->mRotationKeys[j].mValue;
jointFrames.push_back(frame);
}
meshFrames.push_back(jointFrames);
}
WriteJointKeyFrames(meshFrames, fp);
fclose(fp);
}