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\assimputils\utils.h" />
|
||||||
<ClInclude Include="src\convert\convert.h" />
|
<ClInclude Include="src\convert\convert.h" />
|
||||||
<ClInclude Include="src\convert\mesh.h" />
|
<ClInclude Include="src\convert\mesh.h" />
|
||||||
|
<ClInclude Include="src\convert\meshjoint.h" />
|
||||||
<ClInclude Include="src\convert\meshmaterial.h" />
|
<ClInclude Include="src\convert\meshmaterial.h" />
|
||||||
<ClInclude Include="src\convert\meshtriangle.h" />
|
<ClInclude Include="src\convert\meshtriangle.h" />
|
||||||
<ClInclude Include="src\convert\submesh.h" />
|
<ClInclude Include="src\convert\submesh.h" />
|
||||||
|
|
|
@ -68,5 +68,8 @@
|
||||||
<ClInclude Include="src\convert\submesh.h">
|
<ClInclude Include="src\convert\submesh.h">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="src\convert\meshjoint.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
|
@ -8,6 +8,7 @@
|
||||||
#include "meshmaterial.h"
|
#include "meshmaterial.h"
|
||||||
#include "meshtriangle.h"
|
#include "meshtriangle.h"
|
||||||
#include "submesh.h"
|
#include "submesh.h"
|
||||||
|
#include "meshjoint.h"
|
||||||
|
|
||||||
void WriteMeshHeader(FILE *fp);
|
void WriteMeshHeader(FILE *fp);
|
||||||
void WriteVertices(const AssimpVertices &vertices, 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 WriteMaterials(const std::vector<MeshMaterial> &materials, FILE *fp);
|
||||||
void WriteTriangles(const std::vector<MeshTriangle> &triangles, FILE *fp);
|
void WriteTriangles(const std::vector<MeshTriangle> &triangles, FILE *fp);
|
||||||
void WriteSubMeshes(const std::vector<SubMesh> &subMeshes, 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
|
#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);
|
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);
|
WriteSubMeshes(subMeshes, fp);
|
||||||
|
|
||||||
// joints
|
// TODO: cheating for now, but according to the ASSIMP doc's, there could be
|
||||||
// foreach joint:
|
// models which have nodes that are not used by any bones, but still
|
||||||
// - name
|
// affect the skeleton pose, because child nodes *are* used by bones
|
||||||
// - parentindex (or name?)
|
// and so, any parent node transformations will apply and need to be
|
||||||
// - position (vec3d)
|
// carried down the chain. None of the test models I'm using have this
|
||||||
// - rotation (vec3d or quat?)
|
// type of node structure, so I'm ignoring it for now :)
|
||||||
|
|
||||||
// joints to vertices mapping
|
// collect basic skeleton/joint info
|
||||||
// foreach vertex:
|
std::vector<MeshJoint> joints;
|
||||||
// - jointindex
|
std::vector<uint32_t> vertexToJointMap;
|
||||||
// - weight
|
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
|
MeshJoint joint;
|
||||||
// foreach keyframe:
|
joint.name = std::string(bone->mName.data, bone->mName.length);
|
||||||
// foreach joint:
|
if (strcmp(parentNode->mName.data, "$dummy_root") == 0)
|
||||||
// - position (vec3d)
|
joint.parentName = "";
|
||||||
// - rotation (vec3d or quat?)
|
else
|
||||||
|
joint.parentName = std::string(parentNode->mName.data, parentNode->mName.length);
|
||||||
|
node->mTransformation.DecomposeNoScaling(joint.rotation, joint.position);
|
||||||
|
|
||||||
// animation sequences
|
// assumption: sum of all bone->mNumWeights is always == vertices.size()
|
||||||
// foreach sequence:
|
for (unsigned int k = 0; k < bone->mNumWeights; ++k)
|
||||||
// - name
|
{
|
||||||
// - start frame index
|
aiVertexWeight *weight = &bone->mWeights[k];
|
||||||
// - end frame index
|
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);
|
fclose(fp);
|
||||||
}
|
}
|
||||||
|
|
Reference in a new issue