From f5da1d16b24dbb36bc826c3324e618049197eac1 Mon Sep 17 00:00:00 2001 From: gered Date: Thu, 28 Apr 2011 23:22:13 -0400 Subject: [PATCH] skeletal conversion now writes out joints and joint keyframes --- AssimpToMesh/AssimpToMesh.vcxproj | 1 + AssimpToMesh/AssimpToMesh.vcxproj.filters | 3 + AssimpToMesh/src/convert/mesh.h | 4 + AssimpToMesh/src/convert/meshjoint.h | 26 +++++ AssimpToMesh/src/convert/meshutils.cpp | 102 ++++++++++++++++++ AssimpToMesh/src/convert/skeletalanimated.cpp | 87 +++++++++++---- 6 files changed, 203 insertions(+), 20 deletions(-) create mode 100644 AssimpToMesh/src/convert/meshjoint.h diff --git a/AssimpToMesh/AssimpToMesh.vcxproj b/AssimpToMesh/AssimpToMesh.vcxproj index 2c7977b..a0566f3 100644 --- a/AssimpToMesh/AssimpToMesh.vcxproj +++ b/AssimpToMesh/AssimpToMesh.vcxproj @@ -94,6 +94,7 @@ + diff --git a/AssimpToMesh/AssimpToMesh.vcxproj.filters b/AssimpToMesh/AssimpToMesh.vcxproj.filters index 7a4b7e3..ace3169 100644 --- a/AssimpToMesh/AssimpToMesh.vcxproj.filters +++ b/AssimpToMesh/AssimpToMesh.vcxproj.filters @@ -68,5 +68,8 @@ Header Files + + Header Files + \ No newline at end of file diff --git a/AssimpToMesh/src/convert/mesh.h b/AssimpToMesh/src/convert/mesh.h index 6778e38..a536fe5 100644 --- a/AssimpToMesh/src/convert/mesh.h +++ b/AssimpToMesh/src/convert/mesh.h @@ -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 &materials, FILE *fp); void WriteTriangles(const std::vector &triangles, FILE *fp); void WriteSubMeshes(const std::vector &subMeshes, FILE *fp); +void WriteJoints(const std::vector &joints, FILE *fp); +void WriteJointToVertexMap(const std::vector &vertexToJointMap, FILE *fp); +void WriteJointKeyFrames(const std::vector &jointKeyFrames, FILE *fp); #endif diff --git a/AssimpToMesh/src/convert/meshjoint.h b/AssimpToMesh/src/convert/meshjoint.h new file mode 100644 index 0000000..194ca82 --- /dev/null +++ b/AssimpToMesh/src/convert/meshjoint.h @@ -0,0 +1,26 @@ +#ifndef __CONVERT_MESHJOINT_H_INCLUDED__ +#define __CONVERT_MESHJOINT_H_INCLUDED__ + +#include +#include +#include +#include +#include + +struct MeshJoint +{ + std::string name; + std::string parentName; + aiVector3D position; + aiQuaternion rotation; +}; + +struct MeshJointKeyFrame +{ + aiVector3D position; + aiQuaternion rotation; +}; + +typedef std::vector JointKeyFrames; + +#endif diff --git a/AssimpToMesh/src/convert/meshutils.cpp b/AssimpToMesh/src/convert/meshutils.cpp index ceb75bb..f40717c 100644 --- a/AssimpToMesh/src/convert/meshutils.cpp +++ b/AssimpToMesh/src/convert/meshutils.cpp @@ -198,3 +198,105 @@ void WriteSubMeshes(const std::vector &subMeshes, FILE *fp) fwrite(&m->numTriangles, 4, 1, fp); } } + +void WriteJoints(const std::vector &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 &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, 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); + } + } +} diff --git a/AssimpToMesh/src/convert/skeletalanimated.cpp b/AssimpToMesh/src/convert/skeletalanimated.cpp index 1b31a2a..6a7b98d 100644 --- a/AssimpToMesh/src/convert/skeletalanimated.cpp +++ b/AssimpToMesh/src/convert/skeletalanimated.cpp @@ -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 joints; + std::vector 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 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); }