#pragma warning ( disable: 4786 4018 ) #include #include #include "DTSMesh.h" #include "DTSOutputStream.h" #include "DTSInputStream.h" namespace DTS { // ----------------------------------------------------------------------- // Normal encoding transformation // ----------------------------------------------------------------------- #include "DTSNormalTable.cpp" char Mesh::encodeNormal(const Point & normal) { unsigned char bestIndex = 0; float bestDot = -10E30F ; for (int i = 0 ; i < 256 ; i++) { float dot = ( normal.x() * normalTable[i][0] + normal.y() * normalTable[i][1] + normal.z() * normalTable[i][2] ) ; if (dot > bestDot) { bestIndex = i; bestDot = dot; } } return * (char*)&bestIndex; } // ----------------------------------------------------------------------- // Searchs for minor and maximum vertex coordinates // ----------------------------------------------------------------------- void Mesh::calculateBounds() { std::vector::iterator vertex ; bounds.max = Point(-10E30F, -10E30F, -10E30F) ; bounds.min = Point( 10E30F, 10E30F, 10E30F) ; for (vertex = verts.begin() ; vertex != verts.end() ; vertex++) { if (vertex->x() < bounds.min.x()) bounds.min.x(vertex->x()) ; if (vertex->y() < bounds.min.y()) bounds.min.y(vertex->y()) ; if (vertex->z() < bounds.min.z()) bounds.min.z(vertex->z()) ; if (vertex->x() > bounds.max.x()) bounds.max.x(vertex->x()) ; if (vertex->y() > bounds.max.y()) bounds.max.y(vertex->y()) ; if (vertex->z() > bounds.max.z()) bounds.max.z(vertex->z()) ; } } Box Mesh::getBounds(Point trans,Quaternion rot) const { // Compute the bounding box using the given transform. Box bounds2; bounds2.max = Point(-10E30F, -10E30F, -10E30F) ; bounds2.min = Point( 10E30F, 10E30F, 10E30F) ; std::vector::const_iterator vertex ; for (vertex = verts.begin() ; vertex != verts.end() ; vertex++) { Point tv = rot.apply(*vertex) + trans; if (tv.x() < bounds2.min.x()) bounds2.min.x(tv.x()) ; if (tv.y() < bounds2.min.y()) bounds2.min.y(tv.y()) ; if (tv.z() < bounds2.min.z()) bounds2.min.z(tv.z()) ; if (tv.x() > bounds2.max.x()) bounds2.max.x(tv.x()) ; if (tv.y() > bounds2.max.y()) bounds2.max.y(tv.y()) ; if (tv.z() > bounds2.max.z()) bounds2.max.z(tv.z()) ; } return bounds2; } // ----------------------------------------------------------------------- // Calculates center of bounding box // ----------------------------------------------------------------------- void Mesh::calculateCenter() { center = bounds.max.midpoint(bounds.min) ; } // ----------------------------------------------------------------------- // Search for the maximum vertex distance from center // ----------------------------------------------------------------------- void Mesh::calculateRadius() { radius = getRadiusFrom(Point(0,0,0),Quaternion::identity,center) ; } float Mesh::getRadiusFrom(Point trans, Quaternion rot, Point center) const { std::vector::const_iterator vertex ; float radius = 0.0F ; for (vertex = verts.begin() ; vertex != verts.end() ; vertex++) { Point tv = rot.apply(*vertex) + trans; float distance = (tv - center).length() ; if (distance > radius) radius = distance ; } return radius ; } float Mesh::getTubeRadius() const { std::vector::const_iterator vertex ; float radius = 0.0F ; for (vertex = verts.begin() ; vertex != verts.end() ; vertex++) { float distance = (*vertex - center).length(2) ; if (distance > radius) radius = distance ; } return radius ; } float Mesh::getTubeRadiusFrom(Point trans, Quaternion rot, Point center) const { std::vector::const_iterator vertex ; float radius = 0.0F ; for (vertex = verts.begin() ; vertex != verts.end() ; vertex++) { Point tv = rot.apply(*vertex) + trans; float distance = (tv - center).length(2) ; if (distance > radius) radius = distance ; } return radius ; } // ----------------------------------------------------------------------- // Calculate the polygon count // ----------------------------------------------------------------------- int Mesh::getPolyCount() const { int count = 0 ; std::vector::const_iterator p ; for (p = primitives.begin() ; p != primitives.end() ; p++) { if (p->type & Primitive::Strip) count += p->numElements - 2 ; else count += p->numElements / 3 ; } return count ; } // ----------------------------------------------------------------------- // Creates an empty mesh // ----------------------------------------------------------------------- Mesh::Mesh(Type t) { bounds.min = Point(0,0,0) ; bounds.max = Point(0,0,0) ; center = Point(0,0,0) ; radius = 0.0f ; numFrames = 1 ; matFrames = 1 ; vertsPerFrame = 0 ; parent = -1 ; flags = 0 ; type = t ; } // ----------------------------------------------------------------------- // Joins another mesh with this one // ----------------------------------------------------------------------- Mesh & Mesh::operator += (const Mesh & m) { int firstIndex = indices.size(); int firstVertex = verts.size() ; int firstPrimit = primitives.size() ; int i ; verts.insert (verts.end(), m.verts.begin(), m.verts.end()) ; tverts.insert (tverts.end(), m.tverts.begin(), m.tverts.end()) ; indices.insert (indices.end(), m.indices.begin(), m.indices.end()) ; normals.insert (normals.end(), m.normals.begin(), m.normals.end()) ; enormals.insert (enormals.end(), m.enormals.begin(), m.enormals.end()) ; primitives.insert (primitives.end(), m.primitives.begin(), m.primitives.end()) ; for (i = firstIndex ; i < indices.size() ; i++) indices[i] += firstVertex ; for (i = firstPrimit ; i < primitives.size() ; i++) primitives[i].firstElement += firstIndex ; calculateBounds() ; calculateCenter() ; calculateRadius() ; vertsPerFrame = verts.size() ; return *this ; } // ----------------------------------------------------------------------- // Input/output // ----------------------------------------------------------------------- void Mesh::read (InputStream &stream) { stream >> type ; if (type == T_Null) return ; stream.readCheck() ; // Header & Bounds stream >> numFrames >> matFrames >> parent ; stream >> bounds >> center ; int radiusInt ; stream >> radiusInt ; radius = (float)radiusInt ; // Vertexes int numVertexes ; stream >> numVertexes ; verts.resize (numVertexes) ; stream >> verts ; // Texture coordinates int numTVerts ; stream >> numTVerts ; tverts.resize (numTVerts) ; stream >> tverts ; // Normals normals.resize (numVertexes) ; enormals.resize (numVertexes) ; stream >> normals ; stream >> enormals ; // Primitives and other stuff int numPrimitives ; stream >> numPrimitives ; primitives.resize (numPrimitives) ; stream >> primitives ; int numIndices ; stream >> numIndices ; indices.resize (numIndices) ; stream >> indices ; int numMIndices ; stream >> numMIndices ; mindices.resize (numMIndices) ; stream >> mindices ; stream >> vertsPerFrame >> flags ; stream.readCheck() ; } void Mesh::save (OutputStream &stream) const { // Mesh Type stream << type ; if (type == T_Null) return ; // Null mesh has no data stream.storeCheck() ; // Header stream << numFrames << matFrames << parent ; // Header stream << bounds << center << (int)radius ; // Bounds // Vertexes stream << (int) verts.size() ; stream << verts ; // Texture coordinates assert (tverts.size() == verts.size()) ; stream << (int) tverts.size() ; stream << tverts ; // Normals assert (normals.size() == verts.size()) ; assert (enormals.size() == verts.size()) ; stream << normals ; stream << enormals ; // Primitives stream << (int) primitives.size() ; stream << primitives ; stream << (int) indices.size() ; stream << indices ; stream << (int) mindices.size() ; stream << mindices ; // Other small stuff stream << vertsPerFrame << flags ; stream.storeCheck() ; // Skin Mesh support if (type == T_Skin) { // Initial skin vertices & normals of affected vertices. // Yes, we did just write these out, but this is what it wants. stream << (int) verts.size() ; stream << verts; stream << normals; stream << enormals; // Inverse world transforms for bones used in vertex weighting assert(nodeTransform.size() == nodeIndex.size()); stream << (int) nodeIndex.size(); for (int n = 0; n < nodeIndex.size(); n++) stream << nodeTransform[n]; // Vertex tuples. If this more than one entry per vertex, then // these tables should be sorted by vindex. assert (vweight.size() == vindex.size()); assert (vbone.size() == vindex.size()); stream << (int) vindex.size(); stream << vindex; stream << vbone; stream << vweight; // Vertex bone to node table stream << (int) nodeIndex.size(); stream << nodeIndex; stream.storeCheck(); } if (type == T_Sorted) { stream << (int) clusters.size(); stream << clusters; stream << (int) startCluster.size(); stream << startCluster; stream << (int) firstVerts.size(); stream << firstVerts; stream << (int) numVerts.size(); stream << numVerts; stream << (int) firstTVerts.size(); stream << firstTVerts; stream << (int) alwaysWriteDepth; // alwaysWriteDepth deprecated stream.storeCheck(); } } // ----------------------------------------------------------------------- // Utility stuff // ----------------------------------------------------------------------- void Mesh::setMaterial (int n) { std::vector::iterator p ; for (p = primitives.begin() ; p != primitives.end() ; p++) { p->type = (p->type & ~Primitive::MaterialMask) | (n & Primitive::MaterialMask) ; // Just in case p->type &= ~Primitive::NoMaterial ; } } void Mesh::translate(const Point n) { std::vector::iterator v ; for (v = verts.begin() ; v != verts.end() ; v++) *v += n ; calculateBounds() ; calculateCenter() ; calculateRadius() ; } void Mesh::rotate (const Quaternion &q) { std::vector::iterator v ; for (v = verts.begin() ; v != verts.end() ; v++) *v = q.apply(*v); calculateBounds() ; calculateCenter() ; calculateRadius() ; } int Mesh::getVertexBone(int node) { // Finds the bone index in the table, or adds it if it's // not there. The vertex bone & nodeIndex list are here to // track which bones are used by this mesh. int b = 0; for (; b < nodeIndex.size(); b++) if (nodeIndex[b] == node) return b; nodeIndex.push_back(node); nodeTransform.push_back(Matrix<4,4>::identity()); return b; } void Mesh::setNodeTransform(int node,Point t, Quaternion q) { // Build inverse transform, the mesh wants to be able to // transform the vertices into node space. assert(node < nodeTransform.size()); t = q.inverse().apply(-t); Matrix<4,4>::Row row; row[0] = t.x(); row[1] = t.y(); row[2] = t.z(); row[3] = 1; // The toMatrix builds a transposed transform from what we // want, so we need to pass the original quaternion to get // the inverse. nodeTransform[node] = q.toMatrix(); nodeTransform[node].setCol(3,row); } };