// Filename: xFileMesh.cxx // Created by: drose (19Jun01) // //////////////////////////////////////////////////////////////////// // // PANDA 3D SOFTWARE // Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved // // All use of this software is subject to the terms of the Panda 3d // Software license. You should have received a copy of this license // along with this source code; you will also find a current copy of // the license at http://etc.cmu.edu/panda3d/docs/license/ . // // To contact the maintainers of this program write to // panda3d-general@lists.sourceforge.net . // //////////////////////////////////////////////////////////////////// #include "xFileMesh.h" #include "xFileFace.h" #include "xFileVertex.h" #include "xFileNormal.h" #include "xFileMaterial.h" #include "config_xfile.h" #include "eggVertexPool.h" #include "eggVertex.h" #include "eggPolygon.h" #include "eggGroupNode.h" //////////////////////////////////////////////////////////////////// // Function: XFileMesh::Constructor // Access: Public // Description: //////////////////////////////////////////////////////////////////// XFileMesh:: XFileMesh(CoordinateSystem cs) : _cs(cs) { _has_normals = false; _has_colors = false; _has_uvs = false; _has_materials = false; _egg_parent = NULL; } //////////////////////////////////////////////////////////////////// // Function: XFileMesh::Destructor // Access: Public // Description: //////////////////////////////////////////////////////////////////// XFileMesh:: ~XFileMesh() { clear(); } //////////////////////////////////////////////////////////////////// // Function: XFileMesh::clear // Access: Public // Description: Empties all data from the mesh. //////////////////////////////////////////////////////////////////// void XFileMesh:: clear() { Vertices::iterator vi; for (vi = _vertices.begin(); vi != _vertices.end(); ++vi) { XFileVertex *vertex = (*vi); delete vertex; } Normals::iterator ni; for (ni = _normals.begin(); ni != _normals.end(); ++ni) { XFileNormal *normal = (*ni); delete normal; } Materials::iterator mi; for (mi = _materials.begin(); mi != _materials.end(); ++mi) { XFileMaterial *material = (*mi); delete material; } Faces::iterator fi; for (fi = _faces.begin(); fi != _faces.end(); ++fi) { XFileFace *face = (*fi); delete face; } _vertices.clear(); _normals.clear(); _materials.clear(); _faces.clear(); _unique_vertices.clear(); _unique_normals.clear(); _unique_materials.clear(); _has_normals = false; _has_colors = false; _has_uvs = false; _has_materials = false; } //////////////////////////////////////////////////////////////////// // Function: XFileMesh::add_polygon // Access: Public // Description: Adds the indicated polygon to the mesh. //////////////////////////////////////////////////////////////////// void XFileMesh:: add_polygon(EggPolygon *egg_poly) { XFileFace *face = new XFileFace; face->set_from_egg(this, egg_poly); _faces.push_back(face); } //////////////////////////////////////////////////////////////////// // Function: XFileMesh::add_vertex // Access: Public // Description: Creates a new XFileVertex, if one does not already // exist for the indicated vertex, and returns its // index. //////////////////////////////////////////////////////////////////// int XFileMesh:: add_vertex(EggVertex *egg_vertex, EggPrimitive *egg_prim) { int next_index = _vertices.size(); XFileVertex *vertex = new XFileVertex; vertex->set_from_egg(egg_vertex, egg_prim); if (vertex->_has_color) { _has_colors = true; } if (vertex->_has_uv) { _has_uvs = true; } pair result = _unique_vertices.insert(UniqueVertices::value_type(vertex, next_index)); if (result.second) { // Successfully added; this is a new vertex. _vertices.push_back(vertex); return next_index; } else { // Not successfully added; there is already a vertex with these // properties. Return that one instead. delete vertex; return (*result.first).second; } } //////////////////////////////////////////////////////////////////// // Function: XFileMesh::add_normal // Access: Public // Description: Creates a new XFileNormal, if one does not already // exist for the indicated normal, and returns its // index. //////////////////////////////////////////////////////////////////// int XFileMesh:: add_normal(EggVertex *egg_vertex, EggPrimitive *egg_prim) { int next_index = _normals.size(); XFileNormal *normal = new XFileNormal; normal->set_from_egg(egg_vertex, egg_prim); if (normal->_has_normal) { _has_normals = true; } pair result = _unique_normals.insert(UniqueNormals::value_type(normal, next_index)); if (result.second) { // Successfully added; this is a new normal. _normals.push_back(normal); return next_index; } else { // Not successfully added; there is already a normal with these // properties. Return that one instead. delete normal; return (*result.first).second; } } //////////////////////////////////////////////////////////////////// // Function: XFileMesh::add_material // Access: Public // Description: Creates a new XFileMaterial, if one does not already // exist for the indicated material, and returns its // index. //////////////////////////////////////////////////////////////////// int XFileMesh:: add_material(EggPrimitive *egg_prim) { int next_index = _materials.size(); XFileMaterial *material = new XFileMaterial; material->set_from_egg(egg_prim); if (material->has_material()) { _has_materials = true; } pair result = _unique_materials.insert(UniqueMaterials::value_type(material, next_index)); if (result.second) { // Successfully added; this is a new material. _materials.push_back(material); return next_index; } else { // Not successfully added; there is already a material with these // properties. Return that one instead. delete material; return (*result.first).second; } } //////////////////////////////////////////////////////////////////// // Function: XFileMesh::add_vertex // Access: Public // Description: Adds the newly-created XFileVertex unequivocally to // the mesh, returning its index number. The XFileMesh // object becomes the owner of the XFileVertex // pointer, and will delete it when it destructs. //////////////////////////////////////////////////////////////////// int XFileMesh:: add_vertex(XFileVertex *vertex) { int next_index = _vertices.size(); _unique_vertices.insert(UniqueVertices::value_type(vertex, next_index)); _vertices.push_back(vertex); return next_index; } //////////////////////////////////////////////////////////////////// // Function: XFileMesh::add_normal // Access: Public // Description: Adds the newly-created XFileNormal unequivocally to // the mesh, returning its index number. The XFileMesh // object becomes the owner of the XFileNormal // pointer, and will delete it when it destructs. //////////////////////////////////////////////////////////////////// int XFileMesh:: add_normal(XFileNormal *normal) { int next_index = _normals.size(); _unique_normals.insert(UniqueNormals::value_type(normal, next_index)); _normals.push_back(normal); return next_index; } //////////////////////////////////////////////////////////////////// // Function: XFileMesh::add_material // Access: Public // Description: Adds the newly-created XFileMaterial unequivocally to // the mesh, returning its index number. The XFileMesh // object becomes the owner of the XFileMaterial // pointer, and will delete it when it destructs. //////////////////////////////////////////////////////////////////// int XFileMesh:: add_material(XFileMaterial *material) { int next_index = _materials.size(); _unique_materials.insert(UniqueMaterials::value_type(material, next_index)); _materials.push_back(material); return next_index; } //////////////////////////////////////////////////////////////////// // Function: XFileMesh::set_egg_parent // Access: Public // Description: Specifies the egg node that will eventually be the // parent of this mesh, when create_polygons() is later // called. //////////////////////////////////////////////////////////////////// void XFileMesh:: set_egg_parent(EggGroupNode *egg_parent) { // We actually put the mesh under its own group. EggGroup *egg_group = new EggGroup(get_name()); egg_parent->add_child(egg_group); _egg_parent = egg_group; } //////////////////////////////////////////////////////////////////// // Function: XFileMesh::create_polygons // Access: Public // Description: Creates a slew of EggPolygons according to the faces // in the mesh, and adds them to the // previously-indicated parent node. //////////////////////////////////////////////////////////////////// bool XFileMesh:: create_polygons(XFileToEggConverter *converter) { nassertr(_egg_parent != (EggGroupNode *)NULL, false); EggVertexPool *vpool = new EggVertexPool(get_name()); _egg_parent->add_child(vpool); Faces::const_iterator fi; for (fi = _faces.begin(); fi != _faces.end(); ++fi) { XFileFace *face = (*fi); EggPolygon *egg_poly = new EggPolygon; _egg_parent->add_child(egg_poly); // Set up the vertices for the polygon. XFileFace::Vertices::reverse_iterator vi; for (vi = face->_vertices.rbegin(); vi != face->_vertices.rend(); ++vi) { int vertex_index = (*vi)._vertex_index; int normal_index = (*vi)._normal_index; if (vertex_index < 0 || vertex_index >= (int)_vertices.size()) { xfile_cat.error() << "Vertex index out of range in Mesh.\n"; return false; } XFileVertex *vertex = _vertices[vertex_index]; XFileNormal *normal = (XFileNormal *)NULL; if (normal_index >= 0 && normal_index < (int)_normals.size()) { normal = _normals[normal_index]; } // Create a temporary EggVertex before adding it to the pool. EggVertex temp_vtx; temp_vtx.set_external_index(vertex_index); temp_vtx.set_pos(LCAST(double, vertex->_point)); if (vertex->_has_color) { temp_vtx.set_color(vertex->_color); } if (vertex->_has_uv) { TexCoordd uv = LCAST(double, vertex->_uv); // Windows draws the UV's upside-down. uv[1] = 1.0 - uv[1]; temp_vtx.set_uv(uv); } if (normal != (XFileNormal *)NULL && normal->_has_normal) { temp_vtx.set_normal(LCAST(double, normal->_normal)); } // Transform the vertex into the appropriate (global) coordinate // space. temp_vtx.transform(_egg_parent->get_node_to_vertex()); // Now get a real EggVertex matching our template. EggVertex *egg_vtx = vpool->create_unique_vertex(temp_vtx); egg_poly->add_vertex(egg_vtx); } // And apply the material for the polygon. int material_index = face->_material_index; if (material_index >= 0 && material_index < (int)_materials.size()) { XFileMaterial *material = _materials[material_index]; material->apply_to_egg(egg_poly, converter); } } // Now go through all of the vertices and skin them up. EggVertexPool::iterator vi; for (vi = vpool->begin(); vi != vpool->end(); ++vi) { EggVertex *egg_vtx = (*vi); int vertex_index = egg_vtx->get_external_index(); SkinWeights::const_iterator swi; for (swi = _skin_weights.begin(); swi != _skin_weights.end(); ++swi) { const SkinWeightsData &data = (*swi); WeightMap::const_iterator wmi = data._weight_map.find(vertex_index); if (wmi != data._weight_map.end()) { EggGroup *joint = converter->find_joint(data._joint_name, data._matrix_offset); if (joint != (EggGroup *)NULL) { double weight = (*wmi).second; joint->ref_vertex(egg_vtx, weight); } } } } if (!has_normals()) { // If we don't have explicit normals, make some up, per the DX // spec. Since the DX spec doesn't mention anything about a // crease angle, we should be as generous as possible. _egg_parent->recompute_vertex_normals(180.0, _cs); } return true; } //////////////////////////////////////////////////////////////////// // Function: XFileMesh::has_normals // Access: Public // Description: Returns true if any of the vertices or faces added to // this mesh used a normal, false otherwise. //////////////////////////////////////////////////////////////////// bool XFileMesh:: has_normals() const { return _has_normals; } //////////////////////////////////////////////////////////////////// // Function: XFileMesh::has_colors // Access: Public // Description: Returns true if any of the vertices or faces added to // this mesh used a color, false otherwise. //////////////////////////////////////////////////////////////////// bool XFileMesh:: has_colors() const { return _has_colors; } //////////////////////////////////////////////////////////////////// // Function: XFileMesh::has_uvs // Access: Public // Description: Returns true if any of the vertices added to this // mesh used a texture coordinate, false otherwise. //////////////////////////////////////////////////////////////////// bool XFileMesh:: has_uvs() const { return _has_uvs; } //////////////////////////////////////////////////////////////////// // Function: XFileMesh::has_materials // Access: Public // Description: Returns true if any of the faces added to this mesh // used a real material, false otherwise. //////////////////////////////////////////////////////////////////// bool XFileMesh:: has_materials() const { return _has_materials; } //////////////////////////////////////////////////////////////////// // Function: XFileMesh::get_num_materials // Access: Public // Description: Returns the number of distinct materials associated // with the mesh. //////////////////////////////////////////////////////////////////// int XFileMesh:: get_num_materials() const { return _materials.size(); } //////////////////////////////////////////////////////////////////// // Function: XFileMesh::get_material // Access: Public // Description: Returns a pointer to the nth materials associated // with the mesh. //////////////////////////////////////////////////////////////////// XFileMaterial *XFileMesh:: get_material(int n) const { nassertr(n >= 0 && n < (int)_materials.size(), (XFileMaterial *)NULL); return _materials[n]; } //////////////////////////////////////////////////////////////////// // Function: XFileMesh::make_mesh_data // Access: Public // Description: Fills the datagram with the raw data for the DX // Mesh template. //////////////////////////////////////////////////////////////////// void XFileMesh:: make_mesh_data(Datagram &raw_data) { raw_data.clear(); raw_data.add_int32(_vertices.size()); Vertices::const_iterator vi; for (vi = _vertices.begin(); vi != _vertices.end(); ++vi) { XFileVertex *vertex = (*vi); const Vertexf &point = vertex->_point; raw_data.add_float32(point[0]); raw_data.add_float32(point[1]); raw_data.add_float32(point[2]); } raw_data.add_int32(_faces.size()); Faces::const_iterator fi; for (fi = _faces.begin(); fi != _faces.end(); ++fi) { XFileFace *face = (*fi); raw_data.add_int32(face->_vertices.size()); XFileFace::Vertices::const_iterator fvi; for (fvi = face->_vertices.begin(); fvi != face->_vertices.end(); ++fvi) { raw_data.add_int32((*fvi)._vertex_index); } } } //////////////////////////////////////////////////////////////////// // Function: XFileMesh::make_normal_data // Access: Public // Description: Fills the datagram with the raw data for the DX // MeshNormals template. //////////////////////////////////////////////////////////////////// void XFileMesh:: make_normal_data(Datagram &raw_data) { raw_data.clear(); raw_data.add_int32(_normals.size()); Normals::const_iterator ni; for (ni = _normals.begin(); ni != _normals.end(); ++ni) { XFileNormal *normal = (*ni); const Normalf &norm = normal->_normal; raw_data.add_float32(norm[0]); raw_data.add_float32(norm[1]); raw_data.add_float32(norm[2]); } raw_data.add_int32(_faces.size()); Faces::const_iterator fi; for (fi = _faces.begin(); fi != _faces.end(); ++fi) { XFileFace *face = (*fi); raw_data.add_int32(face->_vertices.size()); XFileFace::Vertices::const_iterator fvi; for (fvi = face->_vertices.begin(); fvi != face->_vertices.end(); ++fvi) { raw_data.add_int32((*fvi)._normal_index); } } } //////////////////////////////////////////////////////////////////// // Function: XFileMesh::make_color_data // Access: Public // Description: Fills the datagram with the raw data for the DX // MeshVertexColors template. //////////////////////////////////////////////////////////////////// void XFileMesh:: make_color_data(Datagram &raw_data) { raw_data.clear(); raw_data.add_int32(_vertices.size()); Vertices::const_iterator vi; int i = 0; for (vi = _vertices.begin(); vi != _vertices.end(); ++vi) { XFileVertex *vertex = (*vi); const Colorf &color = vertex->_color; raw_data.add_int32(i); raw_data.add_float32(color[0]); raw_data.add_float32(color[1]); raw_data.add_float32(color[2]); raw_data.add_float32(color[3]); i++; } } //////////////////////////////////////////////////////////////////// // Function: XFileMesh::make_uv_data // Access: Public // Description: Fills the datagram with the raw data for the DX // MeshTextureCoords template. //////////////////////////////////////////////////////////////////// void XFileMesh:: make_uv_data(Datagram &raw_data) { raw_data.clear(); raw_data.add_int32(_vertices.size()); Vertices::const_iterator vi; for (vi = _vertices.begin(); vi != _vertices.end(); ++vi) { XFileVertex *vertex = (*vi); const TexCoordf &uv = vertex->_uv; raw_data.add_float32(uv[0]); raw_data.add_float32(uv[1]); } } //////////////////////////////////////////////////////////////////// // Function: XFileMesh::make_material_list_data // Access: Public // Description: Fills the datagram with the raw data for the DX // MeshMaterialList template. //////////////////////////////////////////////////////////////////// void XFileMesh:: make_material_list_data(Datagram &raw_data) { raw_data.clear(); raw_data.add_int32(_materials.size()); raw_data.add_int32(_faces.size()); Faces::const_iterator fi; for (fi = _faces.begin(); fi != _faces.end(); ++fi) { XFileFace *face = (*fi); raw_data.add_int32(face->_material_index); } } //////////////////////////////////////////////////////////////////// // Function: XFileMesh::read_mesh_data // Access: Public // Description: Fills the structure based on the raw data from the // Mesh template. //////////////////////////////////////////////////////////////////// bool XFileMesh:: read_mesh_data(const Datagram &raw_data) { DatagramIterator di(raw_data); clear(); int i, j; int num_vertices = di.get_int32(); for (i = 0; i < num_vertices; i++) { XFileVertex *vertex = new XFileVertex; vertex->_point[0] = di.get_float32(); vertex->_point[1] = di.get_float32(); vertex->_point[2] = di.get_float32(); add_vertex(vertex); } int num_faces = di.get_int32(); for (i = 0; i < num_faces; i++) { XFileFace *face = new XFileFace; num_vertices = di.get_int32(); for (j = 0; j < num_vertices; j++) { XFileFace::Vertex vertex; vertex._vertex_index = di.get_int32(); vertex._normal_index = -1; face->_vertices.push_back(vertex); } _faces.push_back(face); } if (di.get_remaining_size() != 0) { xfile_cat.warning() << "Ignoring " << di.get_remaining_size() << " trailing Mesh.\n"; } return true; } //////////////////////////////////////////////////////////////////// // Function: XFileMesh::read_normal_data // Access: Public // Description: Fills the structure based on the raw data from the // MeshNormals template. //////////////////////////////////////////////////////////////////// bool XFileMesh:: read_normal_data(const Datagram &raw_data) { DatagramIterator di(raw_data); int num_normals = di.get_int32(); int i; for (i = 0; i < num_normals; i++) { XFileNormal *normal = new XFileNormal; normal->_normal[0] = di.get_float32(); normal->_normal[1] = di.get_float32(); normal->_normal[2] = di.get_float32(); normal->_has_normal = true; add_normal(normal); } int num_faces = di.get_int32(); if (num_faces != _faces.size()) { xfile_cat.error() << "Incorrect number of faces in MeshNormals.\n"; return false; } for (i = 0; i < num_faces; i++) { XFileFace *face = _faces[i]; int num_vertices = di.get_int32(); if (num_vertices != face->_vertices.size()) { xfile_cat.error() << "Incorrect number of vertices for face in MeshNormals.\n"; return false; } for (int j = 0; j < num_vertices; j++) { face->_vertices[j]._normal_index = di.get_int32(); } } if (di.get_remaining_size() != 0) { xfile_cat.warning() << "Ignoring " << di.get_remaining_size() << " trailing MeshNormals.\n"; } return true; } //////////////////////////////////////////////////////////////////// // Function: XFileMesh::read_color_data // Access: Public // Description: Fills the structure based on the raw data from the // MeshVertexColors template. //////////////////////////////////////////////////////////////////// bool XFileMesh:: read_color_data(const Datagram &raw_data) { DatagramIterator di(raw_data); int num_colors = di.get_int32(); int i; for (i = 0; i < num_colors; i++) { unsigned int vertex_index = di.get_int32(); if (vertex_index < 0 || vertex_index >= _vertices.size()) { xfile_cat.error() << "Vertex index out of range in MeshVertexColors.\n"; return false; } XFileVertex *vertex = _vertices[vertex_index]; vertex->_color[0] = di.get_float32(); vertex->_color[1] = di.get_float32(); vertex->_color[2] = di.get_float32(); vertex->_color[3] = di.get_float32(); vertex->_has_color = true; } if (di.get_remaining_size() != 0) { xfile_cat.warning() << "Ignoring " << di.get_remaining_size() << " trailing MeshVertexColors.\n"; } return true; } //////////////////////////////////////////////////////////////////// // Function: XFileMesh::read_uv_data // Access: Public // Description: Fills the structure based on the raw data from the // MeshTextureCoords template. //////////////////////////////////////////////////////////////////// bool XFileMesh:: read_uv_data(const Datagram &raw_data) { DatagramIterator di(raw_data); int num_vertices = di.get_int32(); if (num_vertices != _vertices.size()) { xfile_cat.error() << "Wrong number of vertices in MeshTextureCoords.\n"; return false; } int i; for (i = 0; i < num_vertices; i++) { XFileVertex *vertex = _vertices[i]; vertex->_uv[0] = di.get_float32(); vertex->_uv[1] = di.get_float32(); vertex->_has_uv = true; } if (di.get_remaining_size() != 0) { xfile_cat.warning() << "Ignoring " << di.get_remaining_size() << " trailing MeshTextureCoords.\n"; } return true; } //////////////////////////////////////////////////////////////////// // Function: XFileMesh::read_skin_weights_data // Access: Public // Description: Fills the structure based on the raw data from the // SkinWeights template. //////////////////////////////////////////////////////////////////// bool XFileMesh:: read_skin_weights_data(const Datagram &raw_data) { DatagramIterator di(raw_data); // Create a new SkinWeightsData record for the table. We'll need // this data later when we create the vertices. _skin_weights.push_back(SkinWeightsData()); SkinWeightsData &data = _skin_weights.back(); // The DX system encodes a pointer to a character string in four // bytes within the stream. Weird, in a Microsofty sort of way. data._joint_name = (const char *)di.get_uint32(); int num_weights = di.get_int32(); vector_int vindices; vindices.reserve(num_weights); // Unpack the list of vertices first int i; for (i = 0; i < num_weights; i++) { int vindex = di.get_int32(); if (vindex < 0 || vindex > (int)_vertices.size()) { xfile_cat.error() << "Illegal vertex index " << vindex << " in SkinWeights.\n"; return false; } vindices.push_back(vindex); } // Then unpack the weight for each vertex. for (i = 0; i < num_weights; i++) { float weight = di.get_float32(); data._weight_map[vindices[i]] = weight; } // Finally, read the matrix offset. data._matrix_offset.read_datagram(di); return true; } //////////////////////////////////////////////////////////////////// // Function: XFileMesh::read_material_list_data // Access: Public // Description: Fills the structure based on the raw data from the // MaterialList template. //////////////////////////////////////////////////////////////////// bool XFileMesh:: read_material_list_data(const Datagram &raw_data) { DatagramIterator di(raw_data); di.get_int32(); /* num_materials */ unsigned int num_faces = di.get_int32(); if (num_faces > _faces.size()) { xfile_cat.error() << "Too many faces in MaterialList.\n"; return false; } int material_index = -1; unsigned int i = 0; while (i < num_faces) { XFileFace *face = _faces[i]; material_index = di.get_int32(); face->_material_index = material_index; i++; } // The rest of the faces get the same material index as the last // one in the list. while (i < (int)_faces.size()) { XFileFace *face = _faces[i]; face->_material_index = material_index; i++; } if (di.get_remaining_size() != 0) { xfile_cat.warning() << "Ignoring " << di.get_remaining_size() << " trailing MeshMaterialList.\n"; } return true; }