added everything
This commit is contained in:
363
lib/dtsSDKPlus/DTSDecimator.cpp
Executable file
363
lib/dtsSDKPlus/DTSDecimator.cpp
Executable file
@ -0,0 +1,363 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Decimator
|
||||
// based on "A Simple, Fast, and Effective Polygon Reduction Algorithm"
|
||||
// by Stan Melax from Game Developer Magazine, November 1998
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#pragma warning ( disable: 4786 4018 )
|
||||
|
||||
#include "DTSUtil.h"
|
||||
#include "DTSDecimator.h"
|
||||
|
||||
namespace DTS
|
||||
{
|
||||
static std::vector<DecimatorVertex *> vertices;
|
||||
static std::vector<DecimatorTriangle *> triangles;
|
||||
|
||||
DecimatorTriangle::DecimatorTriangle(DecimatorVertex *v0,DecimatorVertex *v1,DecimatorVertex *v2, S32 _type){
|
||||
assert(v0!=v1 && v1!=v2 && v2!=v0);
|
||||
vertex[0]=v0;
|
||||
vertex[1]=v1;
|
||||
vertex[2]=v2;
|
||||
ComputeNormal();
|
||||
triangles.push_back(this);
|
||||
for(S32 i=0;i<3;i++) {
|
||||
vertex[i]->face.push_back(this);
|
||||
for(S32 j=0;j<3;j++) if(i!=j) {
|
||||
addUniqueElement(vertex[i]->neighbor, vertex[j]);
|
||||
}
|
||||
}
|
||||
type = _type;
|
||||
}
|
||||
DecimatorTriangle::~DecimatorTriangle(){
|
||||
S32 i;
|
||||
delElement( triangles, this );
|
||||
for(i=0;i<3;i++) {
|
||||
if(vertex[i]) delElement(vertex[i]->face,this);
|
||||
}
|
||||
for(i=0;i<3;i++) {
|
||||
S32 i2 = (i+1)%3;
|
||||
if(!vertex[i] || !vertex[i2]) continue;
|
||||
vertex[i ]->RemoveIfNonNeighbor(vertex[i2]);
|
||||
vertex[i2]->RemoveIfNonNeighbor(vertex[i ]);
|
||||
}
|
||||
}
|
||||
S32 DecimatorTriangle::HasVertex(DecimatorVertex *v) {
|
||||
return (v==vertex[0] ||v==vertex[1] || v==vertex[2]);
|
||||
}
|
||||
void DecimatorTriangle::ComputeNormal(){
|
||||
Point v0=vertex[0]->position;
|
||||
Point v1=vertex[1]->position;
|
||||
Point v2=vertex[2]->position;
|
||||
Point e1 = v1 - v0;
|
||||
Point e2 = v2 - v1;
|
||||
crossProduct( e1, e2, &normal );
|
||||
normal.normalize();
|
||||
}
|
||||
void DecimatorTriangle::ReplaceVertex(DecimatorVertex *vold,DecimatorVertex *vnew) {
|
||||
assert(vold && vnew);
|
||||
assert(vold==vertex[0] || vold==vertex[1] || vold==vertex[2]);
|
||||
assert(vnew!=vertex[0] && vnew!=vertex[1] && vnew!=vertex[2]);
|
||||
if(vold==vertex[0]){
|
||||
vertex[0]=vnew;
|
||||
}
|
||||
else if(vold==vertex[1]){
|
||||
vertex[1]=vnew;
|
||||
}
|
||||
else {
|
||||
assert(vold==vertex[2]);
|
||||
vertex[2]=vnew;
|
||||
}
|
||||
S32 i;
|
||||
delElement(vold->face,this);
|
||||
assert(!containsElement(vnew->face,this));
|
||||
vnew->face.push_back(this);
|
||||
for(i=0;i<3;i++) {
|
||||
vold->RemoveIfNonNeighbor(vertex[i]);
|
||||
vertex[i]->RemoveIfNonNeighbor(vold);
|
||||
}
|
||||
for(i=0;i<3;i++) {
|
||||
assert(containsElement(vertex[i]->face,this)==1);
|
||||
for(S32 j=0;j<3;j++) if(i!=j) {
|
||||
addUniqueElement( vertex[i]->neighbor, vertex[j]);
|
||||
}
|
||||
}
|
||||
ComputeNormal();
|
||||
}
|
||||
|
||||
DecimatorVertex::DecimatorVertex(Point v,S32 _id) {
|
||||
position =v;
|
||||
id=_id;
|
||||
vertices.push_back(this);
|
||||
}
|
||||
|
||||
DecimatorVertex::~DecimatorVertex(){
|
||||
assert(face.size()==0);
|
||||
while(neighbor.size()) {
|
||||
delElement(neighbor[0]->neighbor,this);
|
||||
delElement(neighbor,neighbor[0]);
|
||||
}
|
||||
delElement(vertices,this);
|
||||
}
|
||||
void DecimatorVertex::RemoveIfNonNeighbor(DecimatorVertex *n) {
|
||||
// removes n from neighbor list if n isn't a neighbor.
|
||||
if(!containsElement(neighbor,n)) return;
|
||||
for(S32 i=0;i<face.size();i++) {
|
||||
if(face[i]->HasVertex(n)) return;
|
||||
}
|
||||
delElement(neighbor,n);
|
||||
}
|
||||
|
||||
S32 DecimatorVertex::IsBorder()
|
||||
{
|
||||
S32 i,j;
|
||||
for(i=0;i<neighbor.size();i++)
|
||||
{
|
||||
S32 count=0;
|
||||
for(j=0;j<face.size();j++)
|
||||
{
|
||||
if(face[j]->HasVertex(neighbor[i]))
|
||||
{
|
||||
count++;
|
||||
}
|
||||
}
|
||||
assert(count>0);
|
||||
if(count==1)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
F32 Decimator::ComputeEdgeCollapseCost(DecimatorVertex *src,DecimatorVertex *dest) {
|
||||
// if we collapse edge src/dest by moving src to dest then how
|
||||
// much different will the model change, i.e. how much "error".
|
||||
// Texture, vertex normal, and border vertex code was removed
|
||||
// to keep this demo as simple as possible.
|
||||
// The method of determining cost was designed in order
|
||||
// to exploit small and coplanar regions for
|
||||
// effective polygon reduction.
|
||||
// Is is possible to add some checks here to see if "folds"
|
||||
// would be generated. i.e. normal of a remaining face gets
|
||||
// flipped. I never seemed to run into this problem and
|
||||
// therefore never added code to detect this case.
|
||||
S32 i;
|
||||
Point p = dest->position - src->position;
|
||||
F32 edgelength = p.length();
|
||||
F32 curvature = 0;
|
||||
|
||||
// find the "sides" triangles that are on the edge uv
|
||||
std::vector<DecimatorTriangle *> sides;
|
||||
for( i = 0; i < src->face.size(); i++ )
|
||||
{
|
||||
if( src->face[i]->HasVertex( dest ) )
|
||||
{
|
||||
sides.push_back( src->face[i] );
|
||||
}
|
||||
}
|
||||
|
||||
if(src->IsBorder() )
|
||||
{
|
||||
if( sides.size() > 1 )
|
||||
{
|
||||
curvature = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
curvature = 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// use the triangle facing most away from the sides
|
||||
// to determine our curvature term
|
||||
for( i = 0; i < src->face.size(); i++ )
|
||||
{
|
||||
F32 mincurv = 1; // curve for face i and closer side to it
|
||||
for( S32 j = 0; j < sides.size(); j++ )
|
||||
{
|
||||
// use dot product of face normals. '^' defined in Point
|
||||
F32 dotprod = dotProduct( src->face[i]->normal, sides[j]->normal );
|
||||
mincurv = getmin( mincurv, ( 1.0f - dotprod ) * 0.5f );
|
||||
}
|
||||
curvature = getmax( curvature, mincurv );
|
||||
}
|
||||
}
|
||||
/*
|
||||
// check for texture seam ripping
|
||||
S32 nomatch=0;
|
||||
for(i=0;i<src->face.num;i++) {
|
||||
for(S32 j=0;j<sides.num;j++) {
|
||||
// perhaps we should actually compare the positions in uv space
|
||||
if(src->face[i]->texat(src) == sides[j]->texat(src)) break;
|
||||
}
|
||||
if(j==sides.num)
|
||||
{
|
||||
// we didn't find a triangle with edge uv that shares texture coordinates
|
||||
// with face i at vertex src
|
||||
nomatch++;
|
||||
}
|
||||
}
|
||||
if(nomatch) {
|
||||
curvature=1;
|
||||
}
|
||||
*/
|
||||
return edgelength * curvature;
|
||||
}
|
||||
|
||||
void Decimator::ComputeEdgeCostAtVertex(DecimatorVertex *v) {
|
||||
// compute the edge collapse cost for all edges that start
|
||||
// from vertex v. Since we are only interested in reducing
|
||||
// the object by selecting the min cost edge at each step, we
|
||||
// only cache the cost of the least cost edge at this vertex
|
||||
// (in member variable collapse) as well as the value of the
|
||||
// cost (in member variable objdist).
|
||||
if(v->neighbor.size()==0) {
|
||||
// v doesn't have neighbors so it costs nothing to collapse
|
||||
v->collapse=NULL;
|
||||
v->objdist=-0.01f;
|
||||
return;
|
||||
}
|
||||
v->objdist = 1000000;
|
||||
v->collapse=NULL;
|
||||
// search all neighboring edges for "least cost" edge
|
||||
for(S32 i=0;i<v->neighbor.size();i++) {
|
||||
F32 dist;
|
||||
dist = ComputeEdgeCollapseCost(v,v->neighbor[i]);
|
||||
if(dist<v->objdist) {
|
||||
v->collapse=v->neighbor[i]; // candidate for edge collapse
|
||||
v->objdist=dist; // cost of the collapse
|
||||
}
|
||||
}
|
||||
}
|
||||
void Decimator::ComputeAllEdgeCollapseCosts() {
|
||||
// For all the edges, compute the difference it would make
|
||||
// to the model if it was collapsed. The least of these
|
||||
// per vertex is cached in each vertex object.
|
||||
for(S32 i=0;i<vertices.size();i++) {
|
||||
ComputeEdgeCostAtVertex(vertices[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void Decimator::Collapse(DecimatorVertex *u,DecimatorVertex *v){
|
||||
// Collapse the edge uv by moving vertex u onto v
|
||||
// Actually remove tris on uv, then update tris that
|
||||
// have u to have v, and then remove u.
|
||||
if(!v) {
|
||||
// u is a vertex all by itself so just delete it
|
||||
delete u;
|
||||
return;
|
||||
}
|
||||
S32 i;
|
||||
std::vector<DecimatorVertex *>tmp;
|
||||
// make tmp a list of all the neighbors of u
|
||||
for(i=0;i<u->neighbor.size();i++) {
|
||||
tmp.push_back(u->neighbor[i]);
|
||||
}
|
||||
// delete triangles on edge uv:
|
||||
for(i=u->face.size()-1;i>=0;i--) {
|
||||
if(u->face[i]->HasVertex(v)) {
|
||||
delete(u->face[i]);
|
||||
}
|
||||
}
|
||||
// update remaining triangles to have v instead of u
|
||||
for(i=u->face.size()-1;i>=0;i--) {
|
||||
u->face[i]->ReplaceVertex(u,v);
|
||||
}
|
||||
delete u;
|
||||
// recompute the edge collapse costs for neighboring vertices
|
||||
for(i=0;i<tmp.size();i++) {
|
||||
ComputeEdgeCostAtVertex(tmp[i]);
|
||||
}
|
||||
}
|
||||
|
||||
DecimatorVertex *Decimator::MinimumCostEdge(){
|
||||
// Find the edge that when collapsed will affect model the least.
|
||||
// This function actually returns a Vertex, the second vertex
|
||||
// of the edge (collapse candidate) is stored in the vertex data.
|
||||
// Serious optimization opportunity here: this function currently
|
||||
// does a sequential search through an unsorted list :-(
|
||||
// Our algorithm could be O(n*lg(n)) instead of O(n*n)
|
||||
DecimatorVertex *mn=vertices[0];
|
||||
for(S32 i=0;i<vertices.size();i++) {
|
||||
if(vertices[i]->objdist < mn->objdist) {
|
||||
mn = vertices[i];
|
||||
}
|
||||
}
|
||||
return mn;
|
||||
}
|
||||
|
||||
// Public interface
|
||||
|
||||
Decimator::Decimator(std::vector<Primitive>& faces, std::vector<U16>& indices, std::vector<Point>& verts)
|
||||
{
|
||||
// Add the vertices
|
||||
for( S32 i = 0; i < verts.size(); i++ )
|
||||
{
|
||||
DecimatorVertex *v = new DecimatorVertex( verts[i], i );
|
||||
}
|
||||
|
||||
// Add the faces
|
||||
for( S32 i = 0; i < faces.size(); i++ )
|
||||
{
|
||||
DecimatorTriangle *t=new DecimatorTriangle(
|
||||
vertices[indices[faces[i].firstElement]],
|
||||
vertices[indices[faces[i].firstElement+1]],
|
||||
vertices[indices[faces[i].firstElement+2]],
|
||||
faces[i].type );
|
||||
}
|
||||
|
||||
ComputeAllEdgeCollapseCosts(); // cache all edge collapse costs
|
||||
}
|
||||
|
||||
Decimator::~Decimator()
|
||||
{
|
||||
for( S32 i = triangles.size() - 1; i >= 0; i-- )
|
||||
{
|
||||
DecimatorTriangle *tmp = triangles[i];
|
||||
//triangles.Remove(tmp);
|
||||
delete tmp;
|
||||
}
|
||||
|
||||
for( S32 i = vertices.size() - 1; i >= 0; i-- )
|
||||
{
|
||||
DecimatorVertex *tmp = vertices[i];
|
||||
//vertices.Remove(tmp);
|
||||
delete tmp;
|
||||
}
|
||||
}
|
||||
|
||||
void Decimator::ReduceMesh( S32 faceCount )
|
||||
{
|
||||
// reduce the object
|
||||
while(triangles.size() > faceCount)
|
||||
{
|
||||
// get the next vertex to collapse
|
||||
DecimatorVertex *mn = MinimumCostEdge();
|
||||
|
||||
// Collapse this edge
|
||||
Collapse(mn,mn->collapse);
|
||||
}
|
||||
|
||||
primitives.empty();
|
||||
indices.empty();
|
||||
// Build our new primitive and index list
|
||||
for( S32 i = 0; i < triangles.size(); i++ )
|
||||
{
|
||||
if( triangles[i]->vertex[0] != NULL && triangles[i]->vertex[1] != NULL && triangles[i]->vertex[2] != NULL )
|
||||
{
|
||||
Primitive p;
|
||||
p.firstElement = indices.size();
|
||||
p.numElements = 3;
|
||||
p.type = triangles[i]->type;
|
||||
primitives.push_back( p );
|
||||
|
||||
indices.push_back( triangles[i]->vertex[0]->id );
|
||||
indices.push_back( triangles[i]->vertex[1]->id );
|
||||
indices.push_back( triangles[i]->vertex[2]->id );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
81
lib/dtsSDKPlus/DTSDecimator.h
Executable file
81
lib/dtsSDKPlus/DTSDecimator.h
Executable file
@ -0,0 +1,81 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Decimator
|
||||
// based on "A Simple, Fast, and Effective Polygon Reduction Algorithm"
|
||||
// by Stan Melax from Game Developer Magazine, November 1998
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef __DTS_DECIMATOR_H
|
||||
#define __DTS_DECIMATOR_H
|
||||
|
||||
#include "DTSMesh.h"
|
||||
#include "DTSPoint.h"
|
||||
|
||||
namespace DTS
|
||||
{
|
||||
/*
|
||||
* For the polygon reduction algorithm we use data structures
|
||||
* that contain a little bit more information than the usual
|
||||
* indexed face set type of data structure.
|
||||
* From a vertex we wish to be able to quickly get the
|
||||
* neighboring faces and vertices.
|
||||
*/
|
||||
class DecimatorTriangle;
|
||||
class DecimatorVertex;
|
||||
class DecimatorVector;
|
||||
|
||||
class DecimatorTriangle
|
||||
{
|
||||
public:
|
||||
DecimatorVertex* vertex[3]; // the 3 points that make this tri
|
||||
Point normal; // unit vector othogonal to this face
|
||||
S32 type;
|
||||
|
||||
DecimatorTriangle(DecimatorVertex *v0,DecimatorVertex *v1,DecimatorVertex *v2, S32 _type);
|
||||
~DecimatorTriangle();
|
||||
|
||||
void ComputeNormal();
|
||||
void ReplaceVertex(DecimatorVertex *vold,DecimatorVertex *vnew);
|
||||
S32 HasVertex(DecimatorVertex *v);
|
||||
};
|
||||
|
||||
class DecimatorVertex
|
||||
{
|
||||
public:
|
||||
Point position; // location of point in euclidean space
|
||||
S32 id; // place of vertex in original list
|
||||
std::vector<DecimatorVertex *> neighbor; // adjacent vertices
|
||||
std::vector<DecimatorTriangle *> face; // adjacent triangles
|
||||
F32 objdist; // cached cost of collapsing edge
|
||||
DecimatorVertex* collapse; // candidate vertex for collapse
|
||||
|
||||
DecimatorVertex(Point v,S32 _id);
|
||||
~DecimatorVertex();
|
||||
|
||||
S32 IsBorder();
|
||||
void RemoveIfNonNeighbor(DecimatorVertex *n);
|
||||
};
|
||||
|
||||
class Decimator
|
||||
{
|
||||
private:
|
||||
F32 ComputeEdgeCollapseCost(DecimatorVertex *u,DecimatorVertex *v);
|
||||
void ComputeEdgeCostAtVertex(DecimatorVertex *v);
|
||||
void ComputeAllEdgeCollapseCosts();
|
||||
void Collapse(DecimatorVertex *u,DecimatorVertex *v);
|
||||
DecimatorVertex *MinimumCostEdge();
|
||||
|
||||
std::vector<Primitive> primitives;
|
||||
std::vector<U16> indices;
|
||||
|
||||
public:
|
||||
Decimator(std::vector<Primitive>& faces, std::vector<U16>& indices, std::vector<Point>& verts);
|
||||
~Decimator();
|
||||
|
||||
void ReduceMesh( S32 numFaces );
|
||||
|
||||
void GetMesh(std::vector<Primitive>& p, std::vector<U16>& i) { p=primitives; i=indices; }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
49
lib/dtsSDKPlus/DTSPlusTypes.h
Executable file
49
lib/dtsSDKPlus/DTSPlusTypes.h
Executable file
@ -0,0 +1,49 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef DTSPLUSTYPES_H_
|
||||
#define DTSPLUSTYPES_H_
|
||||
|
||||
typedef signed char S8; // Compiler independent Signed Char
|
||||
typedef unsigned char U8; // Compiler independent Unsigned Char
|
||||
|
||||
typedef signed short S16; // Compiler independent Signed 16-bit short
|
||||
typedef unsigned short U16; // Compiler independent Unsigned 16-bit short
|
||||
|
||||
typedef signed int S32; // Compiler independent Signed 32-bit integer
|
||||
typedef unsigned int U32; // Compiler independent Unsigned 32-bit integer
|
||||
|
||||
typedef float F32; // Compiler independent 32-bit F32
|
||||
typedef double F64; // Compiler independent 64-bit F32
|
||||
|
||||
// Simple template used to make sure some operation
|
||||
// gets executed before going out of scope
|
||||
template <class Type> class OnDestroy
|
||||
{
|
||||
protected:
|
||||
Type * mObj;
|
||||
|
||||
void transfer(OnDestroy<Type> * des)
|
||||
{
|
||||
doit();
|
||||
if (des)
|
||||
{
|
||||
mObj = des->mObj;
|
||||
des->mObj=NULL;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void doit() = 0;
|
||||
|
||||
public:
|
||||
|
||||
OnDestroy(Type * obj=NULL) { mObj=obj; }
|
||||
OnDestroy(OnDestroy<Type> & des) { transfer(&des); }
|
||||
virtual ~OnDestroy() {}
|
||||
void operator=(OnDestroy<Type> & des) { transfer(&des); }
|
||||
};
|
||||
|
||||
|
||||
#endif // DTSPLUSTYPES_H_
|
394
lib/dtsSDKPlus/DTSUtil.cpp
Executable file
394
lib/dtsSDKPlus/DTSUtil.cpp
Executable file
@ -0,0 +1,394 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable : 4786 4018)
|
||||
#endif
|
||||
|
||||
#include "DTSUtil.h"
|
||||
#include "appConfig.h"
|
||||
#include "decomp/Decompose.h"
|
||||
#include <stdarg.h>
|
||||
|
||||
#define TEST_DTS_MATH
|
||||
|
||||
namespace DTS
|
||||
{
|
||||
|
||||
const char* avar(const char *message, ...)
|
||||
{
|
||||
static char buffer[4096];
|
||||
va_list args;
|
||||
va_start(args, message);
|
||||
vsprintf(buffer, message, args);
|
||||
return( buffer );
|
||||
}
|
||||
|
||||
|
||||
bool isEqualQ16(const Quaternion & a, const Quaternion &b)
|
||||
{
|
||||
U16 MAX_VAL = 0x7fff;
|
||||
|
||||
// convert components to 16 bit, then test for equality
|
||||
S16 x, y, z, w;
|
||||
x = ((S16)(a.x() * F32(MAX_VAL))) - ((S16)(b.x() * F32(MAX_VAL)));
|
||||
y = ((S16)(a.y() * F32(MAX_VAL))) - ((S16)(b.y() * F32(MAX_VAL)));
|
||||
z = ((S16)(a.z() * F32(MAX_VAL))) - ((S16)(b.z() * F32(MAX_VAL)));
|
||||
w = ((S16)(a.w() * F32(MAX_VAL))) - ((S16)(b.w() * F32(MAX_VAL)));
|
||||
return (x==0) && (y==0) && (z==0) && (w==0);
|
||||
}
|
||||
|
||||
void crossProduct(const Point3D & a, const Point3D & b, Point3D * c)
|
||||
{
|
||||
c->x(a.y() * b.z() - a.z() * b.y());
|
||||
c->y(a.z() * b.x() - a.x() * b.z());
|
||||
c->z(a.x() * b.y() - a.y() * b.x());
|
||||
}
|
||||
|
||||
void convertToTransform(Matrix<4,4,F32> & mat, Quaternion & rot, Point3D & trans, Quaternion & srot, Point3D & scale)
|
||||
{
|
||||
AffineParts parts;
|
||||
decomp_affine(mat,&parts);
|
||||
trans = parts.trans;
|
||||
rot = parts.rot;
|
||||
srot = parts.scaleRot;
|
||||
scale = parts.scale;
|
||||
}
|
||||
|
||||
void decomp_affine(const Matrix<4,4,F32> & mat, AffineParts * parts)
|
||||
{
|
||||
GraphicGems::HMatrix ggMat;
|
||||
GraphicGems::AffineParts ggParts;
|
||||
for (S32 i=0; i<4; i++)
|
||||
{
|
||||
for (S32 j=0; j<4; j++)
|
||||
ggMat[i][j] = mat[i][j];
|
||||
}
|
||||
GraphicGems::decomp_affine(ggMat,&ggParts);
|
||||
parts->rot = Quaternion(F32(ggParts.q.x),F32(ggParts.q.y),F32(ggParts.q.z),F32(ggParts.q.w));
|
||||
parts->scale = Point3D(F32(ggParts.k.x),F32(ggParts.k.y),F32(ggParts.k.z));
|
||||
parts->scaleRot = Quaternion(F32(ggParts.u.x),F32(ggParts.u.y),F32(ggParts.u.z),F32(ggParts.u.w));
|
||||
parts->trans = Point3D(F32(ggParts.t.x),F32(ggParts.t.y),F32(ggParts.t.z));
|
||||
parts->sign = F32(ggParts.f);
|
||||
|
||||
#ifdef TEST_DTS_MATH
|
||||
// Test math (but only in the unscaled case
|
||||
if (isEqual(parts->scale.x(),1.0f,0.01f) && isEqual(parts->scale.y(),1.0f,0.01f) && isEqual(parts->scale.z(),1.0f,0.01f))
|
||||
{
|
||||
Matrix<4,4,F32> mat2 = parts->rot.toMatrix();
|
||||
Vector<F32,4> col;
|
||||
col[0] = parts->trans.x();
|
||||
col[1] = parts->trans.y();
|
||||
col[2] = parts->trans.z();
|
||||
col[3] = 1;
|
||||
mat2.setCol(3,col);
|
||||
for (S32 i=0; i<4; i++)
|
||||
{
|
||||
for (S32 ii=0; ii<4; ii++)
|
||||
{
|
||||
if (!isEqual(mat[i][ii],mat2[i][ii],0.01f))
|
||||
AppConfig::PrintDump(-1,"Doh!");
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void zapScale(Matrix<4,4,F32> & mat)
|
||||
{
|
||||
AffineParts parts;
|
||||
decomp_affine(mat,&parts);
|
||||
|
||||
// now put the matrix back together again without the scale:
|
||||
// mat = mat.rot * mat.pos
|
||||
Vector<F32,4> trans;
|
||||
trans[0] = parts.trans.x();
|
||||
trans[1] = parts.trans.y();
|
||||
trans[2] = parts.trans.z();
|
||||
trans[3] = 1;
|
||||
mat = parts.rot.toMatrix();
|
||||
mat.setCol(3,trans);
|
||||
|
||||
#ifdef TEST_DTS_MATH
|
||||
{
|
||||
// A test...will get rid of once we know it works...
|
||||
Matrix<4,4,F32> mat2;
|
||||
decomp_affine(mat,&parts);
|
||||
trans[0] = parts.trans.x();
|
||||
trans[1] = parts.trans.y();
|
||||
trans[2] = parts.trans.z();
|
||||
trans[3] = 1;
|
||||
mat2 = parts.rot.toMatrix();
|
||||
mat2.setCol(3,trans);
|
||||
for (S32 i=0; i<4; i++)
|
||||
{
|
||||
for (S32 j=0; j<4; j++)
|
||||
{
|
||||
assert(isZero(mat[i][j]-mat2[i][j],0.01f));
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
Matrix<4,4,F32> & getLocalNodeMatrix(AppNode * node, AppNode * parent, const AppTime & time, Matrix<4,4,F32> & matrix, AffineParts & a10, AffineParts & a20)
|
||||
{
|
||||
// Here's the story: the default transforms have no scale. In order to account for scale, the
|
||||
// scale at the default time is folded into the object offset (which is multiplied into the points
|
||||
// before exporting). Because of this, the local transform at a given time must take into account
|
||||
// the scale of the parent and child node at time 0 in addition to the current time. In particular,
|
||||
// the world transform at a given time is WT(time) = T(time) * inverse(Tscale(0))
|
||||
|
||||
// in order to avoid recomputing matrix at default time over and over, we assume that the first request
|
||||
// for the matrix will be at the default time, and thereafter, we will pass that matrix in and reuse it...
|
||||
Matrix<4,4,F32> m1 = node->getNodeTransform(time);
|
||||
Matrix<4,4,F32> m2;
|
||||
if (parent)
|
||||
m2 = parent->getNodeTransform(time);
|
||||
else
|
||||
m2 = Matrix<4,4,F32>::identity();
|
||||
if (time == AppTime::DefaultTime())
|
||||
{
|
||||
decomp_affine(m1,&a10);
|
||||
decomp_affine(m2,&a20);
|
||||
}
|
||||
|
||||
// build the inverse scale matrices
|
||||
Matrix<4,4,F32> stretchRot10,stretchRot20;
|
||||
Matrix<4,4,F32> scaleMat10,scaleMat20;
|
||||
Matrix<4,4,F32> invScale10, invScale20;
|
||||
Point3D sfactor10, sfactor20;
|
||||
stretchRot10 = a10.scaleRot.toMatrix();
|
||||
stretchRot20 = a20.scaleRot.toMatrix();
|
||||
sfactor10 = Point3D(a10.sign/a10.scale.x(),a10.sign/a10.scale.y(),a10.sign/a10.scale.z());
|
||||
sfactor20 = Point3D(a20.sign/a20.scale.x(),a20.sign/a20.scale.y(),a20.sign/a20.scale.z());
|
||||
scaleMat10 = Matrix<4,4,F32>::identity();
|
||||
scaleMat10[0][0] = sfactor10.x();
|
||||
scaleMat10[1][1] = sfactor10.y();
|
||||
scaleMat10[2][2] = sfactor10.z();
|
||||
scaleMat20 = Matrix<4,4,F32>::identity();
|
||||
scaleMat20[0][0] = sfactor20.x();
|
||||
scaleMat20[1][1] = sfactor20.y();
|
||||
scaleMat20[2][2] = sfactor20.z();
|
||||
|
||||
|
||||
invScale10 = stretchRot10 * scaleMat10 * stretchRot10.inverse();
|
||||
invScale20 = stretchRot20 * scaleMat20 * stretchRot20.inverse();
|
||||
|
||||
// build world transforms
|
||||
m1 = m1 * invScale10;
|
||||
m2 = m2 * invScale20;
|
||||
|
||||
// build local transform
|
||||
matrix = m2.inverse() * m1;
|
||||
|
||||
#ifdef TEST_DTS_MATH
|
||||
{
|
||||
Matrix<4,4,F32> testMat;
|
||||
Matrix<4,4,F32> m2inv = m2.inverse();
|
||||
testMat = m2inv * m2;
|
||||
{
|
||||
for (S32 i=0; i<4; i++)
|
||||
for (S32 j=0; j<4; j++)
|
||||
{
|
||||
F32 val = i==j ? 1.0f : 0.0f;
|
||||
assert(isEqual(testMat[i][j],val,0.01f) && "assertion failed");
|
||||
}
|
||||
}
|
||||
testMat = m2 * m2inv;
|
||||
{
|
||||
for (S32 i=0; i<4; i++)
|
||||
for (S32 j=0; j<4; j++)
|
||||
{
|
||||
F32 val = i==j ? 1.0f : 0.0f;
|
||||
assert(isEqual(testMat[i][j],val,0.01f) && "assertion failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return matrix;
|
||||
}
|
||||
|
||||
void getLocalNodeTransform(AppNode * node, AppNode * parent, AffineParts & child0, AffineParts & parent0, const AppTime & time, Quaternion & rot, Point3D & trans, Quaternion & srot, Point3D & scale)
|
||||
{
|
||||
Matrix<4,4,F32> localMat;
|
||||
getLocalNodeMatrix(node,parent,time,localMat,child0,parent0);
|
||||
convertToTransform(localMat,rot,trans,srot,scale);
|
||||
}
|
||||
|
||||
void getBlendNodeTransform(AppNode * node, AppNode * parent, AffineParts & child0, AffineParts & parent0, const AppTime & time, const AppTime & referenceTime, Quaternion & rot, Point3D & trans, Quaternion & srot, Point3D & scale)
|
||||
{
|
||||
Matrix<4,4,F32> m1, invm1, m2, retMat;
|
||||
|
||||
getLocalNodeMatrix(node, parent, referenceTime, m1, child0, parent0);
|
||||
getLocalNodeMatrix(node, parent, time, m2, child0, parent0);
|
||||
invm1 = m1.inverse();
|
||||
retMat = invm1 * m2;
|
||||
convertToTransform(retMat, rot,trans,srot,scale);
|
||||
}
|
||||
|
||||
void getDeltaTransform(AppNode * node, const AppTime & time1, const AppTime & time2, Quaternion & rot, Point3D & trans, Quaternion & srot, Point3D & scale)
|
||||
{
|
||||
Matrix<4,4,F32> m1 = node->getNodeTransform(time1);
|
||||
Matrix<4,4,F32> m2 = node->getNodeTransform(time2);
|
||||
zapScale(m1);
|
||||
zapScale(m2);
|
||||
|
||||
convertToTransform(m1.inverse() * m2,rot,trans,srot,scale);
|
||||
}
|
||||
|
||||
bool neverAnimateNode(AppNode*)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
char * chopNum(char * s)
|
||||
{
|
||||
if (s==NULL)
|
||||
return NULL;
|
||||
|
||||
char * p = s + strlen(s);
|
||||
|
||||
if (p==s)
|
||||
return s;
|
||||
p--;
|
||||
|
||||
// trim spaces from the end
|
||||
while (p!=s && *p==' ')
|
||||
p--;
|
||||
|
||||
// back up until we reach a non-digit
|
||||
// gotta be better way than this...
|
||||
if (isdigit(*p))
|
||||
do
|
||||
{
|
||||
if (p--==s)
|
||||
return p+1;
|
||||
} while (isdigit(*p));
|
||||
|
||||
// allow negative numbers, treat _ as - for Maya
|
||||
if (*p=='-' || *p=='_')
|
||||
p--;
|
||||
|
||||
// trim spaces separating name and number
|
||||
while (*p==' ')
|
||||
{
|
||||
p--;
|
||||
if (p==s)
|
||||
return p;
|
||||
}
|
||||
|
||||
// return first space if there was one,
|
||||
// o.w. return first digit
|
||||
return p+1;
|
||||
}
|
||||
|
||||
char * chopTrailingNumber(const char * fullName, S32 & size)
|
||||
{
|
||||
if (!fullName)
|
||||
{
|
||||
size = -1;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char * buffer = strnew(fullName);
|
||||
char * p = chopNum(buffer);
|
||||
if (*p=='\0')
|
||||
{
|
||||
size = -1;
|
||||
return buffer;
|
||||
}
|
||||
|
||||
size = 1;
|
||||
if (*p=='_')
|
||||
size = -atoi(p+1);
|
||||
else
|
||||
size = atoi(p);
|
||||
*p='\0'; // terminate string
|
||||
return buffer;
|
||||
}
|
||||
|
||||
S32 getTrailingNumber(const char * fullName)
|
||||
{
|
||||
S32 size;
|
||||
delete [] chopTrailingNumber(fullName,size);
|
||||
return size;
|
||||
}
|
||||
|
||||
void tweakName(const char ** name)
|
||||
{
|
||||
char * pre[] = { "BB::", "BBZ::", "SORT::", "SEQUENCE::",
|
||||
"BB_" , "BBZ_" , "SORT_" , "SEQUENCE_" , "" };
|
||||
for (S32 i=0; strlen(pre[i]) != 0; i++)
|
||||
{
|
||||
if (!_strnicmp(*name,pre[i],strlen(pre[i])))
|
||||
{
|
||||
// found prefix...now skip the prefix and return
|
||||
*name += strlen(pre[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const char * getFileBase(const char * str)
|
||||
{
|
||||
const char * ret = strrchr(str,'/');
|
||||
if (!ret)
|
||||
ret = strrchr(str,'\\');
|
||||
if (!ret)
|
||||
ret = strrchr(str,':');
|
||||
if (!ret)
|
||||
ret = str;
|
||||
else
|
||||
++ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
char * getFileBase(char * str)
|
||||
{
|
||||
return (char*)getFileBase((const char*)str);
|
||||
}
|
||||
|
||||
char * getFilePath(const char * str, S32 pad)
|
||||
{
|
||||
const char * slash = getFileBase(str);
|
||||
S32 len = slash-str;
|
||||
char * ret = new char[len+1+pad];
|
||||
strncpy(ret,str,len);
|
||||
ret[len]='\0';
|
||||
return ret;
|
||||
}
|
||||
|
||||
// perform string compare with wildcards (in s2 only) and case insensitivity
|
||||
bool stringEqual(const char * s1, const char * s2)
|
||||
{
|
||||
if (*s1=='\0' && *s2=='\0')
|
||||
return true;
|
||||
|
||||
if (*s2=='*')
|
||||
{
|
||||
if (stringEqual(s1,s2+1))
|
||||
return true;
|
||||
if (*s1=='\0')
|
||||
return false;
|
||||
return stringEqual(s1+1,s2);
|
||||
}
|
||||
if (toupper(*s1)==toupper(*s2))
|
||||
return stringEqual(s1+1,s2+1);
|
||||
return false;
|
||||
}
|
||||
|
||||
char * removeExt(char * str)
|
||||
{
|
||||
char * tmp = _strdup(str);
|
||||
char * ext = strrchr(tmp,'.');
|
||||
if (ext)
|
||||
*ext = 0;
|
||||
return tmp;
|
||||
}
|
||||
};
|
||||
|
176
lib/dtsSDKPlus/DTSUtil.h
Executable file
176
lib/dtsSDKPlus/DTSUtil.h
Executable file
@ -0,0 +1,176 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef DTSUTIL_H_
|
||||
#define DTSUTIL_H_
|
||||
|
||||
#include "DTSTypes.h"
|
||||
#include "DTSShape.h"
|
||||
#include "DTSPlusTypes.h"
|
||||
#include "appNode.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable : 4996)
|
||||
#endif
|
||||
|
||||
namespace DTS
|
||||
{
|
||||
#ifndef M_PI
|
||||
const F64 M_PI = 3.14159265358979323846;
|
||||
#endif
|
||||
const S8 PATH_DELIM_CHAR = '\\';
|
||||
const S8 PATH_DELIM_STR[] = "\\";
|
||||
|
||||
template <class T> inline const T & getmax(const T & t1, const T & t2) { return t1>t2 ? t1 : t2; }
|
||||
template <class T> inline const T & getmin(const T & t1, const T & t2) { return t1<t2 ? t1 : t2; }
|
||||
|
||||
// don't like std:vector insert/erase...
|
||||
|
||||
template <class T> inline void delElementAtIndex(std::vector<T> & vec, S32 idx)
|
||||
{
|
||||
vec.erase(vec.begin() + idx);
|
||||
}
|
||||
|
||||
template <class T> inline void insElementAtIndex(std::vector<T> & vec, S32 idx, const T & el)
|
||||
{
|
||||
vec.insert(vec.begin() + idx,el);
|
||||
}
|
||||
|
||||
template <class T> inline void delElement(std::vector<T> & vec, const T & el)
|
||||
{
|
||||
for(S32 i=0;i<vec.size();i++) {
|
||||
if(vec[i] == el)
|
||||
{
|
||||
vec.erase(vec.begin() + i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <class T> inline bool containsElement(std::vector<T> & vec, const T & el )
|
||||
{
|
||||
S32 count = 0;
|
||||
for(S32 i=0;i<vec.size();i++)
|
||||
{
|
||||
if(vec[i] == el)
|
||||
count++;
|
||||
}
|
||||
|
||||
if (count > 0)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
template <class T> inline void addUniqueElement(std::vector<T> & vec, const T & el )
|
||||
{
|
||||
if( !containsElement(vec, el) )
|
||||
vec.push_back(el);
|
||||
}
|
||||
|
||||
// add a method that Torque vectors has but std::vectors don't
|
||||
template <class T>
|
||||
inline void appendVector(std::vector<T> & m1, const std::vector<T> & m2) { m1.insert(m1.end(),m2.begin(),m2.end()); }
|
||||
|
||||
inline bool isZero(F32 f, F32 tol) { return fabs(f)<tol; }
|
||||
inline bool isZero(const Point2D & a, F32 tol) { return isZero(a.x(),tol) && isZero(a.y(),tol); }
|
||||
inline bool isZero(const Point3D & a, F32 tol) { return isZero(a.x(),tol) && isZero(a.y(),tol) && isZero(a.z(),tol); }
|
||||
inline bool isZero(const Quaternion & a, F32 tol) { isZero(a.x(),tol) && isZero(a.y(),tol) && isZero(a.z(),tol) && isZero(a.w(),tol); }
|
||||
inline bool isEqual(F32 a, F32 b, F32 tol) { return isZero(a-b,tol); }
|
||||
inline bool isEqual(const Point2D & a, const Point2D & b, F32 tol) { return isZero(a-b,tol); }
|
||||
inline bool isEqual(const Point3D & a, const Point3D & b, F32 tol) { return isZero(a-b,tol); }
|
||||
inline bool isEqual(const Quaternion & a, const Quaternion & b, F32 tol) { return isZero(a-b,tol); }
|
||||
extern bool isEqualQ16(const Quaternion & a, const Quaternion &b);
|
||||
|
||||
inline F32 dotProduct(const Point3D & a, const Point3D & b) { return a.x() * b.x() + a.y() * b.y() + a.z() * b.z(); }
|
||||
extern void crossProduct(const Point3D & a, const Point3D & b, Point3D * c);
|
||||
|
||||
template <class T>
|
||||
inline T* constructInPlace(T* p)
|
||||
{
|
||||
return new(p) T;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline void destructInPlace(T* p)
|
||||
{
|
||||
p->~T();
|
||||
}
|
||||
|
||||
struct AffineParts
|
||||
{
|
||||
Point3D trans; // Translation components
|
||||
Quaternion rot; // Essential rotation
|
||||
Quaternion scaleRot; // Stretch rotation
|
||||
Point3D scale; // Stretch factors
|
||||
F32 sign; // Sign of determinant
|
||||
};
|
||||
|
||||
extern void decomp_affine(const Matrix<4,4,F32> &, AffineParts *);
|
||||
extern void zapScale(Matrix<4,4,F32> &);
|
||||
|
||||
extern void getLocalNodeTransform(AppNode * node, AppNode * parent, AffineParts & child0, AffineParts & parent0, const AppTime & time, Quaternion & rot, Point3D & trans, Quaternion & srot, Point3D & scale);
|
||||
extern void getBlendNodeTransform(AppNode * node, AppNode * parent, AffineParts & child0, AffineParts & parent0, const AppTime & time, const AppTime & referenceTime, Quaternion & rot, Point3D & trans, Quaternion & srot, Point3D & scale);
|
||||
extern void getDeltaTransform(AppNode * node, const AppTime & time1, const AppTime & time2, Quaternion & rot, Point3D & trans, Quaternion & srot, Point3D & scale);
|
||||
|
||||
inline void setMembershipArray(std::vector<bool> & m, bool setTo, S32 a, S32 b)
|
||||
{
|
||||
if (m.size() < U32(b))
|
||||
m.resize(b);
|
||||
for (S32 i=a; i<b; i++)
|
||||
m[i]=setTo;
|
||||
}
|
||||
|
||||
inline void setMembershipArray(std::vector<bool> & m, bool setTo, S32 a)
|
||||
{
|
||||
m[a]=setTo;
|
||||
}
|
||||
|
||||
inline bool allSet(std::vector<bool> & m)
|
||||
{
|
||||
for (U32 i=0; i<m.size(); i++)
|
||||
{
|
||||
if (m[i]) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline void overlapSet(std::vector<bool> & m1, const std::vector<bool> & m2)
|
||||
{
|
||||
assert(m1.size()==m2.size());
|
||||
for (U32 i=0; i<m2.size(); i++)
|
||||
{
|
||||
if (m2[i])
|
||||
m1[i]=true;
|
||||
}
|
||||
}
|
||||
|
||||
inline void subtractSet(std::vector<bool> & m1, const std::vector<bool> & m2)
|
||||
{
|
||||
assert(m1.size()==m2.size());
|
||||
for (U32 i=0; i<m2.size(); i++)
|
||||
{
|
||||
if (m2[i])
|
||||
m1[i]=false;
|
||||
}
|
||||
}
|
||||
|
||||
extern S32 getTrailingNumber(const char * fullName);
|
||||
extern char * chopTrailingNumber(const char * fullName, S32 & size);
|
||||
extern void tweakName(const char ** name);
|
||||
extern const char* avar(const char *in_msg, ...);
|
||||
extern bool stringEqual(const char * s1, const char * s2);
|
||||
inline char * strnew(const char * str) { char * ret = new char[strlen(str)+1]; strcpy(ret,str); return ret; }
|
||||
extern char * getFileBase(char * str);
|
||||
extern const char * getFileBase(const char * str);
|
||||
extern char * getFilePath(const char * str, S32 pad=0);
|
||||
|
||||
inline char * strnew(std::string str) { return strnew(str.c_str()); }
|
||||
|
||||
extern char * removeExt(char * str);
|
||||
};
|
||||
|
||||
#endif // DTSUTIL_H_
|
||||
|
3960
lib/dtsSDKPlus/ShapeMimic.cpp
Executable file
3960
lib/dtsSDKPlus/ShapeMimic.cpp
Executable file
File diff suppressed because it is too large
Load Diff
301
lib/dtsSDKPlus/ShapeMimic.h
Executable file
301
lib/dtsSDKPlus/ShapeMimic.h
Executable file
@ -0,0 +1,301 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// The ShapeMimic tries to hold court in both the App world and
|
||||
// the Torque three-space world. It holds a shape tree isomorphic
|
||||
// to what the shape will look like when exported, but maintains
|
||||
// links to App objects and delays computing certain things
|
||||
// until the tsshape is finally created in generateShape().
|
||||
|
||||
#ifndef SHAPEMIMIC_H_
|
||||
#define SHAPEMIMIC_H_
|
||||
|
||||
#include "DTSTypes.h"
|
||||
#include "DTSShape.h"
|
||||
#include "DTSPlusTypes.h"
|
||||
#include "DTSUtil.h"
|
||||
#include "appNode.h"
|
||||
#include "appMesh.h"
|
||||
#include "appSequence.h"
|
||||
#include "appIfl.h"
|
||||
#include "appConfig.h"
|
||||
|
||||
namespace DTS
|
||||
{
|
||||
struct IflMimic
|
||||
{
|
||||
AppIfl * appIfl;
|
||||
S32 materialSlot;
|
||||
};
|
||||
|
||||
struct SkinMimic
|
||||
{
|
||||
typedef std::vector<F32> WeightList;
|
||||
|
||||
AppMesh * appMesh;
|
||||
Mesh * skinMesh;
|
||||
S32 detailSize;
|
||||
S32 skinNum;
|
||||
S32 meshNum;
|
||||
F32 multiResPercent;
|
||||
|
||||
std::vector<Primitive> faces;
|
||||
std::vector<Point3D> verts;
|
||||
std::vector<Point3D> normals;
|
||||
std::vector<Point2D> tverts;
|
||||
std::vector<U16> indices;
|
||||
std::vector<U32> smoothingGroups;
|
||||
std::vector<U32> vertId;
|
||||
|
||||
std::vector<AppNode *> bones;
|
||||
std::vector<WeightList*> weights;
|
||||
|
||||
~SkinMimic() { for (U32 i=0;i<weights.size(); i++) delete weights[i]; multiResPercent = 1.0f; }
|
||||
};
|
||||
|
||||
struct MeshMimic
|
||||
{
|
||||
AppMesh * appMesh;
|
||||
Mesh * tsMesh;
|
||||
SkinMimic * skinMimic;
|
||||
bool billboard; // i.e., face camera
|
||||
bool sortedObject;
|
||||
S32 numVerts; // number of unique vertices
|
||||
S32 meshNum;
|
||||
std::vector<U32> smoothingGroups;
|
||||
std::vector<U32> remap;
|
||||
std::vector<U32> vertId;
|
||||
|
||||
F32 multiResPercent;
|
||||
|
||||
Matrix<4,4,F32> objectOffset; // NOTE: not valid till late in the game
|
||||
|
||||
MeshMimic(AppMesh * mesh) { appMesh = mesh; skinMimic = NULL; tsMesh = NULL; multiResPercent = 1.0f; }
|
||||
};
|
||||
|
||||
struct ObjectMimic
|
||||
{
|
||||
enum { MaxDetails=20 };
|
||||
struct Detail
|
||||
{
|
||||
S32 size;
|
||||
F32 multiResPercent;
|
||||
MeshMimic * mesh;
|
||||
};
|
||||
|
||||
// object name
|
||||
char * name;
|
||||
char * fullName; // name of object in tree
|
||||
|
||||
// each object has several meshes attached
|
||||
S32 numDetails;
|
||||
Detail details[MaxDetails];
|
||||
|
||||
// we'll check the object against this list in the end
|
||||
std::vector<S32> * validDetails;
|
||||
AppNode * inTreeNode; // this is the node that sits in the shape's node hierrarchy
|
||||
AppMesh * inTreeMesh; // this is the mesh that sits in the shape's node hierrarchy
|
||||
|
||||
// The next two items are used for sorting objects
|
||||
// objects are sorted by subTreeNum first, and then
|
||||
// priority (smallest to highest in both cases).
|
||||
S32 subtreeNum;
|
||||
U32 priority;
|
||||
|
||||
// The node in the 3D app this object hangs on (i.e., the one in the shape not
|
||||
// the loose objects that make up the detail levels).
|
||||
AppNode * appParent;
|
||||
|
||||
// Similar to above: the app node that corresponds to tsNode that will
|
||||
// be our parent. Starts out the same as appParent, but is revised
|
||||
// as we prune unwanted nodes from the tree structure.
|
||||
AppNode * appTSParent;
|
||||
|
||||
// ts node index we hang off
|
||||
S32 tsNodeIndex;
|
||||
S32 tsObjectIndex;
|
||||
|
||||
// This is the eventual payoff
|
||||
DTS::Object * tsObject;
|
||||
|
||||
//
|
||||
bool isBone;
|
||||
bool isSkin;
|
||||
|
||||
AppMesh * getSkin()
|
||||
{
|
||||
for (S32 dl=0; dl<numDetails; dl++)
|
||||
if (details[dl].mesh)
|
||||
return details[dl].mesh->skinMimic ? details[dl].mesh->skinMimic->appMesh : NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
~ObjectMimic()
|
||||
{
|
||||
delete [] name;
|
||||
delete [] fullName;
|
||||
for (S32 i=0;i<numDetails;i++)
|
||||
delete details[i].mesh;
|
||||
}
|
||||
};
|
||||
|
||||
struct NodeMimic
|
||||
{
|
||||
// our twin in the max world:
|
||||
AppNode * appNode;
|
||||
|
||||
// our neighbors in the mimic world:
|
||||
NodeMimic * parent;
|
||||
NodeMimic * child;
|
||||
NodeMimic * sibling;
|
||||
|
||||
// transforms at default time
|
||||
AffineParts child0;
|
||||
AffineParts parent0;
|
||||
|
||||
// index of our ts version
|
||||
S32 number;
|
||||
|
||||
// our mimic object
|
||||
std::vector<ObjectMimic*> objects;
|
||||
};
|
||||
|
||||
class ShapeMimic
|
||||
{
|
||||
struct Subtree
|
||||
{
|
||||
std::vector<S32> validDetails;
|
||||
std::vector<const char*> detailNames;
|
||||
std::vector<AppNode*> detailNodes;
|
||||
NodeMimic start;
|
||||
};
|
||||
|
||||
std::vector<Subtree*> subtrees;
|
||||
std::vector<ObjectMimic*> objectList;
|
||||
std::vector<SkinMimic*> skins;
|
||||
std::vector<IflMimic*> iflList;
|
||||
std::vector<AppSequence*> sequences;
|
||||
std::vector<Material> materials;
|
||||
AppNode * boundsNode;
|
||||
|
||||
// this gets filled in late in the game
|
||||
// it holds the nodes that actually go into the shape
|
||||
// in the order they appear in the shape
|
||||
std::vector<NodeMimic*> nodes;
|
||||
|
||||
static std::vector<Quaternion*> nodeRotCache;
|
||||
static std::vector<Point3D*> nodeTransCache;
|
||||
static std::vector<Quaternion*> nodeScaleRotCache;
|
||||
static std::vector<Point3D*> nodeScaleCache;
|
||||
|
||||
static std::vector<AppNode*> cutNodes;
|
||||
static std::vector<AppNode*> cutNodesParents;
|
||||
|
||||
// error control
|
||||
//void setExportError(const char * errStr) { AppConfig::SetExportError(errStr); }
|
||||
//const char * getError() { return AppConfig::GetExportError(); }
|
||||
//bool isError() { return AppConfig::IsExportError(); }
|
||||
|
||||
// called by generateShape
|
||||
void generateBounds(Shape * shape);
|
||||
void generateDetails(Shape * shape);
|
||||
void generateSubtrees(Shape * shape);
|
||||
void generateObjects(Shape * shape);
|
||||
void generateDefaultStates(Shape * shape);
|
||||
void generateIflMaterials(Shape * shape);
|
||||
void generateSequences(Shape * shape);
|
||||
void generateMaterialList(Shape * shape);
|
||||
void generateSkins(Shape * shape);
|
||||
void optimizeMeshes(Shape * shape);
|
||||
void convertSortObjects(Shape * shape);
|
||||
void initShape(Shape*);
|
||||
|
||||
// deeper generate methods...
|
||||
void generateBillboardDetail(AppNode * detailNode, DetailLevel & detail);
|
||||
void sortTSDetails(std::vector<DTS::DetailLevel> & details);
|
||||
void setObjectPriorities(std::vector<ObjectMimic*> & objects);
|
||||
void sortObjectList(std::vector<ObjectMimic*> & objects);
|
||||
void generateNodeTransform(NodeMimic *, const AppTime & time, bool blend, const AppTime & blendReferenceTime, Quaternion & rot, Point3D & trans, Quaternion & qrot, Point3D & scale);
|
||||
void generateObjectState(ObjectMimic *, const AppTime & time, Shape *, bool addFrame, bool addMatFrame);
|
||||
|
||||
// generate animation data
|
||||
void generateFrame(ObjectMimic *, const AppTime & time, bool addFrame, bool addMatFrame);
|
||||
void generateGroundAnimation(Shape *, Sequence &, AppSequenceData &);
|
||||
void generateObjectAnimation(Shape *, Sequence &, AppSequenceData &);
|
||||
void generateFrameTriggers(Shape *, Sequence &, AppSequenceData &, AppSequence *);
|
||||
void generateNodeAnimation(Shape *, Sequence &, AppSequenceData &);
|
||||
|
||||
// add transform data
|
||||
void addNodeRotation(NodeMimic *, const AppTime & time, Shape *, bool blend, Quaternion & rot, bool defaultVal);
|
||||
void addNodeTranslation(NodeMimic *, const AppTime & time, Shape *, bool blend, Point3D & trans, bool defaultVal);
|
||||
void addNodeUniformScale(NodeMimic *, const AppTime & time, Shape *, bool blend, F32 scale);
|
||||
void addNodeAlignedScale(NodeMimic *, const AppTime & time, Shape *, bool blend, Point3D & scale);
|
||||
void addNodeArbitraryScale(NodeMimic *, const AppTime & time, Shape *, bool blend, Quaternion & qrot, Point3D & scale);
|
||||
|
||||
// membership tests for animation
|
||||
void setIflMembership(Shape *, Sequence &, AppSequenceData &, S32 & iflCount);
|
||||
S32 setObjectMembership(Shape *, Sequence &, AppSequenceData &, S32 & objectCount);
|
||||
void setNodeMembership(Shape *, Sequence &, AppSequenceData &,
|
||||
S32 & rotCount, S32 & transCount,
|
||||
S32 & uniformScaleCount, S32 & alignedScaleCount, S32 & arbitraryScaleCount);
|
||||
void setRotationMembership(Shape *, Sequence &, AppSequenceData &, S32 & rotCount);
|
||||
void setTranslationMembership(Shape *, Sequence &, AppSequenceData &, S32 & transCount);
|
||||
void setScaleMembership(Sequence &, AppSequenceData &, S32 & arbitraryScale, S32 & alignedScale, S32 & uniformScale);
|
||||
bool animatesAlignedScale(AppSequenceData &);
|
||||
bool animatesArbitraryScale(AppSequenceData &);
|
||||
S32 setUniformScaleMembership(Sequence &, AppSequenceData &);
|
||||
S32 setAlignedScaleMembership(Sequence &, AppSequenceData &);
|
||||
S32 setArbitraryScaleMembership(Sequence &, AppSequenceData &);
|
||||
|
||||
// add material from mesh
|
||||
S32 addFaceMaterial(AppMesh *,S32 faceNum);
|
||||
S32 addMaterial(Material mat);
|
||||
|
||||
// utility methods
|
||||
void fillNodeTransformCache(std::vector<NodeMimic*> &, Sequence &, AppSequenceData &);
|
||||
ObjectMimic * addObject(AppNode *, AppMesh *, std::vector<S32> * validDetails);
|
||||
ObjectMimic * addObject(AppNode * node, AppMesh * mesh, std::vector<S32> * validDetails, bool multiRes, S32 multiResSize=-1, F32 multiResPercent=1.0f);
|
||||
ObjectMimic * getObject(AppNode *, AppMesh *, char * name, S32 size, S32 * detailNum, F32 multiResPercent, bool matchFullName = true, bool isBone=false, bool isSkin=false);
|
||||
ObjectMimic * addBoneObject(AppNode * node, S32 subtreeNum);
|
||||
MeshMimic * addSkinObject(SkinMimic * skinMimic);
|
||||
S32 addName(const char *, Shape * shape);
|
||||
void computeNormals(std::vector<Primitive> &, std::vector<U16> & indices, std::vector<Point3D> & verts, std::vector<Point3D> & norms, std::vector<U32> & smooth, S32 vertsPerFrame, S32 numFrames);
|
||||
void copyWeightsToVerts(SkinMimic * skinMimic);
|
||||
void collapseVertices(Mesh *, std::vector<U32> & smooth, std::vector<U32> & remap, std::vector<U32> * vertId);
|
||||
bool vertexSame(Point3D & v1, Point3D & v2, Point2D & tv1, Point2D & tv2, U32 smooth1, U32 smooth2, Point3D & norm1, Point3D & norm2, U32 idx1, U32 idx2, std::vector<U32> * vertId);
|
||||
void stripify(std::vector<Primitive> &, std::vector<U16> & indices);
|
||||
void decimate(Mesh * mesh, F32 percentage);
|
||||
void collapseTransforms();
|
||||
bool cut(NodeMimic * mimicNode);
|
||||
void snip(NodeMimic * nodeMimic);
|
||||
bool testCutNodes(AppSequenceData & seqData);
|
||||
NodeMimic * findNextNode(NodeMimic *);
|
||||
|
||||
void dumpShapeNode(Shape * shape, S32 level, S32 nodeIndex, std::vector<S32> & detailSizes);
|
||||
void dumpShape(Shape * shape);
|
||||
|
||||
void getMultiResData(AppNode * node, std::vector<S32> & multiResSize, std::vector<F32> & multiResPercent);
|
||||
void getMultiResData(AppMesh * node, std::vector<S32> & multiResSize, std::vector<F32> & multiResPercent);
|
||||
public:
|
||||
ShapeMimic();
|
||||
~ShapeMimic();
|
||||
|
||||
// add shape items as we walk the scene
|
||||
void addBounds(AppNode * appNode) { boundsNode=appNode; }
|
||||
void addSubtree(AppNode * appNode);
|
||||
void addNode(NodeMimic *,AppNode *, std::vector<S32> &,bool);
|
||||
void addMesh(AppNode * node, AppMesh * mesh) { addObject(node,mesh,NULL); }
|
||||
void addSkin(AppMesh * mesh);
|
||||
void addSkin(AppMesh * mesh, bool multiRes, S32 multiResSize=-1, F32 multiResPercent=1.0f);
|
||||
void addSequence(AppSequence * sequence) { sequences.push_back(sequence); }
|
||||
|
||||
// the payoff...call after adding all of the above
|
||||
Shape * generateShape();
|
||||
};
|
||||
}; // namespace DTS
|
||||
|
||||
|
||||
#endif
|
||||
|
571
lib/dtsSDKPlus/appConfig.cpp
Executable file
571
lib/dtsSDKPlus/appConfig.cpp
Executable file
@ -0,0 +1,571 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable : 4786 4018)
|
||||
#endif
|
||||
|
||||
#include "appConfig.h"
|
||||
#include "appNode.h"
|
||||
|
||||
namespace DTS {
|
||||
|
||||
class AppMessage
|
||||
{
|
||||
char* mMessageId;
|
||||
char* mMessage;
|
||||
public:
|
||||
AppMessage( const char * messageId, const char * message );
|
||||
~AppMessage();
|
||||
|
||||
char* MessageId() { return mMessageId; };
|
||||
char* Message() { return mMessage; };
|
||||
};
|
||||
|
||||
AppMessage::AppMessage( const char * messageId, const char* message )
|
||||
{
|
||||
mMessageId = _strdup( messageId );
|
||||
mMessage = _strdup( message );
|
||||
}
|
||||
|
||||
AppMessage::~AppMessage()
|
||||
{
|
||||
if( mMessageId )
|
||||
free( mMessageId );
|
||||
if( mMessage )
|
||||
free( mMessage );
|
||||
}
|
||||
|
||||
static AppConfig gAppConfig;
|
||||
|
||||
AppConfig * AppConfig::smConfig = &gAppConfig;
|
||||
|
||||
AppConfig::AppConfig()
|
||||
{
|
||||
setInitialDefaults();
|
||||
setupConfigParams();
|
||||
mDumpFile = NULL;
|
||||
}
|
||||
|
||||
AppConfig::~AppConfig()
|
||||
{
|
||||
clearConfigLists();
|
||||
clearConfigParams();
|
||||
if( mDumpFile )
|
||||
{
|
||||
mDumpFile->close();
|
||||
delete mDumpFile;
|
||||
}
|
||||
if (smConfig==this)
|
||||
smConfig=&gAppConfig;
|
||||
}
|
||||
|
||||
void AppConfig::setInitialDefaults()
|
||||
{
|
||||
mEnableSequences = true;
|
||||
mExportOptimized = true;
|
||||
mAllowUnusedMeshes = true;
|
||||
mAllowCollapseTransform = true;
|
||||
mNoMipMap = false;
|
||||
mNoMipMapTranslucent = false;
|
||||
mZapBorder = true;
|
||||
mAnimationDelta = 0.0001f;
|
||||
mSameVertTOL = 0.00005f;
|
||||
mSameNormTOL = 0.005f;
|
||||
mSameVertTOL = 0.00005f;
|
||||
mWeightThreshhold = 0.001f;
|
||||
mWeightsPerVertex = 10;
|
||||
mCyclicSequencePadding = 1.0f / 30.0f;
|
||||
mAppFramesPerSec = 30.0f;
|
||||
mDumpConfig = 0xFFFFFFFF;
|
||||
mErrorString = NULL;
|
||||
mProgressCallback = NULL;
|
||||
mIgnoreSmoothingGroupOnSkinMesh = true;
|
||||
mIgnoreSmoothingGroupDuringCollapse = false;
|
||||
clearConfigLists();
|
||||
}
|
||||
|
||||
void AppConfig::clearConfigLists()
|
||||
{
|
||||
S32 i;
|
||||
|
||||
for (i=0; i<mAlwaysExport.size(); i++)
|
||||
delete [] mAlwaysExport[i];
|
||||
mAlwaysExport.clear();
|
||||
|
||||
for (i=0; i<mNeverExport.size(); i++)
|
||||
delete [] mNeverExport[i];
|
||||
mNeverExport.clear();
|
||||
|
||||
for (i=0; i<mNeverAnimate.size(); i++)
|
||||
delete [] mNeverAnimate[i];
|
||||
mNeverAnimate.clear();
|
||||
|
||||
for (i=0; i<mErrorMessages.size(); i++)
|
||||
delete mErrorMessages[i];
|
||||
mErrorMessages.clear();
|
||||
|
||||
for (i=0; i<mWarningMessages.size(); i++)
|
||||
delete mWarningMessages[i];
|
||||
mWarningMessages.clear();
|
||||
}
|
||||
|
||||
void AppConfig::setConfig(AppConfig * config)
|
||||
{
|
||||
assert(config != NULL);
|
||||
|
||||
if (smConfig!=&gAppConfig)
|
||||
delete smConfig;
|
||||
smConfig = config;
|
||||
}
|
||||
|
||||
bool AppConfig::setDumpFile(const char * path, const char * name)
|
||||
{
|
||||
// open dump file in path specified, with optional name (use dump.dmp by default)
|
||||
// path can include full file name...can also include \,/, or : as path delimitors
|
||||
assert(path && "No path set on dump file");
|
||||
if (name==NULL)
|
||||
name = "dump.html";
|
||||
|
||||
char * fullpath = getFilePath(path,strlen(name)+1);
|
||||
strcat(fullpath,name);
|
||||
|
||||
mDumpFile = new std::ofstream();
|
||||
mDumpFile->open(fullpath,std::ofstream::binary);
|
||||
if( !mDumpFile->fail() )
|
||||
{
|
||||
*mDumpFile << "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\r\n";
|
||||
*mDumpFile << "<html>\r\n<head>\r\n<title>DTS Exporter Report</title>\r\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=iso-8859-1\">\r\n";
|
||||
*mDumpFile << "<style type=\"text/css\">\r\n<!--\r\n";
|
||||
*mDumpFile << "body {\r\nfont-family: Verdana, Arial, Helvetica, sans-serif;\r\nfont-size: 14px;\r\nfont-weight: normal;\r\ncolor: #000000;\r\n}\r\n";
|
||||
*mDumpFile << ".monoText {\r\nfont-size: 12px;\r\nline-height: normal;\r\nfont-family: \"Courier New\", Courier, mono;\r\n}\r\n";
|
||||
*mDumpFile << ".errors {\r\nbackground-color: #FF9999;\r\nborder-width: 5;\r\nborder-style: solid;\r\nborder-color: #FF0000;\r\npadding: 4;\r\n}\r\n";
|
||||
*mDumpFile << ".warnings {\r\nbackground-color: #FFF8AA;\r\nborder-width: 5;\r\nborder-style: solid;\r\nborder-color: #FFCC00;\r\npadding: 4;\r\n}\r\n";
|
||||
*mDumpFile << ".header {\r\nfont-size: 16px;\r\nline-height: 24px;\r\nfont-family:Verdana, Arial, Helvetica, sans-serif;\r\nfont-weight: bold;\r\npadding-left: 0px;\r\n}\r\n";
|
||||
*mDumpFile << "-->\r\n</style>\r\n</head>\r\n<body>\r\n<div class=\"header\">Exporter Report</div>\r\n<pre class=\"monoText\">\r\n";
|
||||
mDumpFile->flush();
|
||||
}
|
||||
return !mDumpFile->fail();
|
||||
}
|
||||
|
||||
bool AppConfig::closeDumpFile()
|
||||
{
|
||||
S32 i;
|
||||
|
||||
*mDumpFile << "</pre>";
|
||||
*mDumpFile << "<div class=\"warnings\">\r\n<div class=\"header\">Warnings:</div>\r\n";
|
||||
if( mWarningMessages.size() == 0 )
|
||||
*mDumpFile << "<div>No warnings.</div>";
|
||||
else
|
||||
for (i=0; i<mWarningMessages.size(); i++)
|
||||
*mDumpFile << "<div><a href=\"http://artist.garagegames.com/dts/warnings/" << mWarningMessages[i]->MessageId() << "\">Warning #" << mWarningMessages[i]->MessageId() << "</a>:" << mWarningMessages[i]->Message() << "</div>";
|
||||
*mDumpFile << "</div>\r\n<br>\r\n";
|
||||
*mDumpFile << "<div class=\"errors\">\r\n<div class=\"header\">Errors:</div>\r\n";
|
||||
if( mErrorMessages.size() == 0 )
|
||||
*mDumpFile << "<div>No errors.</div>";
|
||||
else
|
||||
for (i=0; i<mErrorMessages.size(); i++)
|
||||
*mDumpFile << "<div><a href=\"http://artist.garagegames.com/dts/errors/" << mErrorMessages[i]->MessageId() << "\">Error #" << mErrorMessages[i]->MessageId() << "</a>:" << mErrorMessages[i]->Message() << "</div>";
|
||||
*mDumpFile << "</div>\r\n";
|
||||
*mDumpFile << "</body>\r\n</html>\r\n";
|
||||
mDumpFile->flush();
|
||||
mDumpFile->close();
|
||||
delete mDumpFile;
|
||||
mDumpFile = NULL;
|
||||
return true;
|
||||
}
|
||||
|
||||
void AppConfig::printDump(U32 mask, const char * str)
|
||||
{
|
||||
if (mask && AppConfig::GetDumpMask())
|
||||
{
|
||||
*mDumpFile << str;
|
||||
mDumpFile->flush();
|
||||
}
|
||||
}
|
||||
|
||||
void AppConfig::printWarning( U32 mask, const char * warningId, const char* warningMessage )
|
||||
{
|
||||
if (mask && AppConfig::GetDumpMask())
|
||||
{
|
||||
mWarningMessages.push_back( new AppMessage( warningId, warningMessage ) );
|
||||
printDump( mask, warningMessage );
|
||||
}
|
||||
}
|
||||
|
||||
void AppConfig::printError( U32 mask, const char * errorId, const char* errorMessage )
|
||||
{
|
||||
if (mask && AppConfig::GetDumpMask())
|
||||
{
|
||||
mErrorMessages.push_back( new AppMessage( errorId, errorMessage ) );
|
||||
printDump( mask, errorMessage );
|
||||
}
|
||||
}
|
||||
|
||||
bool AppConfig::alwaysExport(AppNode * node)
|
||||
{
|
||||
const char * name = node->getName();
|
||||
for (S32 i=0; i<mAlwaysExport.size(); i++)
|
||||
if (stringEqual(name,mAlwaysExport[i]))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AppConfig::neverExport(AppNode * node)
|
||||
{
|
||||
const char * name = node->getName();
|
||||
for (S32 i=0; i<mNeverExport.size(); i++)
|
||||
if (stringEqual(name,mNeverExport[i]))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AppConfig::neverAnimate(AppNode * node)
|
||||
{
|
||||
const char * name = node->getName();
|
||||
for (S32 i=0; i<mNeverAnimate.size(); i++)
|
||||
if (stringEqual(name,mNeverAnimate[i]))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
S32 AppConfig::getParamEntry(const char * name, std::vector<char *> & nameTable)
|
||||
{
|
||||
for (S32 i=0; i<nameTable.size(); i++)
|
||||
{
|
||||
if (!strcmp(name,nameTable[i]))
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool AppConfig::searchConfigFile(const char * filename)
|
||||
{
|
||||
const char * ext = strrchr(filename,'.');
|
||||
if (!ext)
|
||||
ext = ""; // just use filename as base
|
||||
|
||||
const char * defaultName = "dtsScene.cfg";
|
||||
const char * pos = filename+strlen(filename)-strlen(ext);
|
||||
char * newname = new char[strlen(filename)+strlen(defaultName)+1];
|
||||
strcpy(newname,filename);
|
||||
|
||||
// try using base filename with .cfg suffix
|
||||
strcpy(newname+(pos-filename),".cfg");
|
||||
if (readConfigFile(newname))
|
||||
{
|
||||
delete [] newname;
|
||||
return true;
|
||||
}
|
||||
|
||||
// try using base filename except no number with .cfg suffix
|
||||
do
|
||||
pos--;
|
||||
while (pos>=filename && *pos<='9' && *pos>='0');
|
||||
pos++;
|
||||
strcpy(newname+(pos-filename),".cfg");
|
||||
if (readConfigFile(newname))
|
||||
{
|
||||
delete [] newname;
|
||||
return true;
|
||||
}
|
||||
|
||||
// try using dtsScene.cfg
|
||||
do
|
||||
pos--;
|
||||
while (pos>=filename && *pos!='\\' && *pos!='/' && *pos!=':');
|
||||
pos++;
|
||||
strcpy(newname+(pos-filename),defaultName);
|
||||
if (readConfigFile(newname))
|
||||
{
|
||||
delete [] newname;
|
||||
return true;
|
||||
}
|
||||
|
||||
// no config file
|
||||
delete [] newname;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AppConfig::readConfigFile(const char * filename)
|
||||
{
|
||||
clearConfigLists();
|
||||
|
||||
std::ifstream is;
|
||||
|
||||
is.open(filename);
|
||||
if (!is.is_open())
|
||||
{
|
||||
printWarning(PDAlways,"101",avar("\r\nConfig file \"%s\" not found.\r\n",filename));
|
||||
return false;
|
||||
}
|
||||
|
||||
printDump(PDAlways,avar("\r\nBegin reading config file \"%s\".\r\n",filename));
|
||||
|
||||
S32 mode = 0; // 0=AlwaysExport:, 1=NeverExport:, 2=NeverAnimate:
|
||||
char buffer[256];
|
||||
do
|
||||
{
|
||||
is.getline(buffer,sizeof(buffer));
|
||||
if (stringEqual(buffer,"AlwaysExport:*"))
|
||||
mode = 0;
|
||||
else if (stringEqual(buffer,"NeverExport:*"))
|
||||
mode = 1;
|
||||
else if (stringEqual(buffer,"NeverAnimate:*"))
|
||||
mode = 2;
|
||||
else if (buffer[0]=='+' || buffer[0]=='-')
|
||||
{
|
||||
bool newVal = buffer[0]=='+';
|
||||
S32 idx = getParamEntry(buffer+1,mBoolParamNames);
|
||||
if (idx>=0)
|
||||
{
|
||||
if (mBoolParamBit[idx])
|
||||
{
|
||||
if (newVal)
|
||||
*mBoolParams[idx] |= mBoolParamBit[idx];
|
||||
else
|
||||
*mBoolParams[idx] &= ~mBoolParamBit[idx];
|
||||
}
|
||||
else
|
||||
*mBoolParams[idx] = newVal;
|
||||
printDump(PDAlways,avar("%s %s.\r\n",mBoolParamNames[idx],newVal ? "enabled" : "disabled"));
|
||||
}
|
||||
else
|
||||
printDump(PDAlways,avar("Unknown bool parameter \"%s\"\r\n",buffer+1));
|
||||
}
|
||||
else if (buffer[0]=='=')
|
||||
{
|
||||
char * endName = strchr(buffer+1,' ');
|
||||
if (endName)
|
||||
{
|
||||
*endName = '\0';
|
||||
S32 idx1 = getParamEntry(buffer+1,mFloatParamNames);
|
||||
S32 idx2 = getParamEntry(buffer+1,mStringParamNames);
|
||||
S32 idx3 = getParamEntry(buffer+1,mIntParamNames);
|
||||
if (idx1>=0)
|
||||
{
|
||||
// Float
|
||||
*mFloatParams[idx1] = F32(atof(endName+1));
|
||||
printDump(PDAlways,avar("%s = %f\r\n",mFloatParamNames[idx1],*mFloatParams[idx1]));
|
||||
}
|
||||
if (idx2>=0)
|
||||
{
|
||||
// string
|
||||
S32 maxLen = mStringParamMaxLen[idx2];
|
||||
strncpy(mStringParams[idx2],endName+1,maxLen-1);
|
||||
mStringParams[idx2][maxLen]='\0';
|
||||
printDump(PDAlways,avar("%s = \"%s\"\r\n",mStringParamNames[idx2],mStringParams[idx2]));
|
||||
}
|
||||
if (idx3>=0)
|
||||
{
|
||||
// S32
|
||||
*mIntParams[idx3] = atoi(endName+1);
|
||||
printDump(PDAlways,avar("%s = %i\r\n",mIntParamNames[idx3],*mIntParams[idx3]));
|
||||
}
|
||||
if (idx1<0 && idx2<0 && idx3<0)
|
||||
printDump(PDAlways,avar("Unknown parameter \"%s\"\r\n",buffer+1));
|
||||
}
|
||||
}
|
||||
else if (buffer[0]!='\\' && buffer[0]!='/' && buffer[0]!=';')
|
||||
{
|
||||
char * name = buffer;
|
||||
while (*name==' ')
|
||||
name++;
|
||||
if (strlen(name))
|
||||
{
|
||||
if (mode == 0)
|
||||
{
|
||||
mAlwaysExport.push_back(strnew(buffer));
|
||||
printDump(PDAlways,avar("Always export node: \"%s\"\r\n",buffer));
|
||||
}
|
||||
else if (mode == 1)
|
||||
{
|
||||
mNeverExport.push_back(strnew(buffer));
|
||||
printDump(PDAlways,avar("Never export node: \"%s\"\r\n",buffer));
|
||||
}
|
||||
else if (mode == 2)
|
||||
{
|
||||
mNeverAnimate.push_back(strnew(buffer));
|
||||
printDump(PDAlways,avar("Never animate transform on node: \"%s\"\r\n",buffer));
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (is.good());
|
||||
|
||||
printDump(PDAlways,"End reading config file.\r\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
void AppConfig::writeConfigFile(const char * filename)
|
||||
{
|
||||
S32 i;
|
||||
|
||||
std::ofstream os;
|
||||
|
||||
os.open(filename);
|
||||
|
||||
os << "AlwaysExport:" << std::endl;
|
||||
for (i=0; i<mAlwaysExport.size(); i++)
|
||||
os << mAlwaysExport[i] << std::endl;
|
||||
|
||||
os << "NeverExport:" << std::endl;
|
||||
for (i=0; i<mNeverExport.size(); i++)
|
||||
os << mNeverExport[i] << std::endl;
|
||||
|
||||
os << "NeverAnimate:" << std::endl;
|
||||
for (i=0; i<mNeverAnimate.size(); i++)
|
||||
os << mNeverAnimate[i] << std::endl;
|
||||
|
||||
for (i=0; i<mBoolParamNames.size(); i++)
|
||||
{
|
||||
bool enabled = mBoolParamBit[i] ? (mBoolParamBit[i] & *mBoolParams[i]) != 0 : *mBoolParams[i]!=0;
|
||||
os << (enabled ? "+" : "-") << mBoolParamNames[i] << std::endl;
|
||||
}
|
||||
|
||||
for (i=0; i<mFloatParamNames.size(); i++)
|
||||
os << mFloatParamNames[i] << "= " << *mFloatParams[i] << std::endl;
|
||||
|
||||
for (i=0; i<mIntParamNames.size(); i++)
|
||||
os << mIntParamNames[i] << "= " << *mIntParams[i] << std::endl;
|
||||
|
||||
for (i=0; i<mStringParamNames.size(); i++)
|
||||
os << mStringParamNames[i] << "= " << mStringParams[i] << std::endl;
|
||||
|
||||
os.close();
|
||||
}
|
||||
|
||||
void AppConfig::setupConfigParams()
|
||||
{
|
||||
// add bool config parameters
|
||||
mBoolParamNames.push_back(strnew("Dump::NodeCollection"));
|
||||
mBoolParams.push_back(&mDumpConfig);
|
||||
mBoolParamBit.push_back(PDPass1);
|
||||
|
||||
mBoolParamNames.push_back(strnew("Dump::ShapeConstruction"));
|
||||
mBoolParams.push_back(&mDumpConfig);
|
||||
mBoolParamBit.push_back(PDPass2);
|
||||
|
||||
mBoolParamNames.push_back(strnew("Dump::NodeCulling"));
|
||||
mBoolParams.push_back(&mDumpConfig);
|
||||
mBoolParamBit.push_back(PDPass3);
|
||||
|
||||
mBoolParamNames.push_back(strnew("Dump::NodeStates"));
|
||||
mBoolParams.push_back(&mDumpConfig);
|
||||
mBoolParamBit.push_back(PDNodeStates);
|
||||
|
||||
mBoolParamNames.push_back(strnew("Dump::NodeStateDetails"));
|
||||
mBoolParams.push_back(&mDumpConfig);
|
||||
mBoolParamBit.push_back(PDNodeStateDetails);
|
||||
|
||||
mBoolParamNames.push_back(strnew("Dump::ObjectStates"));
|
||||
mBoolParams.push_back(&mDumpConfig);
|
||||
mBoolParamBit.push_back(PDObjectStates);
|
||||
|
||||
mBoolParamNames.push_back(strnew("Dump::ObjectStateDetails"));
|
||||
mBoolParams.push_back(&mDumpConfig);
|
||||
mBoolParamBit.push_back(PDObjectStateDetails);
|
||||
|
||||
mBoolParamNames.push_back(strnew("Dump::ObjectOffsets"));
|
||||
mBoolParams.push_back(&mDumpConfig);
|
||||
mBoolParamBit.push_back(PDObjectOffsets);
|
||||
|
||||
mBoolParamNames.push_back(strnew("Dump::SequenceDetails"));
|
||||
mBoolParams.push_back(&mDumpConfig);
|
||||
mBoolParamBit.push_back(PDSequences);
|
||||
|
||||
mBoolParamNames.push_back(strnew("Dump::ShapeHierarchy"));
|
||||
mBoolParams.push_back(&mDumpConfig);
|
||||
mBoolParamBit.push_back(PDShapeHierarchy);
|
||||
|
||||
mBoolParamNames.push_back(strnew("Error::AllowUnusedMeshes"));
|
||||
mBoolParams.push_back((U32*)&mAllowUnusedMeshes);
|
||||
mBoolParamBit.push_back(0);
|
||||
|
||||
mBoolParamNames.push_back(strnew("Param::CollapseTransforms"));
|
||||
mBoolParams.push_back((U32*)&mAllowCollapseTransform);
|
||||
mBoolParamBit.push_back(0);
|
||||
|
||||
mBoolParamNames.push_back(strnew("Param::SequenceExport"));
|
||||
mBoolParams.push_back((U32*)&mEnableSequences);
|
||||
mBoolParamBit.push_back(0);
|
||||
|
||||
mBoolParamNames.push_back(strnew("Materials::NoMipMap"));
|
||||
mBoolParams.push_back((U32*)&mNoMipMap);
|
||||
mBoolParamBit.push_back(0);
|
||||
|
||||
mBoolParamNames.push_back(strnew("Materials::NoMipMapTranslucent"));
|
||||
mBoolParams.push_back((U32*)&mNoMipMapTranslucent);
|
||||
mBoolParamBit.push_back(0);
|
||||
|
||||
mBoolParamNames.push_back(strnew("Materials::ZapBorder"));
|
||||
mBoolParams.push_back((U32*)&mZapBorder);
|
||||
mBoolParamBit.push_back(0);
|
||||
|
||||
mBoolParamNames.push_back(strnew("SmoothingGroup::IgnoreOnSkinMesh"));
|
||||
mBoolParams.push_back((U32*)&mIgnoreSmoothingGroupOnSkinMesh);
|
||||
mBoolParamBit.push_back(0);
|
||||
|
||||
mBoolParamNames.push_back(strnew("SmoothingGroup::IgnoreDuringCollapse"));
|
||||
mBoolParams.push_back((U32*)&mIgnoreSmoothingGroupDuringCollapse);
|
||||
mBoolParamBit.push_back(1);
|
||||
|
||||
// add F32 config parameters
|
||||
mFloatParamNames.push_back(strnew("Params::AnimationDelta"));
|
||||
mFloatParams.push_back(&mAnimationDelta);
|
||||
|
||||
mFloatParamNames.push_back(strnew("Params::SkinWeightThreshhold"));
|
||||
mFloatParams.push_back(&mWeightThreshhold);
|
||||
|
||||
mFloatParamNames.push_back(strnew("Params::SameVertTOL"));
|
||||
mFloatParams.push_back(&mSameVertTOL);
|
||||
|
||||
mFloatParamNames.push_back(strnew("Params::SameTVertTOL"));
|
||||
mFloatParams.push_back(&mSameTVertTOL);
|
||||
|
||||
// add S32 config parameters
|
||||
mIntParamNames.push_back(strnew("Params::weightsPerVertex"));
|
||||
mIntParams.push_back(&mWeightsPerVertex);
|
||||
}
|
||||
|
||||
void AppConfig::clearConfigParams()
|
||||
{
|
||||
S32 i;
|
||||
for (i=0; i<mBoolParamNames.size(); i++)
|
||||
delete [] mBoolParamNames[i];
|
||||
for (i=0; i<mFloatParamNames.size(); i++)
|
||||
delete [] mFloatParamNames[i];
|
||||
for (i=0; i<mIntParamNames.size(); i++)
|
||||
delete [] mIntParamNames[i];
|
||||
for (i=0; i<mStringParamNames.size(); i++)
|
||||
delete [] mStringParamNames[i];
|
||||
|
||||
mBoolParamNames.clear();
|
||||
mBoolParams.clear();
|
||||
mBoolParamBit.clear();
|
||||
mFloatParamNames.clear();
|
||||
mFloatParams.clear();
|
||||
mIntParamNames.clear();
|
||||
mIntParams.clear();
|
||||
mStringParamNames.clear();
|
||||
mStringParams.clear();
|
||||
mStringParamMaxLen.clear();
|
||||
}
|
||||
|
||||
// progress handling
|
||||
void AppConfig::setProgressCallback(progressfnptr callback)
|
||||
{
|
||||
mProgressCallback = callback;
|
||||
}
|
||||
|
||||
void AppConfig::setProgress(F32 minor, F32 major, const char* message)
|
||||
{
|
||||
if (mProgressCallback)
|
||||
{
|
||||
mProgressCallback(minor, major, message);
|
||||
}
|
||||
}
|
||||
}; // namespace DTS
|
||||
|
||||
|
174
lib/dtsSDKPlus/appConfig.h
Executable file
174
lib/dtsSDKPlus/appConfig.h
Executable file
@ -0,0 +1,174 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef DTSAPPCONFIG_H_
|
||||
#define DTSAPPCONFIG_H_
|
||||
|
||||
#include "DTSTypes.h"
|
||||
#include "DTSShape.h"
|
||||
#include "DTSPlusTypes.h"
|
||||
#include "DTSUtil.h"
|
||||
#include <fstream>
|
||||
|
||||
namespace DTS
|
||||
{
|
||||
typedef void (*progressfnptr)(F32,F32,const char*);
|
||||
|
||||
// enum for printDump
|
||||
enum
|
||||
{
|
||||
PDPass1 = 1 << 0, // collect useful nodes
|
||||
PDPass2 = 1 << 1, // put together shape structure
|
||||
PDPass3 = 1 << 2, // cull un-needed nodes
|
||||
PDObjectOffsets = 1 << 3, // display object offset transform during 2nd pass
|
||||
PDNodeStates = 1 << 4, // display as added
|
||||
PDObjectStates = 1 << 5, // ""
|
||||
PDNodeStateDetails = 1 << 6, // details of above
|
||||
PDObjectStateDetails = 1 << 7, // ""
|
||||
PDSequences = 1 << 8,
|
||||
PDShapeHierarchy = 1 << 9,
|
||||
PDAlways = 0xFFFFFFFF
|
||||
};
|
||||
|
||||
class AppNode;
|
||||
class AppMessage;
|
||||
|
||||
class AppConfig
|
||||
{
|
||||
static AppConfig * smConfig;
|
||||
|
||||
F32 mAnimationDelta;
|
||||
F32 mSameVertTOL;
|
||||
F32 mSameTVertTOL;
|
||||
F32 mSameNormTOL;
|
||||
F32 mWeightThreshhold;
|
||||
S32 mWeightsPerVertex;
|
||||
F32 mCyclicSequencePadding;
|
||||
F32 mAppFramesPerSec;
|
||||
|
||||
bool mEnableSequences;
|
||||
bool mExportOptimized;
|
||||
bool mAllowUnusedMeshes;
|
||||
bool mAllowCollapseTransform;
|
||||
bool mNoMipMap;
|
||||
bool mNoMipMapTranslucent;
|
||||
bool mZapBorder;
|
||||
bool mIgnoreSmoothingGroupDuringCollapse;
|
||||
bool mIgnoreSmoothingGroupOnSkinMesh;
|
||||
U32 mDumpConfig;
|
||||
char * mErrorString;
|
||||
std::ofstream *mDumpFile;
|
||||
|
||||
progressfnptr mProgressCallback;
|
||||
|
||||
std::vector<char *> mBoolParamNames;
|
||||
std::vector<U32 *> mBoolParams;
|
||||
std::vector<U32> mBoolParamBit;
|
||||
std::vector<char *> mFloatParamNames;
|
||||
std::vector<F32 *> mFloatParams;
|
||||
std::vector<char *> mIntParamNames;
|
||||
std::vector<S32 *> mIntParams;
|
||||
std::vector<char *> mStringParamNames;
|
||||
std::vector<char *> mStringParams;
|
||||
std::vector<S32> mStringParamMaxLen;
|
||||
|
||||
std::vector<char *> mAlwaysExport;
|
||||
std::vector<char *> mNeverExport;
|
||||
std::vector<char *> mNeverAnimate;
|
||||
std::vector<AppMessage *>mWarningMessages;
|
||||
std::vector<AppMessage *>mErrorMessages;
|
||||
|
||||
virtual void printDump(U32 mask, const char * str);
|
||||
virtual void printWarning(U32 mask, const char * warningId, const char * warningMessage);
|
||||
virtual void printError(U32 mask, const char * errorId, const char * errorMessage);
|
||||
virtual bool setDumpFile(const char * path, const char * name = NULL);
|
||||
virtual bool closeDumpFile();
|
||||
|
||||
virtual bool alwaysExport(AppNode *);
|
||||
virtual bool neverExport(AppNode *);
|
||||
virtual bool neverAnimate(AppNode *);
|
||||
|
||||
bool searchConfigFile(const char * filename);
|
||||
bool readConfigFile(const char * filename);
|
||||
void writeConfigFile(const char * filename);
|
||||
|
||||
void setInitialDefaults();
|
||||
S32 getParamEntry(const char * name, std::vector<char *> & nameTable);
|
||||
void setupConfigParams();
|
||||
void clearConfigParams();
|
||||
void clearConfigLists();
|
||||
void setAppFramesPerSec(F32 fps) { mAppFramesPerSec = fps; };
|
||||
|
||||
// progress handling
|
||||
void setProgressCallback(progressfnptr callback);
|
||||
virtual void setProgress(F32 minor, F32 major, const char* message);
|
||||
|
||||
// error handling
|
||||
void setExportError(const char * errorId, const char * str) { printError( PDAlways, errorId, str ); }
|
||||
bool isExportError() { return mErrorMessages.size() > 0; }
|
||||
const char * getExportError() { return "Errors during export, please check dump file."; }
|
||||
|
||||
public:
|
||||
|
||||
AppConfig();
|
||||
virtual ~AppConfig();
|
||||
|
||||
void setConfig(AppConfig * config);
|
||||
|
||||
// access configuration parameters statically...
|
||||
|
||||
static bool SetDumpFile(const char * path, const char * name = NULL) { return smConfig->setDumpFile(path,name); }
|
||||
static bool CloseDumpFile() { return smConfig->closeDumpFile(); }
|
||||
static void PrintDump(U32 mask, const char * str) { smConfig->printDump(mask,str); }
|
||||
|
||||
static void PrintWarning( U32 mask, const char * warningId, const char* warningMessage ) { smConfig->printWarning( mask, warningId, warningMessage ); }
|
||||
static void PrintError( U32 mask, const char * errorId, const char* errorMessage ) { smConfig->printError( mask, errorId, errorMessage ); }
|
||||
|
||||
static U32 GetDumpMask() { return smConfig->mDumpConfig; }
|
||||
|
||||
static bool AlwaysExport(AppNode * node) { return smConfig->alwaysExport(node); }
|
||||
static bool NeverExport(AppNode * node) { return smConfig->neverExport(node); }
|
||||
static bool NeverAnimate(AppNode * node) { return smConfig->neverAnimate(node); }
|
||||
|
||||
static void SetExportError(const char * errorId, const char * str) { smConfig->setExportError(errorId, str); }
|
||||
static bool IsExportError() { return smConfig->isExportError(); }
|
||||
static const char * GetExportError() { return smConfig->getExportError(); }
|
||||
|
||||
static bool GetEnableSequences() { return smConfig->mEnableSequences; }
|
||||
static bool GetExportOptimized() { return smConfig->mExportOptimized; }
|
||||
static bool GetAllowUnusedMeshes() { return smConfig->mAllowUnusedMeshes; }
|
||||
static bool GetAllowCollapse() { return smConfig->mAllowCollapseTransform; }
|
||||
static bool GetNoMipMap() { return smConfig->mNoMipMap; }
|
||||
static bool GetNoMipMapTranslucent() { return smConfig->mNoMipMapTranslucent; }
|
||||
static bool GetZapBorder() { return smConfig->mZapBorder; }
|
||||
static bool IgnoreSmoothingGroupDuringCollapse() { return smConfig->mIgnoreSmoothingGroupDuringCollapse; }
|
||||
static bool IgnoreSmoothingGroupOnSkinMesh() { return smConfig->mIgnoreSmoothingGroupOnSkinMesh; }
|
||||
static F32 AnimationDelta() { return smConfig->mAnimationDelta; }
|
||||
static F32 SameVertTOL() { return smConfig->mSameVertTOL; }
|
||||
static F32 SameNormTOL() { return smConfig->mSameNormTOL; }
|
||||
static F32 SameTVertTOL() { return smConfig->mSameVertTOL; }
|
||||
static F32 WeightThreshhold() { return smConfig->mWeightThreshhold; }
|
||||
static S32 WeightsPerVertex() { return smConfig->mWeightsPerVertex; }
|
||||
static F32 CyclicSequencePadding() { return smConfig->mCyclicSequencePadding; }
|
||||
static F32 AppFramesPerSec() { return smConfig->mAppFramesPerSec; }
|
||||
|
||||
static void SetAppFramesPerSec(F32 fps) { smConfig->setAppFramesPerSec(fps); }
|
||||
|
||||
static void SetExportOptimized(bool opt) { smConfig->mExportOptimized = opt; }
|
||||
|
||||
static void SetDefaults() { smConfig->setInitialDefaults(); }
|
||||
static bool SearchConfigFile(const char * filename) { return smConfig->searchConfigFile(filename); }
|
||||
static bool ReadConfigFile(const char * filename) { return smConfig->readConfigFile(filename); }
|
||||
static void WriteConfigFile(const char * filename) { smConfig->writeConfigFile(filename); }
|
||||
|
||||
// progress handling
|
||||
static void SetProgressCallback(progressfnptr callback) { smConfig->setProgressCallback(callback); }
|
||||
static void SetProgress(F32 minor, F32 major, const char* message) { smConfig->setProgress(minor, major, message); }
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif // DTSAPPMATERIAL_H_
|
57
lib/dtsSDKPlus/appIfl.cpp
Executable file
57
lib/dtsSDKPlus/appIfl.cpp
Executable file
@ -0,0 +1,57 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable : 4786 4018)
|
||||
#endif
|
||||
|
||||
#include "appIfl.h"
|
||||
#include "DTSUtil.h"
|
||||
#include <fstream>
|
||||
|
||||
namespace DTS
|
||||
{
|
||||
|
||||
AppIfl::AppIfl(const char * fullPath)
|
||||
{
|
||||
mIflFile = strnew(fullPath);
|
||||
|
||||
// load in duration and names
|
||||
|
||||
std::ifstream is;
|
||||
is.open(fullPath);
|
||||
|
||||
char buffer[256];
|
||||
char name[256];
|
||||
S32 duration;
|
||||
while (is.good() && !is.eof())
|
||||
{
|
||||
is.getline(buffer,sizeof(buffer));
|
||||
S32 num = sscanf(buffer,"%s %i",name,&duration);
|
||||
if (num==1)
|
||||
{
|
||||
mNames.push_back(strnew(name));
|
||||
mDurations.push_back(AppTime(1.0f/30.0f,0));
|
||||
}
|
||||
else if (num==2)
|
||||
{
|
||||
mNames.push_back(strnew(name));
|
||||
mDurations.push_back(AppTime(F32(duration)/30.0f,0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AppTime AppIfl::getStartTime() { return AppTime(0,0); }
|
||||
|
||||
|
||||
AppIfl::~AppIfl()
|
||||
{
|
||||
delete [] mIflFile;
|
||||
for (S32 i=0; i<mNames.size(); i++)
|
||||
delete [] mNames[i];
|
||||
}
|
||||
|
||||
}; // namespace DTS
|
||||
|
38
lib/dtsSDKPlus/appIfl.h
Executable file
38
lib/dtsSDKPlus/appIfl.h
Executable file
@ -0,0 +1,38 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef DTSAPPIFL_H_
|
||||
#define DTSAPPIFL_H_
|
||||
|
||||
#include "DTSTypes.h"
|
||||
#include "DTSShape.h"
|
||||
#include "DTSPlusTypes.h"
|
||||
#include "appMesh.h"
|
||||
|
||||
namespace DTS
|
||||
{
|
||||
class AppIfl
|
||||
{
|
||||
char * mIflFile;
|
||||
std::vector<AppTime> mDurations;
|
||||
std::vector<char *> mNames;
|
||||
|
||||
public:
|
||||
|
||||
// Standard AppIfl just needs a path name to the ifl
|
||||
// Can derive from this class, though, in order to support
|
||||
// Ifls in a more application dependent manner, if desired.
|
||||
AppIfl(const char * fullPath);
|
||||
virtual ~AppIfl();
|
||||
|
||||
const char * getFilename() { return mIflFile; }
|
||||
const std::vector<AppTime> & getDurations() { return mDurations; }
|
||||
const std::vector<char*> & getNames() { return mNames; }
|
||||
virtual AppTime getStartTime();
|
||||
};
|
||||
};
|
||||
|
||||
#endif // DTSAPPIFL_H_
|
||||
|
238
lib/dtsSDKPlus/appMesh.cpp
Executable file
238
lib/dtsSDKPlus/appMesh.cpp
Executable file
@ -0,0 +1,238 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable : 4786 4018)
|
||||
#endif
|
||||
|
||||
#include "appMesh.h"
|
||||
#include "appTime.h"
|
||||
#include "dtsUtil.h"
|
||||
#include "appIfl.h"
|
||||
#include "appConfig.h"
|
||||
|
||||
namespace DTS
|
||||
{
|
||||
|
||||
AppMesh::AppMesh()
|
||||
{
|
||||
mLocked = false;
|
||||
mSkinDataFetched = false;
|
||||
}
|
||||
|
||||
AppMesh::~AppMesh()
|
||||
{
|
||||
assert(!mLocked && "Mesh is locked");
|
||||
S32 i;
|
||||
for (i=0; i<mBones.size(); i++)
|
||||
delete mBones[i];
|
||||
for (i=0; i<mWeights.size(); i++)
|
||||
delete mWeights[i];
|
||||
for (i=0; i<mIfls.size(); i++)
|
||||
delete mIfls[i];
|
||||
}
|
||||
|
||||
S32 __cdecl compareFaces( void const *e1, void const *e2 )
|
||||
{
|
||||
const Primitive * face1 = (const Primitive*)e1;
|
||||
const Primitive * face2 = (const Primitive*)e2;
|
||||
|
||||
if (face1->type < face2->type)
|
||||
return -1;
|
||||
else if (face2->type < face1->type)
|
||||
return 1;
|
||||
else return 0;
|
||||
}
|
||||
|
||||
void AppMesh::sortFaceList()
|
||||
{
|
||||
if (mFaces.size() != 0)
|
||||
qsort(&mFaces[0],mFaces.size(),sizeof(Primitive),compareFaces);
|
||||
}
|
||||
|
||||
Box AppMesh::getBounds(const Matrix<4,4,F32> & objectOffset)
|
||||
{
|
||||
assert(!mLocked && "Mesh is locked");
|
||||
|
||||
AppMeshLock lock = lockMesh(AppTime::DefaultTime(),objectOffset);
|
||||
|
||||
Box bounds(Point3D( 10E30f, 10E30f, 10E30f),Point3D(-10E30f,-10E30f,-10E30f));
|
||||
|
||||
const Point3D * verts = getVerts();
|
||||
for (S32 i=0; i<getNumVerts(); i++)
|
||||
{
|
||||
if (bounds.min.x() > verts[i].x())
|
||||
bounds.min.x(verts[i].x());
|
||||
if (bounds.min.y() > verts[i].y())
|
||||
bounds.min.y(verts[i].y());
|
||||
if (bounds.min.z() > verts[i].z())
|
||||
bounds.min.z(verts[i].z());
|
||||
|
||||
if (bounds.max.x() < verts[i].x())
|
||||
bounds.max.x(verts[i].x());
|
||||
if (bounds.max.y() < verts[i].y())
|
||||
bounds.max.y(verts[i].y());
|
||||
if (bounds.max.z() < verts[i].z())
|
||||
bounds.max.z(verts[i].z());
|
||||
}
|
||||
|
||||
return bounds;
|
||||
}
|
||||
|
||||
F32 AppMesh::getRadius(const Matrix<4,4,F32> & objectOffset)
|
||||
{
|
||||
Box bounds = getBounds(objectOffset);
|
||||
Point3D diameter = bounds.max-bounds.min;
|
||||
return diameter.length() * 0.5f;
|
||||
}
|
||||
|
||||
F32 AppMesh::getTubeRadius(const Matrix<4,4,F32> & objectOffset)
|
||||
{
|
||||
Box bounds = getBounds(objectOffset);
|
||||
Point2D diameter(bounds.max.x()-bounds.min.x(),bounds.max.y()-bounds.min.y());
|
||||
return diameter.length() * 0.5f;
|
||||
}
|
||||
|
||||
bool AppMesh::isBillboard()
|
||||
{
|
||||
return !_strnicmp(getName(),"BB::",4) || !_strnicmp(getName(),"BB_",3) || !_strnicmp(getName(),"BBZ::",5) || !_strnicmp(getName(),"BBZ_",4);
|
||||
}
|
||||
|
||||
bool AppMesh::isBillboardZAxis()
|
||||
{
|
||||
return !_strnicmp(getName(),"BBZ::",5) || !_strnicmp(getName(),"BBZ_",4);
|
||||
}
|
||||
|
||||
bool AppMesh::isSorted()
|
||||
{
|
||||
return !_strnicmp(getName(),"SORT::",6) || !_strnicmp(getName(),"SORT_",5);
|
||||
}
|
||||
|
||||
bool AppMesh::isDummy()
|
||||
{
|
||||
return !_strnicmp(getName(), "dummy", 5);
|
||||
}
|
||||
|
||||
bool AppMesh::animatesVis(const AppSequenceData & seqData)
|
||||
{
|
||||
F32 defaultVis = getVisValue(AppTime::DefaultTime());
|
||||
AppTime time = seqData.startTime;
|
||||
for (S32 frame=0; frame<seqData.numFrames; frame++, time += seqData.delta)
|
||||
if (!isEqual(defaultVis,getVisValue(time),0.01f))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AppMesh::animatesMatFrame(const AppSequenceData & seqData)
|
||||
{
|
||||
// don't necessarily want to support this type of animation anymore
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AppMesh::animatesFrame(const AppSequenceData & seqData)
|
||||
{
|
||||
// don't necessarily want to support this type of animation anymore
|
||||
return false;
|
||||
}
|
||||
|
||||
U16 AppMesh::addVertex(const Point3D & vert, const Point3D & norm, Point2D & tvert, U32 vertId)
|
||||
{
|
||||
assert(mVerts.size() == mTVerts.size() && "Verts and TVerts unbalanced");
|
||||
assert(mVerts.size() == mNormals.size() && "Verts and Normals unbalanced");
|
||||
assert(mVerts.size() == mVertId.size() && "Verts and vertIds unbalanced");
|
||||
for (S32 i=0; i<mVerts.size(); i++)
|
||||
{
|
||||
if (vertId != mVertId[i])
|
||||
continue;
|
||||
if (!isEqual(vert,mVerts[i],AppConfig::SameVertTOL()))
|
||||
continue;
|
||||
if (!isEqual(tvert,mTVerts[i],AppConfig::SameTVertTOL()))
|
||||
continue;
|
||||
if (!isEqual(norm,mNormals[i],AppConfig::SameNormTOL()))
|
||||
continue;
|
||||
return i;
|
||||
}
|
||||
mVerts.push_back(vert);
|
||||
mTVerts.push_back(tvert);
|
||||
mNormals.push_back(norm);
|
||||
mVertId.push_back(vertId);
|
||||
return mVerts.size()-1;
|
||||
}
|
||||
|
||||
U16 AppMesh::addVertex(const Point3D & vert, Point2D & tvert, U32 vertId, U32 smooth)
|
||||
{
|
||||
assert(mVerts.size() == mTVerts.size() && "Verts and TVerts unbalanced");
|
||||
assert(mVerts.size() == mSmooth.size() && "Verts and smooth unbalanced");
|
||||
assert(mVerts.size() == mVertId.size() && "Verts and vertIds unbalanced");
|
||||
for (S32 i=0; i<mVerts.size(); i++)
|
||||
{
|
||||
if (vertId != mVertId[i])
|
||||
continue;
|
||||
if (smooth != mSmooth[i])
|
||||
continue;
|
||||
if (!isEqual(vert,mVerts[i],AppConfig::SameVertTOL()))
|
||||
continue;
|
||||
if (!isEqual(tvert,mTVerts[i],AppConfig::SameTVertTOL()))
|
||||
continue;
|
||||
return i;
|
||||
}
|
||||
mVerts.push_back(vert);
|
||||
mTVerts.push_back(tvert);
|
||||
mSmooth.push_back(smooth);
|
||||
mVertId.push_back(vertId);
|
||||
return mVerts.size()-1;
|
||||
}
|
||||
|
||||
void AppMesh::generateFaces(std::vector<Primitive> & faces,
|
||||
std::vector<Point3D> & verts,
|
||||
std::vector<Point2D> & tverts,
|
||||
std::vector<U16> & indices,
|
||||
std::vector<U32> & smooth,
|
||||
std::vector<Point3D> & normals,
|
||||
std::vector<U32> * vertId)
|
||||
{
|
||||
assert(mLocked && "Mesh isn't locked");
|
||||
faces = mFaces;
|
||||
verts = mVerts;
|
||||
tverts = mTVerts;
|
||||
indices = mIndices;
|
||||
normals = mNormals;
|
||||
smooth = mSmooth;
|
||||
if (vertId)
|
||||
*vertId = mVertId;
|
||||
}
|
||||
|
||||
AppMeshLock AppMesh::lockMesh(const AppTime & time, const Matrix<4,4,F32> & objectOffset)
|
||||
{
|
||||
assert(!mLocked && "Mesh is already locked");
|
||||
|
||||
time,objectOffset;
|
||||
|
||||
// if more than one mat type, make mats consecutive
|
||||
sortFaceList();
|
||||
|
||||
mLocked = true;
|
||||
return AppMeshLock(this);
|
||||
}
|
||||
|
||||
void AppMesh::unlockMesh()
|
||||
{
|
||||
assert(mLocked && "Mesh isn't locked");
|
||||
|
||||
mFaces.clear();
|
||||
mVerts.clear();
|
||||
mTVerts.clear();
|
||||
mIndices.clear();
|
||||
mNormals.clear();
|
||||
mSmooth.clear();
|
||||
mVertId.clear();
|
||||
|
||||
mLocked = false;
|
||||
}
|
||||
|
||||
void AppMeshLock::doit() { if (mObj) mObj->unlockMesh(); }
|
||||
|
||||
|
||||
}; // namespace DTS
|
239
lib/dtsSDKPlus/appMesh.h
Executable file
239
lib/dtsSDKPlus/appMesh.h
Executable file
@ -0,0 +1,239 @@
|
||||
//-----------------------------/------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef DTSAPPMESH_H_
|
||||
#define DTSAPPMESH_H_
|
||||
|
||||
#include "DTSTypes.h"
|
||||
#include "DTSShape.h"
|
||||
#include "DTSPlusTypes.h"
|
||||
#include "appSequence.h"
|
||||
|
||||
namespace DTS
|
||||
{
|
||||
class AppIfl;
|
||||
class AppMeshLock;
|
||||
class AppNode;
|
||||
|
||||
class AppMesh
|
||||
{
|
||||
protected:
|
||||
|
||||
std::vector<Primitive> mFaces;
|
||||
std::vector<Point3D> mVerts;
|
||||
std::vector<Point2D> mTVerts;
|
||||
std::vector<U16> mIndices;
|
||||
std::vector<U32> mVertId;
|
||||
|
||||
// One can supply either the normals directly or
|
||||
// 3dsMax style smoothing groups and normals will
|
||||
// be computed for you. Note that one or the other
|
||||
// should be supplied -- if both or neither are
|
||||
// supplied the behavior is not predictable.
|
||||
// In 3dsMax, normals are not trustworthy, so it is
|
||||
// necessary to use smoothing groups instead.
|
||||
std::vector<Point3D> mNormals;
|
||||
std::vector<U32> mSmooth;
|
||||
|
||||
// For skinned meshes, we want bones and weights
|
||||
// Weights stored by (*mWeights[vertIdx])[boneIdx]
|
||||
std::vector<AppNode*> mBones;
|
||||
std::vector<std::vector<F32>*> mWeights;
|
||||
|
||||
// For ifls
|
||||
std::vector<AppIfl*> mIfls;
|
||||
|
||||
// These needs to be maintained by lock/unlock methods
|
||||
bool mLocked;
|
||||
bool mSkinDataFetched;
|
||||
|
||||
void sortFaceList();
|
||||
void lookupSkinData();
|
||||
virtual void getSkinData() = 0;
|
||||
|
||||
public:
|
||||
|
||||
AppMesh();
|
||||
~AppMesh();
|
||||
|
||||
// Not necessary to use these (don't need to keep verts unique) but can be used for
|
||||
// keeping vert list smaller. Add each vert using one or the other method.
|
||||
U16 addVertex(const Point3D & vert, const Point3D & norm, Point2D & tvert, U32 vertId);
|
||||
U16 addVertex(const Point3D & vert, Point2D & tvert, U32 vertId, U32 smooth);
|
||||
|
||||
virtual const char * getName() = 0;
|
||||
|
||||
virtual Matrix<4,4,F32> getMeshTransform(const AppTime & time) = 0;
|
||||
virtual F32 getVisValue(const AppTime & time) = 0;
|
||||
|
||||
virtual bool getFloat(const char * propName, F32 & defaultVal) = 0;
|
||||
virtual bool getInt(const char * propName, S32 & defaultVal) = 0;
|
||||
virtual bool getBool(const char * propName, bool & defaultVal) = 0;
|
||||
|
||||
virtual bool getMaterial(S32 matIdx, Material &) = 0;
|
||||
AppIfl * getIfl(S32 matIdx);
|
||||
|
||||
S32 getNumBones();
|
||||
AppNode * getBone(S32 idx);
|
||||
F32 getWeight(S32 boneIdx, S32 vertIdx);
|
||||
|
||||
virtual bool animatesVis(const AppSequenceData & seqData);
|
||||
virtual bool animatesMatFrame(const AppSequenceData & seqData);
|
||||
virtual bool animatesFrame(const AppSequenceData & seqData);
|
||||
|
||||
virtual Box getBounds(const Matrix<4,4,F32> & objectOffset);
|
||||
virtual F32 getRadius(const Matrix<4,4,F32> & objectOffset);
|
||||
virtual F32 getTubeRadius(const Matrix<4,4,F32> & objectOffset);
|
||||
virtual bool isBillboard();
|
||||
virtual bool isBillboardZAxis();
|
||||
virtual bool isSorted();
|
||||
virtual bool isDummy();
|
||||
bool isSkin() { return getNumBones() != 0; }
|
||||
|
||||
virtual AppMeshLock lockMesh(const AppTime & time, const Matrix<4,4,F32> & objectOffset);
|
||||
virtual void unlockMesh();
|
||||
|
||||
// before and after accessing following methods,
|
||||
// one should always call lock/unlock mesh
|
||||
S32 getNumFaces();
|
||||
const Primitive * getFaces();
|
||||
S32 getFaceMaterial(S32 faceIdx);
|
||||
S32 getNumIndices();
|
||||
const U16 * getIndices();
|
||||
S32 getNumTVerts();
|
||||
const Point2D * getTVerts();
|
||||
S32 getNumVerts();
|
||||
const Point3D * getVerts();
|
||||
S32 getNumNormals();
|
||||
const Point3D * getNormals();
|
||||
void generateFaces(std::vector<Primitive> & faces,
|
||||
std::vector<Point3D> & verts,
|
||||
std::vector<Point2D> & tverts,
|
||||
std::vector<U16> & indices,
|
||||
std::vector<U32> & smooth,
|
||||
std::vector<Point3D> & normals,
|
||||
std::vector<U32> * vertId);
|
||||
|
||||
};
|
||||
|
||||
class AppMeshLock : public OnDestroy<AppMesh>
|
||||
{
|
||||
protected:
|
||||
void doit();
|
||||
public:
|
||||
AppMeshLock(AppMesh * mesh) : OnDestroy<AppMesh>(mesh) {}
|
||||
~AppMeshLock() { transfer(NULL); }
|
||||
};
|
||||
|
||||
inline S32 AppMesh::getNumFaces()
|
||||
{
|
||||
assert(mLocked && "Mesh isn't locked");
|
||||
return mFaces.size();
|
||||
}
|
||||
|
||||
inline const Primitive * AppMesh::getFaces()
|
||||
{
|
||||
assert(mLocked && "Mesh isn't locked");
|
||||
return getNumFaces() ? &mFaces[0] : NULL;
|
||||
}
|
||||
|
||||
inline S32 AppMesh::getFaceMaterial(S32 faceIdx)
|
||||
{
|
||||
assert(mLocked && "Mesh isn't locked");
|
||||
assert(faceIdx>=0 && U32(faceIdx) < mFaces.size() && "Face index out of range");
|
||||
if (mFaces[faceIdx].type & Primitive::NoMaterial)
|
||||
return -1;
|
||||
return mFaces[faceIdx].type & Primitive::MaterialMask;
|
||||
}
|
||||
|
||||
inline S32 AppMesh::getNumIndices()
|
||||
{
|
||||
assert(mLocked && "Mesh isn't locked");
|
||||
return mIndices.size();
|
||||
}
|
||||
|
||||
inline const U16 * AppMesh::getIndices()
|
||||
{
|
||||
assert(mLocked && "Mesh isn't locked");
|
||||
return getNumIndices() ? &mIndices[0] : NULL;
|
||||
}
|
||||
|
||||
inline S32 AppMesh::getNumTVerts()
|
||||
{
|
||||
assert(mLocked && "Mesh isn't locked");
|
||||
return mTVerts.size();
|
||||
}
|
||||
|
||||
inline const Point2D * AppMesh::getTVerts()
|
||||
{
|
||||
assert(mLocked && "Mesh isn't locked");
|
||||
return getNumTVerts() ? &mTVerts[0] : NULL;
|
||||
}
|
||||
|
||||
inline S32 AppMesh::getNumVerts()
|
||||
{
|
||||
assert(mLocked && "Mesh isn't locked");
|
||||
return mVerts.size();
|
||||
}
|
||||
|
||||
inline const Point3D * AppMesh::getVerts()
|
||||
{
|
||||
assert(mLocked && "Mesh isn't locked");
|
||||
return getNumVerts() ? &mVerts[0] : NULL;
|
||||
}
|
||||
|
||||
inline S32 AppMesh::getNumNormals()
|
||||
{
|
||||
assert(mLocked && "Mesh isn't locked");
|
||||
return mNormals.size();
|
||||
}
|
||||
|
||||
inline const Point3D * AppMesh::getNormals()
|
||||
{
|
||||
assert(mLocked && "Mesh isn't locked");
|
||||
return getNumNormals() ? &mNormals[0] : NULL;
|
||||
}
|
||||
|
||||
inline S32 AppMesh::getNumBones()
|
||||
{
|
||||
lookupSkinData();
|
||||
return mBones.size();
|
||||
}
|
||||
|
||||
inline AppNode * AppMesh::getBone(S32 idx)
|
||||
{
|
||||
lookupSkinData();
|
||||
assert(idx>=0 && U32(idx) < mBones.size() && "Bone index out of range");
|
||||
return mBones[idx];
|
||||
}
|
||||
|
||||
inline F32 AppMesh::getWeight(S32 boneIdx, S32 vertIdx)
|
||||
{
|
||||
assert(mLocked && "Mesh isn't locked");
|
||||
lookupSkinData();
|
||||
assert(boneIdx>=0 && U32(boneIdx) < mBones.size() && "Bone index out of range");
|
||||
assert(vertIdx>=0 && U32(vertIdx) < mVertId.size() && "Vertex index out of range");
|
||||
assert(mVertId[vertIdx]<mWeights[boneIdx]->size() && "Vertex id out of range");
|
||||
return (*mWeights[boneIdx])[mVertId[vertIdx]];
|
||||
}
|
||||
|
||||
inline AppIfl * AppMesh::getIfl(S32 matIdx)
|
||||
{
|
||||
assert(matIdx>=0 && U32(matIdx) < mIfls.size() && "Mat index out of ifl range");
|
||||
return mIfls[matIdx];
|
||||
}
|
||||
|
||||
inline void AppMesh::lookupSkinData()
|
||||
{
|
||||
if (mSkinDataFetched)
|
||||
return;
|
||||
getSkinData();
|
||||
mSkinDataFetched = true;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif // DTSAPPMESH_H_
|
102
lib/dtsSDKPlus/appNode.cpp
Executable file
102
lib/dtsSDKPlus/appNode.cpp
Executable file
@ -0,0 +1,102 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable : 4786 4018)
|
||||
#endif
|
||||
|
||||
#include "appNode.h"
|
||||
#include "DTSUtil.h"
|
||||
|
||||
namespace DTS
|
||||
{
|
||||
|
||||
AppNode::AppNode()
|
||||
{
|
||||
mName = NULL;
|
||||
mParentName = NULL;
|
||||
}
|
||||
|
||||
AppNode::~AppNode()
|
||||
{
|
||||
S32 i;
|
||||
for (i=0; i<mMeshes.size(); i++)
|
||||
delete mMeshes[i];
|
||||
for (i=0; i<mChildNodes.size(); i++)
|
||||
delete mChildNodes[i];
|
||||
mMeshes.clear();
|
||||
mChildNodes.clear();
|
||||
|
||||
delete [] mName;
|
||||
delete [] mParentName;
|
||||
}
|
||||
|
||||
S32 AppNode::getNumMesh()
|
||||
{
|
||||
if (mMeshes.size() == 0)
|
||||
buildMeshList();
|
||||
return mMeshes.size();
|
||||
}
|
||||
|
||||
AppMesh * AppNode::getMesh(S32 idx)
|
||||
{
|
||||
if (mMeshes.size() == 0)
|
||||
buildMeshList();
|
||||
if (idx<mMeshes.size() && idx>=0)
|
||||
return mMeshes[idx];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
S32 AppNode::getNumChildNodes()
|
||||
{
|
||||
if (mChildNodes.size() == 0)
|
||||
buildChildList();
|
||||
return mChildNodes.size();
|
||||
}
|
||||
|
||||
AppNode * AppNode::getChildNode(S32 idx)
|
||||
{
|
||||
if (mChildNodes.size() == 0)
|
||||
buildChildList();
|
||||
if (idx<mChildNodes.size() && idx>=0)
|
||||
return mChildNodes[idx];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool AppNode::isBillboard()
|
||||
{
|
||||
return !_strnicmp(getName(),"BB::",4) || !_strnicmp(getName(),"BB_",3) || !_strnicmp(getName(),"BBZ::",5) || !_strnicmp(getName(),"BBZ_",4);
|
||||
}
|
||||
|
||||
bool AppNode::isBillboardZAxis()
|
||||
{
|
||||
return !_strnicmp(getName(),"BBZ::",5) || !_strnicmp(getName(),"BBZ_",4);
|
||||
}
|
||||
|
||||
bool AppNode::isDummy()
|
||||
{
|
||||
// naming convention should work well enough...
|
||||
// ...but can override this method if one wants more
|
||||
return !_strnicmp(getName(), "dummy", 5);
|
||||
}
|
||||
|
||||
bool AppNode::isBounds()
|
||||
{
|
||||
// naming convention should work well enough...
|
||||
// ...but can override this method if one wants more
|
||||
return !_stricmp(getName(), "bounds");
|
||||
}
|
||||
|
||||
bool AppNode::isRoot()
|
||||
{
|
||||
// we assume root node isn't added, so this is never true
|
||||
// but allow for possibility (by overriding this method)
|
||||
// so that isParentRoot still works.
|
||||
return false;
|
||||
}
|
||||
|
||||
}; // namespace DTS
|
||||
|
||||
|
68
lib/dtsSDKPlus/appNode.h
Executable file
68
lib/dtsSDKPlus/appNode.h
Executable file
@ -0,0 +1,68 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef DTSAPPNODE_H_
|
||||
#define DTSAPPNODE_H_
|
||||
|
||||
#include "DTSTypes.h"
|
||||
#include "DTSShape.h"
|
||||
#include "DTSPlusTypes.h"
|
||||
#include "appMesh.h"
|
||||
|
||||
namespace DTS
|
||||
{
|
||||
class AppNode
|
||||
{
|
||||
// add attached meshes and child nodes to app node
|
||||
// the reason these are tracked by AppNode is that
|
||||
// AppNode is responsible for deleting all it's children
|
||||
// and attached meshes.
|
||||
virtual void buildMeshList() = 0;
|
||||
virtual void buildChildList() = 0;
|
||||
|
||||
protected:
|
||||
|
||||
std::vector<AppMesh*> mMeshes;
|
||||
std::vector<AppNode*> mChildNodes;
|
||||
char * mName;
|
||||
char * mParentName;
|
||||
|
||||
public:
|
||||
|
||||
AppNode();
|
||||
virtual ~AppNode();
|
||||
|
||||
S32 getNumMesh();
|
||||
AppMesh * getMesh(S32 idx);
|
||||
|
||||
S32 getNumChildNodes();
|
||||
AppNode * getChildNode(S32 idx);
|
||||
|
||||
|
||||
virtual Matrix<4,4,F32> getNodeTransform(const AppTime & time) = 0;
|
||||
|
||||
virtual bool isEqual(AppNode *) = 0;
|
||||
|
||||
virtual bool animatesTransform(const AppSequenceData & seqData) = 0;
|
||||
|
||||
virtual const char * getName() = 0;
|
||||
virtual const char * getParentName() = 0;
|
||||
|
||||
virtual bool getFloat(const char * propName, F32 & defaultVal) = 0;
|
||||
virtual bool getInt(const char * propName, S32 & defaultVal) = 0;
|
||||
virtual bool getBool(const char * propName, bool & defaultVal) = 0;
|
||||
|
||||
virtual bool isBillboard();
|
||||
virtual bool isBillboardZAxis();
|
||||
virtual bool isParentRoot() = 0;
|
||||
virtual bool isDummy();
|
||||
virtual bool isBounds();
|
||||
virtual bool isRoot();
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif // DTSAPPNODE_H_
|
||||
|
177
lib/dtsSDKPlus/appSceneEnum.cpp
Executable file
177
lib/dtsSDKPlus/appSceneEnum.cpp
Executable file
@ -0,0 +1,177 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable : 4786 4018)
|
||||
#endif
|
||||
|
||||
#include "appSceneEnum.h"
|
||||
#include "appSequence.h"
|
||||
|
||||
namespace DTS
|
||||
{
|
||||
|
||||
AppSequence * AppSceneEnum::getSequence(AppNode * node)
|
||||
{
|
||||
const char * prefix = "Sequence::";
|
||||
const char * name = node->getName();
|
||||
if (!_strnicmp(name,prefix,strlen(prefix)))
|
||||
return new AppSequenceNode(node);
|
||||
prefix = "Sequence_";
|
||||
if (!_strnicmp(name,prefix,strlen(prefix)))
|
||||
return new AppSequenceNode(node);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool AppSceneEnum::processNode(AppNode * node)
|
||||
{
|
||||
// Helper method to help rot nodes that we find in the scene.
|
||||
|
||||
// At this stage we do not need to collect all the nodes
|
||||
// because the tree structure will still be there when we
|
||||
// build the shape. What we need to do right now is grab
|
||||
// the top of all the subtrees, any meshes hanging on the
|
||||
// root level (these will be lower detail levels, we don't
|
||||
// need to grab meshes on the sub-trees because they will
|
||||
// be found when we recurse into the sub-tree), the bounds
|
||||
// node, and any sequences.
|
||||
|
||||
const char * name = node->getName();
|
||||
const char * pname = node->getParentName();
|
||||
|
||||
AppConfig::PrintDump(PDPass1,avar("Processing Node %s with parent %s\r\n", name, pname));
|
||||
|
||||
AppSequence * seq = getSequence(node);
|
||||
if (seq)
|
||||
{
|
||||
sequences.push_back(seq);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (node->isDummy())
|
||||
return false;
|
||||
|
||||
if (isSubtree(node))
|
||||
{
|
||||
// Add this node to the subtree list...
|
||||
AppConfig::PrintDump(PDPass1,avar("Found subtree starting at Node \"%s\"\r\n",name));
|
||||
subtrees.push_back(node);
|
||||
return true;
|
||||
}
|
||||
|
||||
// See if it is a bounding box. If so, save it as THE bounding
|
||||
// box for the scene
|
||||
if (node->isBounds())
|
||||
{
|
||||
if (boundsNode)
|
||||
{
|
||||
AppConfig::SetExportError("4", "More than one bounds node found.");
|
||||
AppConfig::PrintDump(PDPass1,"More than one bounds node found.\r\n");
|
||||
}
|
||||
else
|
||||
AppConfig::PrintDump(PDPass1,"Bounding box found\r\n");
|
||||
boundsNode = node;
|
||||
return true;
|
||||
}
|
||||
|
||||
// if we use this node, then be sure to return true so the caller doesn't delete it
|
||||
bool used = false;
|
||||
|
||||
if (node->getNumMesh()!=0)
|
||||
{
|
||||
for (S32 i=0; i<node->getNumMesh(); i++)
|
||||
{
|
||||
AppMesh * mesh = node->getMesh(i);
|
||||
if (mesh->isSkin())
|
||||
{
|
||||
AppConfig::PrintDump(PDPass1,avar("Skin \"%s\" with parent \"%s\" added to entry list\r\n",mesh->getName(),pname));
|
||||
skins.push_back(mesh);
|
||||
used = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (node->isParentRoot())
|
||||
{
|
||||
AppConfig::PrintDump(PDPass1,avar("Mesh \"%s\" with parent \"%s\" added to entry list\r\n",mesh->getName(),pname));
|
||||
meshNodes.push_back(node);
|
||||
meshes.push_back(mesh);
|
||||
used = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (used)
|
||||
usedNodes.push_back(node);
|
||||
}
|
||||
return used;
|
||||
}
|
||||
|
||||
bool AppSceneEnum::isSubtree(AppNode * node)
|
||||
{
|
||||
return node->isParentRoot() && node->getNumMesh() == 0;
|
||||
}
|
||||
|
||||
AppSceneEnum::AppSceneEnum()
|
||||
{
|
||||
boundsNode = NULL;
|
||||
}
|
||||
|
||||
AppSceneEnum::~AppSceneEnum()
|
||||
{
|
||||
S32 i;
|
||||
for (i=0; i<usedNodes.size(); i++)
|
||||
delete usedNodes[i];
|
||||
for (i=0; i<subtrees.size(); i++)
|
||||
delete subtrees[i];
|
||||
for (i=0; i<sequences.size(); i++)
|
||||
delete sequences[i];
|
||||
delete boundsNode;
|
||||
}
|
||||
|
||||
void AppSceneEnum::updateStatus( const char *stageName, S32 stageNumber, S32 stageProgress, S32 stageProgressMax )
|
||||
{
|
||||
}
|
||||
|
||||
Shape * AppSceneEnum::processScene()
|
||||
{
|
||||
//setExportError(NULL);
|
||||
|
||||
AppConfig::PrintDump(PDPass1,"First pass: enumerate scene...\r\n\r\n");
|
||||
|
||||
enumScene();
|
||||
|
||||
if (!boundsNode)
|
||||
AppConfig::SetExportError(0,"No bounds found");
|
||||
|
||||
if (AppConfig::IsExportError())
|
||||
return NULL;
|
||||
|
||||
AppConfig::PrintDump(PDPass2,"\r\nSecond pass: put shape structure together...\r\n\r\n");
|
||||
|
||||
// set up bounds node
|
||||
shapeMimic.addBounds(boundsNode);
|
||||
|
||||
// add other subtrees
|
||||
S32 i;
|
||||
for (i=0; i<subtrees.size(); i++)
|
||||
shapeMimic.addSubtree(subtrees[i]);
|
||||
|
||||
// add meshes
|
||||
for (i=0; i<meshes.size(); i++)
|
||||
shapeMimic.addMesh(meshNodes[i],meshes[i]);
|
||||
|
||||
// add skin
|
||||
for (i=0; i<skins.size(); i++)
|
||||
shapeMimic.addSkin(skins[i]);
|
||||
|
||||
// add sequences
|
||||
for (i=0; i<sequences.size(); i++)
|
||||
shapeMimic.addSequence(sequences[i]);
|
||||
|
||||
// generate the shape
|
||||
return shapeMimic.generateShape();
|
||||
}
|
||||
|
||||
}; // namespace DTS
|
58
lib/dtsSDKPlus/appSceneEnum.h
Executable file
58
lib/dtsSDKPlus/appSceneEnum.h
Executable file
@ -0,0 +1,58 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef DTSAPPSCENEENUM_H_
|
||||
#define DTSAPPSCENEENUM_H_
|
||||
|
||||
#include "DTSTypes.h"
|
||||
#include "DTSShape.h"
|
||||
#include "DTSPlusTypes.h"
|
||||
#include "ShapeMimic.h"
|
||||
#include "appMesh.h"
|
||||
#include "appNode.h"
|
||||
#include "appSequence.h"
|
||||
#include "appConfig.h"
|
||||
|
||||
namespace DTS
|
||||
{
|
||||
|
||||
class AppSceneEnum
|
||||
{
|
||||
protected:
|
||||
std::vector<AppNode*> usedNodes; // store nodes that need
|
||||
// to be deleted later but
|
||||
// not tracked in other lists
|
||||
|
||||
std::vector<AppMesh*> meshes; // don't delete these...children of nodes in usedNodes
|
||||
std::vector<AppMesh*> skins; // ditto
|
||||
std::vector<AppNode*> meshNodes; // don't delete these...might contain dups
|
||||
std::vector<AppNode*> subtrees;
|
||||
std::vector<AppSequence*> sequences;
|
||||
AppNode * boundsNode;
|
||||
|
||||
ShapeMimic shapeMimic;
|
||||
|
||||
//void setExportError(const char * errStr) { AppConfig::SetExportError(errStr); }
|
||||
//const char * getError() { return AppConfig::GetExportError(); }
|
||||
//bool isError() { return AppConfig::IsExportError(); }
|
||||
|
||||
virtual bool isSubtree(AppNode * node);
|
||||
virtual AppSequence * getSequence(AppNode *);
|
||||
|
||||
bool processNode(AppNode *);
|
||||
|
||||
public:
|
||||
AppSceneEnum();
|
||||
~AppSceneEnum();
|
||||
|
||||
virtual void enumScene() = 0;
|
||||
Shape * processScene();
|
||||
|
||||
virtual void updateStatus( const char *stageName, S32 stageNumber, S32 stageProgress, S32 stageProgressMax );
|
||||
};
|
||||
|
||||
}; // namespace DTS
|
||||
|
||||
#endif // #define DTSAPPSCENEENUM_H_
|
196
lib/dtsSDKPlus/appSequence.cpp
Executable file
196
lib/dtsSDKPlus/appSequence.cpp
Executable file
@ -0,0 +1,196 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable : 4786 4018)
|
||||
#endif
|
||||
|
||||
#include "appSequence.h"
|
||||
#include "appNode.h"
|
||||
#include "appConfig.h"
|
||||
#include "DTSShape.h"
|
||||
|
||||
namespace DTS
|
||||
{
|
||||
|
||||
const char * AppSequenceNode::getName()
|
||||
{
|
||||
const char * prefix = "Sequence::";
|
||||
const char * name = mAppNode->getName();
|
||||
if (!_strnicmp(name,prefix,strlen(prefix)))
|
||||
name += strlen(prefix);
|
||||
return name;
|
||||
}
|
||||
|
||||
void AppSequenceNode::getSequenceData(AppSequenceData * seqData)
|
||||
{
|
||||
seqData->cyclic = true;
|
||||
seqData->blend = false;
|
||||
seqData->ignoreGround = false;
|
||||
|
||||
seqData->enableMorph = false;
|
||||
seqData->enableTVert = false;
|
||||
seqData->enableVis = true;
|
||||
seqData->enableTransform = true;
|
||||
seqData->enableScale = true;
|
||||
seqData->enableUniformScale = true;
|
||||
seqData->enableAlignedScale = true;
|
||||
seqData->enableArbitraryScale = true;
|
||||
seqData->enableIFL = true;
|
||||
|
||||
seqData->forceMorph = false;
|
||||
seqData->forceTVert = false;
|
||||
seqData->forceVis = false;
|
||||
seqData->forceTransform = false;
|
||||
seqData->forceScale = false;
|
||||
|
||||
seqData->frameRate = 30;
|
||||
seqData->groundFrameRate = 10;
|
||||
seqData->numFrames = 0;
|
||||
seqData->groundNumFrames = 0;
|
||||
|
||||
seqData->overrideDuration = -1.0f;
|
||||
|
||||
seqData->priority = 5.0f;
|
||||
|
||||
S32 startFrame = 0;
|
||||
S32 endFrame = 0;
|
||||
S32 blendReferenceFrame = 0;
|
||||
|
||||
mAppNode->getBool("cyclic",seqData->cyclic);
|
||||
mAppNode->getBool("blend",seqData->blend);
|
||||
mAppNode->getBool("ignoreGround",seqData->ignoreGround);
|
||||
mAppNode->getBool("enableMorph",seqData->enableMorph);
|
||||
mAppNode->getBool("enableTVert",seqData->enableTVert);
|
||||
mAppNode->getBool("enableVis",seqData->enableVis);
|
||||
mAppNode->getBool("enableTransform",seqData->enableTransform);
|
||||
mAppNode->getBool("enableScale",seqData->enableScale);
|
||||
mAppNode->getBool("enableUniformScale",seqData->enableUniformScale);
|
||||
mAppNode->getBool("enableAlignedScale",seqData->enableAlignedScale);
|
||||
mAppNode->getBool("enableArbitraryScale",seqData->enableArbitraryScale);
|
||||
mAppNode->getBool("enableIFL",seqData->enableIFL);
|
||||
|
||||
mAppNode->getBool("forceMorph",seqData->forceMorph);
|
||||
mAppNode->getBool("forceTVert",seqData->forceTVert);
|
||||
mAppNode->getBool("forceVis",seqData->forceVis);
|
||||
mAppNode->getBool("forceTransform",seqData->forceTransform);
|
||||
mAppNode->getBool("forceScale",seqData->forceScale);
|
||||
|
||||
mAppNode->getFloat("priority",seqData->priority);
|
||||
|
||||
mAppNode->getInt("startFrame",startFrame);
|
||||
mAppNode->getInt("endFrame",endFrame);
|
||||
mAppNode->getInt("blendReferenceFrame",blendReferenceFrame);
|
||||
mAppNode->getFloat("overrideDuration",seqData->overrideDuration);
|
||||
|
||||
mAppNode->getFloat("frameRate",seqData->frameRate);
|
||||
if (seqData->frameRate<0.000001f)
|
||||
seqData->frameRate = 1.0f/30.0f;
|
||||
|
||||
mAppNode->getFloat("groundFrameRate",seqData->groundFrameRate);
|
||||
if (seqData->groundFrameRate<0.000001f)
|
||||
seqData->groundFrameRate = 1.0f/10.0f;
|
||||
|
||||
// convert from frames to times
|
||||
F32 appFPS = AppConfig::AppFramesPerSec(); // not necessarily same as exported fps
|
||||
F32 startTime = F32(startFrame) / appFPS;
|
||||
F32 endTime = F32(endFrame) / appFPS;
|
||||
F32 blendReferenceTime = F32(blendReferenceFrame) / appFPS;
|
||||
|
||||
seqData->startTime.set(startTime,0);
|
||||
seqData->endTime.set(endTime,0);
|
||||
seqData->blendReferenceTime.set(blendReferenceTime,0);
|
||||
|
||||
F32 duration = seqData->endTime.getF32() - seqData->startTime.getF32();
|
||||
if (seqData->cyclic)
|
||||
// This is required to match up with what is displayed by 3dsMax when
|
||||
// playing animation cyclically. Set to zero for apps that don't need it.
|
||||
duration += AppConfig::CyclicSequencePadding();
|
||||
F32 delta = 0.0f;
|
||||
F32 groundDelta = 0.0f;
|
||||
|
||||
// Get sequence timing information
|
||||
if (mAppNode->getInt("numFrames",seqData->numFrames) && seqData->numFrames>0)
|
||||
seqData->numFrames--;
|
||||
else
|
||||
seqData->numFrames = (S32) ((duration + 0.25f/seqData->frameRate) * seqData->frameRate);
|
||||
delta = seqData->numFrames ? duration / F32(seqData->numFrames) : duration;
|
||||
if (!seqData->cyclic)
|
||||
seqData->numFrames++;
|
||||
|
||||
// Get sequence ground timing information
|
||||
if (mAppNode->getInt("groundNumFrames",seqData->groundNumFrames) && seqData->groundNumFrames>1)
|
||||
{
|
||||
groundDelta = duration / (F32)(seqData->groundNumFrames-1);
|
||||
}
|
||||
else
|
||||
{
|
||||
seqData->groundNumFrames = (S32)((duration + 0.25f/seqData->groundFrameRate) * seqData->groundFrameRate);
|
||||
groundDelta = seqData->groundNumFrames ? duration / (F32) seqData->groundNumFrames : duration;
|
||||
seqData->groundNumFrames++;
|
||||
}
|
||||
|
||||
seqData->duration.set(duration,0);
|
||||
seqData->delta.set(delta,0);
|
||||
seqData->groundDelta.set(groundDelta,0);
|
||||
}
|
||||
|
||||
S32 AppSequenceNode::getNumTriggers()
|
||||
{
|
||||
S32 num = 0;
|
||||
mAppNode->getInt("numTriggers",num);
|
||||
return num;
|
||||
}
|
||||
|
||||
Trigger AppSequenceNode::getTrigger(S32 idx)
|
||||
{
|
||||
char buffer[256];
|
||||
|
||||
sprintf(buffer,"triggerFrame%i",idx);
|
||||
S32 frame = 0;
|
||||
mAppNode->getInt(buffer,frame);
|
||||
|
||||
sprintf(buffer,"triggerState%i",idx);
|
||||
S32 state = 0;
|
||||
mAppNode->getInt(buffer,state);
|
||||
|
||||
AppSequenceData seqData;
|
||||
getSequenceData(&seqData);
|
||||
F32 appFPS = AppConfig::AppFramesPerSec(); // not necessarily same as exported fps
|
||||
|
||||
Trigger ret;
|
||||
if (state<0)
|
||||
ret.state = 1<<(-state-1);
|
||||
else
|
||||
ret.state = (1<<(state-1)) | TriggerState::StateOn;
|
||||
if (seqData.duration.getF32()<0.001f)
|
||||
ret.pos=0;
|
||||
else
|
||||
ret.pos = F32(frame/appFPS-seqData.startTime.getF32())/seqData.duration.getF32();
|
||||
return ret;
|
||||
}
|
||||
|
||||
void AppSequence::setTSSequence(Sequence * seq)
|
||||
{
|
||||
AppSequenceData seqData;
|
||||
getSequenceData(&seqData);
|
||||
|
||||
// fill in some sequence data
|
||||
seq->flags = 0;
|
||||
if (seqData.cyclic)
|
||||
seq->flags |= Sequence::Cyclic;
|
||||
if (seqData.blend)
|
||||
seq->flags |= Sequence::Blend;
|
||||
seq->priority = S32(seqData.priority);
|
||||
seq->numKeyFrames = seqData.numFrames;
|
||||
seq->duration = seqData.overrideDuration>0 ? seqData.overrideDuration : seqData.duration.getF32();
|
||||
seq->toolBegin = seqData.startTime.getF32();
|
||||
seq->numTriggers = 0;
|
||||
seq->firstTrigger = 0;
|
||||
}
|
||||
|
||||
|
||||
}; // namespace DTS
|
||||
|
102
lib/dtsSDKPlus/appSequence.h
Executable file
102
lib/dtsSDKPlus/appSequence.h
Executable file
@ -0,0 +1,102 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef DTSAPPSEQUENCE_H_
|
||||
#define DTSAPPSEQUENCE_H_
|
||||
|
||||
#include "DTSTypes.h"
|
||||
#include "DTSShape.h"
|
||||
#include "DTSPlusTypes.h"
|
||||
#include "appTime.h"
|
||||
|
||||
|
||||
namespace DTS
|
||||
{
|
||||
// Enum for Triggers...
|
||||
namespace TriggerState
|
||||
{
|
||||
enum { StateOn = 1 << 31, InvertOnReverse = 1 << 30, StateMask = 0x1f };
|
||||
};
|
||||
|
||||
class AppSequence;
|
||||
class AppNode;
|
||||
|
||||
struct AppSequenceData
|
||||
{
|
||||
bool cyclic;
|
||||
bool blend;
|
||||
bool ignoreGround;
|
||||
|
||||
bool enableMorph;
|
||||
bool enableTVert;
|
||||
bool enableVis;
|
||||
bool enableTransform;
|
||||
bool enableScale;
|
||||
bool enableUniformScale;
|
||||
bool enableAlignedScale;
|
||||
bool enableArbitraryScale;
|
||||
bool enableIFL;
|
||||
|
||||
bool forceMorph;
|
||||
bool forceTVert;
|
||||
bool forceVis;
|
||||
bool forceTransform;
|
||||
bool forceScale;
|
||||
|
||||
AppTime duration;
|
||||
AppTime delta;
|
||||
AppTime startTime;
|
||||
AppTime endTime;
|
||||
AppTime blendReferenceTime;
|
||||
AppTime groundDelta;
|
||||
|
||||
S32 numFrames;
|
||||
S32 groundNumFrames;
|
||||
|
||||
F32 frameRate;
|
||||
F32 groundFrameRate;
|
||||
|
||||
F32 overrideDuration;
|
||||
|
||||
F32 priority;
|
||||
};
|
||||
|
||||
class AppSequence
|
||||
{
|
||||
public:
|
||||
virtual const char * getName() = 0;
|
||||
|
||||
virtual void getSequenceData(AppSequenceData *) = 0;
|
||||
|
||||
virtual S32 getNumTriggers() = 0;
|
||||
virtual Trigger getTrigger(S32 idx) = 0;
|
||||
|
||||
virtual void setTSSequence(Sequence *);
|
||||
};
|
||||
|
||||
// Create a sequence from an appropriately outfitted node
|
||||
// Uses property values to figure everything out.
|
||||
// This system is adequate for all dts features and will
|
||||
// work in any 3d package which supports naming nodes and adding
|
||||
// property values to nodes. Specific 3d apps can add support
|
||||
// guis to manipulate these sequence objects.
|
||||
class AppSequenceNode : public AppSequence
|
||||
{
|
||||
AppNode * mAppNode;
|
||||
|
||||
public:
|
||||
|
||||
AppSequenceNode(AppNode * node) { mAppNode=node; }
|
||||
|
||||
const char * getName();
|
||||
void getSequenceData(AppSequenceData *);
|
||||
S32 getNumTriggers();
|
||||
Trigger getTrigger(S32 idx);
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif // DTSAPPSEQUENCE_H_
|
||||
|
42
lib/dtsSDKPlus/appTime.cpp
Executable file
42
lib/dtsSDKPlus/appTime.cpp
Executable file
@ -0,0 +1,42 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable : 4786 4018)
|
||||
#endif
|
||||
|
||||
#include "appTime.h"
|
||||
#include <string>
|
||||
|
||||
namespace DTS
|
||||
{
|
||||
|
||||
// Default time...base pose
|
||||
AppTime AppTime::smDefaultTime(0,0);
|
||||
|
||||
// equality tolerance for f64 component
|
||||
F64 AppTime::smTOL = 0.0001;
|
||||
|
||||
// when converting to string, display S32? display F32?
|
||||
bool AppTime::smPrintInt = false;
|
||||
bool AppTime::smPrintFloat = true;
|
||||
|
||||
const char * AppTime::getStr() const
|
||||
{
|
||||
if (!mBuffer)
|
||||
const_cast<char*>(mBuffer) = new char[64];
|
||||
if (smPrintInt && smPrintFloat)
|
||||
sprintf(mBuffer,"%f:%i",F32(f64),u32);
|
||||
else if (smPrintInt)
|
||||
sprintf(mBuffer,"%i",u32);
|
||||
else if (smPrintFloat)
|
||||
sprintf(mBuffer,"%f",F32(f64));
|
||||
else
|
||||
return "";
|
||||
return mBuffer;
|
||||
}
|
||||
|
||||
|
||||
}; // namespace DTS
|
93
lib/dtsSDKPlus/appTime.h
Executable file
93
lib/dtsSDKPlus/appTime.h
Executable file
@ -0,0 +1,93 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef DTSAPPTIME_H_
|
||||
#define DTSAPPTIME_H_
|
||||
|
||||
#include "DTSTypes.h"
|
||||
#include "DTSShape.h"
|
||||
#include "DTSPlusTypes.h"
|
||||
|
||||
namespace DTS
|
||||
{
|
||||
class AppTime
|
||||
{
|
||||
static AppTime smDefaultTime;
|
||||
static F64 smTOL;
|
||||
static bool smPrintInt;
|
||||
static bool smPrintFloat;
|
||||
|
||||
F64 f64;
|
||||
U32 u32;
|
||||
char * mBuffer;
|
||||
|
||||
public:
|
||||
|
||||
void set(F64 f, U32 i) { u32=i;f64=f; }
|
||||
|
||||
// app can use however desired
|
||||
F32 getF32() const { return (F32)f64; }
|
||||
F64 getF64() const { return f64; }
|
||||
U32 getU32() const { return u32; }
|
||||
S32 getS32() const { return S32(u32); }
|
||||
|
||||
const char * getStr() const;
|
||||
|
||||
static const AppTime & DefaultTime() { return smDefaultTime; }
|
||||
static void SetDefaultTime(const AppTime & def) { smDefaultTime = def; }
|
||||
static F64 TOL() { return smTOL; }
|
||||
static void SetTOL(F64 tol) { smTOL = tol; }
|
||||
static void SetPrintInt(bool i) { smPrintInt=i; }
|
||||
static void SetPrintFloat(bool f) { smPrintFloat=f; }
|
||||
|
||||
// operators...
|
||||
AppTime & operator+=(const AppTime & r)
|
||||
{
|
||||
f64 += r.f64;
|
||||
u32 += r.u32;
|
||||
return *this;
|
||||
}
|
||||
AppTime & operator-=(const AppTime & r)
|
||||
{
|
||||
f64 -= r.f64;
|
||||
u32 -= r.u32;
|
||||
return *this;
|
||||
}
|
||||
friend AppTime operator+(const AppTime & a, const AppTime & b)
|
||||
{
|
||||
return AppTime(a.f64+b.f64,a.u32+b.u32);
|
||||
}
|
||||
friend bool operator<(const AppTime & a, const AppTime & b)
|
||||
{
|
||||
return (a.f64 < b.f64 && a.u32 <= b.u32);
|
||||
}
|
||||
friend bool operator>(const AppTime & a, const AppTime & b)
|
||||
{
|
||||
return b<a;
|
||||
}
|
||||
friend bool operator<=(const AppTime & a, const AppTime & b)
|
||||
{
|
||||
return (a.f64 <= b.f64 && a.u32 <= b.u32);
|
||||
}
|
||||
friend bool operator>=(const AppTime & a, const AppTime & b)
|
||||
{
|
||||
return b<=a;
|
||||
}
|
||||
friend bool operator==(const AppTime & a, const AppTime & b)
|
||||
{
|
||||
return ((fabs(a.f64 - b.f64) < AppTime::TOL()) && (a.u32 == b.u32));
|
||||
}
|
||||
friend bool operator!=(const AppTime & a, const AppTime & b)
|
||||
{
|
||||
return !(a==b);
|
||||
}
|
||||
|
||||
AppTime(F64 f, U32 i) { set(f,i); mBuffer=NULL; }
|
||||
AppTime() { set(0,0); mBuffer=NULL; }
|
||||
~AppTime() { delete [] mBuffer; }
|
||||
};
|
||||
};
|
||||
|
||||
#endif // DTSAPPTIME_H_
|
533
lib/dtsSDKPlus/decomp/Decompose.cpp
Executable file
533
lib/dtsSDKPlus/decomp/Decompose.cpp
Executable file
@ -0,0 +1,533 @@
|
||||
/**** Decompose.c ****/
|
||||
/* Ken Shoemake, 1993 */
|
||||
#include <math.h>
|
||||
#include "Decompose.h"
|
||||
|
||||
namespace GraphicGems
|
||||
{
|
||||
|
||||
/******* Matrix Preliminaries *******/
|
||||
|
||||
/** Fill out 3x3 matrix to 4x4 **/
|
||||
#define mat_pad(A) (A[W][X]=A[X][W]=A[W][Y]=A[Y][W]=A[W][Z]=A[Z][W]=0,A[W][W]=1)
|
||||
|
||||
/** Copy nxn matrix A to C using "gets" for assignment **/
|
||||
#define mat_copy(C,gets,A,n) {S32 i,j; for(i=0;i<n;i++) for(j=0;j<n;j++)\
|
||||
C[i][j] gets (A[i][j]);}
|
||||
|
||||
/** Copy transpose of nxn matrix A to C using "gets" for assignment **/
|
||||
#define mat_tpose(AT,gets,A,n) {S32 i,j; for(i=0;i<n;i++) for(j=0;j<n;j++)\
|
||||
AT[i][j] gets (A[j][i]);}
|
||||
|
||||
/** Assign nxn matrix C the element-wise combination of A and B using "op" **/
|
||||
#define mat_binop(C,gets,A,op,B,n) {S32 i,j; for(i=0;i<n;i++) for(j=0;j<n;j++)\
|
||||
C[i][j] gets (A[i][j]) op (B[i][j]);}
|
||||
|
||||
/** Multiply the upper left 3x3 parts of A and B to get AB **/
|
||||
void mat_mult(HMatrix A, HMatrix B, HMatrix AB)
|
||||
{
|
||||
S32 i, j;
|
||||
for (i=0; i<3; i++) for (j=0; j<3; j++)
|
||||
AB[i][j] = A[i][0]*B[0][j] + A[i][1]*B[1][j] + A[i][2]*B[2][j];
|
||||
}
|
||||
|
||||
/** Return dot product of length 3 vectors va and vb **/
|
||||
F64 vdot(F64 *va, F64 *vb)
|
||||
{
|
||||
return (va[0]*vb[0] + va[1]*vb[1] + va[2]*vb[2]);
|
||||
}
|
||||
|
||||
/** Set v to cross product of length 3 vectors va and vb **/
|
||||
void vcross(F64 *va, F64 *vb, F64 *v)
|
||||
{
|
||||
v[0] = va[1]*vb[2] - va[2]*vb[1];
|
||||
v[1] = va[2]*vb[0] - va[0]*vb[2];
|
||||
v[2] = va[0]*vb[1] - va[1]*vb[0];
|
||||
}
|
||||
|
||||
/** Set MadjT to transpose of inverse of M times determinant of M **/
|
||||
void adjoint_transpose(HMatrix M, HMatrix MadjT)
|
||||
{
|
||||
vcross(M[1], M[2], MadjT[0]);
|
||||
vcross(M[2], M[0], MadjT[1]);
|
||||
vcross(M[0], M[1], MadjT[2]);
|
||||
}
|
||||
|
||||
/******* Quaternion Preliminaries *******/
|
||||
|
||||
/* Construct a (possibly non-unit) quaternion from real components. */
|
||||
Quat Qt_(F64 x, F64 y, F64 z, F64 w)
|
||||
{
|
||||
Quat qq;
|
||||
qq.x = x;
|
||||
qq.y = y;
|
||||
qq.z = z;
|
||||
qq.w = w;
|
||||
return (qq);
|
||||
}
|
||||
|
||||
/* Return conjugate of quaternion. */
|
||||
Quat Qt_Conj(Quat q)
|
||||
{
|
||||
Quat qq;
|
||||
qq.x = -q.x;
|
||||
qq.y = -q.y;
|
||||
qq.z = -q.z;
|
||||
qq.w = q.w;
|
||||
return (qq);
|
||||
}
|
||||
|
||||
/* Return quaternion product qL * qR. Note: order is important!
|
||||
* To combine rotations, use the product Mul(qSecond, qFirst),
|
||||
* which gives the effect of rotating by qFirst then qSecond. */
|
||||
Quat Qt_Mul(Quat qL, Quat qR)
|
||||
{
|
||||
Quat qq;
|
||||
qq.w = qL.w*qR.w - qL.x*qR.x - qL.y*qR.y - qL.z*qR.z;
|
||||
qq.x = qL.w*qR.x + qL.x*qR.w + qL.y*qR.z - qL.z*qR.y;
|
||||
qq.y = qL.w*qR.y + qL.y*qR.w + qL.z*qR.x - qL.x*qR.z;
|
||||
qq.z = qL.w*qR.z + qL.z*qR.w + qL.x*qR.y - qL.y*qR.x;
|
||||
return (qq);
|
||||
}
|
||||
|
||||
/* Return product of quaternion q by scalar w. */
|
||||
Quat Qt_Scale(Quat q, F64 w)
|
||||
{
|
||||
Quat qq;
|
||||
qq.w = q.w*w;
|
||||
qq.x = q.x*w;
|
||||
qq.y = q.y*w;
|
||||
qq.z = q.z*w;
|
||||
return (qq);
|
||||
}
|
||||
|
||||
/* Construct a unit quaternion from rotation matrix. Assumes matrix is
|
||||
* used to multiply column vector on the left: vnew = mat vold. Works
|
||||
* correctly for right-handed coordinate system and right-handed rotations.
|
||||
* Translation and perspective components ignored. */
|
||||
Quat Qt_FromMatrix(HMatrix mat)
|
||||
{
|
||||
/* This algorithm avoids near-zero divides by looking for a large component
|
||||
* - first w, then x, y, or z. When the trace is greater than zero,
|
||||
* |w| is greater than 1/2, which is as small as a largest component can be.
|
||||
* Otherwise, the largest diagonal entry corresponds to the largest of |x|,
|
||||
* |y|, or |z|, one of which must be larger than |w|, and at least 1/2. */
|
||||
Quat qu;
|
||||
register F64 tr, s;
|
||||
|
||||
tr = mat[X][X] + mat[Y][Y]+ mat[Z][Z];
|
||||
if (tr >= 0.0) {
|
||||
s = sqrt(tr + mat[W][W]);
|
||||
qu.w = s*0.5;
|
||||
s = 0.5 / s;
|
||||
qu.x = (mat[Z][Y] - mat[Y][Z]) * s;
|
||||
qu.y = (mat[X][Z] - mat[Z][X]) * s;
|
||||
qu.z = (mat[Y][X] - mat[X][Y]) * s;
|
||||
} else {
|
||||
S32 h = X;
|
||||
if (mat[Y][Y] > mat[X][X]) h = Y;
|
||||
if (mat[Z][Z] > mat[h][h]) h = Z;
|
||||
switch (h) {
|
||||
#define caseMacro(i,j,k,I,J,K) \
|
||||
case I:\
|
||||
s = sqrt( (mat[I][I] - (mat[J][J]+mat[K][K])) + mat[W][W] );\
|
||||
qu.i = s*0.5;\
|
||||
s = 0.5 / s;\
|
||||
qu.j = (mat[I][J] + mat[J][I]) * s;\
|
||||
qu.k = (mat[K][I] + mat[I][K]) * s;\
|
||||
qu.w = (mat[K][J] - mat[J][K]) * s;\
|
||||
break
|
||||
caseMacro(x,y,z,X,Y,Z);
|
||||
caseMacro(y,z,x,Y,Z,X);
|
||||
caseMacro(z,x,y,Z,X,Y);
|
||||
}
|
||||
}
|
||||
if (mat[W][W] != 1.0) qu = Qt_Scale(qu, 1/sqrt(mat[W][W]));
|
||||
return (qu);
|
||||
}
|
||||
/******* Decomp Auxiliaries *******/
|
||||
|
||||
static HMatrix mat_id = {
|
||||
{1,0,0,0},
|
||||
{0,1,0,0},
|
||||
{0,0,1,0},
|
||||
{0,0,0,1}
|
||||
};
|
||||
|
||||
/** Compute either the 1 or infinity norm of M, depending on tpose **/
|
||||
F64 mat_norm(HMatrix M, S32 tpose)
|
||||
{
|
||||
S32 i;
|
||||
F64 sum, max;
|
||||
max = 0.0;
|
||||
for (i=0; i<3; i++)
|
||||
{
|
||||
if (tpose)
|
||||
sum = fabs(M[0][i])+fabs(M[1][i])+fabs(M[2][i]);
|
||||
else
|
||||
sum = fabs(M[i][0])+fabs(M[i][1])+fabs(M[i][2]);
|
||||
if (max<sum)
|
||||
max = sum;
|
||||
}
|
||||
return max;
|
||||
}
|
||||
|
||||
F64 norm_inf(HMatrix M) {
|
||||
return mat_norm(M, 0);
|
||||
}
|
||||
F64 norm_one(HMatrix M) {
|
||||
return mat_norm(M, 1);
|
||||
}
|
||||
|
||||
/** Return index of column of M containing maximum abs entry, or -1 if M=0 **/
|
||||
S32 find_max_col(HMatrix M)
|
||||
{
|
||||
F64 abs, max;
|
||||
S32 i, j, col;
|
||||
max = 0.0; col = -1;
|
||||
for (i=0; i<3; i++) for (j=0; j<3; j++) {
|
||||
abs = M[i][j]; if (abs<0.0) abs = -abs;
|
||||
if (abs>max) {max = abs; col = j;}
|
||||
}
|
||||
return col;
|
||||
}
|
||||
|
||||
/** Setup u for Household reflection to zero all v components but first **/
|
||||
void make_reflector(F64 *v, F64 *u)
|
||||
{
|
||||
F64 s = sqrt(vdot(v, v));
|
||||
u[0] = v[0]; u[1] = v[1];
|
||||
u[2] = v[2] + ((v[2]<0.0) ? -s : s);
|
||||
s = sqrt(2.0/vdot(u, u));
|
||||
u[0] = u[0]*s; u[1] = u[1]*s; u[2] = u[2]*s;
|
||||
}
|
||||
|
||||
/** Apply Householder reflection represented by u to column vectors of M **/
|
||||
void reflect_cols(HMatrix M, F64 *u)
|
||||
{
|
||||
S32 i, j;
|
||||
for (i=0; i<3; i++) {
|
||||
F64 s = u[0]*M[0][i] + u[1]*M[1][i] + u[2]*M[2][i];
|
||||
for (j=0; j<3; j++) M[j][i] -= u[j]*s;
|
||||
}
|
||||
}
|
||||
/** Apply Householder reflection represented by u to row vectors of M **/
|
||||
void reflect_rows(HMatrix M, F64 *u)
|
||||
{
|
||||
S32 i, j;
|
||||
for (i=0; i<3; i++) {
|
||||
F64 s = vdot(u, M[i]);
|
||||
for (j=0; j<3; j++) M[i][j] -= u[j]*s;
|
||||
}
|
||||
}
|
||||
|
||||
/** Find orthogonal factor Q of rank 1 (or less) M **/
|
||||
void do_rank1(HMatrix M, HMatrix Q)
|
||||
{
|
||||
F64 v1[3], v2[3], s;
|
||||
S32 col;
|
||||
mat_copy(Q,=,mat_id,4);
|
||||
/* If rank(M) is 1, we should find a non-zero column in M */
|
||||
col = find_max_col(M);
|
||||
if (col<0) return; /* Rank is 0 */
|
||||
v1[0] = M[0][col]; v1[1] = M[1][col]; v1[2] = M[2][col];
|
||||
make_reflector(v1, v1); reflect_cols(M, v1);
|
||||
v2[0] = M[2][0]; v2[1] = M[2][1]; v2[2] = M[2][2];
|
||||
make_reflector(v2, v2); reflect_rows(M, v2);
|
||||
s = M[2][2];
|
||||
if (s<0.0) Q[2][2] = -1.0;
|
||||
reflect_cols(Q, v1); reflect_rows(Q, v2);
|
||||
}
|
||||
|
||||
/** Find orthogonal factor Q of rank 2 (or less) M using adjoint transpose **/
|
||||
void do_rank2(HMatrix M, HMatrix MadjT, HMatrix Q)
|
||||
{
|
||||
F64 v1[3], v2[3];
|
||||
F64 w, x, y, z, c, s, d;
|
||||
S32 col;
|
||||
/* If rank(M) is 2, we should find a non-zero column in MadjT */
|
||||
col = find_max_col(MadjT);
|
||||
if (col<0) {do_rank1(M, Q); return;} /* Rank<2 */
|
||||
v1[0] = MadjT[0][col]; v1[1] = MadjT[1][col]; v1[2] = MadjT[2][col];
|
||||
make_reflector(v1, v1); reflect_cols(M, v1);
|
||||
vcross(M[0], M[1], v2);
|
||||
make_reflector(v2, v2); reflect_rows(M, v2);
|
||||
w = M[0][0]; x = M[0][1]; y = M[1][0]; z = M[1][1];
|
||||
if (w*z>x*y) {
|
||||
c = z+w; s = y-x; d = sqrt(c*c+s*s); c = c/d; s = s/d;
|
||||
Q[0][0] = Q[1][1] = c; Q[0][1] = -(Q[1][0] = s);
|
||||
} else {
|
||||
c = z-w; s = y+x; d = sqrt(c*c+s*s); c = c/d; s = s/d;
|
||||
Q[0][0] = -(Q[1][1] = c); Q[0][1] = Q[1][0] = s;
|
||||
}
|
||||
Q[0][2] = Q[2][0] = Q[1][2] = Q[2][1] = 0.0; Q[2][2] = 1.0;
|
||||
reflect_cols(Q, v1); reflect_rows(Q, v2);
|
||||
}
|
||||
|
||||
|
||||
/******* Polar Decomposition *******/
|
||||
|
||||
/* Polar Decomposition of 3x3 matrix in 4x4,
|
||||
* M = QS. See Nicholas Higham and Robert S. Schreiber,
|
||||
* Fast Polar Decomposition of An Arbitrary Matrix,
|
||||
* Technical Report 88-942, October 1988,
|
||||
* Department of Computer Science, Cornell University.
|
||||
*/
|
||||
F64 polar_decomp(HMatrix M, HMatrix Q, HMatrix S)
|
||||
{
|
||||
#define TOL 1.0e-6
|
||||
HMatrix Mk, MadjTk, Ek;
|
||||
F64 det, M_one, M_inf, MadjT_one, MadjT_inf, E_one, gamma, g1, g2;
|
||||
S32 i, j;
|
||||
mat_tpose(Mk,=,M,3);
|
||||
M_one = norm_one(Mk); M_inf = norm_inf(Mk);
|
||||
do {
|
||||
adjoint_transpose(Mk, MadjTk);
|
||||
det = vdot(Mk[0], MadjTk[0]);
|
||||
if (det==0.0) {do_rank2(Mk, MadjTk, Mk); break;}
|
||||
MadjT_one = norm_one(MadjTk); MadjT_inf = norm_inf(MadjTk);
|
||||
gamma = sqrt(sqrt((MadjT_one*MadjT_inf)/(M_one*M_inf))/fabs(det));
|
||||
g1 = gamma*0.5;
|
||||
g2 = 0.5/(gamma*det);
|
||||
mat_copy(Ek,=,Mk,3);
|
||||
mat_binop(Mk,=,g1*Mk,+,g2*MadjTk,3);
|
||||
mat_copy(Ek,-=,Mk,3);
|
||||
E_one = norm_one(Ek);
|
||||
M_one = norm_one(Mk); M_inf = norm_inf(Mk);
|
||||
} while (E_one>(M_one*TOL));
|
||||
mat_tpose(Q,=,Mk,3); mat_pad(Q);
|
||||
mat_mult(Mk, M, S); mat_pad(S);
|
||||
for (i=0; i<3; i++) for (j=i; j<3; j++)
|
||||
S[i][j] = S[j][i] = 0.5*(S[i][j]+S[j][i]);
|
||||
return (det);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/******* Spectral Decomposition *******/
|
||||
|
||||
/* Compute the spectral decomposition of symmetric positive semi-definite S.
|
||||
* Returns rotation in U and scale factors in result, so that if K is a diagonal
|
||||
* matrix of the scale factors, then S = U K (U transpose). Uses Jacobi method.
|
||||
* See Gene H. Golub and Charles F. Van Loan. Matrix Computations. Hopkins 1983.
|
||||
*/
|
||||
HVect spect_decomp(HMatrix S, HMatrix U)
|
||||
{
|
||||
HVect kv;
|
||||
F64 Diag[3],OffD[3]; /* OffD is off-diag (by omitted index) */
|
||||
F64 g,h,fabsh,fabsOffDi,t,theta,c,s,tau,ta,OffDq,a,b;
|
||||
static S8 nxt[] = {Y,Z,X};
|
||||
S32 sweep, i, j;
|
||||
mat_copy(U,=,mat_id,4);
|
||||
Diag[X] = S[X][X]; Diag[Y] = S[Y][Y]; Diag[Z] = S[Z][Z];
|
||||
OffD[X] = S[Y][Z]; OffD[Y] = S[Z][X]; OffD[Z] = S[X][Y];
|
||||
for (sweep=20; sweep>0; sweep--) {
|
||||
F64 sm = fabs(OffD[X])+fabs(OffD[Y])+fabs(OffD[Z]);
|
||||
if (sm==0.0) break;
|
||||
for (i=Z; i>=X; i--) {
|
||||
S32 p = nxt[i]; S32 q = nxt[p];
|
||||
fabsOffDi = fabs(OffD[i]);
|
||||
g = 100.0*fabsOffDi;
|
||||
if (fabsOffDi>0.0) {
|
||||
h = Diag[q] - Diag[p];
|
||||
fabsh = fabs(h);
|
||||
if (fabsh+g==fabsh) {
|
||||
t = OffD[i]/h;
|
||||
} else {
|
||||
theta = 0.5*h/OffD[i];
|
||||
t = 1.0/(fabs(theta)+sqrt(theta*theta+1.0));
|
||||
if (theta<0.0) t = -t;
|
||||
}
|
||||
c = 1.0/sqrt(t*t+1.0); s = t*c;
|
||||
tau = s/(c+1.0);
|
||||
ta = t*OffD[i]; OffD[i] = 0.0;
|
||||
Diag[p] -= ta; Diag[q] += ta;
|
||||
OffDq = OffD[q];
|
||||
OffD[q] -= s*(OffD[p] + tau*OffD[q]);
|
||||
OffD[p] += s*(OffDq - tau*OffD[p]);
|
||||
for (j=Z; j>=X; j--) {
|
||||
a = U[j][p]; b = U[j][q];
|
||||
U[j][p] -= s*(b + tau*a);
|
||||
U[j][q] += s*(a - tau*b);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
kv.x = Diag[X]; kv.y = Diag[Y]; kv.z = Diag[Z]; kv.w = 1.0;
|
||||
return (kv);
|
||||
}
|
||||
|
||||
/******* Spectral Axis Adjustment *******/
|
||||
|
||||
/* Given a unit quaternion, q, and a scale vector, k, find a unit quaternion, p,
|
||||
* which permutes the axes and turns freely in the plane of duplicate scale
|
||||
* factors, such that q p has the largest possible w component, i.e. the
|
||||
* smallest possible angle. Permutes k's components to go with q p instead of q.
|
||||
* See Ken Shoemake and Tom Duff. Matrix Animation and Polar Decomposition.
|
||||
* Proceedings of Graphics Interface 1992. Details on p. 262-263.
|
||||
*/
|
||||
Quat snuggle(Quat q, HVect *k)
|
||||
{
|
||||
#define SQRTHALF (0.7071067811865475244)
|
||||
#define sgn(n,v) ((n)?-(v):(v))
|
||||
#define swap(a,i,j) {a[3]=a[i]; a[i]=a[j]; a[j]=a[3];}
|
||||
#define cycle(a,p) if (p) {a[3]=a[0]; a[0]=a[1]; a[1]=a[2]; a[2]=a[3];}\
|
||||
else {a[3]=a[2]; a[2]=a[1]; a[1]=a[0]; a[0]=a[3];}
|
||||
Quat p;
|
||||
F64 ka[4];
|
||||
S32 i, turn = -1;
|
||||
ka[X] = k->x; ka[Y] = k->y; ka[Z] = k->z;
|
||||
if (ka[X]==ka[Y]) {if (ka[X]==ka[Z]) turn = W; else turn = Z;}
|
||||
else {if (ka[X]==ka[Z]) turn = Y; else if (ka[Y]==ka[Z]) turn = X;}
|
||||
if (turn>=0) {
|
||||
Quat qtoz, qp;
|
||||
unsigned neg[3], win;
|
||||
F64 mag[3], t;
|
||||
static Quat qxtoz = {0,SQRTHALF,0,SQRTHALF};
|
||||
static Quat qytoz = {SQRTHALF,0,0,SQRTHALF};
|
||||
static Quat qppmm = { 0.5, 0.5,-0.5,-0.5};
|
||||
static Quat qpppp = { 0.5, 0.5, 0.5, 0.5};
|
||||
static Quat qmpmm = {-0.5, 0.5,-0.5,-0.5};
|
||||
static Quat qpppm = { 0.5, 0.5, 0.5,-0.5};
|
||||
static Quat q0001 = { 0.0, 0.0, 0.0, 1.0};
|
||||
static Quat q1000 = { 1.0, 0.0, 0.0, 0.0};
|
||||
switch (turn) {
|
||||
default: return (Qt_Conj(q));
|
||||
case X: q = Qt_Mul(q, qtoz = qxtoz); swap(ka,X,Z) break;
|
||||
case Y: q = Qt_Mul(q, qtoz = qytoz); swap(ka,Y,Z) break;
|
||||
case Z: qtoz = q0001; break;
|
||||
}
|
||||
q = Qt_Conj(q);
|
||||
mag[0] = (F64)q.z*q.z+(F64)q.w*q.w-0.5;
|
||||
mag[1] = (F64)q.x*q.z-(F64)q.y*q.w;
|
||||
mag[2] = (F64)q.y*q.z+(F64)q.x*q.w;
|
||||
for (i=0; i<3; i++) if (neg[i] = (mag[i]<0.0)) mag[i] = -mag[i];
|
||||
if (mag[0]>mag[1]) {if (mag[0]>mag[2]) win = 0; else win = 2;}
|
||||
else {if (mag[1]>mag[2]) win = 1; else win = 2;}
|
||||
switch (win) {
|
||||
case 0: if (neg[0]) p = q1000; else p = q0001; break;
|
||||
case 1: if (neg[1]) p = qppmm; else p = qpppp; cycle(ka,0) break;
|
||||
case 2: if (neg[2]) p = qmpmm; else p = qpppm; cycle(ka,1) break;
|
||||
}
|
||||
qp = Qt_Mul(q, p);
|
||||
t = sqrt(mag[win]+0.5);
|
||||
p = Qt_Mul(p, Qt_(0.0,0.0,-qp.z/t,qp.w/t));
|
||||
p = Qt_Mul(qtoz, Qt_Conj(p));
|
||||
} else {
|
||||
F64 qa[4], pa[4];
|
||||
unsigned lo, hi, neg[4], par = 0;
|
||||
F64 all, big, two;
|
||||
qa[0] = q.x; qa[1] = q.y; qa[2] = q.z; qa[3] = q.w;
|
||||
for (i=0; i<4; i++) {
|
||||
pa[i] = 0.0;
|
||||
if (neg[i] = (qa[i]<0.0)) qa[i] = -qa[i];
|
||||
par ^= neg[i];
|
||||
}
|
||||
/* Find two largest components, indices in hi and lo */
|
||||
if (qa[0]>qa[1]) lo = 0; else lo = 1;
|
||||
if (qa[2]>qa[3]) hi = 2; else hi = 3;
|
||||
if (qa[lo]>qa[hi]) {
|
||||
if (qa[lo^1]>qa[hi]) {hi = lo; lo ^= 1;}
|
||||
else {hi ^= lo; lo ^= hi; hi ^= lo;}
|
||||
} else {if (qa[hi^1]>qa[lo]) lo = hi^1;}
|
||||
all = (qa[0]+qa[1]+qa[2]+qa[3])*0.5;
|
||||
two = (qa[hi]+qa[lo])*SQRTHALF;
|
||||
big = qa[hi];
|
||||
if (all>two) {
|
||||
if (all>big) {/*all*/
|
||||
{S32 i; for (i=0; i<4; i++) pa[i] = sgn(neg[i], 0.5);}
|
||||
cycle(ka,par)
|
||||
} else {/*big*/ pa[hi] = sgn(neg[hi],1.0);}
|
||||
} else {
|
||||
if (two>big) {/*two*/
|
||||
pa[hi] = sgn(neg[hi],SQRTHALF); pa[lo] = sgn(neg[lo], SQRTHALF);
|
||||
if (lo>hi) {hi ^= lo; lo ^= hi; hi ^= lo;}
|
||||
if (hi==W) {hi = "\001\002\000"[lo]; lo = 3-hi-lo;}
|
||||
swap(ka,hi,lo)
|
||||
} else {/*big*/ pa[hi] = sgn(neg[hi],1.0);}
|
||||
}
|
||||
p.x = -pa[0]; p.y = -pa[1]; p.z = -pa[2]; p.w = pa[3];
|
||||
}
|
||||
k->x = ka[X]; k->y = ka[Y]; k->z = ka[Z];
|
||||
return (p);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/******* Decompose Affine Matrix *******/
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/* Decompose 4x4 affine matrix A as TFRUK(U transpose), where t contains the
|
||||
* translation components, q contains the rotation R, u contains U, k contains
|
||||
* scale factors, and f contains the sign of the determinant.
|
||||
* Assumes A transforms column vectors in right-handed coordinates.
|
||||
* See Ken Shoemake and Tom Duff. Matrix Animation and Polar Decomposition.
|
||||
* Proceedings of Graphics Interface 1992.
|
||||
*/
|
||||
void decomp_affine(HMatrix A, AffineParts *parts)
|
||||
{
|
||||
HMatrix Q, S, U;
|
||||
Quat p;
|
||||
F64 det;
|
||||
parts->t = Qt_(A[X][W], A[Y][W], A[Z][W], 0);
|
||||
det = polar_decomp(A, Q, S);
|
||||
if (det<0.0) {
|
||||
mat_copy(Q,=,-Q,3);
|
||||
parts->f = -1;
|
||||
} else parts->f = 1;
|
||||
parts->q = Qt_FromMatrix(Q);
|
||||
parts->k = spect_decomp(S, U);
|
||||
parts->u = Qt_FromMatrix(U);
|
||||
p = snuggle(parts->u, &parts->k);
|
||||
parts->u = Qt_Mul(parts->u, p);
|
||||
}
|
||||
|
||||
/******* Invert Affine Decomposition *******/
|
||||
|
||||
/* Compute inverse of affine decomposition.
|
||||
*/
|
||||
void invert_affine(AffineParts *parts, AffineParts *inverse)
|
||||
{
|
||||
Quat t, p;
|
||||
inverse->f = parts->f;
|
||||
inverse->q = Qt_Conj(parts->q);
|
||||
inverse->u = Qt_Mul(parts->q, parts->u);
|
||||
inverse->k.x = (parts->k.x==0.0) ? 0.0 : 1.0/parts->k.x;
|
||||
inverse->k.y = (parts->k.y==0.0) ? 0.0 : 1.0/parts->k.y;
|
||||
inverse->k.z = (parts->k.z==0.0) ? 0.0 : 1.0/parts->k.z;
|
||||
inverse->k.w = parts->k.w;
|
||||
t = Qt_(-parts->t.x, -parts->t.y, -parts->t.z, 0);
|
||||
t = Qt_Mul(Qt_Conj(inverse->u), Qt_Mul(t, inverse->u));
|
||||
t = Qt_(inverse->k.x*t.x, inverse->k.y*t.y, inverse->k.z*t.z, 0);
|
||||
p = Qt_Mul(inverse->q, inverse->u);
|
||||
t = Qt_Mul(p, Qt_Mul(t, Qt_Conj(p)));
|
||||
inverse->t = (inverse->f>0.0) ? t : Qt_(-t.x, -t.y, -t.z, 0);
|
||||
}
|
||||
|
||||
}; // namespace GraphicGems
|
||||
|
34
lib/dtsSDKPlus/decomp/Decompose.h
Executable file
34
lib/dtsSDKPlus/decomp/Decompose.h
Executable file
@ -0,0 +1,34 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/**** Decompose.h - Basic declarations ****/
|
||||
#ifndef _H_Decompose
|
||||
#define _H_Decompose
|
||||
|
||||
#include "../DTSPlusTypes.h"
|
||||
|
||||
namespace GraphicGems
|
||||
{
|
||||
|
||||
typedef struct {F64 x, y, z, w;} Quat; /* Quaternion */
|
||||
enum QuatPart {X, Y, Z, W};
|
||||
typedef Quat HVect; /* Homogeneous 3D vector */
|
||||
typedef F64 HMatrix[4][4]; /* Right-handed, for column vectors */
|
||||
typedef struct {
|
||||
HVect t; /* Translation components */
|
||||
Quat q; /* Essential rotation */
|
||||
Quat u; /* Stretch rotation */
|
||||
HVect k; /* Stretch factors */
|
||||
F64 f; /* Sign of determinant */
|
||||
} AffineParts;
|
||||
F64 polar_decomp(HMatrix M, HMatrix Q, HMatrix S);
|
||||
HVect spect_decomp(HMatrix S, HMatrix U);
|
||||
Quat snuggle(Quat q, HVect *k);
|
||||
void decomp_affine(HMatrix A, AffineParts *parts);
|
||||
void invert_affine(AffineParts *parts, AffineParts *inverse);
|
||||
|
||||
}; // namespace GraphicGems
|
||||
|
||||
#endif
|
318
lib/dtsSDKPlus/dtsBitMatrix.h
Executable file
318
lib/dtsSDKPlus/dtsBitMatrix.h
Executable file
@ -0,0 +1,318 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "DTSTypes.h"
|
||||
#include "DTSShape.h"
|
||||
#include "DTSMesh.h"
|
||||
#include "DTSPlusTypes.h"
|
||||
#include "DTSUtil.h"
|
||||
|
||||
// This is rather painful. BitVector and BitMatrix copied from engine repository
|
||||
// in order to work with triangle stripper. Would rather use NVidia stripper, but
|
||||
// there is a bug in it whereby we sometimes get flipped faces. So we are stuck
|
||||
// with the old quick/dirty/ugly (but supremely reliable) original implementation.
|
||||
|
||||
namespace DTS
|
||||
{
|
||||
|
||||
#define AssertFatal(a,b) assert(a && b)
|
||||
class BitVector
|
||||
{
|
||||
U8* mBits;
|
||||
U32 mByteSize;
|
||||
|
||||
U32 mSize;
|
||||
|
||||
static U32 calcByteSize(const U32 numBits);
|
||||
|
||||
public:
|
||||
BitVector();
|
||||
BitVector(const U32 _size);
|
||||
~BitVector();
|
||||
|
||||
/// @name Size Management
|
||||
/// @{
|
||||
void setSize(const U32 _size);
|
||||
U32 getSize() const;
|
||||
U32 getByteSize() const;
|
||||
U32 getAllocatedByteSize() const { return mByteSize; }
|
||||
|
||||
const U8* getBits() const { return mBits; }
|
||||
U8* getNCBits() { return mBits; }
|
||||
|
||||
/// @}
|
||||
|
||||
bool copy(const BitVector& from);
|
||||
|
||||
/// @name Mutators
|
||||
/// Note that bits are specified by index, unlike BitSet32.
|
||||
/// @{
|
||||
|
||||
/// Clear all the bits.
|
||||
void clear();
|
||||
|
||||
/// Set all the bits.
|
||||
void set();
|
||||
|
||||
/// Set the specified bit.
|
||||
void set(U32 bit);
|
||||
/// Clear the specified bit.
|
||||
void clear(U32 bit);
|
||||
/// Test that the specified bit is set.
|
||||
bool test(U32 bit) const;
|
||||
|
||||
/// @}
|
||||
};
|
||||
|
||||
inline BitVector::BitVector()
|
||||
{
|
||||
mBits = NULL;
|
||||
mByteSize = 0;
|
||||
|
||||
mSize = 0;
|
||||
}
|
||||
|
||||
|
||||
inline BitVector::BitVector(const U32 _size)
|
||||
{
|
||||
mBits = NULL;
|
||||
mByteSize = 0;
|
||||
|
||||
mSize = 0;
|
||||
|
||||
setSize(_size);
|
||||
}
|
||||
|
||||
inline BitVector::~BitVector()
|
||||
{
|
||||
delete [] mBits;
|
||||
mBits = NULL;
|
||||
mByteSize = 0;
|
||||
|
||||
mSize = 0;
|
||||
}
|
||||
|
||||
inline U32 BitVector::calcByteSize(const U32 numBits)
|
||||
{
|
||||
// Make sure that we are 32 bit aligned
|
||||
//
|
||||
return (((numBits + 0x7) >> 3) + 0x3) & ~0x3;
|
||||
}
|
||||
|
||||
inline void BitVector::setSize(const U32 _size)
|
||||
{
|
||||
if (_size != 0) {
|
||||
U32 newSize = calcByteSize(_size);
|
||||
if (mByteSize < newSize) {
|
||||
delete [] mBits;
|
||||
mBits = new U8[newSize];
|
||||
mByteSize = newSize;
|
||||
}
|
||||
} else {
|
||||
delete [] mBits;
|
||||
mBits = NULL;
|
||||
mByteSize = 0;
|
||||
}
|
||||
|
||||
mSize = _size;
|
||||
}
|
||||
|
||||
inline U32 BitVector::getSize() const
|
||||
{
|
||||
return mSize;
|
||||
}
|
||||
|
||||
inline U32 BitVector::getByteSize() const
|
||||
{
|
||||
return calcByteSize(mSize);
|
||||
}
|
||||
|
||||
inline void BitVector::clear()
|
||||
{
|
||||
if (mSize != 0)
|
||||
memset(mBits, 0x00, calcByteSize(mSize));
|
||||
}
|
||||
|
||||
inline bool BitVector::copy(const BitVector& from)
|
||||
{
|
||||
U32 sourceSize = from.getSize();
|
||||
if (sourceSize) {
|
||||
setSize(sourceSize);
|
||||
memcpy(mBits, from.getBits(), getByteSize());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline void BitVector::set()
|
||||
{
|
||||
if (mSize != 0)
|
||||
memset(mBits, 0xFF, calcByteSize(mSize));
|
||||
}
|
||||
|
||||
inline void BitVector::set(U32 bit)
|
||||
{
|
||||
AssertFatal(bit < mSize, "Error, out of range bit");
|
||||
|
||||
mBits[bit >> 3] |= U8(1 << (bit & 0x7));
|
||||
}
|
||||
|
||||
inline void BitVector::clear(U32 bit)
|
||||
{
|
||||
AssertFatal(bit < mSize, "Error, out of range bit");
|
||||
|
||||
mBits[bit >> 3] &= U8(~(1 << (bit & 0x7)));
|
||||
}
|
||||
|
||||
inline bool BitVector::test(U32 bit) const
|
||||
{
|
||||
AssertFatal(bit < mSize, "Error, out of range bit");
|
||||
|
||||
return (mBits[bit >> 3] & U8(1 << (bit & 0x7))) != 0;
|
||||
}
|
||||
|
||||
class BitMatrix
|
||||
{
|
||||
U32 mWidth;
|
||||
U32 mHeight;
|
||||
U32 mRowByteWidth;
|
||||
|
||||
U8* mBits;
|
||||
U32 mSize;
|
||||
|
||||
BitVector mColFlags;
|
||||
BitVector mRowFlags;
|
||||
|
||||
public:
|
||||
|
||||
/// Create a new bit matrix.
|
||||
///
|
||||
/// @param width Width of matrix in bits.
|
||||
/// @param height Height of matrix in bits.
|
||||
BitMatrix(const U32 width, const U32 height);
|
||||
~BitMatrix();
|
||||
|
||||
/// @name Setters
|
||||
/// @{
|
||||
|
||||
/// Set all the bits in the matrix to false.
|
||||
void clearAllBits();
|
||||
|
||||
/// Set all the bits in the matrix to true.
|
||||
void setAllBits();
|
||||
|
||||
/// Set a bit at a given location in the matrix.
|
||||
void setBit(const U32 x, const U32 y);
|
||||
|
||||
/// Clear a bit at a given location in the matrix.
|
||||
void clearBit(const U32 x, const U32 y);
|
||||
|
||||
/// @}
|
||||
|
||||
/// @name Queries
|
||||
/// @{
|
||||
|
||||
/// Is the specified bit set?
|
||||
bool isSet(const U32 x, const U32 y) const;
|
||||
|
||||
/// Is any bit in the given column set?
|
||||
bool isAnySetCol(const U32 x);
|
||||
|
||||
/// Is any bit in the given row set?
|
||||
bool isAnySetRow(const U32 y);
|
||||
|
||||
/// @}
|
||||
};
|
||||
|
||||
inline BitMatrix::BitMatrix(const U32 width, const U32 height)
|
||||
: mColFlags(width),
|
||||
mRowFlags(height)
|
||||
{
|
||||
AssertFatal(width != 0 && height != 0, "Error, w/h must be non-zero");
|
||||
|
||||
mWidth = width;
|
||||
mHeight = height;
|
||||
mRowByteWidth = (width + 7) >> 3;
|
||||
|
||||
mSize = mRowByteWidth * mHeight;
|
||||
mBits = new U8[mSize];
|
||||
}
|
||||
|
||||
inline BitMatrix::~BitMatrix()
|
||||
{
|
||||
mWidth = 0;
|
||||
mHeight = 0;
|
||||
mRowByteWidth = 0;
|
||||
mSize = 0;
|
||||
|
||||
delete [] mBits;
|
||||
mBits = NULL;
|
||||
}
|
||||
|
||||
inline void BitMatrix::clearAllBits()
|
||||
{
|
||||
AssertFatal(mBits != NULL, "Error, clearing after deletion");
|
||||
|
||||
memset(mBits, 0x00, mSize);
|
||||
mColFlags.clear();
|
||||
mRowFlags.clear();
|
||||
}
|
||||
|
||||
inline void BitMatrix::setAllBits()
|
||||
{
|
||||
AssertFatal(mBits != NULL, "Error, setting after deletion");
|
||||
|
||||
memset(mBits, 0xFF, mSize);
|
||||
mColFlags.set();
|
||||
mRowFlags.set();
|
||||
}
|
||||
|
||||
inline void BitMatrix::setBit(const U32 x, const U32 y)
|
||||
{
|
||||
AssertFatal(x < mWidth && y < mHeight, "Error, out of bounds bit!");
|
||||
|
||||
U8* pRow = &mBits[y * mRowByteWidth];
|
||||
|
||||
U8* pByte = &pRow[x >> 3];
|
||||
*pByte |= 1 << (x & 0x7);
|
||||
|
||||
mColFlags.set(x);
|
||||
mRowFlags.set(y);
|
||||
}
|
||||
|
||||
inline void BitMatrix::clearBit(const U32 x, const U32 y)
|
||||
{
|
||||
AssertFatal(x < mWidth && y < mHeight, "Error, out of bounds bit!");
|
||||
|
||||
U8* pRow = &mBits[y * mRowByteWidth];
|
||||
|
||||
U8* pByte = &pRow[x >> 3];
|
||||
*pByte &= ~(1 << (x & 0x7));
|
||||
}
|
||||
|
||||
inline bool BitMatrix::isSet(const U32 x, const U32 y) const
|
||||
{
|
||||
AssertFatal(x < mWidth && y < mHeight, "Error, out of bounds bit!");
|
||||
|
||||
U8* pRow = &mBits[y * mRowByteWidth];
|
||||
|
||||
U8* pByte = &pRow[x >> 3];
|
||||
return (*pByte & (1 << (x & 0x7))) != 0;
|
||||
}
|
||||
|
||||
inline bool BitMatrix::isAnySetCol(const U32 x)
|
||||
{
|
||||
AssertFatal(x < mWidth, "Error, out of bounds column!");
|
||||
|
||||
return mColFlags.test(x);
|
||||
}
|
||||
|
||||
inline bool BitMatrix::isAnySetRow(const U32 y)
|
||||
{
|
||||
AssertFatal(y < mHeight, "Error, out of bounds row!");
|
||||
|
||||
return mRowFlags.test(y);
|
||||
}
|
||||
} // namespace DTS
|
109
lib/dtsSDKPlus/nvStripWrap.cpp
Executable file
109
lib/dtsSDKPlus/nvStripWrap.cpp
Executable file
@ -0,0 +1,109 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable : 4786 4018)
|
||||
#endif
|
||||
|
||||
#include "nvStripWrap.h"
|
||||
#include "nvtristrip/NvTriStrip.h"
|
||||
#include "nvtristrip/NvTriStripObjects.h"
|
||||
|
||||
namespace DTS
|
||||
{
|
||||
|
||||
void nvMakeStrips(std::vector<Primitive> & primitives, std::vector<U16> & indices, S32 cacheSize, U32 matIndex)
|
||||
{
|
||||
// incoming indices may have some "holes" in them...e.g., may have index 1,2,3,6,7 only
|
||||
// map to 0..N-1
|
||||
std::vector<S16> map;
|
||||
std::vector<S16> remap;
|
||||
U16 next=0;
|
||||
S32 i;
|
||||
for (i=0; i<indices.size(); i++)
|
||||
{
|
||||
while (indices[i]>=map.size())
|
||||
map.push_back(-1);
|
||||
if (map[indices[i]]<0)
|
||||
{
|
||||
map[indices[i]]=next++;
|
||||
remap.push_back(indices[i]);
|
||||
}
|
||||
indices[i] = map[indices[i]];
|
||||
}
|
||||
|
||||
PrimitiveGroup * primGroups;
|
||||
U16 numGroups;
|
||||
GenerateStrips(&indices[0],indices.size(),&primGroups,&numGroups);
|
||||
|
||||
for (i=0; i<numGroups; i++)
|
||||
{
|
||||
assert(primGroups[i].type == PT_STRIP);
|
||||
Primitive strip;
|
||||
strip.type = matIndex;
|
||||
strip.firstElement = indices.size();
|
||||
strip.numElements = primGroups[i].numIndices;
|
||||
primitives.push_back(strip);
|
||||
for (S32 j=0; j<primGroups[i].numIndices; j++)
|
||||
indices.push_back(primGroups[i].indices[j]);
|
||||
}
|
||||
delete [] primGroups;
|
||||
|
||||
// remap indices...
|
||||
for (i=0; i<indices.size(); i++)
|
||||
indices[i] = remap[indices[i]];
|
||||
}
|
||||
|
||||
void nvStripWrap(std::vector<Primitive> & faces, std::vector<U16> & indices, S32 cacheSize)
|
||||
{
|
||||
// set stripper parameters...we'll just set some default ones
|
||||
SetCacheSize(cacheSize);
|
||||
SetStitchStrips(true); // engine can handle this, so let it
|
||||
SetListsOnly(false); // engine can handle this, so let it
|
||||
SetMinStripSize(0); // engine can handle this, so let it
|
||||
|
||||
// main strip loop...
|
||||
U32 start, end, i;
|
||||
std::vector<Primitive> someStrips;
|
||||
std::vector<Primitive> retStrips;
|
||||
std::vector<U16> someIndices;
|
||||
std::vector<U16> retIndices;
|
||||
for (start = 0; start<faces.size(); start=end)
|
||||
{
|
||||
for (end=start; end<faces.size() && faces[start].type==faces[end].type; end++)
|
||||
;
|
||||
|
||||
// copy start to end faces into new list -- this is so we end up doing less copying
|
||||
// down the road (when we are doing the look ahead simulation)
|
||||
someStrips.clear();
|
||||
someIndices.clear();
|
||||
for (i=start;i<end;i++)
|
||||
{
|
||||
someIndices.push_back(indices[faces[i].firstElement + 0]);
|
||||
someIndices.push_back(indices[faces[i].firstElement + 1]);
|
||||
someIndices.push_back(indices[faces[i].firstElement + 2]);
|
||||
}
|
||||
|
||||
U32 matIndex = faces[start].type ^ (Primitive::Triangles|Primitive::Strip);
|
||||
nvMakeStrips(someStrips,someIndices,cacheSize,matIndex);
|
||||
|
||||
// now move strips and indices into larger list
|
||||
S32 startStrips = retStrips.size();
|
||||
retStrips.resize(startStrips+someStrips.size());
|
||||
S32 startIndices = retIndices.size();
|
||||
retIndices.resize(startIndices+someIndices.size());
|
||||
memcpy(&retStrips[startStrips],&someStrips[0],someStrips.size()*sizeof(Primitive));
|
||||
memcpy(&retIndices[startIndices],&someIndices[0],someIndices.size()*sizeof(U16));
|
||||
// now adjust start of new strips
|
||||
for (i=startStrips; i<retStrips.size(); i++)
|
||||
retStrips[i].firstElement += startIndices;
|
||||
}
|
||||
indices = retIndices;
|
||||
faces = retStrips;
|
||||
}
|
||||
|
||||
}; // namespace DTS
|
||||
|
18
lib/dtsSDKPlus/nvStripWrap.h
Executable file
18
lib/dtsSDKPlus/nvStripWrap.h
Executable file
@ -0,0 +1,18 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "DTSTypes.h"
|
||||
#include "DTSShape.h"
|
||||
#include "DTSMesh.h"
|
||||
#include "DTSPlusTypes.h"
|
||||
#include "DTSUtil.h"
|
||||
|
||||
|
||||
namespace DTS
|
||||
{
|
||||
|
||||
extern void nvStripWrap(std::vector<Primitive> &, std::vector<U16> & indices, S32 cahceSize);
|
||||
|
||||
};
|
310
lib/dtsSDKPlus/nvtristrip/NvTriStrip.cpp
Executable file
310
lib/dtsSDKPlus/nvtristrip/NvTriStrip.cpp
Executable file
@ -0,0 +1,310 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copied from NVidia Triangle Strip SDK, available from NVidia website
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable : 4786)
|
||||
#endif
|
||||
|
||||
#include "NvTriStripObjects.h"
|
||||
#include "NvTriStrip.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
//private data
|
||||
static U32 cacheSize = CACHESIZE_GEFORCE1_2;
|
||||
static bool bStitchStrips = true;
|
||||
static U32 minStripSize = 0;
|
||||
static bool bListsOnly = false;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// SetListsOnly()
|
||||
//
|
||||
// If set to true, will return an optimized list, with no strips at all.
|
||||
//
|
||||
// Default value: false
|
||||
//
|
||||
void SetListsOnly(const bool _bListsOnly)
|
||||
{
|
||||
bListsOnly = _bListsOnly;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// SetCacheSize()
|
||||
//
|
||||
// Sets the cache size which the stripfier uses to optimize the data.
|
||||
// Controls the length of the generated individual strips.
|
||||
// This is the "actual" cache size, so 24 for GeForce3 and 16 for GeForce1/2
|
||||
// You may want to play around with this number to tweak performance.
|
||||
//
|
||||
// Default value: 16
|
||||
//
|
||||
void SetCacheSize(const U32 _cacheSize)
|
||||
{
|
||||
cacheSize = _cacheSize;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// SetStitchStrips()
|
||||
//
|
||||
// bool to indicate whether to stitch together strips into one huge strip or not.
|
||||
// If set to true, you'll get back one huge strip stitched together using degenerate
|
||||
// triangles.
|
||||
// If set to false, you'll get back a large number of separate strips.
|
||||
//
|
||||
// Default value: true
|
||||
//
|
||||
void SetStitchStrips(const bool _bStitchStrips)
|
||||
{
|
||||
bStitchStrips = _bStitchStrips;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// SetMinStripSize()
|
||||
//
|
||||
// Sets the minimum acceptable size for a strip, in triangles.
|
||||
// All strips generated which are shorter than this will be thrown into one big, separate list.
|
||||
//
|
||||
// Default value: 0
|
||||
//
|
||||
void SetMinStripSize(const U32 _minStripSize)
|
||||
{
|
||||
minStripSize = _minStripSize;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// GenerateStrips()
|
||||
//
|
||||
// in_indices: input index list, the indices you would use to render
|
||||
// in_numIndices: number of entries in in_indices
|
||||
// primGroups: array of optimized/stripified PrimitiveGroups
|
||||
// numGroups: number of groups returned
|
||||
//
|
||||
// Be sure to call delete[] on the returned primGroups to avoid leaking mem
|
||||
//
|
||||
void GenerateStrips(const U16* in_indices, const U32 in_numIndices,
|
||||
PrimitiveGroup** primGroups, U16* numGroups)
|
||||
{
|
||||
//put data in format that the stripifier likes
|
||||
WordVec tempIndices;
|
||||
tempIndices.resize(in_numIndices);
|
||||
U16 maxIndex = 0;
|
||||
U32 i;
|
||||
for(i = 0; i < in_numIndices; i++)
|
||||
{
|
||||
tempIndices[i] = in_indices[i];
|
||||
if(in_indices[i] > maxIndex)
|
||||
maxIndex = in_indices[i];
|
||||
}
|
||||
NvStripInfoVec tempStrips;
|
||||
NvFaceInfoVec tempFaces;
|
||||
|
||||
NvStripifier stripifier;
|
||||
|
||||
//do actual stripification
|
||||
stripifier.Stripify(tempIndices, cacheSize, minStripSize, maxIndex, tempStrips, tempFaces);
|
||||
|
||||
//stitch strips together
|
||||
IntVec stripIndices;
|
||||
U32 numSeparateStrips = 0;
|
||||
|
||||
if(bListsOnly)
|
||||
{
|
||||
//if we're outputting only lists, we're done
|
||||
*numGroups = 1;
|
||||
(*primGroups) = new PrimitiveGroup[*numGroups];
|
||||
PrimitiveGroup* primGroupArray = *primGroups;
|
||||
|
||||
//count the total number of indices
|
||||
U32 numIndices = 0;
|
||||
U32 i;
|
||||
for(i = 0; i < tempStrips.size(); i++)
|
||||
{
|
||||
numIndices += tempStrips[i]->m_faces.size() * 3;
|
||||
}
|
||||
|
||||
//add in the list
|
||||
numIndices += tempFaces.size() * 3;
|
||||
|
||||
primGroupArray[0].type = PT_LIST;
|
||||
primGroupArray[0].numIndices = numIndices;
|
||||
primGroupArray[0].indices = new U16[numIndices];
|
||||
|
||||
//do strips
|
||||
U32 indexCtr = 0;
|
||||
for(U32 k = 0; k < tempStrips.size(); k++)
|
||||
{
|
||||
for(U32 j = 0; j < tempStrips[i]->m_faces.size(); j++)
|
||||
{
|
||||
//degenerates are of no use with lists
|
||||
if(!NvStripifier::IsDegenerate(tempStrips[i]->m_faces[j]))
|
||||
{
|
||||
primGroupArray[0].indices[indexCtr++] = tempStrips[k]->m_faces[j]->m_v0;
|
||||
primGroupArray[0].indices[indexCtr++] = tempStrips[k]->m_faces[j]->m_v1;
|
||||
primGroupArray[0].indices[indexCtr++] = tempStrips[k]->m_faces[j]->m_v2;
|
||||
}
|
||||
else
|
||||
{
|
||||
//we've removed a tri, reduce the number of indices
|
||||
primGroupArray[0].numIndices -= 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//do lists
|
||||
for(i = 0; i < tempFaces.size(); i++)
|
||||
{
|
||||
primGroupArray[0].indices[indexCtr++] = tempFaces[i]->m_v0;
|
||||
primGroupArray[0].indices[indexCtr++] = tempFaces[i]->m_v1;
|
||||
primGroupArray[0].indices[indexCtr++] = tempFaces[i]->m_v2;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
stripifier.CreateStrips(tempStrips, stripIndices, bStitchStrips, numSeparateStrips);
|
||||
|
||||
//if we're stitching strips together, we better get back only one strip from CreateStrips()
|
||||
assert( (bStitchStrips && (numSeparateStrips == 1)) || !bStitchStrips);
|
||||
|
||||
//convert to output format
|
||||
*numGroups = numSeparateStrips; //for the strips
|
||||
if(tempFaces.size() != 0)
|
||||
(*numGroups)++; //we've got a list as well, increment
|
||||
(*primGroups) = new PrimitiveGroup[*numGroups];
|
||||
|
||||
PrimitiveGroup* primGroupArray = *primGroups;
|
||||
|
||||
//first, the strips
|
||||
S32 startingLoc = 0;
|
||||
for(U32 stripCtr = 0; stripCtr < numSeparateStrips; stripCtr++)
|
||||
{
|
||||
S32 stripLength = 0;
|
||||
|
||||
if(!bStitchStrips)
|
||||
{
|
||||
//if we've got multiple strips, we need to figure out the correct length
|
||||
U32 i;
|
||||
for(i = startingLoc; i < stripIndices.size(); i++)
|
||||
{
|
||||
if(stripIndices[i] == -1)
|
||||
break;
|
||||
}
|
||||
|
||||
stripLength = i - startingLoc;
|
||||
}
|
||||
else
|
||||
stripLength = stripIndices.size();
|
||||
|
||||
primGroupArray[stripCtr].type = PT_STRIP;
|
||||
primGroupArray[stripCtr].indices = new U16[stripLength];
|
||||
primGroupArray[stripCtr].numIndices = stripLength;
|
||||
|
||||
S32 indexCtr = 0;
|
||||
for(S32 i = startingLoc; i < stripLength + startingLoc; i++)
|
||||
primGroupArray[stripCtr].indices[indexCtr++] = stripIndices[i];
|
||||
|
||||
//we add 1 to account for the -1 separating strips
|
||||
//this doesn't break the stitched case since we'll exit the loop
|
||||
startingLoc += stripLength + 1;
|
||||
}
|
||||
|
||||
//next, the list
|
||||
if(tempFaces.size() != 0)
|
||||
{
|
||||
S32 faceGroupLoc = (*numGroups) - 1; //the face group is the last one
|
||||
primGroupArray[faceGroupLoc].type = PT_LIST;
|
||||
primGroupArray[faceGroupLoc].indices = new U16[tempFaces.size() * 3];
|
||||
primGroupArray[faceGroupLoc].numIndices = tempFaces.size() * 3;
|
||||
S32 indexCtr = 0;
|
||||
for(U32 i = 0; i < tempFaces.size(); i++)
|
||||
{
|
||||
primGroupArray[faceGroupLoc].indices[indexCtr++] = tempFaces[i]->m_v0;
|
||||
primGroupArray[faceGroupLoc].indices[indexCtr++] = tempFaces[i]->m_v1;
|
||||
primGroupArray[faceGroupLoc].indices[indexCtr++] = tempFaces[i]->m_v2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//clean up everything
|
||||
|
||||
//delete strips
|
||||
for(i = 0; i < tempStrips.size(); i++)
|
||||
{
|
||||
for(U32 j = 0; j < tempStrips[i]->m_faces.size(); j++)
|
||||
{
|
||||
delete tempStrips[i]->m_faces[j];
|
||||
tempStrips[i]->m_faces[j] = NULL;
|
||||
}
|
||||
delete tempStrips[i];
|
||||
tempStrips[i] = NULL;
|
||||
}
|
||||
|
||||
//delete faces
|
||||
for(i = 0; i < tempFaces.size(); i++)
|
||||
{
|
||||
delete tempFaces[i];
|
||||
tempFaces[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// RemapIndices()
|
||||
//
|
||||
// Function to remap your indices to improve spatial locality in your vertex buffer.
|
||||
//
|
||||
// in_primGroups: array of PrimitiveGroups you want remapped
|
||||
// numGroups: number of entries in in_primGroups
|
||||
// numVerts: number of vertices in your vertex buffer, also can be thought of as the range
|
||||
// of acceptable values for indices in your primitive groups.
|
||||
// remappedGroups: array of remapped PrimitiveGroups
|
||||
//
|
||||
// Note that, according to the remapping handed back to you, you must reorder your
|
||||
// vertex buffer.
|
||||
//
|
||||
void RemapIndices(const PrimitiveGroup* in_primGroups, const U16 numGroups,
|
||||
const U16 numVerts, PrimitiveGroup** remappedGroups)
|
||||
{
|
||||
(*remappedGroups) = new PrimitiveGroup[numGroups];
|
||||
|
||||
//caches oldIndex --> newIndex conversion
|
||||
S32 *indexCache;
|
||||
indexCache = new S32[numVerts];
|
||||
memset(indexCache, -1, sizeof(S32)*numVerts);
|
||||
|
||||
//loop over primitive groups
|
||||
U32 indexCtr = 0;
|
||||
for(S32 i = 0; i < numGroups; i++)
|
||||
{
|
||||
U32 numIndices = in_primGroups[i].numIndices;
|
||||
|
||||
//init remapped group
|
||||
(*remappedGroups)[i].type = in_primGroups[i].type;
|
||||
(*remappedGroups)[i].numIndices = numIndices;
|
||||
(*remappedGroups)[i].indices = new U16[numIndices];
|
||||
|
||||
for(U32 j = 0; j < numIndices; j++)
|
||||
{
|
||||
S32 cachedIndex = indexCache[in_primGroups[i].indices[j]];
|
||||
if(cachedIndex == -1) //we haven't seen this index before
|
||||
{
|
||||
//point to "last" vertex in VB
|
||||
(*remappedGroups)[i].indices[j] = indexCtr;
|
||||
|
||||
//add to index cache, increment
|
||||
indexCache[in_primGroups[i].indices[j]] = indexCtr++;
|
||||
}
|
||||
else
|
||||
{
|
||||
//we've seen this index before
|
||||
(*remappedGroups)[i].indices[j] = cachedIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delete[] indexCache;
|
||||
}
|
135
lib/dtsSDKPlus/nvtristrip/NvTriStrip.h
Executable file
135
lib/dtsSDKPlus/nvtristrip/NvTriStrip.h
Executable file
@ -0,0 +1,135 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copied from NVidia Triangle Strip SDK, available from NVidia website
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef NVTRISTRIP_H
|
||||
#define NVTRISTRIP_H
|
||||
|
||||
#ifndef NULL
|
||||
#define NULL 0
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Mod for Torque, 1/3/04
|
||||
// Compile directly in Torque
|
||||
//#pragma comment(lib, "nvtristrip")
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Public interface for stripifier
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//GeForce1 and 2 cache size
|
||||
#define CACHESIZE_GEFORCE1_2 16
|
||||
|
||||
//GeForce3 cache size
|
||||
#define CACHESIZE_GEFORCE3 24
|
||||
|
||||
enum PrimType
|
||||
{
|
||||
PT_LIST,
|
||||
PT_STRIP,
|
||||
PT_FAN
|
||||
};
|
||||
|
||||
struct PrimitiveGroup
|
||||
{
|
||||
PrimType type;
|
||||
U32 numIndices;
|
||||
U16* indices;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
PrimitiveGroup() : type(PT_STRIP), numIndices(0), indices(NULL) {}
|
||||
~PrimitiveGroup()
|
||||
{
|
||||
if(indices)
|
||||
delete[] indices;
|
||||
indices = NULL;
|
||||
}
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// SetCacheSize()
|
||||
//
|
||||
// Sets the cache size which the stripfier uses to optimize the data.
|
||||
// Controls the length of the generated individual strips.
|
||||
// This is the "actual" cache size, so 24 for GeForce3 and 16 for GeForce1/2
|
||||
// You may want to play around with this number to tweak performance.
|
||||
//
|
||||
// Default value: 16
|
||||
//
|
||||
void SetCacheSize(const U32 cacheSize);
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// SetStitchStrips()
|
||||
//
|
||||
// bool to indicate whether to stitch together strips into one huge strip or not.
|
||||
// If set to true, you'll get back one huge strip stitched together using degenerate
|
||||
// triangles.
|
||||
// If set to false, you'll get back a large number of separate strips.
|
||||
//
|
||||
// Default value: true
|
||||
//
|
||||
void SetStitchStrips(const bool bStitchStrips);
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// SetMinStripSize()
|
||||
//
|
||||
// Sets the minimum acceptable size for a strip, in triangles.
|
||||
// All strips generated which are shorter than this will be thrown into one big, separate list.
|
||||
//
|
||||
// Default value: 0
|
||||
//
|
||||
void SetMinStripSize(const U32 minSize);
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// SetListsOnly()
|
||||
//
|
||||
// If set to true, will return an optimized list, with no strips at all.
|
||||
//
|
||||
// Default value: false
|
||||
//
|
||||
void SetListsOnly(const bool bListsOnly);
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// GenerateStrips()
|
||||
//
|
||||
// in_indices: input index list, the indices you would use to render
|
||||
// in_numIndices: number of entries in in_indices
|
||||
// primGroups: array of optimized/stripified PrimitiveGroups
|
||||
// numGroups: number of groups returned
|
||||
//
|
||||
// Be sure to call delete[] on the returned primGroups to avoid leaking mem
|
||||
//
|
||||
void GenerateStrips(const U16* in_indices, const U32 in_numIndices,
|
||||
PrimitiveGroup** primGroups, U16* numGroups);
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// RemapIndices()
|
||||
//
|
||||
// Function to remap your indices to improve spatial locality in your vertex buffer.
|
||||
//
|
||||
// in_primGroups: array of PrimitiveGroups you want remapped
|
||||
// numGroups: number of entries in in_primGroups
|
||||
// numVerts: number of vertices in your vertex buffer, also can be thought of as the range
|
||||
// of acceptable values for indices in your primitive groups.
|
||||
// remappedGroups: array of remapped PrimitiveGroups
|
||||
//
|
||||
// Note that, according to the remapping handed back to you, you must reorder your
|
||||
// vertex buffer.
|
||||
//
|
||||
// Credit goes to the MS Xbox crew for the idea for this interface.
|
||||
//
|
||||
void RemapIndices(const PrimitiveGroup* in_primGroups, const U16 numGroups,
|
||||
const U16 numVerts, PrimitiveGroup** remappedGroups);
|
||||
|
||||
#endif
|
1750
lib/dtsSDKPlus/nvtristrip/NvTriStripObjects.cpp
Executable file
1750
lib/dtsSDKPlus/nvtristrip/NvTriStripObjects.cpp
Executable file
File diff suppressed because it is too large
Load Diff
255
lib/dtsSDKPlus/nvtristrip/NvTriStripObjects.h
Executable file
255
lib/dtsSDKPlus/nvtristrip/NvTriStripObjects.h
Executable file
@ -0,0 +1,255 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copied from NVidia Triangle Strip SDK, available from NVidia website
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef NV_TRISTRIP_OBJECTS_H
|
||||
#define NV_TRISTRIP_OBJECTS_H
|
||||
|
||||
#include <assert.h>
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
typedef U32 UINT;
|
||||
typedef short WORD;
|
||||
#endif
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include "VertexCache.h"
|
||||
#include "../DTSPlusTypes.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Types defined for stripification
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct MyVertex {
|
||||
F32 x, y, z;
|
||||
F32 nx, ny, nz;
|
||||
};
|
||||
|
||||
typedef MyVertex MyVector;
|
||||
|
||||
struct MyFace {
|
||||
S32 v1, v2, v3;
|
||||
F32 nx, ny, nz;
|
||||
};
|
||||
|
||||
|
||||
class NvFaceInfo {
|
||||
public:
|
||||
|
||||
// vertex indices
|
||||
NvFaceInfo(S32 v0, S32 v1, S32 v2){
|
||||
m_v0 = v0; m_v1 = v1; m_v2 = v2;
|
||||
m_stripId = -1;
|
||||
m_testStripId = -1;
|
||||
m_experimentId = -1;
|
||||
}
|
||||
|
||||
// data members are left public
|
||||
S32 m_v0, m_v1, m_v2;
|
||||
S32 m_stripId; // real strip Id
|
||||
S32 m_testStripId; // strip Id in an experiment
|
||||
S32 m_experimentId; // in what experiment was it given an experiment Id?
|
||||
};
|
||||
|
||||
// nice and dumb edge class that points knows its
|
||||
// indices, the two faces, and the next edge using
|
||||
// the lesser of the indices
|
||||
class NvEdgeInfo {
|
||||
public:
|
||||
|
||||
// constructor puts 1 ref on us
|
||||
NvEdgeInfo (S32 v0, S32 v1){
|
||||
m_v0 = v0;
|
||||
m_v1 = v1;
|
||||
m_face0 = NULL;
|
||||
m_face1 = NULL;
|
||||
m_nextV0 = NULL;
|
||||
m_nextV1 = NULL;
|
||||
|
||||
// we will appear in 2 lists. this is a good
|
||||
// way to make sure we delete it the second time
|
||||
// we hit it in the edge infos
|
||||
m_refCount = 2;
|
||||
|
||||
}
|
||||
|
||||
// ref and unref
|
||||
void Unref () { if (--m_refCount == 0) delete this; }
|
||||
|
||||
// data members are left public
|
||||
UINT m_refCount;
|
||||
NvFaceInfo *m_face0, *m_face1;
|
||||
S32 m_v0, m_v1;
|
||||
NvEdgeInfo *m_nextV0, *m_nextV1;
|
||||
};
|
||||
|
||||
|
||||
// This class is a quick summary of parameters used
|
||||
// to begin a triangle strip. Some operations may
|
||||
// want to create lists of such items, so they were
|
||||
// pulled out into a class
|
||||
class NvStripStartInfo {
|
||||
public:
|
||||
NvStripStartInfo(NvFaceInfo *startFace, NvEdgeInfo *startEdge, bool toV1){
|
||||
m_startFace = startFace;
|
||||
m_startEdge = startEdge;
|
||||
m_toV1 = toV1;
|
||||
}
|
||||
NvFaceInfo *m_startFace;
|
||||
NvEdgeInfo *m_startEdge;
|
||||
bool m_toV1;
|
||||
};
|
||||
|
||||
|
||||
typedef std::vector<NvFaceInfo*> NvFaceInfoVec;
|
||||
typedef std::list <NvFaceInfo*> NvFaceInfoList;
|
||||
typedef std::list <NvFaceInfoVec*> NvStripList;
|
||||
typedef std::vector<NvEdgeInfo*> NvEdgeInfoVec;
|
||||
|
||||
typedef std::vector<WORD> WordVec;
|
||||
typedef std::vector<S32> IntVec;
|
||||
typedef std::vector<MyVertex> MyVertexVec;
|
||||
typedef std::vector<MyFace> MyFaceVec;
|
||||
|
||||
template<class T>
|
||||
inline void SWAP(T& first, T& second)
|
||||
{
|
||||
T temp = first;
|
||||
first = second;
|
||||
second = temp;
|
||||
}
|
||||
|
||||
// This is a summary of a strip that has been built
|
||||
class NvStripInfo {
|
||||
public:
|
||||
|
||||
// A little information about the creation of the triangle strips
|
||||
NvStripInfo(const NvStripStartInfo &startInfo, S32 stripId, S32 experimentId = -1) :
|
||||
m_startInfo(startInfo)
|
||||
{
|
||||
m_stripId = stripId;
|
||||
m_experimentId = experimentId;
|
||||
visited = false;
|
||||
m_numDegenerates = 0;
|
||||
}
|
||||
|
||||
// This is an experiment if the experiment id is >= 0
|
||||
inline bool IsExperiment () const { return m_experimentId >= 0; }
|
||||
|
||||
inline bool IsInStrip (const NvFaceInfo *faceInfo) const
|
||||
{
|
||||
if(faceInfo == NULL)
|
||||
return false;
|
||||
|
||||
return (m_experimentId >= 0 ? faceInfo->m_testStripId == m_stripId : faceInfo->m_stripId == m_stripId);
|
||||
}
|
||||
|
||||
bool SharesEdge(const NvFaceInfo* faceInfo, NvEdgeInfoVec &edgeInfos);
|
||||
|
||||
// take the given forward and backward strips and combine them together
|
||||
void Combine(const NvFaceInfoVec &forward, const NvFaceInfoVec &backward);
|
||||
|
||||
//returns true if the face is "unique", i.e. has a vertex which doesn't exist in the faceVec
|
||||
bool Unique(NvFaceInfoVec& faceVec, NvFaceInfo* face);
|
||||
|
||||
// mark the triangle as taken by this strip
|
||||
bool IsMarked (NvFaceInfo *faceInfo);
|
||||
void MarkTriangle(NvFaceInfo *faceInfo);
|
||||
|
||||
// build the strip
|
||||
void Build(NvEdgeInfoVec &edgeInfos, NvFaceInfoVec &faceInfos);
|
||||
|
||||
// public data members
|
||||
NvStripStartInfo m_startInfo;
|
||||
NvFaceInfoVec m_faces;
|
||||
S32 m_stripId;
|
||||
S32 m_experimentId;
|
||||
|
||||
bool visited;
|
||||
|
||||
S32 m_numDegenerates;
|
||||
};
|
||||
|
||||
typedef std::vector<NvStripInfo*> NvStripInfoVec;
|
||||
|
||||
|
||||
//The actual stripifier
|
||||
class NvStripifier {
|
||||
public:
|
||||
|
||||
// Constructor
|
||||
NvStripifier();
|
||||
~NvStripifier();
|
||||
|
||||
//the target vertex cache size, the structure to place the strips in, and the input indices
|
||||
void Stripify(const WordVec &in_indices, const S32 in_cacheSize, const S32 in_minStripLength,
|
||||
const U16 maxIndex, NvStripInfoVec &allStrips, NvFaceInfoVec &allFaces);
|
||||
void CreateStrips(const NvStripInfoVec& allStrips, IntVec& stripIndices, const bool bStitchStrips, U32& numSeparateStrips);
|
||||
|
||||
static S32 GetUniqueVertexInB(NvFaceInfo *faceA, NvFaceInfo *faceB);
|
||||
//static S32 GetSharedVertex(NvFaceInfo *faceA, NvFaceInfo *faceB);
|
||||
static void GetSharedVertices(NvFaceInfo *faceA, NvFaceInfo *faceB, S32* vertex0, S32* vertex1);
|
||||
|
||||
static bool IsDegenerate(const NvFaceInfo* face);
|
||||
|
||||
protected:
|
||||
|
||||
WordVec indices;
|
||||
S32 cacheSize;
|
||||
S32 minStripLength;
|
||||
F32 meshJump;
|
||||
bool bFirstTimeResetPoint;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Big mess of functions called during stripification
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//********************
|
||||
bool IsMoneyFace(const NvFaceInfo& face);
|
||||
bool FaceContainsIndex(const NvFaceInfo& face, const U32 index);
|
||||
|
||||
bool IsCW(NvFaceInfo *faceInfo, S32 v0, S32 v1);
|
||||
bool NextIsCW(const S32 numIndices);
|
||||
|
||||
bool IsDegenerate(const U16 v0, const U16 v1, const U16 v2);
|
||||
|
||||
static S32 GetNextIndex(const WordVec &indices, NvFaceInfo *face);
|
||||
static NvEdgeInfo *FindEdgeInfo(NvEdgeInfoVec &edgeInfos, S32 v0, S32 v1);
|
||||
static NvFaceInfo *FindOtherFace(NvEdgeInfoVec &edgeInfos, S32 v0, S32 v1, NvFaceInfo *faceInfo);
|
||||
NvFaceInfo *FindGoodResetPoint(NvFaceInfoVec &faceInfos, NvEdgeInfoVec &edgeInfos);
|
||||
|
||||
void FindAllStrips(NvStripInfoVec &allStrips, NvFaceInfoVec &allFaceInfos, NvEdgeInfoVec &allEdgeInfos, S32 numSamples);
|
||||
void SplitUpStripsAndOptimize(NvStripInfoVec &allStrips, NvStripInfoVec &outStrips, NvEdgeInfoVec& edgeInfos, NvFaceInfoVec& outFaceList);
|
||||
void RemoveSmallStrips(NvStripInfoVec& allStrips, NvStripInfoVec& allBigStrips, NvFaceInfoVec& faceList);
|
||||
|
||||
bool FindTraversal(NvFaceInfoVec &faceInfos, NvEdgeInfoVec &edgeInfos, NvStripInfo *strip, NvStripStartInfo &startInfo);
|
||||
S32 CountRemainingTris(std::list<NvStripInfo*>::iterator iter, std::list<NvStripInfo*>::iterator end);
|
||||
|
||||
void CommitStrips(NvStripInfoVec &allStrips, const NvStripInfoVec &strips);
|
||||
|
||||
F32 AvgStripSize(const NvStripInfoVec &strips);
|
||||
S32 FindStartPoint(NvFaceInfoVec &faceInfos, NvEdgeInfoVec &edgeInfos);
|
||||
|
||||
void UpdateCacheStrip(VertexCache* vcache, NvStripInfo* strip);
|
||||
void UpdateCacheFace(VertexCache* vcache, NvFaceInfo* face);
|
||||
F32 CalcNumHitsStrip(VertexCache* vcache, NvStripInfo* strip);
|
||||
S32 CalcNumHitsFace(VertexCache* vcache, NvFaceInfo* face);
|
||||
S32 NumNeighbors(NvFaceInfo* face, NvEdgeInfoVec& edgeInfoVec);
|
||||
|
||||
void BuildStripifyInfo(NvFaceInfoVec &faceInfos, NvEdgeInfoVec &edgeInfos, const U16 maxIndex);
|
||||
bool AlreadyExists(NvFaceInfo* faceInfo, NvFaceInfoVec& faceInfos);
|
||||
|
||||
// let our strip info classes and the other classes get
|
||||
// to these protected stripificaton methods if they want
|
||||
friend class NvStripInfo;
|
||||
};
|
||||
|
||||
#endif
|
32
lib/dtsSDKPlus/nvtristrip/README.txt
Executable file
32
lib/dtsSDKPlus/nvtristrip/README.txt
Executable file
@ -0,0 +1,32 @@
|
||||
README for NvTriStrip, library version
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
To use:
|
||||
-#include "NvTriStrip.h"
|
||||
-put nvtristrip.lib in your library path (the pragma in nvtristrip.h will automatically look for the library).
|
||||
|
||||
Check out NvTriStrip.h for the interface.
|
||||
|
||||
See the StripTest source code (in function LoadXFileStripped) for an example of using the library.
|
||||
|
||||
Features:
|
||||
-generates strips from arbitrary geometry.
|
||||
-flexibly optimizes for post TnL vertex caches (16 on GeForce1/2, 24 on GeForce3).
|
||||
-can stitch together strips using degenerate triangles, or not.
|
||||
-can output lists instead of strips.
|
||||
-can optionally throw excessively small strips into a list instead.
|
||||
-can remap indices to improve spatial locality in your vertex buffers.
|
||||
|
||||
On cache sizes:
|
||||
Note that it's better to UNDERESTIMATE the cache size instead of OVERESTIMATING.
|
||||
So, if you're targetting GeForce1, 2, and 3, be conservative and use the GeForce1_2 cache
|
||||
size, NOT the GeForce3 cache size.
|
||||
This will make sure you don't "blow" the cache of the GeForce1 and 2.
|
||||
Also note that the cache size you specify is the "actual" cache size, not the "effective"
|
||||
cache size you may have heard about. This is 16 for GeForce1 and 2, and 24 for GeForce3.
|
||||
|
||||
Credit goes to Curtis Beeson and Joe Demers for the basis for this stripifier and to Jason Regier and
|
||||
Jon Stone at Blizzard for providing a much cleaner version of CreateStrips().
|
||||
|
||||
Questions/comments email cem@nvidia.com
|
||||
|
86
lib/dtsSDKPlus/nvtristrip/VertexCache.h
Executable file
86
lib/dtsSDKPlus/nvtristrip/VertexCache.h
Executable file
@ -0,0 +1,86 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copied from NVidia Triangle Strip SDK, available from NVidia website
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef VERTEX_CACHE_H
|
||||
#define VERTEX_CACHE_H
|
||||
|
||||
#include "../DTSPlusTypes.h"
|
||||
|
||||
class VertexCache
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
VertexCache(S32 size)
|
||||
{
|
||||
numEntries = size;
|
||||
|
||||
entries = new S32[numEntries];
|
||||
|
||||
for(S32 i = 0; i < numEntries; i++)
|
||||
entries[i] = -1;
|
||||
}
|
||||
|
||||
VertexCache() { VertexCache(16); }
|
||||
~VertexCache() { delete[] entries; entries = 0; }
|
||||
|
||||
bool InCache(S32 entry)
|
||||
{
|
||||
bool returnVal = false;
|
||||
for(S32 i = 0; i < numEntries; i++)
|
||||
{
|
||||
if(entries[i] == entry)
|
||||
{
|
||||
returnVal = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return returnVal;
|
||||
}
|
||||
|
||||
S32 AddEntry(S32 entry)
|
||||
{
|
||||
S32 removed;
|
||||
|
||||
removed = entries[numEntries - 1];
|
||||
|
||||
//push everything right one
|
||||
for(S32 i = numEntries - 2; i >= 0; i--)
|
||||
{
|
||||
entries[i + 1] = entries[i];
|
||||
}
|
||||
|
||||
entries[0] = entry;
|
||||
|
||||
return removed;
|
||||
}
|
||||
|
||||
void Clear()
|
||||
{
|
||||
memset(entries, -1, sizeof(S32) * numEntries);
|
||||
}
|
||||
|
||||
void Copy(VertexCache* inVcache)
|
||||
{
|
||||
for(S32 i = 0; i < numEntries; i++)
|
||||
{
|
||||
inVcache->Set(i, entries[i]);
|
||||
}
|
||||
}
|
||||
|
||||
S32 At(S32 index) { return entries[index]; }
|
||||
void Set(S32 index, S32 value) { entries[index] = value; }
|
||||
|
||||
private:
|
||||
|
||||
S32 *entries;
|
||||
S32 numEntries;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
39
lib/dtsSDKPlus/sceneEnum.h
Executable file
39
lib/dtsSDKPlus/sceneEnum.h
Executable file
@ -0,0 +1,39 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef APP_SCENEENUM_H_
|
||||
#define APP_SCENEENUM_H_
|
||||
|
||||
namespace DTS
|
||||
{
|
||||
|
||||
class AppSceneEnum
|
||||
{
|
||||
protected:
|
||||
std::vector<AppMesh*> meshes;
|
||||
std::vector<AppMesh*> skins;
|
||||
std::vector<AppNode*> subTrees;
|
||||
std::vector<AppSequence*> sequences;
|
||||
AppNode * boundsNode;
|
||||
|
||||
ShapeMimic shapeMimic;
|
||||
|
||||
void setExportError(const char * errStr) { AppConfig::SetExportError(errStr); }
|
||||
const char * getError() { return AppConfig::GetExportError(); }
|
||||
bool isError() { return AppConfig::IsExportError(); }
|
||||
|
||||
void processNode(AppNode *);
|
||||
|
||||
public:
|
||||
AppSceneEnum();
|
||||
~AppSceneEnum();
|
||||
|
||||
virtual void enumScene() = 0;
|
||||
Shape * processScene();
|
||||
};
|
||||
|
||||
}; // namespace DTS
|
||||
|
||||
#endif // #define APP_SCENEENUM_H_
|
836
lib/dtsSDKPlus/stripper.cpp
Executable file
836
lib/dtsSDKPlus/stripper.cpp
Executable file
@ -0,0 +1,836 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable : 4786 4018)
|
||||
#endif
|
||||
|
||||
#include "stripper.h"
|
||||
#include "appConfig.h"
|
||||
|
||||
namespace DTS
|
||||
{
|
||||
|
||||
F32 Stripper::adjacencyWeight = 5;
|
||||
F32 Stripper::noswapWeight = 3;
|
||||
F32 Stripper::alreadyCachedWeight = 1;
|
||||
U32 Stripper::cacheSize = 16;
|
||||
U32 Stripper::simK = 5;
|
||||
|
||||
|
||||
Stripper::Stripper(std::vector<Primitive> & _faces, std::vector<U16> & _faceIndices)
|
||||
: faces(_faces), faceIndices(_faceIndices), adjacent(_faces.size(),_faces.size())
|
||||
{
|
||||
// keep track of whether face used in a strip yet
|
||||
used.resize(faces.size());
|
||||
for (S32 i=0; i<faces.size(); i++)
|
||||
used[i]=false;
|
||||
|
||||
clearCache();
|
||||
cacheMisses = 0; // definitely don't want to clear this every time we clear the cache!
|
||||
|
||||
bestLength = -1;
|
||||
}
|
||||
|
||||
Stripper::Stripper(Stripper & stripper)
|
||||
: faces(stripper.faces), faceIndices(stripper.faceIndices), adjacent(stripper.faces.size(),stripper.faces.size()),
|
||||
numAdjacent(stripper.numAdjacent), used(stripper.used), vertexCache(stripper.vertexCache),
|
||||
recentFaces(stripper.recentFaces), strips(stripper.strips), stripIndices(stripper.stripIndices)
|
||||
{
|
||||
currentFace = stripper.currentFace;
|
||||
limitStripLength = stripper.limitStripLength;
|
||||
bestLength = stripper.bestLength;
|
||||
cacheMisses = stripper.cacheMisses;
|
||||
adjacent.clearAllBits();
|
||||
for (S32 i=0; i<faces.size(); i++)
|
||||
for (S32 j=0; j<faces.size(); j++)
|
||||
if (stripper.adjacent.isSet(i,j))
|
||||
adjacent.setBit(i,j);
|
||||
}
|
||||
|
||||
Stripper::~Stripper()
|
||||
{
|
||||
}
|
||||
|
||||
void Stripper::testCache(S32 addedFace)
|
||||
{
|
||||
S32 * cache = &vertexCache.back();
|
||||
|
||||
// make sure last 3 verts on strip list are last 3 verts in cache
|
||||
U16 * indices = &stripIndices.back();
|
||||
if (*indices!=*cache || *(indices-1)!=*(cache-1) || *(indices-2)!=*(cache-2))
|
||||
{
|
||||
AppConfig::SetExportError("14", "Assertion failed when stripping (11)");
|
||||
return;
|
||||
}
|
||||
|
||||
if (addedFace>=0)
|
||||
{
|
||||
// make sure current face is still last 3 items in the cache
|
||||
Primitive & face = faces[addedFace];
|
||||
U32 idx0 = faceIndices[face.firstElement+0];
|
||||
U32 idx1 = faceIndices[face.firstElement+1];
|
||||
U32 idx2 = faceIndices[face.firstElement+2];
|
||||
if ( idx0!=*(cache) && idx0!=*(cache-1) && idx0!=*(cache-2))
|
||||
{
|
||||
AppConfig::SetExportError("14", "Assertion failed when stripping (8)");
|
||||
return;
|
||||
}
|
||||
if ( idx1!=*(cache) && idx1!=*(cache-1) && idx1!=*(cache-2))
|
||||
{
|
||||
AppConfig::SetExportError("14", "Assertion failed when stripping (9)");
|
||||
return;
|
||||
}
|
||||
if ( idx2!=*(cache) && idx2!=*(cache-1) && idx2!=*(cache-2))
|
||||
{
|
||||
AppConfig::SetExportError("14", "Assertion failed when stripping (10)");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Stripper::copyParams(Stripper * from)
|
||||
{
|
||||
limitStripLength = from->limitStripLength;
|
||||
}
|
||||
|
||||
void Stripper::makeStrips()
|
||||
{
|
||||
// if already encountered an error, then
|
||||
// we'll just go through the motions
|
||||
if (AppConfig::IsExportError()) return;
|
||||
|
||||
// main strip loop...
|
||||
U32 start, end, i, sz;
|
||||
std::vector<Primitive> someFaces;
|
||||
std::vector<U16> someIndices;
|
||||
for (start = 0; start<faces.size(); start=end)
|
||||
{
|
||||
for (end=start; end<faces.size() && faces[start].type==faces[end].type; end++)
|
||||
;
|
||||
|
||||
if (start==0 && end==faces.size())
|
||||
{
|
||||
// all same material, no need to copy anything
|
||||
makeStripsB();
|
||||
return;
|
||||
}
|
||||
|
||||
// copy start to end faces into new list -- this is so we end up doing less copying
|
||||
// down the road (when we are doing the look ahead simulation)
|
||||
someFaces.clear();
|
||||
someIndices.clear();
|
||||
for (i=start;i<end;i++)
|
||||
{
|
||||
someFaces.push_back(faces[i]);
|
||||
someFaces.back().firstElement = someIndices.size();
|
||||
someIndices.push_back(faceIndices[faces[i].firstElement + 0]);
|
||||
someIndices.push_back(faceIndices[faces[i].firstElement + 1]);
|
||||
someIndices.push_back(faceIndices[faces[i].firstElement + 2]);
|
||||
}
|
||||
|
||||
// strip these...
|
||||
Stripper someStrips(someFaces,someIndices);
|
||||
someStrips.copyParams(this);
|
||||
someStrips.makeStripsB();
|
||||
if (AppConfig::IsExportError()) return;
|
||||
|
||||
// copy these strips into our arrays
|
||||
sz = strips.size();
|
||||
strips.resize(sz+someStrips.strips.size());
|
||||
for (i=0; i<someStrips.strips.size(); i++)
|
||||
{
|
||||
strips[i+sz] = someStrips.strips[i];
|
||||
strips[i+sz].firstElement += stripIndices.size();
|
||||
}
|
||||
sz = stripIndices.size();
|
||||
stripIndices.resize(sz+someStrips.stripIndices.size());
|
||||
memcpy(&stripIndices[sz],&someStrips.stripIndices[0],someStrips.stripIndices.size()*sizeof(U16));
|
||||
|
||||
// update cache misses
|
||||
cacheMisses += someStrips.getCacheMisses();
|
||||
}
|
||||
}
|
||||
|
||||
void Stripper::makeStripsB()
|
||||
{
|
||||
// if already encountered an error, then
|
||||
// we'll just go through the motions
|
||||
if (AppConfig::IsExportError()) return;
|
||||
|
||||
// set adjacency info
|
||||
setAdjacency(0,faces.size());
|
||||
|
||||
while (1)
|
||||
{
|
||||
strips.push_back(Primitive());
|
||||
Primitive & strip = strips.back();
|
||||
strip.firstElement = stripIndices.size();
|
||||
strip.numElements = 0;
|
||||
if (faces[0].type & Primitive::NoMaterial)
|
||||
strip.type = Primitive::NoMaterial;
|
||||
else
|
||||
strip.type = faces[0].type & Primitive::MaterialMask;
|
||||
strip.type |= Primitive::Strip | Primitive::Indexed;
|
||||
|
||||
if (!startStrip(strip,0,faces.size()))
|
||||
{
|
||||
strips.pop_back();
|
||||
break;
|
||||
}
|
||||
|
||||
while (addStrip(strip,0,faces.size()));
|
||||
|
||||
// if already encountered an error, then
|
||||
// we'll just go through the motions
|
||||
if (AppConfig::IsExportError()) return;
|
||||
}
|
||||
|
||||
// let's make sure everything is legit up till here
|
||||
U32 i;
|
||||
for (i=0; i<faces.size(); i++)
|
||||
{
|
||||
if (!used[i])
|
||||
{
|
||||
AppConfig::SetExportError("14", "Assertion failed when stripping (1)");
|
||||
return;
|
||||
}
|
||||
}
|
||||
for (i=0; i<numAdjacent.size(); i++)
|
||||
{
|
||||
if (numAdjacent[i])
|
||||
{
|
||||
AppConfig::SetExportError("14", "Assertion failed when stripping (2)");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// make sure all faces were used, o.w. it's an error
|
||||
for (i=0; i<used.size(); i++)
|
||||
{
|
||||
if (!used[i])
|
||||
{
|
||||
AppConfig::SetExportError("14", "Assertion failed when stripping (3)");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// used for simulating addition of "len" more faces with a forced strip restart after "restart" faces
|
||||
S32 Stripper::continueStrip(S32 startFace, S32 endFace, S32 len, S32 restart)
|
||||
{
|
||||
// if already encountered an error, then
|
||||
// we'll just go through the motions
|
||||
if (AppConfig::IsExportError()) return 0;
|
||||
|
||||
Primitive & strip = strips.back();
|
||||
|
||||
while (restart && addStrip(strip,startFace,endFace))
|
||||
restart--,len--;
|
||||
|
||||
// either restarted when we were forced to or restarted on our own
|
||||
// either way, continue adding faces till len==0
|
||||
while (len)
|
||||
{
|
||||
strips.push_back(Primitive());
|
||||
Primitive & strip = strips.back();
|
||||
strip.firstElement = stripIndices.size();
|
||||
strip.numElements = 0;
|
||||
if (faces[startFace].type & Primitive::NoMaterial)
|
||||
strip.type = Primitive::NoMaterial;
|
||||
else
|
||||
strip.type = faces[startFace].type & Primitive::MaterialMask;
|
||||
strip.type |= Primitive::Strip | Primitive::Indexed;
|
||||
|
||||
if (!startStrip(strip,startFace,endFace))
|
||||
{
|
||||
strips.pop_back();
|
||||
break;
|
||||
}
|
||||
len--;
|
||||
|
||||
while (len && addStrip(strip,startFace,endFace))
|
||||
len--;
|
||||
|
||||
// if already encountered an error, then
|
||||
// we'll just go through the motions
|
||||
if (AppConfig::IsExportError()) return 0;
|
||||
}
|
||||
|
||||
return restart;
|
||||
}
|
||||
|
||||
void Stripper::setAdjacency(S32 startFace, S32 endFace)
|
||||
{
|
||||
// if already encountered an error, then
|
||||
// we'll just go through the motions
|
||||
if (AppConfig::IsExportError()) return;
|
||||
|
||||
// two faces adjacent only if wound the same way (so shared edge must appear in opp. order)
|
||||
S32 i,j;
|
||||
numAdjacent.resize(faces.size());
|
||||
for (i=0; i<numAdjacent.size(); i++)
|
||||
numAdjacent[i] = 0;
|
||||
|
||||
adjacent.clearAllBits();
|
||||
|
||||
for (i=startFace; i<endFace-1; i++)
|
||||
{
|
||||
// the i-indices...
|
||||
U32 idx0 = faceIndices[faces[i].firstElement + 0];
|
||||
U32 idx1 = faceIndices[faces[i].firstElement + 1];
|
||||
U32 idx2 = faceIndices[faces[i].firstElement + 2];
|
||||
|
||||
for (j=i+1; j<endFace; j++)
|
||||
{
|
||||
// the j-indices...
|
||||
U32 jdx0 = faceIndices[faces[j].firstElement + 0];
|
||||
U32 jdx1 = faceIndices[faces[j].firstElement + 1];
|
||||
U32 jdx2 = faceIndices[faces[j].firstElement + 2];
|
||||
|
||||
// we don't want to be adjacent if we share all our vertices...
|
||||
if ( ( idx0==jdx0 || idx0==jdx1 || idx0==jdx2) &&
|
||||
( idx1==jdx0 || idx1==jdx1 || idx1==jdx2) &&
|
||||
( idx2==jdx0 || idx2==jdx1 || idx2==jdx2) )
|
||||
continue;
|
||||
|
||||
// test adjacency...
|
||||
if ( ((idx0==jdx1) && (idx1==jdx0)) || ((idx0==jdx2) && (idx1==jdx1)) || ((idx0==jdx0) && (idx1==jdx2)) ||
|
||||
((idx1==jdx1) && (idx2==jdx0)) || ((idx1==jdx2) && (idx2==jdx1)) || ((idx1==jdx0) && (idx2==jdx2)) ||
|
||||
((idx2==jdx1) && (idx0==jdx0)) || ((idx2==jdx2) && (idx0==jdx1)) || ((idx2==jdx0) && (idx0==jdx2)) )
|
||||
{
|
||||
// i,j are adjacent
|
||||
if (adjacent.isSet(i,j) || adjacent.isSet(j,i))
|
||||
{
|
||||
AppConfig::SetExportError("15", "wtf (1)");
|
||||
return;
|
||||
}
|
||||
adjacent.setBit(i,j);
|
||||
adjacent.setBit(j,i);
|
||||
if (!adjacent.isSet(i,j) || !adjacent.isSet(j,i))
|
||||
{
|
||||
AppConfig::SetExportError("15", "wtf (2)");
|
||||
return;
|
||||
}
|
||||
numAdjacent[i]++;
|
||||
numAdjacent[j]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Stripper::clearCache()
|
||||
{
|
||||
vertexCache.resize(cacheSize);
|
||||
for (S32 i=0; i<vertexCache.size(); i++)
|
||||
vertexCache[i] = -1;
|
||||
}
|
||||
|
||||
void Stripper::addToCache(S32 vertexIndex)
|
||||
{
|
||||
S32 i;
|
||||
for (i=0; i<vertexCache.size(); i++)
|
||||
if (vertexCache[i]==vertexIndex)
|
||||
break;
|
||||
if (i==vertexCache.size())
|
||||
cacheMisses++;
|
||||
|
||||
delElementAtIndex(vertexCache,0);
|
||||
vertexCache.push_back(vertexIndex);
|
||||
}
|
||||
|
||||
void Stripper::addToCache(S32 vertexIndex, U32 posFromBack)
|
||||
{
|
||||
// theoretically this could result in a cache miss, but never used that way so
|
||||
// we won't check...
|
||||
|
||||
delElementAtIndex(vertexCache,0);
|
||||
insElementAtIndex(vertexCache,vertexCache.size()-posFromBack,vertexIndex);
|
||||
}
|
||||
|
||||
bool Stripper::startStrip(Primitive & strip, S32 startFace, S32 endFace)
|
||||
{
|
||||
// if already encountered an error, then
|
||||
// we'll just go through the motions
|
||||
if (AppConfig::IsExportError()) return false;
|
||||
|
||||
S32 i,j;
|
||||
|
||||
S32 bestFace = -1;
|
||||
|
||||
// first search the list of faces with neighbors that were recently visited
|
||||
for (i=0;i<recentFaces.size();i++)
|
||||
{
|
||||
if (!used[recentFaces[i]])
|
||||
{
|
||||
bestFace = recentFaces[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
recentFaces.clear();
|
||||
|
||||
// if we didn't find one above, search for a good face
|
||||
if (bestFace<0)
|
||||
{
|
||||
S32 bestScore = -1;
|
||||
for (i=startFace; i<endFace; i++)
|
||||
{
|
||||
if (used[i])
|
||||
continue;
|
||||
|
||||
// how many of the verts are in the cache?
|
||||
// Question: should we favor verts that occur early/late in the cache?
|
||||
U32 inCache = 0;
|
||||
U32 idx0 = faceIndices[faces[i].firstElement + 0];
|
||||
U32 idx1 = faceIndices[faces[i].firstElement + 1];
|
||||
U32 idx2 = faceIndices[faces[i].firstElement + 2];
|
||||
|
||||
for (j=0; j<vertexCache.size(); j++)
|
||||
if (vertexCache[j] == idx0)
|
||||
{
|
||||
inCache++;
|
||||
break;
|
||||
}
|
||||
for (j=0; j<vertexCache.size(); j++)
|
||||
if (vertexCache[j] == idx1)
|
||||
{
|
||||
inCache++;
|
||||
break;
|
||||
}
|
||||
for (j=0; j<vertexCache.size(); j++)
|
||||
if (vertexCache[j] == idx2)
|
||||
{
|
||||
inCache++;
|
||||
break;
|
||||
}
|
||||
S32 currentScore = (inCache<<4) + numAdjacent[i];
|
||||
if (currentScore>bestScore)
|
||||
{
|
||||
bestFace = i;
|
||||
bestScore = currentScore;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if no face...
|
||||
if (bestFace<0)
|
||||
return false;
|
||||
|
||||
// start the strip -- add in standard order...may be changed later
|
||||
strip.numElements += 3;
|
||||
stripIndices.push_back(faceIndices[faces[bestFace].firstElement + 0]);
|
||||
addToCache(stripIndices.back());
|
||||
stripIndices.push_back(faceIndices[faces[bestFace].firstElement + 1]);
|
||||
addToCache(stripIndices.back());
|
||||
stripIndices.push_back(faceIndices[faces[bestFace].firstElement + 2]);
|
||||
addToCache(stripIndices.back());
|
||||
|
||||
testCache(bestFace);
|
||||
|
||||
// adjust adjacency information
|
||||
for (j=startFace; j<endFace; j++)
|
||||
{
|
||||
if (j==bestFace || used[j])
|
||||
continue;
|
||||
if (adjacent.isSet(bestFace,j))
|
||||
{
|
||||
numAdjacent[j]--;
|
||||
if (numAdjacent[j]<0)
|
||||
{
|
||||
AppConfig::SetExportError("14", "Assertion failed when stripping (4)");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// mark face as used
|
||||
used[bestFace] = true;
|
||||
numAdjacent[bestFace] = 0;
|
||||
currentFace = bestFace;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Stripper::getVerts(S32 face, S32 & oldVert0, S32 & oldVert1, S32 & addVert)
|
||||
{
|
||||
// if already encountered an error, then
|
||||
// we'll just go through the motions
|
||||
if (AppConfig::IsExportError()) return;
|
||||
|
||||
std::vector<S32> prev;
|
||||
addVert = -1;
|
||||
Primitive & addFace = faces[face];
|
||||
U32 idx0 = faceIndices[addFace.firstElement+0];
|
||||
U32 idx1 = faceIndices[addFace.firstElement+1];
|
||||
U32 idx2 = faceIndices[addFace.firstElement+2];
|
||||
S32 * cache = &vertexCache.back();
|
||||
if (idx0==*cache || idx0==*(cache-1) || idx0==*(cache-2))
|
||||
prev.push_back(idx0);
|
||||
else
|
||||
addVert = idx0;
|
||||
if (idx1==*cache || idx1==*(cache-1) || idx1==*(cache-2))
|
||||
prev.push_back(idx1);
|
||||
else
|
||||
addVert = idx1;
|
||||
if (idx2==*cache || idx2==*(cache-1) || idx2==*(cache-2))
|
||||
prev.push_back(idx2);
|
||||
else
|
||||
addVert = idx2;
|
||||
if (addVert<0 || prev.size()!=2)
|
||||
{
|
||||
AppConfig::SetExportError("14", "Assertion failed when stripping (5)");
|
||||
return;
|
||||
}
|
||||
|
||||
if (idx1==addVert)
|
||||
{
|
||||
// swap order of oldVerts
|
||||
oldVert0 = prev[1];
|
||||
oldVert1 = prev[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
// keep order
|
||||
oldVert0 = prev[0];
|
||||
oldVert1 = prev[1];
|
||||
}
|
||||
}
|
||||
|
||||
// assumes start + 0,1,2 is a triangle or first 3 indices of a strip
|
||||
void Stripper::rotateFace(S32 start, std::vector<U16> & indices)
|
||||
{
|
||||
U32 tmp = indices[start];
|
||||
indices[start] = indices[start+1];
|
||||
indices[start+1] = indices[start+2];
|
||||
indices[start+2] = tmp;
|
||||
|
||||
S32 * cache = &vertexCache.back();
|
||||
tmp = *(cache-2);
|
||||
*(cache-2) = *(cache-1);
|
||||
*(cache-1) = *cache;
|
||||
*cache = tmp;
|
||||
|
||||
testCache(currentFace);
|
||||
}
|
||||
|
||||
bool Stripper::swapNeeded(S32 oldVert0, S32 oldVert1)
|
||||
{
|
||||
S32 * cache = &vertexCache.back();
|
||||
|
||||
return (*cache!=oldVert0 || *(cache-1)!=oldVert1) && (*cache!=oldVert1 || *(cache-1)!=oldVert0);
|
||||
|
||||
// Long form:
|
||||
// if ( (*cache==oldVert0 && *(cache-1)==oldVert1) || (*cache==oldVert1 && *(cache-1)==oldVert0) )
|
||||
// return false;
|
||||
// else
|
||||
// return true;
|
||||
}
|
||||
|
||||
F32 Stripper::getScore(S32 face, bool ignoreOrder)
|
||||
{
|
||||
// score face depending on following criteria:
|
||||
// -- select face with fewest adjacencies?
|
||||
// -- select face that continues strip without swap?
|
||||
// -- select face with new vertex already in cache?
|
||||
// weighting of each criteria controlled by adjacencyWeight, noswapWeight, and alreadyCachedWeight
|
||||
// if ignoreOrder is true, don't worry about swaps
|
||||
|
||||
if (face<0)
|
||||
return -100000.0f;
|
||||
|
||||
// get the 2 verts from the last face and the 1 new vert
|
||||
S32 oldVert0, oldVert1, addVert;
|
||||
getVerts(face, oldVert0, oldVert1, addVert);
|
||||
|
||||
F32 score = 0.0f;
|
||||
|
||||
// fewer adjacent faces the better...
|
||||
score -= numAdjacent[face] * adjacencyWeight;
|
||||
|
||||
// reward if no swap needed...don't worry about order (only same facing faces get here)
|
||||
if (!ignoreOrder && !swapNeeded(oldVert0,oldVert1))
|
||||
score += noswapWeight;
|
||||
|
||||
// if new vertex in the cache, add the already in cache bonus...
|
||||
for (S32 i=0;i<vertexCache.size();i++)
|
||||
if (vertexCache[i]==addVert)
|
||||
{
|
||||
score += alreadyCachedWeight;
|
||||
break;
|
||||
}
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
bool Stripper::faceHasEdge(S32 face, U32 idx0, U32 idx1)
|
||||
{
|
||||
// return true if face has edge idx0, idx1 (in that order)
|
||||
S32 start = faces[face].firstElement;
|
||||
U32 vi0 = faceIndices[start+0];
|
||||
U32 vi1 = faceIndices[start+1];
|
||||
U32 vi2 = faceIndices[start+2];
|
||||
|
||||
if ( (vi0==idx0 && vi1==idx1) || (vi1==idx0 && vi2==idx1) || (vi2==idx0 && vi0==idx1) )
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void Stripper::getAdjacentFaces(S32 startFace, S32 endFace, S32 face, S32 & face0, S32 & face1, S32 & face2)
|
||||
{
|
||||
// if already encountered an error, then
|
||||
// we'll just go through the motions
|
||||
if (AppConfig::IsExportError()) return;
|
||||
|
||||
// we return one face per edge...ties go to face with fewest adjacencies
|
||||
S32 adj0=-1,adj1=-1,adj2=-1;
|
||||
face0=face1=face2=-1;
|
||||
|
||||
U32 idx0 = faceIndices[faces[face].firstElement + 0];
|
||||
U32 idx1 = faceIndices[faces[face].firstElement + 1];
|
||||
U32 idx2 = faceIndices[faces[face].firstElement + 2];
|
||||
for (S32 i=startFace; i<endFace; i++)
|
||||
{
|
||||
if (i==face || used[i])
|
||||
continue;
|
||||
|
||||
if (!adjacent.isSet(face,i))
|
||||
continue;
|
||||
|
||||
// which edge is this face on
|
||||
if (faceHasEdge(i,idx1,idx0))
|
||||
{
|
||||
if (adj0<0 || numAdjacent[i]<adj0)
|
||||
{
|
||||
face0 = i;
|
||||
adj0 = numAdjacent[i];
|
||||
}
|
||||
}
|
||||
else if (faceHasEdge(i,idx2,idx1))
|
||||
{
|
||||
if (adj1<0 || numAdjacent[i]<adj1)
|
||||
{
|
||||
face1 = i;
|
||||
adj1 = numAdjacent[i];
|
||||
}
|
||||
}
|
||||
else if (faceHasEdge(i,idx0,idx2))
|
||||
{
|
||||
if (adj2<0 || numAdjacent[i]<adj2)
|
||||
{
|
||||
face2 = i;
|
||||
adj2 = numAdjacent[i];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AppConfig::SetExportError("14", "Assertion failed when stripping (6)");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Stripper::stripLongEnough(S32 startFace, S32 endFace)
|
||||
{
|
||||
// if already encountered an error, then
|
||||
// we'll just go through the motions
|
||||
if (AppConfig::IsExportError()) return false;
|
||||
|
||||
if (!limitStripLength)
|
||||
return false;
|
||||
|
||||
// simulate stopping the strip here and continuing for cacheSize+simK more rounds
|
||||
Stripper strip0(*this);
|
||||
strip0.setLimitStripLength(false);
|
||||
strip0.resetCacheMisses();
|
||||
strip0.continueStrip(startFace,endFace,cacheSize+simK,0);
|
||||
U32 stop0 = strip0.getCacheMisses();
|
||||
|
||||
// re-simulate previous best length (-1)
|
||||
U32 bestMisses;
|
||||
if (--bestLength<2)
|
||||
bestLength = 1;
|
||||
Stripper stripper(*this);
|
||||
stripper.setLimitStripLength(false);
|
||||
stripper.resetCacheMisses();
|
||||
stripper.continueStrip(startFace,endFace,cacheSize+simK,bestLength);
|
||||
bestMisses = stripper.getCacheMisses();
|
||||
if (bestMisses<=stop0)
|
||||
return false;
|
||||
|
||||
for (S32 i=1; i<cacheSize+simK; i++)
|
||||
{
|
||||
if (i==bestLength)
|
||||
continue;
|
||||
|
||||
Stripper stripper(*this);
|
||||
stripper.setLimitStripLength(false);
|
||||
stripper.resetCacheMisses();
|
||||
S32 underShoot = stripper.continueStrip(startFace,endFace,cacheSize+simK,i);
|
||||
U32 misses = stripper.getCacheMisses();
|
||||
if (misses<bestMisses)
|
||||
{
|
||||
bestMisses = misses;
|
||||
bestLength = i - underShoot;
|
||||
}
|
||||
if (misses<=stop0)
|
||||
return false;
|
||||
// undershoot is how much we missed our restart target by...i.e., if we wanted
|
||||
// to restart after 5 faces and underShoot is 1, then we restarted after 4.
|
||||
// If undershoot is positive, then we're going to end up restarting at the same
|
||||
// place on future iterations, so break out of the loop now to save time
|
||||
if (underShoot>0)
|
||||
break;
|
||||
}
|
||||
|
||||
// survived the gauntlet...
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Stripper::canGo(S32 face)
|
||||
{
|
||||
if (face<0)
|
||||
return false;
|
||||
|
||||
U32 idx0 = faceIndices[faces[face].firstElement + 0];
|
||||
U32 idx1 = faceIndices[faces[face].firstElement + 1];
|
||||
U32 idx2 = faceIndices[faces[face].firstElement + 2];
|
||||
|
||||
U32 last = stripIndices.back();
|
||||
if (last==idx0 || last==idx1 || last==idx2)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Stripper::addStrip(Primitive & strip, S32 startFace, S32 endFace)
|
||||
{
|
||||
// if already encountered an error, then
|
||||
// we'll just go through the motions
|
||||
if (AppConfig::IsExportError()) return false;
|
||||
|
||||
if (strip.numElements>3 && stripLongEnough(startFace,endFace))
|
||||
return false;
|
||||
|
||||
testCache(currentFace);
|
||||
|
||||
// get unused faces adjacent to current face (choose only faces pointing same way)
|
||||
// if more than one face adjacent on an edge (unusual case) chooses one with lowest adjacency count
|
||||
S32 face0, face1, face2;
|
||||
getAdjacentFaces(startFace,endFace,currentFace,face0,face1,face2);
|
||||
|
||||
// one more check -- make sure most recent vert is in face
|
||||
// this can happen in exceptional case where we "back up"
|
||||
// using common edge between previous two faces, but not the
|
||||
// previous face (a v-junction?)
|
||||
bool bad0,bad1,bad2;
|
||||
bad0=bad1=bad2=false;
|
||||
if (strip.numElements!=3 && !canGo(face0))
|
||||
face0=-1;
|
||||
if (strip.numElements!=3 && !canGo(face1))
|
||||
face1=-1;
|
||||
if (strip.numElements!=3 && !canGo(face2))
|
||||
face2=-1;
|
||||
|
||||
if (face0<0 && face1<0 && face2<0)
|
||||
return false;
|
||||
|
||||
testCache(currentFace);
|
||||
|
||||
// score faces, choose highest score
|
||||
F32 score0 = getScore(face0,strip.numElements==3);
|
||||
F32 score1 = getScore(face1,strip.numElements==3);
|
||||
F32 score2 = getScore(face2,strip.numElements==3);
|
||||
S32 bestFace = -1;
|
||||
if (score0>=score1 && score0>=score2)
|
||||
bestFace = face0;
|
||||
else if (score1>=score0 && score1>=score2)
|
||||
bestFace = face1;
|
||||
else if (score2>=score1 && score2>=score0)
|
||||
bestFace = face2;
|
||||
|
||||
testCache(currentFace);
|
||||
|
||||
// add new element
|
||||
S32 oldVert0, oldVert1, addVert;
|
||||
getVerts(bestFace,oldVert0,oldVert1,addVert);
|
||||
|
||||
testCache(currentFace);
|
||||
|
||||
if (strip.numElements==3)
|
||||
{
|
||||
testCache(currentFace);
|
||||
|
||||
// special case...rotate previous element until we can add this face
|
||||
U32 doh=0;
|
||||
while (swapNeeded(oldVert0,oldVert1))
|
||||
{
|
||||
rotateFace(strip.firstElement,stripIndices);
|
||||
if (++doh==3)
|
||||
{
|
||||
AppConfig::SetExportError("14", "Assertion error while stripping: infinite loop");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
stripIndices.push_back(addVert);
|
||||
addToCache(addVert);
|
||||
strip.numElements++;
|
||||
|
||||
testCache(bestFace);
|
||||
}
|
||||
else
|
||||
{
|
||||
testCache(currentFace);
|
||||
|
||||
if (swapNeeded(oldVert0,oldVert1))
|
||||
{
|
||||
U32 sz = stripIndices.size();
|
||||
U32 dup = stripIndices[sz-3];
|
||||
insElementAtIndex(stripIndices,sz-1,U16(dup));
|
||||
stripIndices[sz-1] = dup;
|
||||
addToCache(dup,1);
|
||||
strip.numElements++;
|
||||
|
||||
testCache(-1);
|
||||
}
|
||||
|
||||
stripIndices.push_back(addVert);
|
||||
strip.numElements++;
|
||||
addToCache(addVert);
|
||||
|
||||
testCache(bestFace);
|
||||
}
|
||||
|
||||
// add other faces to recentFaces list
|
||||
if (face0>=0 && face0!=bestFace)
|
||||
recentFaces.push_back(face0);
|
||||
if (face1>=0 && face1!=bestFace)
|
||||
recentFaces.push_back(face1);
|
||||
if (face2>=0 && face2!=bestFace)
|
||||
recentFaces.push_back(face2);
|
||||
|
||||
// adjust adjacency information
|
||||
for (S32 j=startFace; j<endFace; j++)
|
||||
{
|
||||
if (j==bestFace || used[j])
|
||||
continue;
|
||||
if (adjacent.isSet(bestFace,j))
|
||||
{
|
||||
numAdjacent[j]--;
|
||||
if (numAdjacent[j]<0)
|
||||
{
|
||||
AppConfig::SetExportError("14", "Assertion failed when stripping (7)");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// mark face as used
|
||||
used[bestFace] = true;
|
||||
numAdjacent[bestFace] = 0;
|
||||
currentFace = bestFace;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace DTS
|
77
lib/dtsSDKPlus/stripper.h
Executable file
77
lib/dtsSDKPlus/stripper.h
Executable file
@ -0,0 +1,77 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "DTSTypes.h"
|
||||
#include "DTSShape.h"
|
||||
#include "DTSMesh.h"
|
||||
#include "DTSPlusTypes.h"
|
||||
#include "DTSUtil.h"
|
||||
#include "dtsBitMatrix.h" // see comment in dtsMatrix.h
|
||||
|
||||
namespace DTS
|
||||
{
|
||||
class Stripper
|
||||
{
|
||||
std::vector<S32> numAdjacent;
|
||||
std::vector<bool> used;
|
||||
BitMatrix adjacent;
|
||||
std::vector<S32> vertexCache;
|
||||
std::vector<S32> recentFaces;
|
||||
S32 currentFace;
|
||||
bool limitStripLength;
|
||||
S32 bestLength;
|
||||
U32 cacheMisses;
|
||||
|
||||
std::vector<Primitive> strips;
|
||||
std::vector<U16> stripIndices;
|
||||
|
||||
std::vector<Primitive> & faces;
|
||||
std::vector<U16> & faceIndices;
|
||||
|
||||
void clearCache();
|
||||
void addToCache(S32 vertexIndex);
|
||||
void addToCache(S32 vertexIndex, U32 posFromBack);
|
||||
|
||||
void getVerts(S32 face, S32 & oldVert0, S32 & oldVert1, S32 & addVert);
|
||||
void rotateFace(S32 start, std::vector<U16> & indices);
|
||||
bool swapNeeded(S32 oldVert0, S32 oldVert1);
|
||||
F32 getScore(S32 face, bool ignoreOrder);
|
||||
bool faceHasEdge(S32 face, U32 idx0, U32 idx1);
|
||||
void getAdjacentFaces(S32 startFace, S32 endFace, S32 face, S32 & face0, S32 & face1, S32 & face2);
|
||||
|
||||
void setAdjacency(S32 startFace, S32 endFace);
|
||||
bool startStrip(Primitive & strip, S32 startFace, S32 endFace);
|
||||
bool addStrip(Primitive & strip, S32 startFace, S32 endFace);
|
||||
bool stripLongEnough(S32 startFace, S32 endFace);
|
||||
|
||||
void testCache(S32 addedFace);
|
||||
bool canGo(S32 face);
|
||||
|
||||
void makeStripsB(); // makeStrips() from faces...assumes all faces have same material index
|
||||
void copyParams(Stripper *from);
|
||||
|
||||
public:
|
||||
|
||||
Stripper(std::vector<Primitive> & faces, std::vector<U16> & indices);
|
||||
Stripper(Stripper &);
|
||||
~Stripper();
|
||||
|
||||
void makeStrips();
|
||||
S32 continueStrip(S32 startFace, S32 endFace, S32 len, S32 restart); // used for simulation...
|
||||
void getStrips(std::vector<Primitive> & s, std::vector<U16> & si) { s=strips; si=stripIndices; }
|
||||
|
||||
void setLimitStripLength(bool lim) { limitStripLength = lim; }
|
||||
void resetCacheMisses() { cacheMisses = 0; }
|
||||
U32 getCacheMisses() { return cacheMisses; }
|
||||
|
||||
// adjust strip building strategy
|
||||
static F32 adjacencyWeight;
|
||||
static F32 noswapWeight;
|
||||
static F32 alreadyCachedWeight;
|
||||
static U32 cacheSize;
|
||||
static U32 simK;
|
||||
};
|
||||
|
||||
}
|
1224
lib/dtsSDKPlus/translucentSort.cpp
Executable file
1224
lib/dtsSDKPlus/translucentSort.cpp
Executable file
File diff suppressed because it is too large
Load Diff
104
lib/dtsSDKPlus/translucentSort.h
Executable file
104
lib/dtsSDKPlus/translucentSort.h
Executable file
@ -0,0 +1,104 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TRANS_SORT
|
||||
#define _TRANS_SORT
|
||||
|
||||
#include "DTSTypes.h"
|
||||
#include "DTSShape.h"
|
||||
#include "DTSPlusTypes.h"
|
||||
#include "DTSUtil.h"
|
||||
|
||||
namespace DTS
|
||||
{
|
||||
|
||||
typedef std::vector<bool> IntegerSet;
|
||||
|
||||
class TranslucentSort
|
||||
{
|
||||
std::vector<IntegerSet*> frontClusters;
|
||||
std::vector<IntegerSet*> backClusters;
|
||||
std::vector<S32> middleCluster;
|
||||
|
||||
Point3D splitNormal;
|
||||
F32 splitK;
|
||||
|
||||
S32 mNumBigFaces;
|
||||
S32 mMaxDepth;
|
||||
bool mZLayerUp;
|
||||
bool mZLayerDown;
|
||||
|
||||
S32 currentDepth;
|
||||
|
||||
TranslucentSort * frontSort;
|
||||
TranslucentSort * backSort;
|
||||
|
||||
struct FaceInfo
|
||||
{
|
||||
bool used;
|
||||
S32 priority;
|
||||
S32 parentFace;
|
||||
S32 childFace1;
|
||||
S32 childFace2;
|
||||
S32 childFace3;
|
||||
Point3D normal;
|
||||
F32 k;
|
||||
IntegerSet isInFrontOfMe;
|
||||
IntegerSet isBehindMe;
|
||||
IntegerSet isCutByMe;
|
||||
IntegerSet isCoplanarWithMe;
|
||||
};
|
||||
std::vector<FaceInfo*> faceInfoList;
|
||||
std::vector<FaceInfo*> saveFaceInfoList;
|
||||
|
||||
std::vector<Primitive> & mFaces;
|
||||
std::vector<U16> & mIndices;
|
||||
std::vector<Point3D> & mVerts;
|
||||
std::vector<Point3D> & mNorms;
|
||||
std::vector<Point2D> & mTVerts;
|
||||
|
||||
void initFaces();
|
||||
void initFaceInfo(Primitive & face, FaceInfo & faceInfo, bool setPriority = true);
|
||||
void setFaceInfo(Primitive & face, FaceInfo & faceInfo);
|
||||
void clearFaces(IntegerSet &);
|
||||
void saveFaceInfo();
|
||||
void restoreFaceInfo();
|
||||
void addFaces(IntegerSet *, std::vector<Primitive> & faces, std::vector<U16> & indices, bool continueLast = false);
|
||||
void addFaces(std::vector<IntegerSet *> &, std::vector<Primitive> & faces, std::vector<U16> & indices, bool continueLast = false);
|
||||
void addOrderedFaces(std::vector<S32> &, std::vector<Primitive> &, std::vector<U16> & indices, bool continueLast = false);
|
||||
void splitFace(S32 faceIndex, Point3D normal, F32 k);
|
||||
void splitFace2(S32 faceIndex, Point3D normal, F32 k);
|
||||
void sort();
|
||||
|
||||
// routines for sorting faces when there is no perfect solution for all cases
|
||||
void copeSort(std::vector<S32> &);
|
||||
void layerSort(std::vector<S32> &, bool upFirst);
|
||||
|
||||
// these are for debugging
|
||||
bool anyInFrontOfPlane(Point3D normal, F32 k);
|
||||
bool anyBehindPlane(Point3D normal, F32 k);
|
||||
|
||||
//
|
||||
void generateClusters(std::vector<Cluster> & clusters, std::vector<Primitive> & faces, std::vector<U16> & indices, S32 retIndex = -1);
|
||||
|
||||
TranslucentSort(TranslucentSort *);
|
||||
TranslucentSort(std::vector<Primitive> & faces,
|
||||
std::vector<U16> & indices,
|
||||
std::vector<Point3D> & verts,
|
||||
std::vector<Point3D> & norms,
|
||||
std::vector<Point2D> & tverts,
|
||||
S32 numBigFaces, S32 maxDepth, bool zLayerUp, bool zLayerDown);
|
||||
|
||||
~TranslucentSort();
|
||||
|
||||
public:
|
||||
|
||||
static void generateSortedMesh(Mesh * mesh, S32 numBigFaces, S32 maxDepth, bool zLayerUp, bool zLayerDown);
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
#endif // _TRANS_SORT
|
||||
|
Reference in New Issue
Block a user