OpenClonk
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros
StdMeshLoaderXml.cpp
Go to the documentation of this file.
1 /*
2  * OpenClonk, http://www.openclonk.org
3  *
4  * Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
5  * Copyright (c) 2009-2016, The OpenClonk Team and contributors
6  *
7  * Distributed under the terms of the ISC license; see accompanying file
8  * "COPYING" for details.
9  *
10  * "Clonk" is a registered trademark of Matthes Bender, used with permission.
11  * See accompanying file "TRADEMARK" for details.
12  *
13  * To redistribute this file separately, substitute the full license texts
14  * for the above references.
15  */
16 
17 // A loader for the OGRE .mesh XML file format
18 
19 #include "C4Include.h"
20 #include "lib/StdMesh.h"
21 #include "lib/StdMeshLoader.h"
22 #include <tinyxml.h>
23 
24 // Helper class to load things from an XML file with error checking
26 {
27 public:
28  StdMeshXML(const char* filename, const char* xml_data);
29 
30  const char* RequireStrAttribute(TiXmlElement* element, const char* attribute) const;
31  int RequireIntAttribute(TiXmlElement* element, const char* attribute) const;
32  float RequireFloatAttribute(TiXmlElement* element, const char* attribute) const;
33 
34  TiXmlElement* RequireFirstChild(TiXmlElement* element, const char* child);
35 
36  void LoadGeometry(StdMesh& mesh, std::vector<StdSubMesh::Vertex>& vertices, TiXmlElement* boneassignments_elem);
37  void LoadBoneAssignments(StdMesh& mesh, std::vector<StdSubMesh::Vertex>& vertices, TiXmlElement* boneassignments_elem);
38 
39  void Error(const StdStrBuf& message, TiXmlElement* element) const;
40  void Error(const StdStrBuf& message, int row) const;
41 
42 private:
43  TiXmlDocument Document;
44  StdStrBuf FileName;
45 };
46 
47 StdMeshLoader::StdMeshXML::StdMeshXML(const char* filename, const char* xml_data):
48  FileName(filename)
49 {
50  Document.Parse(xml_data);
51  if (Document.Error())
52  Error(StdStrBuf(Document.ErrorDesc()), Document.ErrorRow());
53 }
54 
55 const char* StdMeshLoader::StdMeshXML::RequireStrAttribute(TiXmlElement* element, const char* attribute) const
56 {
57  const char* value = element->Attribute(attribute);
58  if (!value) Error(FormatString("Element '%s' does not have attribute '%s'", element->Value(), attribute), element);
59  return value;
60 }
61 
62 int StdMeshLoader::StdMeshXML::RequireIntAttribute(TiXmlElement* element, const char* attribute) const
63 {
64  int retval;
65  if (element->QueryIntAttribute(attribute, &retval) != TIXML_SUCCESS)
66  Error(FormatString("Element '%s' does not have integer attribute '%s'", element->Value(), attribute), element);
67  return retval;
68 }
69 
70 float StdMeshLoader::StdMeshXML::RequireFloatAttribute(TiXmlElement* element, const char* attribute) const
71 {
72  float retval = 0;
73  if (element->QueryFloatAttribute(attribute, &retval) != TIXML_SUCCESS)
74  Error(FormatString("Element '%s' does not have floating point attribute '%s'", element->Value(), attribute), element);
75  return retval;
76 }
77 
78 TiXmlElement* StdMeshLoader::StdMeshXML::RequireFirstChild(TiXmlElement* element, const char* child)
79 {
80  TiXmlElement* retval;
81 
82  if (element)
83  {
84  retval = element->FirstChildElement(child);
85  if (!retval)
86  Error(FormatString("Element '%s' does not contain '%s' child", element->Value(), child), element);
87  }
88  else
89  {
90  retval = Document.RootElement();
91  if (strcmp(retval->Value(), child) != 0)
92  Error(FormatString("Root element is not '%s'", child), retval);
93  }
94 
95  return retval;
96 }
97 
98 void StdMeshLoader::StdMeshXML::Error(const StdStrBuf& message, TiXmlElement* element) const
99 {
100  Error(message, element->Row());
101 }
102 
103 void StdMeshLoader::StdMeshXML::Error(const StdStrBuf& message, int row) const
104 {
105  throw StdMeshLoader::LoaderException(FormatString("%s:%u: %s", FileName.getData(), row, message.getData()).getData());
106 }
107 
108 void StdMeshLoader::StdMeshXML::LoadGeometry(StdMesh& mesh, std::vector<StdSubMesh::Vertex>& vertices, TiXmlElement* geometry_elem)
109 {
110  // Check whether mesh has any vertices so far -- we need this later for
111  // initialization of bounding box and bounding sphere.
112  bool hasVertices = false;
113  if(!mesh.SharedVertices.empty()) hasVertices = true;
114  for(unsigned int i = 0; i < mesh.SubMeshes.size(); ++i)
115  if(!mesh.SubMeshes[i].Vertices.empty())
116  hasVertices = true;
117 
118  int VertexCount = RequireIntAttribute(geometry_elem, "vertexcount");
119  vertices.resize(VertexCount);
120 
121  static const unsigned int POSITIONS = 1;
122  static const unsigned int NORMALS = 2;
123  static const unsigned int TEXCOORDS = 4;
124 
125  // Individual vertex attributes can be split up in multiple vertex buffers
126  unsigned int loaded_attributes = 0;
127  for(TiXmlElement* buffer_elem = geometry_elem->FirstChildElement("vertexbuffer"); buffer_elem != nullptr; buffer_elem = buffer_elem->NextSiblingElement("vertexbuffer"))
128  {
129  unsigned int attributes = 0;
130  if(buffer_elem->Attribute("positions")) attributes |= POSITIONS;
131  if(buffer_elem->Attribute("normals")) attributes |= NORMALS;
132  if(buffer_elem->Attribute("texture_coords")) attributes |= TEXCOORDS;
133 
134  unsigned int i;
135  TiXmlElement* vertex_elem;
136  for (vertex_elem = buffer_elem->FirstChildElement("vertex"), i = 0; vertex_elem != nullptr && i < vertices.size(); vertex_elem = vertex_elem->NextSiblingElement("vertex"), ++i)
137  {
138  if(attributes & POSITIONS)
139  {
140  TiXmlElement* position_elem = RequireFirstChild(vertex_elem, "position");
141 
142  vertices[i].x = RequireFloatAttribute(position_elem, "x");
143  vertices[i].y = RequireFloatAttribute(position_elem, "y");
144  vertices[i].z = RequireFloatAttribute(position_elem, "z");
145  }
146 
147  if(attributes & NORMALS)
148  {
149  TiXmlElement* normal_elem = RequireFirstChild(vertex_elem, "normal");
150 
151  vertices[i].nx = RequireFloatAttribute(normal_elem, "x");
152  vertices[i].ny = RequireFloatAttribute(normal_elem, "y");
153  vertices[i].nz = RequireFloatAttribute(normal_elem, "z");
154  }
155 
156  if(attributes & TEXCOORDS)
157  {
158  // FIXME: The Ogre format supports denoting multiple texture coordinates, but the rendering code only supports one
159  // currently only the first set is read, any additional ones are ignored
160  TiXmlElement* texcoord_elem = RequireFirstChild(vertex_elem, "texcoord");
161  vertices[i].u = RequireFloatAttribute(texcoord_elem, "u");
162  vertices[i].v = RequireFloatAttribute(texcoord_elem, "v");
163  }
164 
165  vertices[i] = OgreToClonk::TransformVertex(vertices[i]);
166 
167  if (attributes & POSITIONS)
168  {
169  const float d = std::sqrt(vertices[i].x*vertices[i].x
170  + vertices[i].y*vertices[i].y
171  + vertices[i].z*vertices[i].z);
172 
173  // Construct BoundingBox
174  StdMeshBox& BoundingBox = mesh.BoundingBox;
175  if (i == 0 && !hasVertices)
176  {
177  // First vertex
178  BoundingBox.x1 = BoundingBox.x2 = vertices[i].x;
179  BoundingBox.y1 = BoundingBox.y2 = vertices[i].y;
180  BoundingBox.z1 = BoundingBox.z2 = vertices[i].z;
181  mesh.BoundingRadius = d;
182  }
183  else
184  {
185  BoundingBox.x1 = std::min(vertices[i].x, BoundingBox.x1);
186  BoundingBox.x2 = std::max(vertices[i].x, BoundingBox.x2);
187  BoundingBox.y1 = std::min(vertices[i].y, BoundingBox.y1);
188  BoundingBox.y2 = std::max(vertices[i].y, BoundingBox.y2);
189  BoundingBox.z1 = std::min(vertices[i].z, BoundingBox.z1);
190  BoundingBox.z2 = std::max(vertices[i].z, BoundingBox.z2);
191  mesh.BoundingRadius = std::max(mesh.BoundingRadius, d);
192  }
193  }
194  }
195 
196  if(vertex_elem != nullptr)
197  Error(FormatString("Too many vertices in vertexbuffer"), buffer_elem);
198  if(i < vertices.size())
199  Error(FormatString("Not enough vertices in vertexbuffer"), buffer_elem);
200 
201  loaded_attributes |= attributes;
202  }
203 
204  static const unsigned int REQUIRED_ATTRIBUTES = POSITIONS | NORMALS | TEXCOORDS;
205  if((loaded_attributes & REQUIRED_ATTRIBUTES) != REQUIRED_ATTRIBUTES)
206  Error(FormatString("Not all required vertex attributes (positions, normals, texcoords) present in mesh geometry"), geometry_elem);
207 }
208 
209 void StdMeshLoader::StdMeshXML::LoadBoneAssignments(StdMesh& mesh, std::vector<StdSubMesh::Vertex>& vertices, TiXmlElement* boneassignments_elem)
210 {
211  for (TiXmlElement* vertexboneassignment_elem = boneassignments_elem->FirstChildElement("vertexboneassignment"); vertexboneassignment_elem != nullptr; vertexboneassignment_elem = vertexboneassignment_elem->NextSiblingElement("vertexboneassignment"))
212  {
213  int BoneID = RequireIntAttribute(vertexboneassignment_elem, "boneindex");
214  int VertexIndex = RequireIntAttribute(vertexboneassignment_elem, "vertexindex");
215  float weight = RequireFloatAttribute(vertexboneassignment_elem, "weight");
216 
217  if (VertexIndex < 0 || static_cast<unsigned int>(VertexIndex) >= vertices.size())
218  Error(FormatString("Vertex index in bone assignment (%d) is out of range", VertexIndex), vertexboneassignment_elem);
219 
220  // maybe not needed, see comment below
221  const StdMeshBone* bone = nullptr;
222  for (unsigned int i = 0; !bone && i < mesh.GetSkeleton().GetNumBones(); ++i)
223  if (mesh.GetSkeleton().GetBone(i).ID == BoneID)
224  bone = &mesh.GetSkeleton().GetBone(i);
225 
226  if (!bone) Error(FormatString("There is no such bone with ID %d", BoneID), vertexboneassignment_elem);
227 
228  // Find first bone assignment with a zero weight (i.e. is unused)
229  StdSubMesh::Vertex& vertex = vertices[VertexIndex];
230  // Check quickly if all weight slots are used
231  if (vertex.bone_weight[StdMeshVertex::MaxBoneWeightCount - 1] != 0)
232  {
233  Error(FormatString("Vertex %d is influenced by more than %d bones", VertexIndex, static_cast<int>(StdMeshVertex::MaxBoneWeightCount)), vertexboneassignment_elem);
234  }
235  for (size_t weight_index = 0; weight_index < StdMeshVertex::MaxBoneWeightCount; ++weight_index)
236  {
237  if (vertex.bone_weight[weight_index] == 0)
238  {
239  vertex.bone_weight[weight_index] = weight;
240  vertex.bone_index[weight_index] = bone->Index;
241  break;
242  }
243  }
244  }
245 
246  // Normalize vertex bone assignment weights (this is not guaranteed in the
247  // Ogre file format).
248  for (unsigned int i = 0; i < vertices.size(); ++i)
249  {
250  StdSubMesh::Vertex& vertex = vertices[i];
251  float sum = 0.0;
252  for (float weight : vertex.bone_weight)
253  sum += weight;
254  if (sum != 0)
255  for (float &weight : vertex.bone_weight)
256  weight /= sum;
257  else
258  vertex.bone_weight[0] = 1.0f;
259  }
260 }
261 
262 StdMesh *StdMeshLoader::LoadMeshXml(const char* xml_data, size_t size, const StdMeshMatManager& manager, StdMeshSkeletonLoader& skel_loader, const char* filename)
263 {
264  StdMeshXML xml(filename ? filename : "<unknown>", xml_data);
265 
266  std::unique_ptr<StdMesh> mesh(new StdMesh);
267 
268  TiXmlElement* mesh_elem = xml.RequireFirstChild(nullptr, "mesh");
269 
270  // Load shared geometry, if any
271  TiXmlElement* sharedgeometry_elem = mesh_elem->FirstChildElement("sharedgeometry");
272  if(sharedgeometry_elem != nullptr)
273  xml.LoadGeometry(*mesh, mesh->SharedVertices, sharedgeometry_elem);
274 
275  TiXmlElement* submeshes_elem = xml.RequireFirstChild(mesh_elem, "submeshes");
276 
277  TiXmlElement* submesh_elem_base = xml.RequireFirstChild(submeshes_elem, "submesh");
278  for (TiXmlElement* submesh_elem = submesh_elem_base; submesh_elem != nullptr; submesh_elem = submesh_elem->NextSiblingElement("submesh"))
279  {
280  mesh->SubMeshes.push_back(StdSubMesh());
281  StdSubMesh& submesh = mesh->SubMeshes.back();
282 
283  const char* material = xml.RequireStrAttribute(submesh_elem, "material");
284  submesh.Material = manager.GetMaterial(material);
285  if (!submesh.Material)
286  xml.Error(FormatString("There is no such material named '%s'", material), submesh_elem);
287 
288  const char* usesharedvertices = submesh_elem->Attribute("usesharedvertices");
289  const std::vector<StdMesh::Vertex>* vertices;
290  if(!usesharedvertices || strcmp(usesharedvertices, "true") != 0)
291  {
292  TiXmlElement* geometry_elem = xml.RequireFirstChild(submesh_elem, "geometry");
293  xml.LoadGeometry(*mesh, submesh.Vertices, geometry_elem);
294  vertices = &submesh.Vertices;
295  }
296  else
297  {
298  if(mesh->SharedVertices.empty())
299  xml.Error(StdCopyStrBuf("Submesh specifies to use shared vertices but there is no shared geometry"), submesh_elem);
300  vertices = &mesh->SharedVertices;
301  }
302 
303  TiXmlElement* faces_elem = xml.RequireFirstChild(submesh_elem, "faces");
304  int FaceCount = xml.RequireIntAttribute(faces_elem, "count");
305  submesh.Faces.resize(FaceCount);
306 
307  unsigned int i = 0;
308  for (TiXmlElement* face_elem = faces_elem->FirstChildElement("face"); face_elem != nullptr && i < submesh.Faces.size(); face_elem = face_elem->NextSiblingElement("face"), ++i)
309  {
310  int v[3];
311 
312  v[0] = xml.RequireIntAttribute(face_elem, "v1");
313  v[1] = xml.RequireIntAttribute(face_elem, "v2");
314  v[2] = xml.RequireIntAttribute(face_elem, "v3");
315 
316  for (unsigned int j = 0; j < 3; ++j)
317  {
318  if (v[j] < 0 || static_cast<unsigned int>(v[j]) >= vertices->size())
319  xml.Error(FormatString("Vertex index v%u (%d) is out of range", j+1, v[j]), face_elem);
320  submesh.Faces[i].Vertices[j] = v[j];
321  }
322  }
323  }
324 
325  // We allow bounding box to be empty if it's only due to Z direction since
326  // this is what goes inside the screen in Clonk.
327  if(mesh->BoundingBox.x1 == mesh->BoundingBox.x2 || mesh->BoundingBox.y1 == mesh->BoundingBox.y2)
328  xml.Error(StdCopyStrBuf("Bounding box is empty"), mesh_elem);
329 
330  // Read skeleton, if any
331  TiXmlElement* skeletonlink_elem = mesh_elem->FirstChildElement("skeletonlink");
332  if (skeletonlink_elem)
333  {
334  const char* name = xml.RequireStrAttribute(skeletonlink_elem, "name");
335  StdCopyStrBuf xml_filename(name); xml_filename.Append(".xml");
336 
337  StdCopyStrBuf skeleton_filename;
338  StdMeshSkeletonLoader::MakeFullSkeletonPath(skeleton_filename, filename, xml_filename.getData());
339 
340  mesh->Skeleton = skel_loader.GetSkeletonByName(skeleton_filename);
341  if (!mesh->Skeleton) xml.Error(FormatString("Failed to load '%s'", skeleton_filename.getData()), skeletonlink_elem);
342 
343  // Vertex<->Bone assignments for shared geometry
344  if (sharedgeometry_elem)
345  {
346  TiXmlElement* boneassignments_elem = xml.RequireFirstChild(mesh_elem, "boneassignments");
347  xml.LoadBoneAssignments(*mesh, mesh->SharedVertices, boneassignments_elem);
348  }
349 
350  // Vertex<->Bone assignments for all vertices (need to go through SubMeshes again...)
351  unsigned int submesh_index = 0;
352  for (TiXmlElement* submesh_elem = submesh_elem_base; submesh_elem != nullptr; submesh_elem = submesh_elem->NextSiblingElement("submesh"), ++submesh_index)
353  {
354  StdSubMesh& submesh = mesh->SubMeshes[submesh_index];
355  if (!submesh.Vertices.empty())
356  {
357  TiXmlElement* boneassignments_elem = xml.RequireFirstChild(submesh_elem, "boneassignments");
358  xml.LoadBoneAssignments(*mesh, submesh.Vertices, boneassignments_elem);
359  }
360  }
361  }
362  else
363  {
364  // Mesh has no skeleton
365  // Bone assignements do not make sense then, as the
366  // actual bones are defined in the skeleton file.
367  for (TiXmlElement* submesh_elem = submesh_elem_base; submesh_elem != nullptr; submesh_elem = submesh_elem->NextSiblingElement("submesh"))
368  {
369  TiXmlElement* boneassignments_elem = submesh_elem->FirstChildElement("boneassignments");
370  if (boneassignments_elem)
371  xml.Error(StdStrBuf("Mesh has bone assignments, but no skeleton"), boneassignments_elem);
372  }
373 
374  TiXmlElement* boneassignments_elem = mesh_elem->FirstChildElement("boneassignments");
375  if (boneassignments_elem)
376  xml.Error(StdStrBuf("Mesh has bone assignments, but no skeleton"), boneassignments_elem);
377  }
378 
379  return mesh.release();
380 }
381 
382 void StdMeshSkeletonLoader::LoadSkeletonXml(const char* groupname, const char* filename, const char *sourcefile, size_t size)
383 {
384  if (sourcefile == nullptr)
385  {
386  throw Ogre::InsufficientData(FormatString("Failed to load '%s/%s'", groupname, filename).getData());
387  }
388 
389  std::shared_ptr<StdMeshLoader::StdMeshXML> skeleton(new StdMeshLoader::StdMeshXML(filename, sourcefile));
390 
391  TiXmlElement* skeleton_elem = skeleton->RequireFirstChild(nullptr, "skeleton");
392  TiXmlElement* bones_elem = skeleton->RequireFirstChild(skeleton_elem, "bones");
393 
394  // Read bones. Don't insert into Master bone table yet, as the master bone
395  // table is sorted hierarchically, and we will read the hierarchy only
396  // afterwards.
397  std::vector<StdMeshBone*> bones;
398  for (TiXmlElement* bone_elem = bones_elem->FirstChildElement("bone"); bone_elem != nullptr; bone_elem = bone_elem->NextSiblingElement("bone"))
399  {
400  StdMeshBone* bone = new StdMeshBone;
401  bones.push_back(bone);
402 
403  bone->ID = skeleton->RequireIntAttribute(bone_elem, "id");
404  bone->Name = skeleton->RequireStrAttribute(bone_elem, "name");
405  // TODO: Make sure ID and name are unique
406 
407  bone->Parent = nullptr;
408  // Index of bone will be set when building Master Bone Table later
409 
410  TiXmlElement* position_elem = skeleton->RequireFirstChild(bone_elem, "position");
411  TiXmlElement* rotation_elem = skeleton->RequireFirstChild(bone_elem, "rotation");
412  TiXmlElement* axis_elem = skeleton->RequireFirstChild(rotation_elem, "axis");
413 
414  StdMeshVector d, r;
415  d.x = skeleton->RequireFloatAttribute(position_elem, "x");
416  d.y = skeleton->RequireFloatAttribute(position_elem, "y");
417  d.z = skeleton->RequireFloatAttribute(position_elem, "z");
418  float angle = skeleton->RequireFloatAttribute(rotation_elem, "angle");
419  r.x = skeleton->RequireFloatAttribute(axis_elem, "x");
420  r.y = skeleton->RequireFloatAttribute(axis_elem, "y");
421  r.z = skeleton->RequireFloatAttribute(axis_elem, "z");
422 
424  bone->Transformation.rotate = StdMeshQuaternion::AngleAxis(angle, r);
425  bone->Transformation.translate = d;
426 
427  // We need this later for applying animations, and attaching meshes, therefore cache it here
428  bone->InverseTransformation = StdMeshTransformation::Inverse(bone->Transformation);
429  }
430 
431  // Bone hierarchy
432  TiXmlElement* bonehierarchy_elem = skeleton->RequireFirstChild(skeleton_elem, "bonehierarchy");
433  for (TiXmlElement* boneparent_elem = bonehierarchy_elem->FirstChildElement("boneparent"); boneparent_elem != nullptr; boneparent_elem = boneparent_elem->NextSiblingElement("boneparent"))
434  {
435  const char* child_name = skeleton->RequireStrAttribute(boneparent_elem, "bone");
436  const char* parent_name = skeleton->RequireStrAttribute(boneparent_elem, "parent");
437 
438  // Lookup the two bones
439  StdMeshBone* child = nullptr;
440  StdMeshBone* parent = nullptr;
441  for (unsigned int i = 0; i < bones.size() && (!child || !parent); ++i)
442  {
443  if (!child && bones[i]->Name == child_name)
444  child = bones[i];
445  if (!parent && bones[i]->Name == parent_name)
446  parent = bones[i];
447  }
448 
449  if (!child) skeleton->Error(FormatString("There is no such bone with name '%s'", child_name), boneparent_elem);
450  if (!parent) skeleton->Error(FormatString("There is no such bone with name '%s'", parent_name), boneparent_elem);
451 
452  child->Parent = parent;
453  parent->Children.push_back(child);
454  }
455 
456  std::shared_ptr<StdMeshSkeleton> Skeleton(new StdMeshSkeleton);
457 
458  // Fill master bone table in hierarchical order:
459  for (unsigned int i = 0; i < bones.size(); ++i)
460  if (bones[i]->Parent == nullptr)
461  Skeleton->AddMasterBone(bones[i]);
462 
463  // Load Animations
464  TiXmlElement* animations_elem = skeleton_elem->FirstChildElement("animations");
465  if (animations_elem)
466  {
467  for (TiXmlElement* animation_elem = animations_elem->FirstChildElement("animation"); animation_elem != nullptr; animation_elem = animation_elem->NextSiblingElement("animation"))
468  {
469  StdCopyStrBuf name(skeleton->RequireStrAttribute(animation_elem, "name"));
470  if (Skeleton->Animations.find(name) != Skeleton->Animations.end())
471  skeleton->Error(FormatString("There is already an animation with name '%s'", name.getData()), animation_elem);
472 
473  StdMeshAnimation& animation = Skeleton->Animations.insert(std::make_pair(name, StdMeshAnimation())).first->second;
474  animation.Name = name;
475  animation.Length = skeleton->RequireFloatAttribute(animation_elem, "length");
476  animation.Tracks.resize(Skeleton->GetNumBones());
477  animation.OriginSkeleton = &(*Skeleton);
478 
479  TiXmlElement* tracks_elem = skeleton->RequireFirstChild(animation_elem, "tracks");
480  for (TiXmlElement* track_elem = tracks_elem->FirstChildElement("track"); track_elem != nullptr; track_elem = track_elem->NextSiblingElement("track"))
481  {
482  const char* bone_name = skeleton->RequireStrAttribute(track_elem, "bone");
483  StdMeshBone* bone = nullptr;
484  for (unsigned int i = 0; !bone && i < Skeleton->GetNumBones(); ++i)
485  if (Skeleton->Bones[i]->Name == bone_name)
486  bone = Skeleton->Bones[i];
487  if (!bone) skeleton->Error(FormatString("There is no such bone with name '%s'", bone_name), track_elem);
488 
489  if (animation.Tracks[bone->Index] != nullptr) skeleton->Error(FormatString("There is already a track for bone '%s' in animation '%s'", bone_name, animation.Name.getData()), track_elem);
490 
491  StdMeshTrack* track = new StdMeshTrack;
492  animation.Tracks[bone->Index] = track;
493 
494  TiXmlElement* keyframes_elem = skeleton->RequireFirstChild(track_elem, "keyframes");
495  for (TiXmlElement* keyframe_elem = keyframes_elem->FirstChildElement("keyframe"); keyframe_elem != nullptr; keyframe_elem = keyframe_elem->NextSiblingElement("keyframe"))
496  {
497  float time = skeleton->RequireFloatAttribute(keyframe_elem, "time");
498  StdMeshKeyFrame& frame = track->Frames[time];
499 
500  TiXmlElement* translate_elem = keyframe_elem->FirstChildElement("translate");
501  TiXmlElement* rotate_elem = keyframe_elem->FirstChildElement("rotate");
502  TiXmlElement* scale_elem = keyframe_elem->FirstChildElement("scale");
503 
504  StdMeshVector d, s, r;
505  d.x = d.y = d.z = 0.0f;
507  r.x = r.y = 0.0f; r.z = 1.0f;
508  float angle = 0.0f;
509 
510  if (translate_elem)
511  {
512  d.x = skeleton->RequireFloatAttribute(translate_elem, "x");
513  d.y = skeleton->RequireFloatAttribute(translate_elem, "y");
514  d.z = skeleton->RequireFloatAttribute(translate_elem, "z");
515  }
516 
517  if (rotate_elem)
518  {
519  TiXmlElement* axis_elem = skeleton->RequireFirstChild(rotate_elem, "axis");
520  angle = skeleton->RequireFloatAttribute(rotate_elem, "angle");
521  r.x = skeleton->RequireFloatAttribute(axis_elem, "x");
522  r.y = skeleton->RequireFloatAttribute(axis_elem, "y");
523  r.z = skeleton->RequireFloatAttribute(axis_elem, "z");
524  }
525 
526  if (scale_elem)
527  {
528  s.x = skeleton->RequireFloatAttribute(scale_elem, "x");
529  s.y = skeleton->RequireFloatAttribute(scale_elem, "y");
530  s.z = skeleton->RequireFloatAttribute(scale_elem, "z");
531  }
532 
533  frame.Transformation.scale = s;
534  frame.Transformation.rotate = StdMeshQuaternion::AngleAxis(angle, r);
535  frame.Transformation.translate = bone->InverseTransformation.rotate * (bone->InverseTransformation.scale * d);
537  }
538  }
539  }
540  }
541 
542  // is there even any xml file that we load from?
543  // it looks like this could never work: if the mesh has no skeleton, then the code below will fail because of a null pointer...
544 
545  // Apply parent transformation to each bone transformation. We need to
546  // do this late since animation keyframe computation needs the bone
547  // transformations, not bone+parent.
548  for (unsigned int i = 0; i < Skeleton->GetNumBones(); ++i)
549  {
550  // Apply parent transformation
551  if (Skeleton->Bones[i]->Parent)
552  Skeleton->Bones[i]->Transformation = Skeleton->Bones[i]->Parent->Transformation * OgreToClonk::TransformTransformation(Skeleton->Bones[i]->Transformation);
553  else
554  Skeleton->Bones[i]->Transformation = OgreToClonk::TransformTransformation(Skeleton->Bones[i]->Transformation);
555 
556  // Update inverse
557  Skeleton->Bones[i]->InverseTransformation = StdMeshTransformation::Inverse(Skeleton->Bones[i]->Transformation);
558  }
559 
560  StoreSkeleton(groupname, filename, Skeleton);
561 }
const char * getData() const
Definition: StdBuf.h:450
float y2
Definition: StdMesh.h:149
static StdMesh * LoadMeshXml(const char *sourcefile, size_t size, const StdMeshMatManager &mat_mgr, StdMeshSkeletonLoader &loader, const char *filename=0)
#define z
int ID
Definition: StdMesh.h:35
float bone_weight[MaxBoneWeightCount]
Definition: StdMeshMath.h:44
std::shared_ptr< StdMeshSkeleton > GetSkeletonByName(const StdStrBuf &name) const
float x2
Definition: StdMesh.h:149
StdCopyStrBuf Name
Definition: StdMesh.h:99
float z1
Definition: StdMesh.h:148
StdMeshTransformation Transformation
Definition: StdMesh.h:39
float RequireFloatAttribute(TiXmlElement *element, const char *attribute) const
int RequireIntAttribute(TiXmlElement *element, const char *attribute) const
const StdMeshSkeleton & GetSkeleton() const
Definition: StdMesh.h:205
float z2
Definition: StdMesh.h:149
StdMeshTransformation TransformTransformation(const StdMeshTransformation &trans)
const StdMeshBone & GetBone(size_t i) const
Definition: StdMesh.h:118
void LoadGeometry(StdMesh &mesh, std::vector< StdSubMesh::Vertex > &vertices, TiXmlElement *boneassignments_elem)
TiXmlElement * RequireFirstChild(TiXmlElement *element, const char *child)
const char * RequireStrAttribute(TiXmlElement *element, const char *attribute) const
float y1
Definition: StdMesh.h:148
void Append(const char *pnData, size_t iChars)
Definition: StdBuf.h:527
static StdMeshVector UnitScale()
Definition: StdMeshMath.cpp:35
uint16_t bone_index[MaxBoneWeightCount]
Definition: StdMeshMath.h:45
void Error(const StdStrBuf &message, TiXmlElement *element) const
StdCopyStrBuf Name
Definition: StdMesh.h:36
static const size_t MaxBoneWeightCount
Definition: StdMeshMath.h:37
float x1
Definition: StdMesh.h:148
StdMeshTransformation InverseTransformation
Definition: StdMesh.h:41
unsigned int Index
Definition: StdMesh.h:34
StdMeshXML(const char *filename, const char *xml_data)
const StdMeshMaterial * GetMaterial(const char *material_name) const
void LoadBoneAssignments(StdMesh &mesh, std::vector< StdSubMesh::Vertex > &vertices, TiXmlElement *boneassignments_elem)
static void MakeFullSkeletonPath(StdCopyStrBuf &out, const char *groupname, const char *filename)
Definition: StdMeshLoader.h:67
#define s
size_t GetNumBones() const
Definition: StdMesh.h:119
StdMeshTransformation Transformation
Definition: StdMesh.h:70
StdSubMesh::Vertex TransformVertex(const StdSubMesh::Vertex &vertex)
void LoadSkeletonXml(const char *groupname, const char *filename, const char *sourcefile, size_t size)
StdStrBuf FormatString(const char *szFmt,...)
Definition: StdBuf.cpp:277