Panda3D

xFileMesh.cxx

00001 // Filename: xFileMesh.cxx
00002 // Created by:  drose (19Jun01)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) Carnegie Mellon University.  All rights reserved.
00008 //
00009 // All use of this software is subject to the terms of the revised BSD
00010 // license.  You should have received a copy of this license along
00011 // with this source code in a file named "LICENSE."
00012 //
00013 ////////////////////////////////////////////////////////////////////
00014 
00015 #include "xFileMesh.h"
00016 #include "xFileFace.h"
00017 #include "xFileVertex.h"
00018 #include "xFileNormal.h"
00019 #include "xFileMaterial.h"
00020 #include "xFileDataNode.h"
00021 #include "config_xfile.h"
00022 #include "string_utils.h"
00023 #include "eggVertexPool.h"
00024 #include "eggVertex.h"
00025 #include "eggPolygon.h"
00026 #include "eggGroupNode.h"
00027 
00028 ////////////////////////////////////////////////////////////////////
00029 //     Function: XFileMesh::Constructor
00030 //       Access: Public
00031 //  Description:
00032 ////////////////////////////////////////////////////////////////////
00033 XFileMesh::
00034 XFileMesh(CoordinateSystem cs) : _cs(cs) {
00035   _has_normals = false;
00036   _has_colors = false;
00037   _has_uvs = false;
00038   _has_materials = false;
00039   _egg_parent = NULL;
00040 }
00041 
00042 ////////////////////////////////////////////////////////////////////
00043 //     Function: XFileMesh::Destructor
00044 //       Access: Public
00045 //  Description:
00046 ////////////////////////////////////////////////////////////////////
00047 XFileMesh::
00048 ~XFileMesh() {
00049   clear();
00050 }
00051 
00052 ////////////////////////////////////////////////////////////////////
00053 //     Function: XFileMesh::clear
00054 //       Access: Public
00055 //  Description: Empties all data from the mesh.
00056 ////////////////////////////////////////////////////////////////////
00057 void XFileMesh::
00058 clear() {
00059   Vertices::iterator vi;
00060   for (vi = _vertices.begin(); vi != _vertices.end(); ++vi) {
00061     XFileVertex *vertex = (*vi);
00062     delete vertex;
00063   }
00064   Normals::iterator ni;
00065   for (ni = _normals.begin(); ni != _normals.end(); ++ni) {
00066     XFileNormal *normal = (*ni);
00067     delete normal;
00068   }
00069   Materials::iterator mi;
00070   for (mi = _materials.begin(); mi != _materials.end(); ++mi) {
00071     XFileMaterial *material = (*mi);
00072     delete material;
00073   }
00074   Faces::iterator fi;
00075   for (fi = _faces.begin(); fi != _faces.end(); ++fi) {
00076     XFileFace *face = (*fi);
00077     delete face;
00078   }
00079 
00080   _vertices.clear();
00081   _normals.clear();
00082   _materials.clear();
00083   _faces.clear();
00084 
00085   _unique_vertices.clear();
00086   _unique_normals.clear();
00087   _unique_materials.clear();
00088 
00089   _has_normals = false;
00090   _has_colors = false;
00091   _has_uvs = false;
00092   _has_materials = false;
00093 }
00094 
00095 ////////////////////////////////////////////////////////////////////
00096 //     Function: XFileMesh::add_polygon
00097 //       Access: Public
00098 //  Description: Adds the indicated polygon to the mesh.
00099 ////////////////////////////////////////////////////////////////////
00100 void XFileMesh::
00101 add_polygon(EggPolygon *egg_poly) {
00102   XFileFace *face = new XFileFace;
00103   face->set_from_egg(this, egg_poly);
00104   _faces.push_back(face);
00105 }
00106 
00107 ////////////////////////////////////////////////////////////////////
00108 //     Function: XFileMesh::add_vertex
00109 //       Access: Public
00110 //  Description: Creates a new XFileVertex, if one does not already
00111 //               exist for the indicated vertex, and returns its
00112 //               index.
00113 ////////////////////////////////////////////////////////////////////
00114 int XFileMesh::
00115 add_vertex(EggVertex *egg_vertex, EggPrimitive *egg_prim) {
00116   int next_index = _vertices.size();
00117   XFileVertex *vertex = new XFileVertex;
00118   vertex->set_from_egg(egg_vertex, egg_prim);
00119   if (vertex->_has_color) {
00120     _has_colors = true;
00121   }
00122   if (vertex->_has_uv) {
00123     _has_uvs = true;
00124   }
00125 
00126   pair<UniqueVertices::iterator, bool> result =
00127     _unique_vertices.insert(UniqueVertices::value_type(vertex, next_index));
00128 
00129   if (result.second) {
00130     // Successfully added; this is a new vertex.
00131     _vertices.push_back(vertex);
00132     return next_index;
00133   } else {
00134     // Not successfully added; there is already a vertex with these
00135     // properties.  Return that one instead.
00136     delete vertex;
00137     return (*result.first).second;
00138   }
00139 }
00140 
00141 
00142 ////////////////////////////////////////////////////////////////////
00143 //     Function: XFileMesh::add_normal
00144 //       Access: Public
00145 //  Description: Creates a new XFileNormal, if one does not already
00146 //               exist for the indicated normal, and returns its
00147 //               index.
00148 ////////////////////////////////////////////////////////////////////
00149 int XFileMesh::
00150 add_normal(EggVertex *egg_vertex, EggPrimitive *egg_prim) {
00151   int next_index = _normals.size();
00152   XFileNormal *normal = new XFileNormal;
00153   normal->set_from_egg(egg_vertex, egg_prim);
00154   if (normal->_has_normal) {
00155     _has_normals = true;
00156   }
00157 
00158   pair<UniqueNormals::iterator, bool> result =
00159     _unique_normals.insert(UniqueNormals::value_type(normal, next_index));
00160 
00161   if (result.second) {
00162     // Successfully added; this is a new normal.
00163     _normals.push_back(normal);
00164     return next_index;
00165   } else {
00166     // Not successfully added; there is already a normal with these
00167     // properties.  Return that one instead.
00168     delete normal;
00169     return (*result.first).second;
00170   }
00171 }
00172 
00173 ////////////////////////////////////////////////////////////////////
00174 //     Function: XFileMesh::add_material
00175 //       Access: Public
00176 //  Description: Creates a new XFileMaterial, if one does not already
00177 //               exist for the indicated material, and returns its
00178 //               index.
00179 ////////////////////////////////////////////////////////////////////
00180 int XFileMesh::
00181 add_material(EggPrimitive *egg_prim) {
00182   int next_index = _materials.size();
00183   XFileMaterial *material = new XFileMaterial;
00184   material->set_from_egg(egg_prim);
00185   if (material->has_material()) {
00186     _has_materials = true;
00187   }
00188 
00189   pair<UniqueMaterials::iterator, bool> result =
00190     _unique_materials.insert(UniqueMaterials::value_type(material, next_index));
00191 
00192   if (result.second) {
00193     // Successfully added; this is a new material.
00194     _materials.push_back(material);
00195     return next_index;
00196   } else {
00197     // Not successfully added; there is already a material with these
00198     // properties.  Return that one instead.
00199     delete material;
00200     return (*result.first).second;
00201   }
00202 }
00203 
00204 
00205 ////////////////////////////////////////////////////////////////////
00206 //     Function: XFileMesh::add_vertex
00207 //       Access: Public
00208 //  Description: Adds the newly-created XFileVertex unequivocally to
00209 //               the mesh, returning its index number.  The XFileMesh
00210 //               object becomes the owner of the XFileVertex
00211 //               pointer, and will delete it when it destructs.
00212 ////////////////////////////////////////////////////////////////////
00213 int XFileMesh::
00214 add_vertex(XFileVertex *vertex) {
00215   int next_index = _vertices.size();
00216   _unique_vertices.insert(UniqueVertices::value_type(vertex, next_index));
00217   _vertices.push_back(vertex);
00218   return next_index;
00219 }
00220 
00221 ////////////////////////////////////////////////////////////////////
00222 //     Function: XFileMesh::add_normal
00223 //       Access: Public
00224 //  Description: Adds the newly-created XFileNormal unequivocally to
00225 //               the mesh, returning its index number.  The XFileMesh
00226 //               object becomes the owner of the XFileNormal
00227 //               pointer, and will delete it when it destructs.
00228 ////////////////////////////////////////////////////////////////////
00229 int XFileMesh::
00230 add_normal(XFileNormal *normal) {
00231   int next_index = _normals.size();
00232   _unique_normals.insert(UniqueNormals::value_type(normal, next_index));
00233   _normals.push_back(normal);
00234   return next_index;
00235 }
00236 
00237 ////////////////////////////////////////////////////////////////////
00238 //     Function: XFileMesh::add_material
00239 //       Access: Public
00240 //  Description: Adds the newly-created XFileMaterial unequivocally to
00241 //               the mesh, returning its index number.  The XFileMesh
00242 //               object becomes the owner of the XFileMaterial
00243 //               pointer, and will delete it when it destructs.
00244 ////////////////////////////////////////////////////////////////////
00245 int XFileMesh::
00246 add_material(XFileMaterial *material) {
00247   int next_index = _materials.size();
00248   _unique_materials.insert(UniqueMaterials::value_type(material, next_index));
00249   _materials.push_back(material);
00250   return next_index;
00251 }
00252 
00253 ////////////////////////////////////////////////////////////////////
00254 //     Function: XFileMesh::set_egg_parent
00255 //       Access: Public
00256 //  Description: Specifies the egg node that will eventually be the
00257 //               parent of this mesh, when create_polygons() is later
00258 //               called.
00259 ////////////////////////////////////////////////////////////////////
00260 void XFileMesh::
00261 set_egg_parent(EggGroupNode *egg_parent) {
00262   // We actually put the mesh under its own group.
00263   EggGroup *egg_group = new EggGroup(get_name());
00264   egg_parent->add_child(egg_group);
00265 
00266   _egg_parent = egg_group;
00267 }
00268 
00269 ////////////////////////////////////////////////////////////////////
00270 //     Function: XFileMesh::create_polygons
00271 //       Access: Public
00272 //  Description: Creates a slew of EggPolygons according to the faces
00273 //               in the mesh, and adds them to the
00274 //               previously-indicated parent node.
00275 ////////////////////////////////////////////////////////////////////
00276 bool XFileMesh::
00277 create_polygons(XFileToEggConverter *converter) {
00278   nassertr(_egg_parent != (EggGroupNode *)NULL, false);
00279 
00280   EggVertexPool *vpool = new EggVertexPool(get_name());
00281   _egg_parent->add_child(vpool);
00282   Faces::const_iterator fi;
00283   for (fi = _faces.begin(); fi != _faces.end(); ++fi) {
00284     XFileFace *face = (*fi);
00285 
00286     EggPolygon *egg_poly = new EggPolygon;
00287     _egg_parent->add_child(egg_poly);
00288 
00289     // Set up the vertices for the polygon.
00290     XFileFace::Vertices::reverse_iterator vi;
00291     for (vi = face->_vertices.rbegin(); vi != face->_vertices.rend(); ++vi) {
00292       int vertex_index = (*vi)._vertex_index;
00293       int normal_index = (*vi)._normal_index;
00294       if (vertex_index < 0 || vertex_index >= (int)_vertices.size()) {
00295         xfile_cat.warning()
00296           << "Vertex index out of range in Mesh " << get_name() << "\n";
00297         continue;
00298       }
00299       XFileVertex *vertex = _vertices[vertex_index];
00300       XFileNormal *normal = (XFileNormal *)NULL;
00301 
00302       if (normal_index >= 0 && normal_index < (int)_normals.size()) {
00303         normal = _normals[normal_index];
00304       }
00305 
00306       // Create a temporary EggVertex before adding it to the pool.
00307       EggVertex temp_vtx;
00308       temp_vtx.set_external_index(vertex_index);
00309       temp_vtx.set_pos(vertex->_point);
00310       if (vertex->_has_color) {
00311         temp_vtx.set_color(vertex->_color);
00312       }
00313       if (vertex->_has_uv) {
00314         LTexCoordd uv = vertex->_uv;
00315         // Windows draws the UV's upside-down.
00316         uv[1] = 1.0 - uv[1];
00317         temp_vtx.set_uv(uv);
00318       }
00319 
00320       if (normal != (XFileNormal *)NULL && normal->_has_normal) {
00321         temp_vtx.set_normal(normal->_normal);
00322       }
00323 
00324       // We are given the vertex in local space; we need to transform
00325       // it into global space.  If the vertex has been skinned, that
00326       // means the global space of all of its joints (modified by the
00327       // matrix_offset provided in the skinning data).
00328       double net_weight = 0.0;
00329       LMatrix4d weighted_transform(0.0, 0.0, 0.0, 0.0,
00330                                    0.0, 0.0, 0.0, 0.0,
00331                                    0.0, 0.0, 0.0, 0.0,
00332                                    0.0, 0.0, 0.0, 0.0);
00333       SkinWeights::const_iterator swi;
00334       for (swi = _skin_weights.begin(); swi != _skin_weights.end(); ++swi) {
00335         const SkinWeightsData &data = (*swi);
00336         WeightMap::const_iterator wmi = data._weight_map.find(vertex_index);
00337         if (wmi != data._weight_map.end()) {
00338           EggGroup *joint = converter->find_joint(data._joint_name);
00339           if (joint != (EggGroup *)NULL) {
00340             double weight = (*wmi).second;
00341             LMatrix4d mat = data._matrix_offset;
00342             mat *= joint->get_node_to_vertex();
00343             weighted_transform += mat * weight;
00344             net_weight += weight;
00345           }
00346         }
00347       }
00348 
00349       if (net_weight == 0.0) {
00350         // The vertex had no joint membership.  Transform it into the
00351         // appropriate (global) space based on its parent.
00352         temp_vtx.transform(_egg_parent->get_node_to_vertex());
00353 
00354       } else {
00355         // The vertex was skinned into one or more joints.  Therefore,
00356         // transform it according to the blended matrix_offset from
00357         // the skinning data.
00358         weighted_transform /= net_weight;
00359         temp_vtx.transform(weighted_transform);
00360       }
00361 
00362       // Now get a real EggVertex matching our template.
00363       EggVertex *egg_vtx = vpool->create_unique_vertex(temp_vtx);
00364       egg_poly->add_vertex(egg_vtx);
00365     }
00366 
00367     // And apply the material for the polygon.
00368     int material_index = face->_material_index;
00369     if (material_index >= 0 && material_index < (int)_materials.size()) {
00370       XFileMaterial *material = _materials[material_index];
00371       material->apply_to_egg(egg_poly, converter);
00372     }
00373   }
00374 
00375   // Now go through all of the vertices and skin them up.
00376   EggVertexPool::iterator vi;
00377   for (vi = vpool->begin(); vi != vpool->end(); ++vi) {
00378     EggVertex *egg_vtx = (*vi);
00379     int vertex_index = egg_vtx->get_external_index();
00380 
00381     SkinWeights::const_iterator swi;
00382     for (swi = _skin_weights.begin(); swi != _skin_weights.end(); ++swi) {
00383       const SkinWeightsData &data = (*swi);
00384       WeightMap::const_iterator wmi = data._weight_map.find(vertex_index);
00385       if (wmi != data._weight_map.end()) {
00386         EggGroup *joint = converter->find_joint(data._joint_name);
00387         if (joint != (EggGroup *)NULL) {
00388           double weight = (*wmi).second;
00389           joint->ref_vertex(egg_vtx, weight);
00390         }
00391       }
00392     }
00393   }
00394 
00395   if (!has_normals()) {
00396     // If we don't have explicit normals, make some up, per the DX
00397     // spec.  Since the DX spec doesn't mention anything about a
00398     // crease angle, we should be as generous as possible.
00399     _egg_parent->recompute_vertex_normals(180.0, _cs);
00400   }
00401 
00402   return true;
00403 }
00404 
00405 ////////////////////////////////////////////////////////////////////
00406 //     Function: XFileMesh::has_normals
00407 //       Access: Public
00408 //  Description: Returns true if any of the vertices or faces added to
00409 //               this mesh used a normal, false otherwise.
00410 ////////////////////////////////////////////////////////////////////
00411 bool XFileMesh::
00412 has_normals() const {
00413   return _has_normals;
00414 }
00415 
00416 ////////////////////////////////////////////////////////////////////
00417 //     Function: XFileMesh::has_colors
00418 //       Access: Public
00419 //  Description: Returns true if any of the vertices or faces added to
00420 //               this mesh used a color, false otherwise.
00421 ////////////////////////////////////////////////////////////////////
00422 bool XFileMesh::
00423 has_colors() const {
00424   return _has_colors;
00425 }
00426 
00427 ////////////////////////////////////////////////////////////////////
00428 //     Function: XFileMesh::has_uvs
00429 //       Access: Public
00430 //  Description: Returns true if any of the vertices added to this
00431 //               mesh used a texture coordinate, false otherwise.
00432 ////////////////////////////////////////////////////////////////////
00433 bool XFileMesh::
00434 has_uvs() const {
00435   return _has_uvs;
00436 }
00437 
00438 ////////////////////////////////////////////////////////////////////
00439 //     Function: XFileMesh::has_materials
00440 //       Access: Public
00441 //  Description: Returns true if any of the faces added to this mesh
00442 //               used a real material, false otherwise.
00443 ////////////////////////////////////////////////////////////////////
00444 bool XFileMesh::
00445 has_materials() const {
00446   return _has_materials;
00447 }
00448 
00449 ////////////////////////////////////////////////////////////////////
00450 //     Function: XFileMesh::get_num_materials
00451 //       Access: Public
00452 //  Description: Returns the number of distinct materials associated
00453 //               with the mesh.
00454 ////////////////////////////////////////////////////////////////////
00455 int XFileMesh::
00456 get_num_materials() const {
00457   return _materials.size();
00458 }
00459 
00460 ////////////////////////////////////////////////////////////////////
00461 //     Function: XFileMesh::get_material
00462 //       Access: Public
00463 //  Description: Returns a pointer to the nth materials associated
00464 //               with the mesh.
00465 ////////////////////////////////////////////////////////////////////
00466 XFileMaterial *XFileMesh::
00467 get_material(int n) const {
00468   nassertr(n >= 0 && n < (int)_materials.size(), (XFileMaterial *)NULL);
00469   return _materials[n];
00470 }
00471 
00472 ////////////////////////////////////////////////////////////////////
00473 //     Function: XFileMesh::make_x_mesh
00474 //       Access: Public
00475 //  Description: Creates an X structure corresponding to the mesh.
00476 ////////////////////////////////////////////////////////////////////
00477 XFileDataNode *XFileMesh::
00478 make_x_mesh(XFileNode *x_parent, const string &suffix) {
00479   XFileDataNode *x_mesh = x_parent->add_Mesh("mesh" + suffix);
00480 
00481   // First, fill in the table of vertices.
00482   XFileDataObject &x_vertices = (*x_mesh)["vertices"];
00483 
00484   Vertices::const_iterator vi;
00485   for (vi = _vertices.begin(); vi != _vertices.end(); ++vi) {
00486     XFileVertex *vertex = (*vi);
00487     x_vertices.add_Vector(x_mesh->get_x_file(), vertex->_point);
00488   }
00489   (*x_mesh)["nVertices"] = x_vertices.size();
00490 
00491   // Then, create the list of faces that index into the above vertices.
00492   XFileDataObject &x_faces = (*x_mesh)["faces"];
00493   Faces::const_iterator fi;
00494   for (fi = _faces.begin(); fi != _faces.end(); ++fi) {
00495     XFileFace *face = (*fi);
00496 
00497     XFileDataObject &x_mesh_face = x_faces.add_MeshFace(x_mesh->get_x_file());
00498     XFileDataObject &x_faceVertexIndices = x_mesh_face["faceVertexIndices"];
00499     XFileFace::Vertices::const_iterator fvi;
00500     for (fvi = face->_vertices.begin();
00501          fvi != face->_vertices.end();
00502          ++fvi) {
00503       x_faceVertexIndices.add_int((*fvi)._vertex_index);
00504     }
00505     x_mesh_face["nFaceVertexIndices"] = x_faceVertexIndices.size();
00506   }
00507   (*x_mesh)["nFaces"] = x_faces.size();
00508 
00509   // Now, add in any supplemental data.
00510 
00511   if (has_normals()) {
00512     // Tack on normals.
00513     make_x_normals(x_mesh, suffix);
00514   }
00515   if (has_colors()) {
00516     // Tack on colors.
00517     make_x_colors(x_mesh, suffix);
00518   }
00519   if (has_uvs()) {
00520     // Tack on uvs.
00521     make_x_uvs(x_mesh, suffix);
00522   }
00523   if (has_materials()) {
00524     // Tack on materials.
00525     make_x_material_list(x_mesh, suffix);
00526   }
00527 
00528   return x_mesh;
00529 }
00530 
00531 ////////////////////////////////////////////////////////////////////
00532 //     Function: XFileMesh::make_x_normals
00533 //       Access: Public
00534 //  Description: Creates a MeshNormals table for the mesh.
00535 ////////////////////////////////////////////////////////////////////
00536 XFileDataNode *XFileMesh::
00537 make_x_normals(XFileNode *x_mesh, const string &suffix) {
00538   XFileDataNode *x_meshNormals = x_mesh->add_MeshNormals("norms" + suffix);
00539 
00540   XFileDataObject &x_normals = (*x_meshNormals)["normals"];
00541 
00542   Normals::const_iterator ni;
00543   for (ni = _normals.begin(); ni != _normals.end(); ++ni) {
00544     XFileNormal *normal = (*ni);
00545     x_normals.add_Vector(x_mesh->get_x_file(), normal->_normal);
00546   }
00547   (*x_meshNormals)["nNormals"] = x_normals.size();
00548 
00549   // Then, create the list of faces that index into the above normals.
00550   XFileDataObject &x_faces = (*x_meshNormals)["faceNormals"];
00551   Faces::const_iterator fi;
00552   for (fi = _faces.begin(); fi != _faces.end(); ++fi) {
00553     XFileFace *face = (*fi);
00554 
00555     XFileDataObject &x_normals_face = x_faces.add_MeshFace(x_mesh->get_x_file());
00556     XFileDataObject &x_faceVertexIndices = x_normals_face["faceVertexIndices"];
00557     XFileFace::Vertices::const_iterator fvi;
00558     for (fvi = face->_vertices.begin();
00559          fvi != face->_vertices.end();
00560          ++fvi) {
00561       x_faceVertexIndices.add_int((*fvi)._normal_index);
00562     }
00563     x_normals_face["nFaceVertexIndices"] = x_faceVertexIndices.size();
00564   }
00565   (*x_meshNormals)["nFaceNormals"] = x_faces.size();
00566 
00567   return x_meshNormals;
00568 }
00569 
00570 ////////////////////////////////////////////////////////////////////
00571 //     Function: XFileMesh::make_x_colors
00572 //       Access: Public
00573 //  Description: Creates a MeshVertexColors table for the mesh.
00574 ////////////////////////////////////////////////////////////////////
00575 XFileDataNode *XFileMesh::
00576 make_x_colors(XFileNode *x_mesh, const string &suffix) {
00577   XFileDataNode *x_meshColors = x_mesh->add_MeshVertexColors("colors" + suffix);
00578 
00579   XFileDataObject &x_colors = (*x_meshColors)["vertexColors"];
00580 
00581   Vertices::const_iterator vi;
00582   int i = 0;
00583   for (vi = _vertices.begin(); vi != _vertices.end(); ++vi) {
00584     XFileVertex *vertex = (*vi);
00585     const LColor &color = vertex->_color;
00586     x_colors.add_IndexedColor(x_mesh->get_x_file(), i, color);
00587     i++;
00588   }
00589 
00590   (*x_meshColors)["nVertexColors"] = x_colors.size();
00591   
00592   return x_meshColors;
00593 }
00594 
00595 ////////////////////////////////////////////////////////////////////
00596 //     Function: XFileMesh::make_x_uvs
00597 //       Access: Public
00598 //  Description: Creates a MeshTextureCoords table for the mesh.
00599 ////////////////////////////////////////////////////////////////////
00600 XFileDataNode *XFileMesh::
00601 make_x_uvs(XFileNode *x_mesh, const string &suffix) {
00602   XFileDataNode *x_meshUvs = x_mesh->add_MeshTextureCoords("uvs" + suffix);
00603 
00604   XFileDataObject &x_uvs = (*x_meshUvs)["textureCoords"];
00605   
00606   Vertices::const_iterator vi;
00607   for (vi = _vertices.begin(); vi != _vertices.end(); ++vi) {
00608     XFileVertex *vertex = (*vi);
00609     x_uvs.add_Coords2d(x_mesh->get_x_file(), vertex->_uv);
00610   }
00611 
00612   (*x_meshUvs)["nTextureCoords"] = x_uvs.size();
00613 
00614   return x_meshUvs;
00615 }
00616 
00617 ////////////////////////////////////////////////////////////////////
00618 //     Function: XFileMesh::make_x_material_list
00619 //       Access: Public
00620 //  Description: Creates a MeshMaterialList table for the mesh.
00621 ////////////////////////////////////////////////////////////////////
00622 XFileDataNode *XFileMesh::
00623 make_x_material_list(XFileNode *x_mesh, const string &suffix) {
00624   XFileDataNode *x_meshMaterials = 
00625     x_mesh->add_MeshMaterialList("materials" + suffix);
00626 
00627   // First, build up the list of faces the reference the materials.
00628   XFileDataObject &x_indexes = (*x_meshMaterials)["faceIndexes"];
00629 
00630   Faces::const_iterator fi;
00631   for (fi = _faces.begin(); fi != _faces.end(); ++fi) {
00632     XFileFace *face = (*fi);
00633     x_indexes.add_int(face->_material_index);
00634   }
00635 
00636   (*x_meshMaterials)["nFaceIndexes"] = x_indexes.size();
00637 
00638   // Now, build up the list of materials themselves.  Each material is
00639   // a child of the MeshMaterialList node, rather than an element of
00640   // an array.
00641   for (size_t i = 0; i < _materials.size(); i++) {
00642     XFileMaterial *material = _materials[i];
00643 
00644     material->make_x_material(x_meshMaterials,
00645                               suffix + "_" + format_string(i));
00646   }
00647 
00648   (*x_meshMaterials)["nMaterials"] = (int)_materials.size();
00649 
00650   return x_meshMaterials;
00651 }
00652 
00653 ////////////////////////////////////////////////////////////////////
00654 //     Function: XFileMesh::fill_mesh
00655 //       Access: Public
00656 //  Description: Fills the structure based on the raw data from the
00657 //               X file's Mesh object.
00658 ////////////////////////////////////////////////////////////////////
00659 bool XFileMesh::
00660 fill_mesh(XFileDataNode *obj) {
00661   clear();
00662 
00663   int i, j;
00664 
00665   const XFileDataObject &vertices = (*obj)["vertices"];
00666   for (i = 0; i < vertices.size(); i++) {
00667     XFileVertex *vertex = new XFileVertex;
00668     vertex->_point = vertices[i].vec3();
00669     add_vertex(vertex);
00670   }
00671 
00672   const XFileDataObject &faces = (*obj)["faces"];
00673   for (i = 0; i < faces.size(); i++) {
00674     XFileFace *face = new XFileFace;
00675 
00676     const XFileDataObject &faceIndices = faces[i]["faceVertexIndices"];
00677 
00678     for (j = 0; j < faceIndices.size(); j++) {
00679       XFileFace::Vertex vertex;
00680       vertex._vertex_index = faceIndices[j].i();
00681       vertex._normal_index = -1;
00682 
00683       face->_vertices.push_back(vertex);
00684     }
00685     _faces.push_back(face);
00686   }
00687 
00688   // Some properties are stored as children of the mesh.
00689   int num_objects = obj->get_num_objects();
00690   for (i = 0; i < num_objects; i++) {
00691     if (!fill_mesh_child(obj->get_object(i))) {
00692       return false;
00693     }
00694   }
00695 
00696   return true;
00697 }
00698 
00699 ////////////////////////////////////////////////////////////////////
00700 //     Function: XFileMesh::fill_mesh_child
00701 //       Access: Public
00702 //  Description: Fills the structure based on one of the children of
00703 //               the Mesh object.
00704 ////////////////////////////////////////////////////////////////////
00705 bool XFileMesh::
00706 fill_mesh_child(XFileDataNode *obj) {
00707   if (obj->is_standard_object("MeshNormals")) {
00708     if (!fill_normals(obj)) {
00709       return false;
00710     }
00711 
00712   } else if (obj->is_standard_object("MeshVertexColors")) {
00713     if (!fill_colors(obj)) {
00714       return false;
00715     }
00716 
00717   } else if (obj->is_standard_object("MeshTextureCoords")) {
00718     if (!fill_uvs(obj)) {
00719       return false;
00720     }
00721 
00722   } else if (obj->is_standard_object("MeshMaterialList")) {
00723     if (!fill_material_list(obj)) {
00724       return false;
00725     }
00726 
00727   } else if (obj->is_standard_object("XSkinMeshHeader")) {
00728     // Quietly ignore a skin mesh header.
00729     
00730   } else if (obj->is_standard_object("SkinWeights")) {
00731     if (!fill_skin_weights(obj)) {
00732       return false;
00733     }
00734 
00735   } else {
00736     if (xfile_cat.is_debug()) {
00737       xfile_cat.debug()
00738         << "Ignoring mesh data object of unknown type: "
00739         << obj->get_template_name() << "\n";
00740     }
00741   }
00742   
00743   return true;
00744 }
00745 
00746 ////////////////////////////////////////////////////////////////////
00747 //     Function: XFileMesh::fill_normals
00748 //       Access: Public
00749 //  Description: Fills the structure based on the raw data from the
00750 //               MeshNormals template.
00751 ////////////////////////////////////////////////////////////////////
00752 bool XFileMesh::
00753 fill_normals(XFileDataNode *obj) {
00754   int i, j;
00755 
00756   const XFileDataObject &normals = (*obj)["normals"];
00757   for (i = 0; i < normals.size(); i++) {
00758     XFileNormal *normal = new XFileNormal;
00759     normal->_normal = normals[i].vec3();
00760     normal->_has_normal = true;
00761     add_normal(normal);
00762   }
00763 
00764   const XFileDataObject &faceNormals = (*obj)["faceNormals"];
00765   if (faceNormals.size() != (int)_faces.size()) {
00766     xfile_cat.warning()
00767       << "Incorrect number of faces in MeshNormals within " 
00768       << get_name() << "\n";
00769   }
00770 
00771   int num_normals = min(faceNormals.size(), (int)_faces.size());
00772   for (i = 0; i < num_normals; i++) {
00773     XFileFace *face = _faces[i];
00774 
00775     const XFileDataObject &faceIndices = faceNormals[i]["faceVertexIndices"];
00776 
00777     if (faceIndices.size() != (int)face->_vertices.size()) {
00778       xfile_cat.warning() 
00779         << "Incorrect number of vertices for face in MeshNormals within "
00780         << get_name() << "\n";
00781     }
00782 
00783     int num_vertices = min(faceIndices.size(), (int)face->_vertices.size());
00784     for (j = 0; j < num_vertices; j++) {
00785       face->_vertices[j]._normal_index = faceIndices[j].i();
00786     }
00787   }
00788 
00789   return true;
00790 }
00791 
00792 ////////////////////////////////////////////////////////////////////
00793 //     Function: XFileMesh::fill_colors
00794 //       Access: Public
00795 //  Description: Fills the structure based on the raw data from the
00796 //               MeshVertexColors template.
00797 ////////////////////////////////////////////////////////////////////
00798 bool XFileMesh::
00799 fill_colors(XFileDataNode *obj) {
00800   const XFileDataObject &vertexColors = (*obj)["vertexColors"];
00801   for (int i = 0; i < vertexColors.size(); i++) {
00802     int vertex_index = vertexColors[i]["index"].i();
00803     if (vertex_index < 0 || vertex_index >= (int)_vertices.size()) {
00804       xfile_cat.warning()
00805         << "Vertex index out of range in MeshVertexColors within " 
00806         << get_name() << "\n";
00807       continue;
00808     }
00809 
00810     XFileVertex *vertex = _vertices[vertex_index];
00811     vertex->_color = LCAST(PN_stdfloat, vertexColors[i]["indexColor"].vec4());
00812     vertex->_has_color = true;
00813   }
00814 
00815   return true;
00816 }
00817 
00818 ////////////////////////////////////////////////////////////////////
00819 //     Function: XFileMesh::fill_uvs
00820 //       Access: Public
00821 //  Description: Fills the structure based on the raw data from the
00822 //               MeshTextureCoords template.
00823 ////////////////////////////////////////////////////////////////////
00824 bool XFileMesh::
00825 fill_uvs(XFileDataNode *obj) {
00826   const XFileDataObject &textureCoords = (*obj)["textureCoords"];
00827   if (textureCoords.size() != (int)_vertices.size()) {
00828     xfile_cat.warning()
00829       << "Wrong number of vertices in MeshTextureCoords within " 
00830       << get_name() << "\n";
00831   }
00832 
00833   int num_texcoords = min(textureCoords.size(), (int)_vertices.size());
00834   for (int i = 0; i < num_texcoords; i++) {
00835     XFileVertex *vertex = _vertices[i];
00836     vertex->_uv = textureCoords[i].vec2();
00837     vertex->_has_uv = true;
00838   }
00839 
00840   return true;
00841 }
00842 
00843 ////////////////////////////////////////////////////////////////////
00844 //     Function: XFileMesh::fill_skin_weights
00845 //       Access: Public
00846 //  Description: Fills the structure based on the raw data from the
00847 //               SkinWeights template.
00848 ////////////////////////////////////////////////////////////////////
00849 bool XFileMesh::
00850 fill_skin_weights(XFileDataNode *obj) {
00851   // Create a new SkinWeightsData record for the table.  We'll need
00852   // this data later when we create the vertices.
00853   _skin_weights.push_back(SkinWeightsData());
00854   SkinWeightsData &data = _skin_weights.back();
00855 
00856   data._joint_name = (*obj)["transformNodeName"].s();
00857 
00858   const XFileDataObject &vertexIndices = (*obj)["vertexIndices"];
00859   const XFileDataObject &weights = (*obj)["weights"];
00860 
00861   if (weights.size() != vertexIndices.size()) {
00862     xfile_cat.warning()
00863       << "Inconsistent number of vertices in SkinWeights within " << get_name() << "\n";
00864   }
00865 
00866   // Unpack the weight for each vertex.
00867   size_t num_weights = min(weights.size(), vertexIndices.size());
00868   for (size_t i = 0; i < num_weights; i++) {
00869     int vindex = vertexIndices[i].i();
00870     double weight = weights[i].d();
00871 
00872     if (vindex < 0 || vindex > (int)_vertices.size()) {
00873       xfile_cat.warning()
00874         << "Illegal vertex index " << vindex << " in SkinWeights.\n";
00875       continue;
00876     }
00877     data._weight_map[vindex] = weight;
00878   }
00879 
00880   // Also retrieve the matrix offset.
00881   data._matrix_offset = (*obj)["matrixOffset"]["matrix"].mat4();
00882 
00883   return true;
00884 }
00885 
00886 ////////////////////////////////////////////////////////////////////
00887 //     Function: XFileMesh::fill_material_list
00888 //       Access: Public
00889 //  Description: Fills the structure based on the raw data from the
00890 //               MeshMaterialList template.
00891 ////////////////////////////////////////////////////////////////////
00892 bool XFileMesh::
00893 fill_material_list(XFileDataNode *obj) {
00894   const XFileDataObject &faceIndexes = (*obj)["faceIndexes"];
00895   if (faceIndexes.size() > (int)_faces.size()) {
00896     xfile_cat.warning()
00897       << "Too many faces in MeshMaterialList within " << get_name() << "\n";
00898   }
00899 
00900   int material_index = -1;
00901   int i = 0;
00902   while (i < faceIndexes.size() && i < (int)_faces.size()) {
00903     XFileFace *face = _faces[i];
00904     material_index = faceIndexes[i].i();
00905     face->_material_index = material_index;
00906     i++;
00907   }
00908 
00909   // The rest of the faces get the same material index as the last
00910   // one in the list.
00911   while (i < (int)_faces.size()) {
00912     XFileFace *face = _faces[i];
00913     face->_material_index = material_index;
00914     i++;
00915   }
00916 
00917   // Now look for children of the MaterialList object.  These should
00918   // all be Material objects.
00919   int num_objects = obj->get_num_objects();
00920   for (i = 0; i < num_objects; i++) {
00921     XFileDataNode *child = obj->get_object(i);
00922     if (child->is_standard_object("Material")) {
00923       XFileMaterial *material = new XFileMaterial;
00924       if (!material->fill_material(child)) {
00925         delete material;
00926         return false;
00927       }
00928       add_material(material);
00929 
00930     } else {
00931       if (xfile_cat.is_debug()) {
00932         xfile_cat.debug()
00933           << "Ignoring material list object of unknown type: "
00934           << child->get_template_name() << "\n";
00935       }
00936     }
00937   }
00938 
00939   return true;
00940 }
 All Classes Functions Variables Enumerations