skeletal conversion now writes out joints and joint keyframes
This commit is contained in:
parent
a0513ab83c
commit
f5da1d16b2
|
@ -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" />
|
||||
|
|
|
@ -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>
|
|
@ -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
|
||||
|
|
26
AssimpToMesh/src/convert/meshjoint.h
Normal file
26
AssimpToMesh/src/convert/meshjoint.h
Normal 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
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Reference in a new issue