OpenClonk
StdMeshLoader Class Reference

#include <StdMeshLoader.h>

Classes

class  LoaderException
 
class  StdMeshXML
 

Static Public Member Functions

static StdMeshLoadMeshBinary (const char *sourcefile, size_t size, const StdMeshMatManager &mat_mgr, StdMeshSkeletonLoader &loader, const char *filename=nullptr)
 
static StdMeshLoadMeshXml (const char *sourcefile, size_t size, const StdMeshMatManager &mat_mgr, StdMeshSkeletonLoader &loader, const char *filename=nullptr)
 

Detailed Description

Definition at line 33 of file StdMeshLoader.h.

Member Function Documentation

◆ LoadMeshBinary()

StdMesh * StdMeshLoader::LoadMeshBinary ( const char *  sourcefile,
size_t  size,
const StdMeshMatManager mat_mgr,
StdMeshSkeletonLoader loader,
const char *  filename = nullptr 
)
static

Definition at line 436 of file StdMeshLoaderBinary.cpp.

437 {
438  std::unique_ptr<Ogre::Mesh::Chunk> root;
439  Ogre::DataStream stream(sourcefile, length);
440 
441  // First chunk must be the header
442  root = Ogre::Mesh::Chunk::Read(&stream);
443  if (root->GetType() != Ogre::Mesh::CID_Header)
444  throw Ogre::Mesh::InvalidVersion();
445 
446  // Second chunk is the mesh itself
447  root = Ogre::Mesh::Chunk::Read(&stream);
448  if (root->GetType() != Ogre::Mesh::CID_Mesh)
449  throw Ogre::Mesh::InvalidVersion();
450 
451  // Generate mesh from data
452  Ogre::Mesh::ChunkMesh &cmesh = *static_cast<Ogre::Mesh::ChunkMesh*>(root.get());
453  std::unique_ptr<StdMesh> mesh(new StdMesh);
454 
455  // if the mesh has a skeleton, then try loading
456  // it from the loader by the definition name
457  if (!cmesh.skeletonFile.empty())
458  {
459  StdCopyStrBuf skeleton_filename = StdCopyStrBuf();
460  StdMeshSkeletonLoader::MakeFullSkeletonPath(skeleton_filename, filename, cmesh.skeletonFile.c_str());
461 
462  mesh->Skeleton = loader.GetSkeletonByName(skeleton_filename);
463 
464  // with this exception the assert below is useless
465  // also, I think the bone_lookup should only be used if there is a skeleton anyway
466  // so there could be meshes without bones even?
467  if (mesh->Skeleton == nullptr)
468  {
469  StdCopyStrBuf exception("The specified skeleton file was not found: ");
470  exception.Append(skeleton_filename.getData());
471  throw Ogre::InsufficientData(exception.getData());
472  }
473  }
474 
475  assert(mesh->Skeleton != nullptr); // the bone assignments could instead be added only, if there is a skeleton
476 
477  // Build bone handle->index quick access table
478  std::map<uint16_t, size_t> bone_lookup;
479  for (size_t i = 0; i < mesh->GetSkeleton().GetNumBones(); ++i)
480  {
481  bone_lookup[mesh->GetSkeleton().GetBone(i).ID] = i;
482  }
483 
484  // Read submeshes
485  mesh->SubMeshes.reserve(cmesh.submeshes.size());
486  for (size_t i = 0; i < cmesh.submeshes.size(); ++i)
487  {
488  mesh->SubMeshes.push_back(StdSubMesh());
489  StdSubMesh &sm = mesh->SubMeshes.back();
490  Ogre::Mesh::ChunkSubmesh &csm = *cmesh.submeshes[i];
491  sm.Material = mat_mgr.GetMaterial(csm.material.c_str());
492  if (!sm.Material)
493  throw Ogre::Mesh::InvalidMaterial();
494  if (csm.operation != Ogre::Mesh::ChunkSubmesh::SO_TriList)
495  throw Ogre::Mesh::NotImplemented("Submesh operations other than TriList aren't implemented yet");
496  sm.Faces.resize(csm.faceVertices.size() / 3);
497  for (size_t face = 0; face < sm.Faces.size(); ++face)
498  {
499  sm.Faces[face].Vertices[0] = csm.faceVertices[face * 3 + 0];
500  sm.Faces[face].Vertices[1] = csm.faceVertices[face * 3 + 1];
501  sm.Faces[face].Vertices[2] = csm.faceVertices[face * 3 + 2];
502  }
503  Ogre::Mesh::ChunkGeometry &geo = *(csm.hasSharedVertices ? cmesh.geometry : csm.geometry);
504  sm.Vertices = ReadSubmeshGeometry(geo, filename);
505 
506  // Read bone assignments
507  std::vector<Ogre::Mesh::BoneAssignment> &boneAssignments = (csm.hasSharedVertices ? cmesh.boneAssignments : csm.boneAssignments);
508  assert(!csm.hasSharedVertices || csm.boneAssignments.empty());
509  for(const auto &ba : boneAssignments)
510  {
511  if (ba.vertex >= sm.GetNumVertices())
512  throw Ogre::Mesh::VertexNotFound();
513  if (bone_lookup.find(ba.bone) == bone_lookup.end())
514  throw Ogre::Skeleton::BoneNotFound();
515  size_t bone_index = bone_lookup[ba.bone];
516  // Check quickly if all weight slots are used
517  StdSubMesh::Vertex &vertex = sm.Vertices[ba.vertex];
518  if (vertex.bone_weight[StdMeshVertex::MaxBoneWeightCount - 1] != 0)
519  {
520  throw Ogre::Mesh::NotImplemented("Vertex is influenced by too many bones");
521  }
522  for (size_t weight_index = 0; weight_index < StdMeshVertex::MaxBoneWeightCount; ++weight_index)
523  {
524  if (vertex.bone_weight[weight_index] == 0)
525  {
526  vertex.bone_weight[weight_index] = ba.weight;
527  vertex.bone_index[weight_index] = bone_index;
528  break;
529  }
530  }
531  }
532 
533  // Normalize bone assignments
534  for(StdSubMesh::Vertex &vertex : sm.Vertices)
535  {
536  float sum = 0;
537  for (float weight : vertex.bone_weight)
538  sum += weight;
539  if (sum != 0)
540  for (float &weight : vertex.bone_weight)
541  weight /= sum;
542  else
543  vertex.bone_weight[0] = 1.0f;
544  }
545  }
546 
547  // Construct bounding box. Don't use bounds and radius from cmesh
548  // because they are in a different coordinate frame.
549  //mesh->BoundingBox = cmesh.bounds;
550  //mesh->BoundingRadius = cmesh.radius;
551 
552  bool first = true;
553  for (unsigned int i = 0; i < mesh->SubMeshes.size() + 1; ++i)
554  {
555  const std::vector<StdSubMesh::Vertex>* vertices = nullptr;
556  if (i < mesh->SubMeshes.size())
557  vertices = &mesh->SubMeshes[i].Vertices;
558  else
559  vertices = &mesh->SharedVertices;
560 
561  for (const auto & vertex : *vertices)
562  {
563  const float d = std::sqrt(vertex.x*vertex.x
564  + vertex.y*vertex.y
565  + vertex.z*vertex.z);
566 
567  // First vertex
568  if (first)
569  {
570  mesh->BoundingBox.x1 = mesh->BoundingBox.x2 = vertex.x;
571  mesh->BoundingBox.y1 = mesh->BoundingBox.y2 = vertex.y;
572  mesh->BoundingBox.z1 = mesh->BoundingBox.z2 = vertex.z;
573  mesh->BoundingRadius = d;
574  first = false;
575  }
576  else
577  {
578  mesh->BoundingBox.x1 = std::min(vertex.x, mesh->BoundingBox.x1);
579  mesh->BoundingBox.x2 = std::max(vertex.x, mesh->BoundingBox.x2);
580  mesh->BoundingBox.y1 = std::min(vertex.y, mesh->BoundingBox.y1);
581  mesh->BoundingBox.y2 = std::max(vertex.y, mesh->BoundingBox.y2);
582  mesh->BoundingBox.z1 = std::min(vertex.z, mesh->BoundingBox.z1);
583  mesh->BoundingBox.z2 = std::max(vertex.z, mesh->BoundingBox.z2);
584  mesh->BoundingRadius = std::max(mesh->BoundingRadius, d);
585  }
586  }
587  }
588 
589  // We allow bounding box to be empty if it's only due to Z direction since
590  // this is what goes inside the screen in Clonk.
591  if(mesh->BoundingBox.x1 == mesh->BoundingBox.x2 || mesh->BoundingBox.y1 == mesh->BoundingBox.y2)
592  throw Ogre::Mesh::EmptyBoundingBox();
593 
594  return mesh.release();
595 }
static std::unique_ptr< Chunk > Read(DataStream *stream)
std::unique_ptr< ChunkGeometry > geometry
std::vector< BoneAssignment > boneAssignments
unique_ptr_vector< ChunkSubmesh > submeshes
std::vector< size_t > faceVertices
std::unique_ptr< ChunkGeometry > geometry
enum Ogre::Mesh::ChunkSubmesh::SubmeshOperation SO_TriList
std::vector< BoneAssignment > boneAssignments
const StdMeshMaterial * GetMaterial(const char *material_name) const
static void MakeFullSkeletonPath(StdCopyStrBuf &out, const char *groupname, const char *filename)
Definition: StdMeshLoader.h:66
std::shared_ptr< StdMeshSkeleton > GetSkeletonByName(const StdStrBuf &name) const
const char * getData() const
Definition: StdBuf.h:442
size_t GetNumVertices() const
Definition: StdMesh.h:165
uint16_t bone_index[MaxBoneWeightCount]
Definition: StdMeshMath.h:45
static const size_t MaxBoneWeightCount
Definition: StdMeshMath.h:37
float bone_weight[MaxBoneWeightCount]
Definition: StdMeshMath.h:44

