forked from Mirrors/openclonk
Added mesh loading (from OGRE XML file)
parent
e52a4364b6
commit
2a3382b4ea
|
@ -37,8 +37,8 @@ protected:
|
|||
|
||||
// Interface to load skeleton files. Given a filename occuring in the
|
||||
// mesh file, this should load the skeleton file from wherever the mesh file
|
||||
// was loaded from, for example from a C4Group. Return an empty string if
|
||||
// the loading failed.
|
||||
// was loaded from, for example from a C4Group. Return default-construted
|
||||
// StdStrBuf with NULL data in case of error.
|
||||
class StdMeshSkeletonLoader
|
||||
{
|
||||
public:
|
||||
|
@ -73,14 +73,16 @@ class StdMeshBone
|
|||
{
|
||||
friend class StdMesh;
|
||||
public:
|
||||
unsigned int Index; // Index in master bone array
|
||||
StdMeshBone() {}
|
||||
|
||||
unsigned int Index; // Index in master bone table
|
||||
int ID; // Bone ID
|
||||
StdStrBuf Name; // Bone name
|
||||
|
||||
// Bone transformation
|
||||
StdMeshMatrix trans;
|
||||
StdMeshMatrix Trans;
|
||||
// Inverse transformation
|
||||
StdMeshMatrix inverse_trans;
|
||||
StdMeshMatrix InverseTrans;
|
||||
|
||||
const StdMeshBone* GetParent() const { return Parent; }
|
||||
|
||||
|
@ -149,8 +151,12 @@ class StdMeshAnimation
|
|||
{
|
||||
friend class StdMesh;
|
||||
public:
|
||||
StdMeshAnimation() {}
|
||||
StdMeshAnimation(const StdMeshAnimation& other);
|
||||
~StdMeshAnimation();
|
||||
|
||||
StdMeshAnimation& operator=(const StdMeshAnimation& other);
|
||||
|
||||
StdStrBuf Name;
|
||||
float Length;
|
||||
|
||||
|
@ -164,8 +170,10 @@ public:
|
|||
StdMesh();
|
||||
~StdMesh();
|
||||
|
||||
// Throws StdMeshError
|
||||
void InitXML(const char* xml_data, StdMeshSkeletonLoader& skel_loader, const StdMeshMatManager& manager);
|
||||
// filename is only used to show it in error messages. The model is
|
||||
// loaded from xml_data.
|
||||
// Throws StdMeshError.
|
||||
void InitXML(const char* filename, const char* xml_data, StdMeshSkeletonLoader& skel_loader, const StdMeshMatManager& manager);
|
||||
|
||||
const StdMeshVertex& GetVertex(unsigned int i) const { return Vertices[i]; }
|
||||
unsigned int GetNumVertices() const { return Vertices.size(); }
|
||||
|
@ -180,6 +188,11 @@ public:
|
|||
const StdMeshMaterial& GetMaterial() const { return *Material; }
|
||||
|
||||
private:
|
||||
void AddMasterBone(StdMeshBone* bone);
|
||||
|
||||
StdMesh(const StdMesh& other); // non-copyable
|
||||
StdMesh& operator=(const StdMesh& other); // non-assignable
|
||||
|
||||
// Remember bone assignments for vertices
|
||||
class Vertex: public StdMeshVertex
|
||||
{
|
||||
|
|
|
@ -18,11 +18,88 @@
|
|||
|
||||
#include <StdMesh.h>
|
||||
|
||||
#include <tinyxml/tinyxml.h>
|
||||
|
||||
StdMeshError::StdMeshError(const StdStrBuf& message, const char* file, unsigned int line)
|
||||
{
|
||||
Buf.Format("%s[%u]: %s", file, line, message.getData());
|
||||
}
|
||||
|
||||
// Helper class to load things from an XML file with error checking
|
||||
class StdMeshXML
|
||||
{
|
||||
public:
|
||||
StdMeshXML(const char* filename, const char* xml_data);
|
||||
|
||||
const char* RequireStrAttribute(TiXmlElement* element, const char* attribute) const;
|
||||
int RequireIntAttribute(TiXmlElement* element, const char* attribute) const;
|
||||
float RequireFloatAttribute(TiXmlElement* element, const char* attribute) const;
|
||||
|
||||
TiXmlElement* RequireFirstChild(TiXmlElement* element, const char* child);
|
||||
|
||||
void Error(const StdStrBuf& message, TiXmlElement* element) const;
|
||||
|
||||
private:
|
||||
TiXmlDocument Document;
|
||||
StdStrBuf FileName;
|
||||
};
|
||||
|
||||
StdMeshXML::StdMeshXML(const char* filename, const char* xml_data):
|
||||
FileName(filename)
|
||||
{
|
||||
Document.Parse(filename);
|
||||
if(Document.Error())
|
||||
throw StdMeshError(StdStrBuf(Document.ErrorDesc()), FileName.getData(), Document.ErrorRow());
|
||||
}
|
||||
|
||||
const char* StdMeshXML::RequireStrAttribute(TiXmlElement* element, const char* attribute) const
|
||||
{
|
||||
const char* value = element->Attribute(attribute);
|
||||
if(!value) Error(FormatString("Element '%s' does not have attribute '%s'", element->Value(), attribute), element);
|
||||
return value;
|
||||
}
|
||||
|
||||
int StdMeshXML::RequireIntAttribute(TiXmlElement* element, const char* attribute) const
|
||||
{
|
||||
int retval;
|
||||
if(element->QueryIntAttribute(attribute, &retval) != TIXML_SUCCESS)
|
||||
Error(FormatString("Element '%s' does not have integer attribute '%s'", element->Value(), attribute), element);
|
||||
return retval;
|
||||
}
|
||||
|
||||
float StdMeshXML::RequireFloatAttribute(TiXmlElement* element, const char* attribute) const
|
||||
{
|
||||
float retval;
|
||||
if(element->QueryFloatAttribute(attribute, &retval) != TIXML_SUCCESS)
|
||||
Error(FormatString("Element '%s' does not have integer attribute '%s'", element->Value(), attribute), element);
|
||||
return retval;
|
||||
}
|
||||
|
||||
TiXmlElement* StdMeshXML::RequireFirstChild(TiXmlElement* element, const char* child)
|
||||
{
|
||||
TiXmlElement* retval;
|
||||
|
||||
if(element)
|
||||
{
|
||||
retval = element->FirstChildElement(child);
|
||||
if(!retval)
|
||||
Error(FormatString("Element '%s' does not contain '%s' child", element->Value(), child), element);
|
||||
}
|
||||
else
|
||||
{
|
||||
retval = Document.RootElement();
|
||||
if(strcmp(retval->Value(), child) != 0)
|
||||
Error(FormatString("Root element is not '%s'", child), retval);
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
void StdMeshXML::Error(const StdStrBuf& message, TiXmlElement* element) const
|
||||
{
|
||||
throw StdMeshError(message, FileName.getData(), element->Row());
|
||||
}
|
||||
|
||||
void StdMeshMatrix::SetIdentity()
|
||||
{
|
||||
a[0][0] = 1.0f; a[0][1] = 0.0f; a[0][2] = 0.0f; a[0][3] = 0.0f;
|
||||
|
@ -206,12 +283,40 @@ StdMeshMatrix StdMeshTrack::GetTransformAt(float time) const
|
|||
return trans1;
|
||||
}
|
||||
|
||||
StdMeshAnimation::StdMeshAnimation(const StdMeshAnimation& other):
|
||||
Name(other.Name), Length(other.Length), Tracks(other.Tracks.size())
|
||||
{
|
||||
// Note that all Tracks are already default-initialized to zero
|
||||
for(unsigned int i = 0; i < Tracks.size(); ++i)
|
||||
if(other.Tracks[i])
|
||||
Tracks[i] = new StdMeshTrack(*other.Tracks[i]);
|
||||
}
|
||||
|
||||
StdMeshAnimation::~StdMeshAnimation()
|
||||
{
|
||||
for(unsigned int i = 0; i < Tracks.size(); ++i)
|
||||
delete Tracks[i];
|
||||
}
|
||||
|
||||
StdMeshAnimation& StdMeshAnimation::operator=(const StdMeshAnimation& other)
|
||||
{
|
||||
if(this == &other) return *this;
|
||||
|
||||
Name = other.Name;
|
||||
Length = other.Length;
|
||||
|
||||
for(unsigned int i = 0; i < Tracks.size(); ++i)
|
||||
delete Tracks[i];
|
||||
|
||||
Tracks.resize(other.Tracks.size());
|
||||
|
||||
for(unsigned int i = 0; i < Tracks.size(); ++i)
|
||||
if(other.Tracks[i])
|
||||
Tracks[i] = new StdMeshTrack(*other.Tracks[i]);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
StdMesh::StdMesh():
|
||||
Material(NULL)
|
||||
{
|
||||
|
@ -223,7 +328,286 @@ StdMesh::~StdMesh()
|
|||
delete Bones[i];
|
||||
}
|
||||
|
||||
void StdMesh::InitXML(const char* xml_data, StdMeshSkeletonLoader& skel_loader, const StdMeshMatManager& manager)
|
||||
void StdMesh::InitXML(const char* filename, const char* xml_data, StdMeshSkeletonLoader& skel_loader, const StdMeshMatManager& manager)
|
||||
{
|
||||
|
||||
StdMeshXML mesh(filename, xml_data);
|
||||
|
||||
TiXmlElement* mesh_elem = mesh.RequireFirstChild(NULL, "mesh");
|
||||
TiXmlElement* submeshes_elem = mesh.RequireFirstChild(mesh_elem, "submeshes");
|
||||
// Load first submesh only for now
|
||||
TiXmlElement* submesh_elem = mesh.RequireFirstChild(submeshes_elem, "submesh");
|
||||
TiXmlElement* geometry_elem = mesh.RequireFirstChild(submesh_elem, "geometry");
|
||||
|
||||
int VertexCount = mesh.RequireIntAttribute(geometry_elem, "vertexcount");
|
||||
Vertices.resize(VertexCount);
|
||||
|
||||
TiXmlElement* buffer_elem = mesh.RequireFirstChild(geometry_elem, "vertexbuffer");
|
||||
|
||||
unsigned int i = 0;
|
||||
for(TiXmlElement* vertex_elem = buffer_elem->FirstChildElement("vertex"); vertex_elem != NULL && i < Vertices.size(); vertex_elem = vertex_elem->NextSiblingElement("vertex"), ++i)
|
||||
{
|
||||
TiXmlElement* position_elem = mesh.RequireFirstChild(vertex_elem, "position");
|
||||
TiXmlElement* normal_elem = mesh.RequireFirstChild(vertex_elem, "normal");
|
||||
TiXmlElement* texcoord_elem = mesh.RequireFirstChild(vertex_elem, "texcoord");
|
||||
|
||||
Vertices[i].x = mesh.RequireFloatAttribute(position_elem, "x");
|
||||
Vertices[i].y = mesh.RequireFloatAttribute(position_elem, "y");
|
||||
Vertices[i].z = mesh.RequireFloatAttribute(position_elem, "z");
|
||||
Vertices[i].nx = mesh.RequireFloatAttribute(normal_elem, "x");
|
||||
Vertices[i].ny = mesh.RequireFloatAttribute(normal_elem, "y");
|
||||
Vertices[i].nz = mesh.RequireFloatAttribute(normal_elem, "z");
|
||||
Vertices[i].u = mesh.RequireFloatAttribute(texcoord_elem, "u");
|
||||
Vertices[i].v = mesh.RequireFloatAttribute(texcoord_elem, "v");
|
||||
}
|
||||
|
||||
TiXmlElement* faces_elem = mesh.RequireFirstChild(submesh_elem, "faces");
|
||||
int FaceCount = mesh.RequireIntAttribute(faces_elem, "count");
|
||||
Faces.resize(FaceCount);
|
||||
|
||||
i = 0;
|
||||
for(TiXmlElement* face_elem = faces_elem->FirstChildElement("face"); face_elem != NULL && i < Faces.size(); face_elem = face_elem->NextSiblingElement("face"), ++i)
|
||||
{
|
||||
int v[3];
|
||||
|
||||
v[0] = mesh.RequireIntAttribute(face_elem, "v1");
|
||||
v[1] = mesh.RequireIntAttribute(face_elem, "v2");
|
||||
v[2] = mesh.RequireIntAttribute(face_elem, "v3");
|
||||
|
||||
for(unsigned int j = 0; j < 3; ++j)
|
||||
{
|
||||
if(v[j] < 0 || static_cast<unsigned int>(v[j]) >= Vertices.size())
|
||||
mesh.Error(FormatString("Vertex index v%u (%d) is out of range", j+1, v[j]), face_elem);
|
||||
Faces[i].Vertices[j] = v[j];
|
||||
}
|
||||
}
|
||||
|
||||
// Read skeleton
|
||||
TiXmlElement* skeletonlink_elem = mesh.RequireFirstChild(mesh_elem, "skeletonlink");
|
||||
const char* name = mesh.RequireStrAttribute(skeletonlink_elem, "name");
|
||||
StdStrBuf xml_filename(name); xml_filename.Append(".xml");
|
||||
|
||||
StdStrBuf skeleton_xml_data = skel_loader.LoadSkeleton(xml_filename.getData());
|
||||
if(skeleton_xml_data.isNull()) mesh.Error(FormatString("Failed to load '%s'", xml_filename.getData()), skeletonlink_elem);
|
||||
|
||||
StdMeshXML skeleton(xml_filename.getData(), skeleton_xml_data.getData());
|
||||
TiXmlElement* skeleton_elem = skeleton.RequireFirstChild(NULL, "skeleton");
|
||||
TiXmlElement* bones_elem = skeleton.RequireFirstChild(skeleton_elem, "bones");
|
||||
|
||||
// Read bones. Don't insert into Master bone table yet, as the master bone
|
||||
// table is sorted hierarchically, and we will read the hierarchy only
|
||||
// afterwards.
|
||||
std::vector<StdMeshBone*> bones;
|
||||
for(TiXmlElement* bone_elem = bones_elem->FirstChildElement("bone"); bone_elem != NULL; bone_elem = bone_elem->NextSiblingElement("bone"))
|
||||
{
|
||||
StdMeshBone* bone = new StdMeshBone;
|
||||
bones.push_back(bone);
|
||||
|
||||
bone->ID = skeleton.RequireIntAttribute(bone_elem, "id");
|
||||
bone->Name = skeleton.RequireStrAttribute(bone_elem, "name");
|
||||
|
||||
// TODO: Make sure ID and name are unique
|
||||
|
||||
TiXmlElement* position_elem = skeleton.RequireFirstChild(bone_elem, "position");
|
||||
TiXmlElement* rotation_elem = skeleton.RequireFirstChild(bone_elem, "rotation");
|
||||
TiXmlElement* axis_elem = skeleton.RequireFirstChild(rotation_elem, "axis");
|
||||
|
||||
float dx = skeleton.RequireFloatAttribute(position_elem, "x");
|
||||
float dy = skeleton.RequireFloatAttribute(position_elem, "y");
|
||||
float dz = skeleton.RequireFloatAttribute(position_elem, "z");
|
||||
float angle = skeleton.RequireFloatAttribute(rotation_elem, "angle");
|
||||
float rx = skeleton.RequireFloatAttribute(axis_elem, "x");
|
||||
float ry = skeleton.RequireFloatAttribute(axis_elem, "y");
|
||||
float rz = skeleton.RequireFloatAttribute(axis_elem, "z");
|
||||
|
||||
StdMeshMatrix helper;
|
||||
helper.SetTranslate(dx, dy, dz);
|
||||
bone->Trans.SetRotate(angle, rx, ry, rz);
|
||||
bone->Trans.Transform(helper);
|
||||
|
||||
helper.SetRotate(-angle, rx, ry, rz);
|
||||
bone->InverseTrans.SetTranslate(-dx, -dy, -dz);
|
||||
bone->InverseTrans.Transform(helper);
|
||||
|
||||
bone->Parent = NULL;
|
||||
|
||||
// Index of bone will be set when building Master Bone Table later
|
||||
}
|
||||
|
||||
// Bone hierarchy
|
||||
TiXmlElement* bonehierarchy_elem = skeleton.RequireFirstChild(skeleton_elem, "bonehierarchy");
|
||||
for(TiXmlElement* boneparent_elem = bonehierarchy_elem->FirstChildElement("boneparent"); boneparent_elem != NULL; boneparent_elem = boneparent_elem->NextSiblingElement("boneparent"))
|
||||
{
|
||||
const char* child_name = skeleton.RequireStrAttribute(boneparent_elem, "bone");
|
||||
const char* parent_name = skeleton.RequireStrAttribute(boneparent_elem, "parent");
|
||||
|
||||
// Lookup the two bones
|
||||
StdMeshBone* child = NULL;
|
||||
StdMeshBone* parent = NULL;
|
||||
for(unsigned int i = 0; i < bones.size() && (!child || !parent); ++i)
|
||||
{
|
||||
if(!child && bones[i]->Name == child_name)
|
||||
child = bones[i];
|
||||
if(!parent && bones[i]->Name == parent_name)
|
||||
parent = bones[i];
|
||||
}
|
||||
|
||||
if(!child) skeleton.Error(FormatString("There is no such bone with name '%s'", child_name), boneparent_elem);
|
||||
if(!parent) skeleton.Error(FormatString("There is no such bone with name '%s'", parent_name), boneparent_elem);
|
||||
|
||||
child->Parent = parent;
|
||||
parent->Children.push_back(child);
|
||||
|
||||
// Apply parent transformation
|
||||
child->Trans.Transform(parent->Trans);
|
||||
child->InverseTrans.Mul(parent->InverseTrans);
|
||||
}
|
||||
|
||||
// Fill master bone table in hierarchical order:
|
||||
for(unsigned int i = 0; i < bones.size(); ++i)
|
||||
if(bones[i]->Parent == NULL)
|
||||
AddMasterBone(bones[i]);
|
||||
|
||||
// Vertex<->Bone assignments
|
||||
TiXmlElement* boneassignments_elem = mesh.RequireFirstChild(submesh_elem, "boneassignments");
|
||||
for(TiXmlElement* vertexboneassignment_elem = boneassignments_elem->FirstChildElement("vertexboneassignment"); vertexboneassignment_elem != NULL; vertexboneassignment_elem = vertexboneassignment_elem->NextSiblingElement("vertexboneassignment"))
|
||||
{
|
||||
int BoneID = mesh.RequireIntAttribute(vertexboneassignment_elem, "boneindex");
|
||||
int VertexIndex = mesh.RequireIntAttribute(vertexboneassignment_elem, "vertexindex");
|
||||
float weight = mesh.RequireFloatAttribute(vertexboneassignment_elem, "weight");
|
||||
|
||||
if(VertexIndex < 0 || static_cast<unsigned int>(VertexIndex) >= Vertices.size())
|
||||
mesh.Error(FormatString("Vertex index in bone assignment (%d) is out of range", VertexIndex), vertexboneassignment_elem);
|
||||
|
||||
StdMeshBone* bone = NULL;
|
||||
for(unsigned int i = 0; !bone && i < bones.size(); ++i)
|
||||
if(bones[i]->ID == BoneID)
|
||||
bone = bones[i];
|
||||
|
||||
if(!bone) mesh.Error(FormatString("There is no such bone with index %d", BoneID), vertexboneassignment_elem);
|
||||
|
||||
Vertex& vertex = Vertices[VertexIndex];
|
||||
vertex.BoneAssignments.push_back(StdMeshVertexBoneAssignment());
|
||||
StdMeshVertexBoneAssignment& assignment = vertex.BoneAssignments.back();
|
||||
assignment.BoneIndex = bone->Index;
|
||||
assignment.Weight = weight;
|
||||
}
|
||||
|
||||
// Normalize vertex bone assignment weights (this is not guaranteed in the
|
||||
// Ogre file format).
|
||||
for(unsigned int i = 0; i < Vertices.size(); ++i)
|
||||
{
|
||||
Vertex& vertex = Vertices[i];
|
||||
float sum = 0.0f;
|
||||
for(unsigned int j = 0; j < vertex.BoneAssignments.size(); ++j)
|
||||
sum += vertex.BoneAssignments[j].Weight;
|
||||
for(unsigned int j = 0; j < vertex.BoneAssignments.size(); ++j)
|
||||
vertex.BoneAssignments[j].Weight /= sum;
|
||||
}
|
||||
|
||||
// Load Animations
|
||||
TiXmlElement* animations_elem = skeleton.RequireFirstChild(skeleton_elem, "animations");
|
||||
for(TiXmlElement* animation_elem = animations_elem->FirstChildElement("animation"); animation_elem != NULL; animation_elem = animation_elem->NextSiblingElement("animation"))
|
||||
{
|
||||
StdStrBuf name(skeleton.RequireStrAttribute(animation_elem, "name"));
|
||||
if(Animations.find(name) != Animations.end())
|
||||
skeleton.Error(FormatString("There is already an animation with name '%s'", name.getData()), animation_elem);
|
||||
|
||||
StdMeshAnimation& animation = Animations.insert(std::make_pair(name, StdMeshAnimation())).first->second;
|
||||
animation.Name = name;
|
||||
animation.Length = skeleton.RequireFloatAttribute(animation_elem, "length");
|
||||
animation.Tracks.resize(Bones.size());
|
||||
|
||||
TiXmlElement* tracks_elem = skeleton.RequireFirstChild(animation_elem, "tracks");
|
||||
for(TiXmlElement* track_elem = tracks_elem->FirstChildElement("track"); track_elem != NULL; track_elem = track_elem->NextSiblingElement("track"))
|
||||
{
|
||||
const char* bone_name = skeleton.RequireStrAttribute(track_elem, "bone");
|
||||
StdMeshBone* bone = NULL;
|
||||
for(unsigned int i = 0; !bone && i < Bones.size(); ++i)
|
||||
if(Bones[i]->Name == bone_name)
|
||||
bone = Bones[i];
|
||||
if(!bone) skeleton.Error(FormatString("There is no such bone with name '%s'", bone_name), track_elem);
|
||||
|
||||
if(animation.Tracks[bone->Index] != NULL) skeleton.Error(FormatString("There is already a track for bone '%s' in animation '%s'", bone_name, animation.Name.getData()), track_elem);
|
||||
|
||||
StdMeshTrack* track = new StdMeshTrack;
|
||||
animation.Tracks[bone->Index] = track;
|
||||
|
||||
TiXmlElement* keyframes_elem = skeleton.RequireFirstChild(track_elem, "keyframes");
|
||||
for(TiXmlElement* keyframe_elem = keyframes_elem->FirstChildElement("keyframe"); keyframe_elem != NULL; keyframe_elem = keyframe_elem->NextSiblingElement("keyframe"))
|
||||
{
|
||||
float time = skeleton.RequireFloatAttribute(keyframe_elem, "time");
|
||||
StdMeshKeyFrame& frame = track->Frames[time];
|
||||
|
||||
TiXmlElement* translate_elem = skeleton.RequireFirstChild(keyframe_elem, "translate");
|
||||
TiXmlElement* rotate_elem = skeleton.RequireFirstChild(keyframe_elem, "rotate");
|
||||
TiXmlElement* scale_elem = skeleton.RequireFirstChild(scale_elem, "scale");
|
||||
TiXmlElement* axis_elem = skeleton.RequireFirstChild(rotate_elem, "axis");
|
||||
|
||||
float dx = skeleton.RequireFloatAttribute(translate_elem, "x");
|
||||
float dy = skeleton.RequireFloatAttribute(translate_elem, "y");
|
||||
float dz = skeleton.RequireFloatAttribute(translate_elem, "z");
|
||||
float sx = skeleton.RequireFloatAttribute(scale_elem, "x");
|
||||
float sy = skeleton.RequireFloatAttribute(scale_elem, "y");
|
||||
float sz = skeleton.RequireFloatAttribute(scale_elem, "z");
|
||||
float angle = skeleton.RequireFloatAttribute(rotate_elem, "angle");
|
||||
float rx = skeleton.RequireFloatAttribute(axis_elem, "x");
|
||||
float ry = skeleton.RequireFloatAttribute(axis_elem, "y");
|
||||
float rz = skeleton.RequireFloatAttribute(axis_elem, "z");
|
||||
|
||||
StdMeshMatrix helper;
|
||||
frame.Trans.SetRotate(angle, rx, ry, rz);
|
||||
helper.SetScale(sx, sy, sz);
|
||||
frame.Trans.Transform(helper);
|
||||
helper.SetTranslate(-dx, -dy, -dz);
|
||||
frame.Trans.Transform(helper);
|
||||
}
|
||||
}
|
||||
|
||||
// Apply bone transformation on animation frames. We need to do this
|
||||
// after the actual loading because we need to walk the bone list
|
||||
// hierarchically.
|
||||
for(unsigned int i = 0; i < Bones.size(); ++i)
|
||||
{
|
||||
if(animation.Tracks[i])
|
||||
{
|
||||
StdMeshTrack& track = *animation.Tracks[i];
|
||||
|
||||
// Get next parent track
|
||||
StdMeshTrack* parent_track = NULL;
|
||||
StdMeshBone* parent_bone = Bones[i]->Parent;
|
||||
while(parent_bone && !(parent_track = animation.Tracks[parent_bone->Index]))
|
||||
parent_bone = parent_bone->Parent;
|
||||
assert(!parent_bone || parent_track);
|
||||
|
||||
for(std::map<float, StdMeshKeyFrame>::iterator iter = track.Frames.begin(); iter != track.Frames.end(); ++iter)
|
||||
{
|
||||
// TODO: If this bone's track is not as smooth as the parent animation
|
||||
// (which means if there is more than one keyframe in the parent
|
||||
// animation for each keyframe pair in the this bone's animation),
|
||||
// then we need to insert additional child keyframes, so that the
|
||||
// transformation for the child does not skip parent keyframes.
|
||||
StdMeshKeyFrame& frame = iter->second;
|
||||
|
||||
// Apply transformation of parent tracks (for which we computed
|
||||
// already the bone transformations, as we walk the bone list
|
||||
// hierarchically) in bone's coordinate system.
|
||||
frame.Trans.Mul(Bones[i]->InverseTrans);
|
||||
frame.Trans.Transform(Bones[i]->Trans);
|
||||
|
||||
if(parent_bone)
|
||||
frame.Trans.Transform(parent_track->GetTransformAt(iter->first));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StdMesh::AddMasterBone(StdMeshBone* bone)
|
||||
{
|
||||
bone->Index = Bones.size(); // Remember index in master bone table
|
||||
Bones.push_back(bone);
|
||||
for(unsigned int i = 0; i < bone->Children.size(); ++i)
|
||||
AddMasterBone(bone->Children[i]);
|
||||
}
|
||||
|
||||
// vim: et ts=2 sw=2
|
||||
|
|
Loading…
Reference in New Issue