Panda3D
xFileMesh.cxx
Go to the documentation of this file.
1 /**
2  * PANDA 3D SOFTWARE
3  * Copyright (c) Carnegie Mellon University. All rights reserved.
4  *
5  * All use of this software is subject to the terms of the revised BSD
6  * license. You should have received a copy of this license along
7  * with this source code in a file named "LICENSE."
8  *
9  * @file xFileMesh.cxx
10  * @author drose
11  * @date 2001-06-19
12  */
13 
14 #include "xFileMesh.h"
15 #include "xFileToEggConverter.h"
16 #include "xFileFace.h"
17 #include "xFileVertex.h"
18 #include "xFileNormal.h"
19 #include "xFileMaterial.h"
20 #include "xFileDataNode.h"
21 #include "config_xfile.h"
22 #include "string_utils.h"
23 #include "eggVertexPool.h"
24 #include "eggVertex.h"
25 #include "eggPolygon.h"
26 #include "eggGroup.h"
27 #include "eggGroupNode.h"
28 
29 using std::min;
30 using std::string;
31 
32 /**
33  *
34  */
35 XFileMesh::
36 XFileMesh(CoordinateSystem cs) : _cs(cs) {
37  _has_normals = false;
38  _has_colors = false;
39  _has_uvs = false;
40  _has_materials = false;
41  _egg_parent = nullptr;
42 }
43 
44 /**
45  *
46  */
47 XFileMesh::
48 ~XFileMesh() {
49  clear();
50 }
51 
52 /**
53  * Empties all data from the mesh.
54  */
55 void XFileMesh::
56 clear() {
57  Vertices::iterator vi;
58  for (vi = _vertices.begin(); vi != _vertices.end(); ++vi) {
59  XFileVertex *vertex = (*vi);
60  delete vertex;
61  }
62  Normals::iterator ni;
63  for (ni = _normals.begin(); ni != _normals.end(); ++ni) {
64  XFileNormal *normal = (*ni);
65  delete normal;
66  }
67  Materials::iterator mi;
68  for (mi = _materials.begin(); mi != _materials.end(); ++mi) {
69  XFileMaterial *material = (*mi);
70  delete material;
71  }
72  Faces::iterator fi;
73  for (fi = _faces.begin(); fi != _faces.end(); ++fi) {
74  XFileFace *face = (*fi);
75  delete face;
76  }
77 
78  _vertices.clear();
79  _normals.clear();
80  _materials.clear();
81  _faces.clear();
82 
83  _unique_vertices.clear();
84  _unique_normals.clear();
85  _unique_materials.clear();
86 
87  _has_normals = false;
88  _has_colors = false;
89  _has_uvs = false;
90  _has_materials = false;
91 }
92 
93 /**
94  * Adds the indicated polygon to the mesh.
95  */
96 void XFileMesh::
97 add_polygon(EggPolygon *egg_poly) {
98  XFileFace *face = new XFileFace;
99  face->set_from_egg(this, egg_poly);
100  _faces.push_back(face);
101 }
102 
103 /**
104  * Creates a new XFileVertex, if one does not already exist for the indicated
105  * vertex, and returns its index.
106  */
107 int XFileMesh::
108 add_vertex(EggVertex *egg_vertex, EggPrimitive *egg_prim) {
109  int next_index = _vertices.size();
110  XFileVertex *vertex = new XFileVertex;
111  vertex->set_from_egg(egg_vertex, egg_prim);
112  if (vertex->_has_color) {
113  _has_colors = true;
114  }
115  if (vertex->_has_uv) {
116  _has_uvs = true;
117  }
118 
119  std::pair<UniqueVertices::iterator, bool> result =
120  _unique_vertices.insert(UniqueVertices::value_type(vertex, next_index));
121 
122  if (result.second) {
123  // Successfully added; this is a new vertex.
124  _vertices.push_back(vertex);
125  return next_index;
126  } else {
127  // Not successfully added; there is already a vertex with these
128  // properties. Return that one instead.
129  delete vertex;
130  return (*result.first).second;
131  }
132 }
133 
134 
135 /**
136  * Creates a new XFileNormal, if one does not already exist for the indicated
137  * normal, and returns its index.
138  */
139 int XFileMesh::
140 add_normal(EggVertex *egg_vertex, EggPrimitive *egg_prim) {
141  int next_index = _normals.size();
142  XFileNormal *normal = new XFileNormal;
143  normal->set_from_egg(egg_vertex, egg_prim);
144  if (normal->_has_normal) {
145  _has_normals = true;
146  }
147 
148  std::pair<UniqueNormals::iterator, bool> result =
149  _unique_normals.insert(UniqueNormals::value_type(normal, next_index));
150 
151  if (result.second) {
152  // Successfully added; this is a new normal.
153  _normals.push_back(normal);
154  return next_index;
155  } else {
156  // Not successfully added; there is already a normal with these
157  // properties. Return that one instead.
158  delete normal;
159  return (*result.first).second;
160  }
161 }
162 
163 /**
164  * Creates a new XFileMaterial, if one does not already exist for the
165  * indicated material, and returns its index.
166  */
167 int XFileMesh::
169  int next_index = _materials.size();
170  XFileMaterial *material = new XFileMaterial;
171  material->set_from_egg(egg_prim);
172  if (material->has_material()) {
173  _has_materials = true;
174  }
175 
176  std::pair<UniqueMaterials::iterator, bool> result =
177  _unique_materials.insert(UniqueMaterials::value_type(material, next_index));
178 
179  if (result.second) {
180  // Successfully added; this is a new material.
181  _materials.push_back(material);
182  return next_index;
183  } else {
184  // Not successfully added; there is already a material with these
185  // properties. Return that one instead.
186  delete material;
187  return (*result.first).second;
188  }
189 }
190 
191 
192 /**
193  * Adds the newly-created XFileVertex unequivocally to the mesh, returning its
194  * index number. The XFileMesh object becomes the owner of the XFileVertex
195  * pointer, and will delete it when it destructs.
196  */
197 int XFileMesh::
199  if (vertex->_has_color) {
200  _has_colors = true;
201  }
202  if (vertex->_has_uv) {
203  _has_uvs = true;
204  }
205 
206  int next_index = _vertices.size();
207  _unique_vertices.insert(UniqueVertices::value_type(vertex, next_index));
208  _vertices.push_back(vertex);
209  return next_index;
210 }
211 
212 /**
213  * Adds the newly-created XFileNormal unequivocally to the mesh, returning its
214  * index number. The XFileMesh object becomes the owner of the XFileNormal
215  * pointer, and will delete it when it destructs.
216  */
217 int XFileMesh::
219  if (normal->_has_normal) {
220  _has_normals = true;
221  }
222 
223  int next_index = _normals.size();
224  _unique_normals.insert(UniqueNormals::value_type(normal, next_index));
225  _normals.push_back(normal);
226  return next_index;
227 }
228 
229 /**
230  * Adds the newly-created XFileMaterial unequivocally to the mesh, returning
231  * its index number. The XFileMesh object becomes the owner of the
232  * XFileMaterial pointer, and will delete it when it destructs.
233  */
234 int XFileMesh::
236  if (material->has_material()) {
237  _has_materials = true;
238  }
239 
240  int next_index = _materials.size();
241  _unique_materials.insert(UniqueMaterials::value_type(material, next_index));
242  _materials.push_back(material);
243  return next_index;
244 }
245 
246 /**
247  * Specifies the egg node that will eventually be the parent of this mesh,
248  * when create_polygons() is later called.
249  */
250 void XFileMesh::
252  // We actually put the mesh under its own group.
253  EggGroup *egg_group = new EggGroup(get_name());
254  egg_parent->add_child(egg_group);
255 
256  _egg_parent = egg_group;
257 }
258 
259 /**
260  * Creates a slew of EggPolygons according to the faces in the mesh, and adds
261  * them to the previously-indicated parent node.
262  */
263 bool XFileMesh::
265  nassertr(_egg_parent != nullptr, false);
266 
267  EggVertexPool *vpool = new EggVertexPool(get_name());
268  _egg_parent->add_child(vpool);
269  Faces::const_iterator fi;
270  for (fi = _faces.begin(); fi != _faces.end(); ++fi) {
271  XFileFace *face = (*fi);
272 
273  EggPolygon *egg_poly = new EggPolygon;
274  _egg_parent->add_child(egg_poly);
275 
276  // Set up the vertices for the polygon.
277  XFileFace::Vertices::reverse_iterator vi;
278  for (vi = face->_vertices.rbegin(); vi != face->_vertices.rend(); ++vi) {
279  int vertex_index = (*vi)._vertex_index;
280  int normal_index = (*vi)._normal_index;
281  if (vertex_index < 0 || vertex_index >= (int)_vertices.size()) {
282  xfile_cat.warning()
283  << "Vertex index out of range in Mesh " << get_name() << "\n";
284  continue;
285  }
286  XFileVertex *vertex = _vertices[vertex_index];
287  XFileNormal *normal = nullptr;
288 
289  if (normal_index >= 0 && normal_index < (int)_normals.size()) {
290  normal = _normals[normal_index];
291  }
292 
293  // Create a temporary EggVertex before adding it to the pool.
294  EggVertex temp_vtx;
295  temp_vtx.set_external_index(vertex_index);
296  temp_vtx.set_pos(vertex->_point);
297  if (vertex->_has_color) {
298  temp_vtx.set_color(vertex->_color);
299  }
300  if (vertex->_has_uv) {
301  LTexCoordd uv = vertex->_uv;
302  // Windows draws the UV's upside-down.
303  uv[1] = 1.0 - uv[1];
304  temp_vtx.set_uv(uv);
305  }
306 
307  if (normal != nullptr && normal->_has_normal) {
308  temp_vtx.set_normal(normal->_normal);
309  }
310 
311  // We are given the vertex in local space; we need to transform it into
312  // global space. If the vertex has been skinned, that means the global
313  // space of all of its joints (modified by the matrix_offset provided in
314  // the skinning data).
315  double net_weight = 0.0;
316  LMatrix4d weighted_transform(0.0, 0.0, 0.0, 0.0,
317  0.0, 0.0, 0.0, 0.0,
318  0.0, 0.0, 0.0, 0.0,
319  0.0, 0.0, 0.0, 0.0);
320  SkinWeights::const_iterator swi;
321  for (swi = _skin_weights.begin(); swi != _skin_weights.end(); ++swi) {
322  const SkinWeightsData &data = (*swi);
323  WeightMap::const_iterator wmi = data._weight_map.find(vertex_index);
324  if (wmi != data._weight_map.end()) {
325  EggGroup *joint = converter->find_joint(data._joint_name);
326  if (joint != nullptr) {
327  double weight = (*wmi).second;
328  LMatrix4d mat = data._matrix_offset;
329  mat *= joint->get_node_to_vertex();
330  weighted_transform += mat * weight;
331  net_weight += weight;
332  }
333  }
334  }
335 
336  if (net_weight == 0.0) {
337  // The vertex had no joint membership. Transform it into the
338  // appropriate (global) space based on its parent.
339  temp_vtx.transform(_egg_parent->get_node_to_vertex());
340 
341  } else {
342  // The vertex was skinned into one or more joints. Therefore,
343  // transform it according to the blended matrix_offset from the
344  // skinning data.
345  weighted_transform /= net_weight;
346  temp_vtx.transform(weighted_transform);
347  }
348 
349  // Now get a real EggVertex matching our template.
350  EggVertex *egg_vtx = vpool->create_unique_vertex(temp_vtx);
351  egg_poly->add_vertex(egg_vtx);
352  }
353 
354  // And apply the material for the polygon.
355  int material_index = face->_material_index;
356  if (material_index >= 0 && material_index < (int)_materials.size()) {
357  XFileMaterial *material = _materials[material_index];
358  material->apply_to_egg(egg_poly, converter);
359  }
360  }
361 
362  // Now go through all of the vertices and skin them up.
364  for (vi = vpool->begin(); vi != vpool->end(); ++vi) {
365  EggVertex *egg_vtx = (*vi);
366  int vertex_index = egg_vtx->get_external_index();
367 
368  SkinWeights::const_iterator swi;
369  for (swi = _skin_weights.begin(); swi != _skin_weights.end(); ++swi) {
370  const SkinWeightsData &data = (*swi);
371  WeightMap::const_iterator wmi = data._weight_map.find(vertex_index);
372  if (wmi != data._weight_map.end()) {
373  EggGroup *joint = converter->find_joint(data._joint_name);
374  if (joint != nullptr) {
375  double weight = (*wmi).second;
376  joint->ref_vertex(egg_vtx, weight);
377  }
378  }
379  }
380  }
381 
382  if (!has_normals()) {
383  // If we don't have explicit normals, make some up, per the DX spec.
384  // Since the DX spec doesn't mention anything about a crease angle, we
385  // should be as generous as possible.
386  _egg_parent->recompute_vertex_normals(180.0, _cs);
387  }
388 
389  return true;
390 }
391 
392 /**
393  * Returns true if any of the vertices or faces added to this mesh used a
394  * normal, false otherwise.
395  */
396 bool XFileMesh::
397 has_normals() const {
398  return _has_normals;
399 }
400 
401 /**
402  * Returns true if any of the vertices or faces added to this mesh used a
403  * color, false otherwise.
404  */
405 bool XFileMesh::
406 has_colors() const {
407  return _has_colors;
408 }
409 
410 /**
411  * Returns true if any of the vertices added to this mesh used a texture
412  * coordinate, false otherwise.
413  */
414 bool XFileMesh::
415 has_uvs() const {
416  return _has_uvs;
417 }
418 
419 /**
420  * Returns true if any of the faces added to this mesh used a real material,
421  * false otherwise.
422  */
423 bool XFileMesh::
424 has_materials() const {
425  return _has_materials;
426 }
427 
428 /**
429  * Returns the number of distinct materials associated with the mesh.
430  */
431 int XFileMesh::
433  return _materials.size();
434 }
435 
436 /**
437  * Returns a pointer to the nth materials associated with the mesh.
438  */
440 get_material(int n) const {
441  nassertr(n >= 0 && n < (int)_materials.size(), nullptr);
442  return _materials[n];
443 }
444 
445 /**
446  * Creates an X structure corresponding to the mesh.
447  */
449 make_x_mesh(XFileNode *x_parent, const string &suffix) {
450  XFileDataNode *x_mesh = x_parent->add_Mesh("mesh" + suffix);
451 
452  // First, fill in the table of vertices.
453  XFileDataObject &x_vertices = (*x_mesh)["vertices"];
454 
455  Vertices::const_iterator vi;
456  for (vi = _vertices.begin(); vi != _vertices.end(); ++vi) {
457  XFileVertex *vertex = (*vi);
458  x_vertices.add_Vector(x_mesh->get_x_file(), vertex->_point);
459  }
460  (*x_mesh)["nVertices"] = x_vertices.size();
461 
462  // Then, create the list of faces that index into the above vertices.
463  XFileDataObject &x_faces = (*x_mesh)["faces"];
464  Faces::const_iterator fi;
465  for (fi = _faces.begin(); fi != _faces.end(); ++fi) {
466  XFileFace *face = (*fi);
467 
468  XFileDataObject &x_mesh_face = x_faces.add_MeshFace(x_mesh->get_x_file());
469  XFileDataObject &x_faceVertexIndices = x_mesh_face["faceVertexIndices"];
470  XFileFace::Vertices::const_iterator fvi;
471  for (fvi = face->_vertices.begin();
472  fvi != face->_vertices.end();
473  ++fvi) {
474  x_faceVertexIndices.add_int((*fvi)._vertex_index);
475  }
476  x_mesh_face["nFaceVertexIndices"] = x_faceVertexIndices.size();
477  }
478  (*x_mesh)["nFaces"] = x_faces.size();
479 
480  // Now, add in any supplemental data.
481 
482  if (has_normals()) {
483  // Tack on normals.
484  make_x_normals(x_mesh, suffix);
485  }
486  if (has_colors()) {
487  // Tack on colors.
488  make_x_colors(x_mesh, suffix);
489  }
490  if (has_uvs()) {
491  // Tack on uvs.
492  make_x_uvs(x_mesh, suffix);
493  }
494  if (has_materials()) {
495  // Tack on materials.
496  make_x_material_list(x_mesh, suffix);
497  }
498 
499  return x_mesh;
500 }
501 
502 /**
503  * Creates a MeshNormals table for the mesh.
504  */
506 make_x_normals(XFileNode *x_mesh, const string &suffix) {
507  XFileDataNode *x_meshNormals = x_mesh->add_MeshNormals("norms" + suffix);
508 
509  XFileDataObject &x_normals = (*x_meshNormals)["normals"];
510 
511  Normals::const_iterator ni;
512  for (ni = _normals.begin(); ni != _normals.end(); ++ni) {
513  XFileNormal *normal = (*ni);
514  x_normals.add_Vector(x_mesh->get_x_file(), normal->_normal);
515  }
516  (*x_meshNormals)["nNormals"] = x_normals.size();
517 
518  // Then, create the list of faces that index into the above normals.
519  XFileDataObject &x_faces = (*x_meshNormals)["faceNormals"];
520  Faces::const_iterator fi;
521  for (fi = _faces.begin(); fi != _faces.end(); ++fi) {
522  XFileFace *face = (*fi);
523 
524  XFileDataObject &x_normals_face = x_faces.add_MeshFace(x_mesh->get_x_file());
525  XFileDataObject &x_faceVertexIndices = x_normals_face["faceVertexIndices"];
526  XFileFace::Vertices::const_iterator fvi;
527  for (fvi = face->_vertices.begin();
528  fvi != face->_vertices.end();
529  ++fvi) {
530  x_faceVertexIndices.add_int((*fvi)._normal_index);
531  }
532  x_normals_face["nFaceVertexIndices"] = x_faceVertexIndices.size();
533  }
534  (*x_meshNormals)["nFaceNormals"] = x_faces.size();
535 
536  return x_meshNormals;
537 }
538 
539 /**
540  * Creates a MeshVertexColors table for the mesh.
541  */
543 make_x_colors(XFileNode *x_mesh, const string &suffix) {
544  XFileDataNode *x_meshColors = x_mesh->add_MeshVertexColors("colors" + suffix);
545 
546  XFileDataObject &x_colors = (*x_meshColors)["vertexColors"];
547 
548  Vertices::const_iterator vi;
549  int i = 0;
550  for (vi = _vertices.begin(); vi != _vertices.end(); ++vi) {
551  XFileVertex *vertex = (*vi);
552  const LColor &color = vertex->_color;
553  x_colors.add_IndexedColor(x_mesh->get_x_file(), i, color);
554  i++;
555  }
556 
557  (*x_meshColors)["nVertexColors"] = x_colors.size();
558 
559  return x_meshColors;
560 }
561 
562 /**
563  * Creates a MeshTextureCoords table for the mesh.
564  */
566 make_x_uvs(XFileNode *x_mesh, const string &suffix) {
567  XFileDataNode *x_meshUvs = x_mesh->add_MeshTextureCoords("uvs" + suffix);
568 
569  XFileDataObject &x_uvs = (*x_meshUvs)["textureCoords"];
570 
571  Vertices::const_iterator vi;
572  for (vi = _vertices.begin(); vi != _vertices.end(); ++vi) {
573  XFileVertex *vertex = (*vi);
574  x_uvs.add_Coords2d(x_mesh->get_x_file(), vertex->_uv);
575  }
576 
577  (*x_meshUvs)["nTextureCoords"] = x_uvs.size();
578 
579  return x_meshUvs;
580 }
581 
582 /**
583  * Creates a MeshMaterialList table for the mesh.
584  */
586 make_x_material_list(XFileNode *x_mesh, const string &suffix) {
587  XFileDataNode *x_meshMaterials =
588  x_mesh->add_MeshMaterialList("materials" + suffix);
589 
590  // First, build up the list of faces the reference the materials.
591  XFileDataObject &x_indexes = (*x_meshMaterials)["faceIndexes"];
592 
593  Faces::const_iterator fi;
594  for (fi = _faces.begin(); fi != _faces.end(); ++fi) {
595  XFileFace *face = (*fi);
596  x_indexes.add_int(face->_material_index);
597  }
598 
599  (*x_meshMaterials)["nFaceIndexes"] = x_indexes.size();
600 
601  // Now, build up the list of materials themselves. Each material is a child
602  // of the MeshMaterialList node, rather than an element of an array.
603  for (size_t i = 0; i < _materials.size(); i++) {
604  XFileMaterial *material = _materials[i];
605 
606  material->make_x_material(x_meshMaterials,
607  suffix + "_" + format_string(i));
608  }
609 
610  (*x_meshMaterials)["nMaterials"] = (int)_materials.size();
611 
612  return x_meshMaterials;
613 }
614 
615 /**
616  * Fills the structure based on the raw data from the X file's Mesh object.
617  */
618 bool XFileMesh::
620  clear();
621 
622  int i, j;
623 
624  const XFileDataObject &vertices = (*obj)["vertices"];
625  for (i = 0; i < vertices.size(); i++) {
626  XFileVertex *vertex = new XFileVertex;
627  vertex->_point = vertices[i].vec3();
628  add_vertex(vertex);
629  }
630 
631  const XFileDataObject &faces = (*obj)["faces"];
632  for (i = 0; i < faces.size(); i++) {
633  XFileFace *face = new XFileFace;
634 
635  const XFileDataObject &faceIndices = faces[i]["faceVertexIndices"];
636 
637  for (j = 0; j < faceIndices.size(); j++) {
638  XFileFace::Vertex vertex;
639  vertex._vertex_index = faceIndices[j].i();
640  vertex._normal_index = -1;
641 
642  face->_vertices.push_back(vertex);
643  }
644  _faces.push_back(face);
645  }
646 
647  // Some properties are stored as children of the mesh.
648  int num_objects = obj->get_num_objects();
649  for (i = 0; i < num_objects; i++) {
650  if (!fill_mesh_child(obj->get_object(i))) {
651  return false;
652  }
653  }
654 
655  return true;
656 }
657 
658 /**
659  * Fills the structure based on one of the children of the Mesh object.
660  */
661 bool XFileMesh::
663  if (obj->is_standard_object("MeshNormals")) {
664  if (!fill_normals(obj)) {
665  return false;
666  }
667 
668  } else if (obj->is_standard_object("MeshVertexColors")) {
669  if (!fill_colors(obj)) {
670  return false;
671  }
672 
673  } else if (obj->is_standard_object("MeshTextureCoords")) {
674  if (!fill_uvs(obj)) {
675  return false;
676  }
677 
678  } else if (obj->is_standard_object("MeshMaterialList")) {
679  if (!fill_material_list(obj)) {
680  return false;
681  }
682 
683  } else if (obj->is_standard_object("XSkinMeshHeader")) {
684  // Quietly ignore a skin mesh header.
685 
686  } else if (obj->is_standard_object("SkinWeights")) {
687  if (!fill_skin_weights(obj)) {
688  return false;
689  }
690 
691  } else {
692  if (xfile_cat.is_debug()) {
693  xfile_cat.debug()
694  << "Ignoring mesh data object of unknown type: "
695  << obj->get_template_name() << "\n";
696  }
697  }
698 
699  return true;
700 }
701 
702 /**
703  * Fills the structure based on the raw data from the MeshNormals template.
704  */
705 bool XFileMesh::
707  int i, j;
708 
709  const XFileDataObject &normals = (*obj)["normals"];
710  for (i = 0; i < normals.size(); i++) {
711  XFileNormal *normal = new XFileNormal;
712  normal->_normal = normals[i].vec3();
713  normal->_has_normal = true;
714  add_normal(normal);
715  }
716 
717  const XFileDataObject &faceNormals = (*obj)["faceNormals"];
718  if (faceNormals.size() != (int)_faces.size()) {
719  xfile_cat.warning()
720  << "Incorrect number of faces in MeshNormals within "
721  << get_name() << "\n";
722  }
723 
724  int num_normals = min(faceNormals.size(), (int)_faces.size());
725  for (i = 0; i < num_normals; i++) {
726  XFileFace *face = _faces[i];
727 
728  const XFileDataObject &faceIndices = faceNormals[i]["faceVertexIndices"];
729 
730  if (faceIndices.size() != (int)face->_vertices.size()) {
731  xfile_cat.warning()
732  << "Incorrect number of vertices for face in MeshNormals within "
733  << get_name() << "\n";
734  }
735 
736  int num_vertices = min(faceIndices.size(), (int)face->_vertices.size());
737  for (j = 0; j < num_vertices; j++) {
738  face->_vertices[j]._normal_index = faceIndices[j].i();
739  }
740  }
741 
742  return true;
743 }
744 
745 /**
746  * Fills the structure based on the raw data from the MeshVertexColors
747  * template.
748  */
749 bool XFileMesh::
751  const XFileDataObject &vertexColors = (*obj)["vertexColors"];
752  for (int i = 0; i < vertexColors.size(); i++) {
753  int vertex_index = vertexColors[i]["index"].i();
754  if (vertex_index < 0 || vertex_index >= (int)_vertices.size()) {
755  xfile_cat.warning()
756  << "Vertex index out of range in MeshVertexColors within "
757  << get_name() << "\n";
758  continue;
759  }
760 
761  XFileVertex *vertex = _vertices[vertex_index];
762  vertex->_color = LCAST(PN_stdfloat, vertexColors[i]["indexColor"].vec4());
763  vertex->_has_color = true;
764  }
765 
766  return true;
767 }
768 
769 /**
770  * Fills the structure based on the raw data from the MeshTextureCoords
771  * template.
772  */
773 bool XFileMesh::
775  const XFileDataObject &textureCoords = (*obj)["textureCoords"];
776  if (textureCoords.size() != (int)_vertices.size()) {
777  xfile_cat.warning()
778  << "Wrong number of vertices in MeshTextureCoords within "
779  << get_name() << "\n";
780  }
781 
782  int num_texcoords = min(textureCoords.size(), (int)_vertices.size());
783  for (int i = 0; i < num_texcoords; i++) {
784  XFileVertex *vertex = _vertices[i];
785  vertex->_uv = textureCoords[i].vec2();
786  vertex->_has_uv = true;
787  }
788 
789  return true;
790 }
791 
792 /**
793  * Fills the structure based on the raw data from the SkinWeights template.
794  */
795 bool XFileMesh::
797  // Create a new SkinWeightsData record for the table. We'll need this data
798  // later when we create the vertices.
799  _skin_weights.push_back(SkinWeightsData());
800  SkinWeightsData &data = _skin_weights.back();
801 
802  data._joint_name = (*obj)["transformNodeName"].s();
803 
804  const XFileDataObject &vertexIndices = (*obj)["vertexIndices"];
805  const XFileDataObject &weights = (*obj)["weights"];
806 
807  if (weights.size() != vertexIndices.size()) {
808  xfile_cat.warning()
809  << "Inconsistent number of vertices in SkinWeights within " << get_name() << "\n";
810  }
811 
812  // Unpack the weight for each vertex.
813  size_t num_weights = min(weights.size(), vertexIndices.size());
814  for (size_t i = 0; i < num_weights; i++) {
815  int vindex = vertexIndices[i].i();
816  double weight = weights[i].d();
817 
818  if (vindex < 0 || vindex > (int)_vertices.size()) {
819  xfile_cat.warning()
820  << "Illegal vertex index " << vindex << " in SkinWeights.\n";
821  continue;
822  }
823  data._weight_map[vindex] = weight;
824  }
825 
826  // Also retrieve the matrix offset.
827  data._matrix_offset = (*obj)["matrixOffset"]["matrix"].mat4();
828 
829  return true;
830 }
831 
832 /**
833  * Fills the structure based on the raw data from the MeshMaterialList
834  * template.
835  */
836 bool XFileMesh::
838  const XFileDataObject &faceIndexes = (*obj)["faceIndexes"];
839  if (faceIndexes.size() > (int)_faces.size()) {
840  xfile_cat.warning()
841  << "Too many faces in MeshMaterialList within " << get_name() << "\n";
842  }
843 
844  int material_index = -1;
845  int i = 0;
846  while (i < faceIndexes.size() && i < (int)_faces.size()) {
847  XFileFace *face = _faces[i];
848  material_index = faceIndexes[i].i();
849  face->_material_index = material_index;
850  i++;
851  }
852 
853  // The rest of the faces get the same material index as the last one in the
854  // list.
855  while (i < (int)_faces.size()) {
856  XFileFace *face = _faces[i];
857  face->_material_index = material_index;
858  i++;
859  }
860 
861  // Now look for children of the MaterialList object. These should all be
862  // Material objects.
863  int num_objects = obj->get_num_objects();
864  for (i = 0; i < num_objects; i++) {
865  XFileDataNode *child = obj->get_object(i);
866  if (child->is_standard_object("Material")) {
867  XFileMaterial *material = new XFileMaterial;
868  if (!material->fill_material(child)) {
869  delete material;
870  return false;
871  }
872  add_material(material);
873 
874  } else {
875  if (xfile_cat.is_debug()) {
876  xfile_cat.debug()
877  << "Ignoring material list object of unknown type: "
878  << child->get_template_name() << "\n";
879  }
880  }
881  }
882 
883  return true;
884 }
A base class for any of a number of kinds of geometry primitives: polygons, point lights,...
Definition: eggPrimitive.h:47
void clear()
Empties all data from the mesh.
Definition: xFileMesh.cxx:56
XFileDataNode * make_x_colors(XFileNode *x_mesh, const std::string &suffix)
Creates a MeshVertexColors table for the mesh.
Definition: xFileMesh.cxx:543
XFileDataNode * add_MeshTextureCoords(const std::string &name)
Creates a new MeshTextureCoords instance, as a child of this node.
Definition: xFileNode.cxx:345
This is an iterator adaptor that converts any iterator that returns a pair (e.g.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
bool fill_mesh(XFileDataNode *obj)
Fills the structure based on the raw data from the X file's Mesh object.
Definition: xFileMesh.cxx:619
XFileDataObject & add_int(int int_value)
Appends a new integer value to the data object, if it makes sense to do so.
XFileDataNode * add_MeshNormals(const std::string &name)
Creates a new MeshNormals instance, as a child of this node.
Definition: xFileNode.cxx:315
void set_pos(double pos)
Sets the vertex position.
Definition: eggVertex.I:42
bool has_normals() const
Returns true if any of the vertices or faces added to this mesh used a normal, false otherwise.
Definition: xFileMesh.cxx:397
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A base class for nodes in the hierarchy that are not leaf nodes.
Definition: eggGroupNode.h:46
int i() const
Unambiguously returns the object's representation as an integer, or 0 if the object has no integer re...
This represents a single normal associated with an XFileFace.
Definition: xFileNormal.h:28
bool has_uvs() const
Returns true if any of the vertices added to this mesh used a texture coordinate, false otherwise.
Definition: xFileMesh.cxx:415
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
XFileDataNode * make_x_material(XFileNode *x_meshMaterials, const std::string &suffix)
Creates a Material object for the material list.
bool fill_normals(XFileDataNode *obj)
Fills the structure based on the raw data from the MeshNormals template.
Definition: xFileMesh.cxx:706
int add_vertex(EggVertex *egg_vertex, EggPrimitive *egg_prim)
Creates a new XFileVertex, if one does not already exist for the indicated vertex,...
Definition: xFileMesh.cxx:108
void apply_to_egg(EggPrimitive *egg_prim, XFileToEggConverter *converter)
Applies the properties in the material to the indicated egg primitive.
int add_material(EggPrimitive *egg_prim)
Creates a new XFileMaterial, if one does not already exist for the indicated material,...
Definition: xFileMesh.cxx:168
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
XFileDataObject & add_MeshFace(XFile *x_file)
Appends a new MeshFace instance.
XFileDataNode * make_x_material_list(XFileNode *x_mesh, const std::string &suffix)
Creates a MeshMaterialList table for the mesh.
Definition: xFileMesh.cxx:586
void set_external_index(int external_index)
Sets a special index number that is associated with the EggVertex (but is not written to the egg file...
Definition: eggVertex.I:292
void set_from_egg(EggPrimitive *egg_prim)
Sets the structure up from the indicated egg data.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
iterator end() const
Returns an iterator that can be used to traverse through all the vertices in the pool.
XFileDataNode * add_Mesh(const std::string &name)
Creates a new Mesh instance, as a child of this node.
Definition: xFileNode.cxx:300
bool fill_material_list(XFileDataNode *obj)
Fills the structure based on the raw data from the MeshMaterialList template.
Definition: xFileMesh.cxx:837
bool fill_skin_weights(XFileDataNode *obj)
Fills the structure based on the raw data from the SkinWeights template.
Definition: xFileMesh.cxx:796
void set_from_egg(EggVertex *egg_vertex, EggPrimitive *egg_poly)
Sets the structure up from the indicated egg data.
Definition: xFileVertex.cxx:35
int add_normal(EggVertex *egg_vertex, EggPrimitive *egg_prim)
Creates a new XFileNormal, if one does not already exist for the indicated normal,...
Definition: xFileMesh.cxx:140
bool has_material() const
Returns true if this material represents something meaningful, or false if the default material is su...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
int get_num_objects() const
Returns the list of child objects of this node.
Definition: xFileNode.I:47
iterator begin() const
Returns an iterator that can be used to traverse through all the vertices in the pool.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void add_polygon(EggPolygon *egg_poly)
Adds the indicated polygon to the mesh.
Definition: xFileMesh.cxx:97
void set_from_egg(EggVertex *egg_vertex, EggPrimitive *egg_prim)
Sets the structure up from the indicated egg data.
Definition: xFileNormal.cxx:32
The main glue of the egg hierarchy, this corresponds to the <Group>, <Instance>, and <Joint> type nod...
Definition: eggGroup.h:34
XFileMaterial * get_material(int n) const
Returns a pointer to the nth materials associated with the mesh.
Definition: xFileMesh.cxx:440
XFileDataObject & add_Vector(XFile *x_file, const LVecBase3d &vector)
Appends a new Vector instance.
A single node of an X file.
Definition: xFileNode.h:39
bool fill_colors(XFileDataNode *obj)
Fills the structure based on the raw data from the MeshVertexColors template.
Definition: xFileMesh.cxx:750
XFileDataObject & add_Coords2d(XFile *x_file, const LVecBase2d &coords)
Appends a new Coords2d instance.
XFileDataObject & add_IndexedColor(XFile *x_file, int index, const LColor &color)
Appends a new IndexedColor instance.
void transform(const LMatrix4d &mat)
Applies the indicated transformation matrix to the vertex.
Definition: eggVertex.cxx:672
void set_egg_parent(EggGroupNode *egg_parent)
Specifies the egg node that will eventually be the parent of this mesh, when create_polygons() is lat...
Definition: xFileMesh.cxx:251
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Any one-, two-, three-, or four-component vertex, possibly with attributes such as a normal.
Definition: eggVertex.h:39
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
bool fill_uvs(XFileDataNode *obj)
Fills the structure based on the raw data from the MeshTextureCoords template.
Definition: xFileMesh.cxx:774
const LMatrix4d & get_node_to_vertex() const
Returns the transformation matrix suitable for converting vertices in the coordinate space of the nod...
Definition: eggNode.I:183
bool fill_mesh_child(XFileDataNode *obj)
Fills the structure based on one of the children of the Mesh object.
Definition: xFileMesh.cxx:662
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
XFileDataNode * make_x_uvs(XFileNode *x_mesh, const std::string &suffix)
Creates a MeshTextureCoords table for the mesh.
Definition: xFileMesh.cxx:566
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void set_from_egg(XFileMesh *mesh, EggPolygon *egg_poly)
Sets the structure up from the indicated egg data.
Definition: xFileFace.cxx:30
int size() const
Returns the number of nested data objects within this object.
A single polygon.
Definition: eggPolygon.h:24
const std::string & get_template_name() const
A convenience function to return the name of the template used to define this data object.
Definition: xFileDataNode.I:43
This represents a single vertex associated with an XFileFace.
Definition: xFileVertex.h:26
XFileDataNode * make_x_mesh(XFileNode *x_parent, const std::string &suffix)
Creates an X structure corresponding to the mesh.
Definition: xFileMesh.cxx:449
This represents a single face of an XFileMesh.
Definition: xFileFace.h:26
LVecBase2d vec2() const
Returns the object's representation as an LVecBase2d.
XFileDataNode * add_MeshVertexColors(const std::string &name)
Creates a new MeshVertexColors instance, as a child of this node.
Definition: xFileNode.cxx:330
EggNode * add_child(EggNode *node)
Adds the indicated child to the group and returns it.
bool create_polygons(XFileToEggConverter *converter)
Creates a slew of EggPolygons according to the faces in the mesh, and adds them to the previously-ind...
Definition: xFileMesh.cxx:264
This represents an X file "material", which consists of a color, lighting, and/or texture specificati...
Definition: xFileMaterial.h:31
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This is an abstract base class for an XFileNode which is also an XFileDataObject.
Definition: xFileDataNode.h:33
void ref_vertex(EggVertex *vert, double membership=1.0)
Adds the vertex to the set of those referenced by the group, at the indicated membership level.
Definition: eggGroup.cxx:608
bool has_colors() const
Returns true if any of the vertices or faces added to this mesh used a color, false otherwise.
Definition: xFileMesh.cxx:406
EggGroup * find_joint(const std::string &joint_name)
This is called by set_animation_frame, for the purposes of building the frame data for the animation–...
int get_external_index() const
Returns the number set by set_external_index().
Definition: eggVertex.I:300
bool fill_material(XFileDataNode *obj)
Fills the structure based on the raw data from the X file's Material object.
XFileDataNode * get_object(int n) const
Returns the nth child object of this node.
Definition: xFileNode.I:57
XFileDataNode * add_MeshMaterialList(const std::string &name)
Creates a new MeshMaterialList instance, as a child of this node.
Definition: xFileNode.cxx:360
double d() const
Unambiguously returns the object's representation as a double, or 0.0 if the object has no double rep...
bool has_materials() const
Returns true if any of the faces added to this mesh used a real material, false otherwise.
Definition: xFileMesh.cxx:424
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
virtual bool is_standard_object(const std::string &template_name) const
Returns true if this node represents an instance of the standard template with the indicated name,...
LVecBase3d vec3() const
Returns the object's representation as an LVecBase3d.
void set_uv(const LTexCoordd &texCoord)
Replaces the unnamed UV coordinate pair on the vertex with the indicated value.
Definition: eggVertex.I:193
EggVertex * create_unique_vertex(const EggVertex &copy)
Creates a new vertex in the pool that is a copy of the indicated one and returns it.
A collection of vertices.
Definition: eggVertexPool.h:41
EggVertex * add_vertex(EggVertex *vertex)
Adds the indicated vertex to the end of the primitive's list of vertices, and returns it.
void recompute_vertex_normals(double threshold, CoordinateSystem cs=CS_default)
Recomputes all the vertex normals for polygon geometry at this group node and below so that they accu...
The abstract base class for a number of different types of data elements that may be stored in the X ...
int get_num_materials() const
Returns the number of distinct materials associated with the mesh.
Definition: xFileMesh.cxx:432
XFileDataNode * make_x_normals(XFileNode *x_mesh, const std::string &suffix)
Creates a MeshNormals table for the mesh.
Definition: xFileMesh.cxx:506