References StdStrBuf::Append(), StdMeshVertex::bone_index, StdMeshVertex::bone_weight, Ogre::Mesh::ChunkMesh::boneAssignments, Ogre::Mesh::ChunkSubmesh::boneAssignments, Ogre::Mesh::CID_Header, Ogre::Mesh::CID_Mesh, Ogre::Mesh::ChunkSubmesh::faceVertices, Ogre::Mesh::ChunkMesh::geometry, Ogre::Mesh::ChunkSubmesh::geometry, StdStrBuf::getData(), StdMeshMatManager::GetMaterial(), StdSubMesh::GetNumVertices(), StdMeshSkeletonLoader::GetSkeletonByName(), Ogre::Mesh::ChunkSubmesh::hasSharedVertices, StdMeshSkeletonLoader::MakeFullSkeletonPath(), Ogre::Mesh::ChunkSubmesh::material, StdMeshVertex::MaxBoneWeightCount, Ogre::Mesh::Chunk::Read(), Ogre::Mesh::ChunkMesh::skeletonFile, Ogre::Mesh::ChunkSubmesh::SO_TriList, Ogre::Mesh::ChunkMesh::submeshes, StdMeshVertex::x, StdMeshVertex::y, and StdMeshVertex::z.

Referenced by C4DefGraphics::LoadMesh().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ LoadMeshXml()

