OpenClonk
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros
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=0)
 
static StdMeshLoadMeshXml (const char *sourcefile, size_t size, const StdMeshMatManager &mat_mgr, StdMeshSkeletonLoader &loader, const char *filename=0)
 

Detailed Description

Definition at line 34 of file StdMeshLoader.h.

Member Function Documentation

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

Definition at line 438 of file StdMeshLoaderBinary.cpp.

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::ChunkSubmesh::operation, 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().

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

Here is the call graph for this function:

Here is the caller graph for this function:

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

Definition at line 262 of file StdMeshLoaderXml.cpp.

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().

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 }
const char * getData() const
Definition: StdBuf.h:450
static void MakeFullSkeletonPath(StdCopyStrBuf &out, const char *groupname, const char *filename)
Definition: StdMeshLoader.h:67
StdStrBuf FormatString(const char *szFmt,...)
Definition: StdBuf.cpp:277

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: