463 lines
13 KiB
C++
Executable File
463 lines
13 KiB
C++
Executable File
|
|
#pragma warning ( disable: 4786 )
|
|
#include <vector>
|
|
#include <cmath>
|
|
|
|
#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<Point>::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<Point>::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<Point>::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<Point>::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<Point>::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<Primitive>::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) 0; // alwaysWriteDepth deprecated
|
|
|
|
stream.storeCheck();
|
|
}
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
// Utility stuff
|
|
// -----------------------------------------------------------------------
|
|
|
|
void Mesh::setMaterial (int n)
|
|
{
|
|
std::vector<Primitive>::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<Point>::iterator v ;
|
|
for (v = verts.begin() ; v != verts.end() ; v++)
|
|
*v += n ;
|
|
|
|
calculateBounds() ;
|
|
calculateCenter() ;
|
|
calculateRadius() ;
|
|
}
|
|
|
|
void Mesh::rotate (const Quaternion &q)
|
|
{
|
|
std::vector<Point>::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);
|
|
}
|
|
|
|
};
|