StdMesh * StdMeshLoader::LoadMeshXml ( const char *  sourcefile,
size_t  size,
const StdMeshMatManager mat_mgr,
StdMeshSkeletonLoader loader,
const char *  filename = nullptr 
)
static

Definition at line 261 of file StdMeshLoaderXml.cpp.

262 {
263  StdMeshXML xml(filename ? filename : "<unknown>", xml_data);
264 
265  std::unique_ptr<StdMesh> mesh(new StdMesh);
266 
267  TiXmlElement* mesh_elem = xml.RequireFirstChild(nullptr, "mesh");
268 
269  // Load shared geometry, if any
270  TiXmlElement* sharedgeometry_elem = mesh_elem->FirstChildElement("sharedgeometry");
271  if(sharedgeometry_elem != nullptr)
272  xml.LoadGeometry(*mesh, mesh->SharedVertices, sharedgeometry_elem);
273 
274  TiXmlElement* submeshes_elem = xml.RequireFirstChild(mesh_elem, "submeshes");
275 
276  TiXmlElement* submesh_elem_base = xml.RequireFirstChild(submeshes_elem, "submesh");
277  for (TiXmlElement* submesh_elem = submesh_elem_base; submesh_elem != nullptr; submesh_elem = submesh_elem->NextSiblingElement("submesh"))
278  {
279  mesh->SubMeshes.push_back(StdSubMesh());
280  StdSubMesh& submesh = mesh->SubMeshes.back();
281 
282  const char* material = xml.RequireStrAttribute(submesh_elem, "material");
283  submesh.Material = manager.GetMaterial(material);
284  if (!submesh.Material)
285  xml.Error(FormatString("There is no such material named '%s'", material), submesh_elem);
286 
287  const char* usesharedvertices = submesh_elem->Attribute("usesharedvertices");
288  const std::vector<StdMesh::Vertex>* vertices;
289  if(!usesharedvertices || strcmp(usesharedvertices, "true") != 0)
290  {
291  TiXmlElement* geometry_elem = xml.RequireFirstChild(submesh_elem, "geometry");
292  xml.LoadGeometry(*mesh, submesh.Vertices, geometry_elem);
293  vertices = &submesh.Vertices;
294  }
295  else
296  {
297  if(mesh->SharedVertices.empty())
298  xml.Error(StdCopyStrBuf("Submesh specifies to use shared vertices but there is no shared geometry"), submesh_elem);
299  vertices = &mesh->SharedVertices;
300  }
301 
302  TiXmlElement* faces_elem = xml.RequireFirstChild(submesh_elem, "faces");
303  int FaceCount = xml.RequireIntAttribute(faces_elem, "count");
304  submesh.Faces.resize(FaceCount);
305 
306  unsigned int i = 0;
307  for (TiXmlElement* face_elem = faces_elem->FirstChildElement("face"); face_elem != nullptr && i < submesh.Faces.size(); face_elem = face_elem->NextSiblingElement("face"), ++i)
308  {
309  int v[3];
310 
311  v[0] = xml.RequireIntAttribute(face_elem, "v1");
312  v[1] = xml.RequireIntAttribute(face_elem, "v2");
313  v[2] = xml.RequireIntAttribute(face_elem, "v3");
314 
315  for (unsigned int j = 0; j < 3; ++j)
316  {
317  if (v[j] < 0 || static_cast<unsigned int>(v[j]) >= vertices->size())
318  xml.Error(FormatString("Vertex index v%u (%d) is out of range", j+1, v[j]), face_elem);
319  submesh.Faces[i].Vertices[j] = v[j];
320  }
321  }
322  }
323 
324  // We allow bounding box to be empty if it's only due to Z direction since
325  // this is what goes inside the screen in Clonk.
326  if(mesh->BoundingBox.x1 == mesh->BoundingBox.x2 || mesh->BoundingBox.y1 == mesh->BoundingBox.y2)
327  xml.Error(StdCopyStrBuf("Bounding box is empty"), mesh_elem);
328 
329  // Read skeleton, if any
330  TiXmlElement* skeletonlink_elem = mesh_elem->FirstChildElement("skeletonlink");
331  if (skeletonlink_elem)
332  {
333  const char* name = xml.RequireStrAttribute(skeletonlink_elem, "name");
334  StdCopyStrBuf xml_filename(name); xml_filename.Append(".xml");
335 
336  StdCopyStrBuf skeleton_filename;
337  StdMeshSkeletonLoader::MakeFullSkeletonPath(skeleton_filename, filename, xml_filename.getData());
338 
339  mesh->Skeleton = skel_loader.GetSkeletonByName(skeleton_filename);
340  if (!mesh->Skeleton) xml.Error(FormatString("Failed to load '%s'", skeleton_filename.getData()), skeletonlink_elem);
341 
342  // Vertex<->Bone assignments for shared geometry
343  if (sharedgeometry_elem)
344  {
345  TiXmlElement* boneassignments_elem = xml.RequireFirstChild(mesh_elem, "boneassignments");
346  xml.LoadBoneAssignments(*mesh, mesh->SharedVertices, boneassignments_elem);
347  }
348 
349  // Vertex<->Bone assignments for all vertices (need to go through SubMeshes again...)
350  unsigned int submesh_index = 0;
351  for (TiXmlElement* submesh_elem = submesh_elem_base; submesh_elem != nullptr; submesh_elem = submesh_elem->NextSiblingElement("submesh"), ++submesh_index)
352  {
353  StdSubMesh& submesh = mesh->SubMeshes[submesh_index];
354  if (!submesh.Vertices.empty())
355  {
356  TiXmlElement* boneassignments_elem = xml.RequireFirstChild(submesh_elem, "boneassignments");
357  xml.LoadBoneAssignments(*mesh, submesh.Vertices, boneassignments_elem);
358  }
359  }
360  }
361  else
362  {
363  // Mesh has no skeleton
364  // Bone assignements do not make sense then, as the
365  // actual bones are defined in the skeleton file.
366  for (TiXmlElement* submesh_elem = submesh_elem_base; submesh_elem != nullptr; submesh_elem = submesh_elem->NextSiblingElement("submesh"))
367  {
368  TiXmlElement* boneassignments_elem = submesh_elem->FirstChildElement("boneassignments");
369  if (boneassignments_elem)
370  xml.Error(StdStrBuf("Mesh has bone assignments, but no skeleton"), boneassignments_elem);
371  }
372 
373  TiXmlElement* boneassignments_elem = mesh_elem->FirstChildElement("boneassignments");
374  if (boneassignments_elem)
375  xml.Error(StdStrBuf("Mesh has bone assignments, but no skeleton"), boneassignments_elem);
376  }
377 
378  return mesh.release();
379 }
StdStrBuf FormatString(const char *szFmt,...)
Definition: StdBuf.cpp:270

References StdStrBuf::Append(), StdMeshLoader::StdMeshXML::Error(), FormatString(), StdStrBuf::getData(), StdMeshMatManager::GetMaterial(), StdMeshSkeletonLoader::GetSkeletonByName(), StdMeshLoader::StdMeshXML::LoadBoneAssignments(), StdMeshLoader::StdMeshXML::LoadGeometry(), StdMeshSkeletonLoader::MakeFullSkeletonPath(), StdMeshLoader::StdMeshXML::RequireFirstChild(), StdMeshLoader::StdMeshXML::RequireIntAttribute(), and StdMeshLoader::StdMeshXML::RequireStrAttribute().

Referenced by C4DefGraphics::LoadMesh().

Here is the call graph for this function:
Here is the caller graph for this function:

The documentation for this class was generated from the following files: