tge/engine/ts/tsShape.cc
2025-02-17 23:17:30 -06:00

1715 lines
55 KiB
C++
Executable File

//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "ts/tsShape.h"
#include "ts/tsLastDetail.h"
#include "core/stringTable.h"
#include "console/console.h"
#include "ts/tsShapeInstance.h"
#include "collision/convex.h"
#include "platform/platformGL.h"
#include "util/safeDelete.h"
/// most recent version -- this is the version we write
S32 TSShape::smVersion = 24;
/// the version currently being read...valid only during a read
S32 TSShape::smReadVersion = -1;
const U32 TSShape::smMostRecentExporterVersion = DTS_EXPORTER_CURRENT_VERSION;
F32 TSShape::smAlphaOutLastDetail = -1.0f;
F32 TSShape::smAlphaInBillboard = 0.15f;
F32 TSShape::smAlphaOutBillboard = 0.1f;
F32 TSShape::smAlphaInDefault = -1.0f;
F32 TSShape::smAlphaOutDefault = -1.0f;
// don't bother even loading this many of the highest detail levels (but
// always load last renderable detail)
S32 TSShape::smNumSkipLoadDetails = 0;
bool TSShape::smInitOnRead = true;
TSShape::TSShape()
{
materialList = NULL;
mReadVersion = -1; // -1 means constructed from scratch (e.g., in exporter or no read yet)
mMemoryBlock = NULL;
mSequencesConstructed = false;
mVertexBuffer = (U32)-1;
mCallbackKey = (U32)-1;
VECTOR_SET_ASSOCIATION(sequences);
VECTOR_SET_ASSOCIATION(billboardDetails);
VECTOR_SET_ASSOCIATION(detailCollisionAccelerators);
VECTOR_SET_ASSOCIATION(names);
}
TSShape::~TSShape()
{
clearDynamicData();
delete materialList;
S32 i;
// before deleting meshes, we have to delete decals and get them out
// of the mesh list...this is a legacy issue from when decals were meshes
for (i=0; i<decals.size(); i++)
{
for (S32 j=0; j<decals[i].numMeshes; j++)
{
if ((TSDecalMesh*)meshes[decals[i].startMeshIndex+j])
destructInPlace((TSDecalMesh*)meshes[decals[i].startMeshIndex+j]);
meshes[decals[i].startMeshIndex+j]=NULL;
}
}
// everything left over here is a legit mesh
for (i=0; i<meshes.size(); i++)
if (meshes[i])
destructInPlace(meshes[i]);
for (i=0; i<sequences.size(); i++)
destructInPlace(&sequences[i]);
for (i=0; i<billboardDetails.size(); i++)
{
SAFE_DELETE(billboardDetails[i]);
}
billboardDetails.clear();
// Delete any generated accelerators
S32 dca;
for (dca = 0; dca < detailCollisionAccelerators.size(); dca++)
{
ConvexHullAccelerator* accel = detailCollisionAccelerators[dca];
if (accel != NULL)
{
delete [] accel->vertexList;
delete [] accel->normalList;
for (S32 j = 0; j < accel->numVerts; j++)
delete [] accel->emitStrings[j];
delete [] accel->emitStrings;
delete accel;
}
}
for (dca = 0; dca < detailCollisionAccelerators.size(); dca++)
detailCollisionAccelerators[dca] = NULL;
delete [] mMemoryBlock;
mMemoryBlock = NULL;
if (mVertexBuffer != -1)
if (dglDoesSupportVertexBuffer())
glFreeVertexBufferEXT(mVertexBuffer);
else
AssertFatal(false,"Vertex buffer should have already been freed!");
if (mCallbackKey != -1)
TextureManager::unregisterEventCallback(mCallbackKey);
}
void TSShape::clearDynamicData()
{
}
const char * TSShape::getName(S32 nameIndex) const
{
AssertFatal(nameIndex>=0 && nameIndex<names.size(),"TSShape::getName");
return names[nameIndex];
}
S32 TSShape::findName(const char * name) const
{
for (S32 i=0; i<names.size(); i++)
if (!dStricmp(name,names[i]))
return i;
return -1;
}
S32 TSShape::findNode(S32 nameIndex) const
{
for (S32 i=0; i<nodes.size(); i++)
if (nodes[i].nameIndex==nameIndex)
return i;
return -1;
}
S32 TSShape::findObject(S32 nameIndex) const
{
for (S32 i=0; i<objects.size(); i++)
if (objects[i].nameIndex==nameIndex)
return i;
return -1;
}
S32 TSShape::findDecal(S32 nameIndex) const
{
for (S32 i=0; i<decals.size(); i++)
if (decals[i].nameIndex==nameIndex)
return i;
return -1;
}
S32 TSShape::findIflMaterial(S32 nameIndex) const
{
for (S32 i=0; i<iflMaterials.size(); i++)
if (iflMaterials[i].nameIndex==nameIndex)
return i;
return -1;
}
S32 TSShape::findDetail(S32 nameIndex) const
{
for (S32 i=0; i<details.size(); i++)
if (details[i].nameIndex==nameIndex)
return i;
return -1;
}
S32 TSShape::findSequence(S32 nameIndex) const
{
for (S32 i=0; i<sequences.size(); i++)
if (sequences[i].nameIndex==nameIndex)
return i;
return -1;
}
void TSShape::init()
{
clearDynamicData();
S32 numSubShapes = subShapeFirstNode.size();
AssertFatal(numSubShapes==subShapeFirstObject.size(),"TSShape::init");
S32 i,j;
// set up parent/child relationships on nodes and objects
for (i=0; i<nodes.size(); i++)
nodes[i].firstObject = nodes[i].firstChild = nodes[i].nextSibling = -1;
for (i=0; i<nodes.size(); i++)
{
S32 parentIndex = nodes[i].parentIndex;
if (parentIndex>=0)
{
if (nodes[parentIndex].firstChild<0)
nodes[parentIndex].firstChild=i;
else
{
S32 child = nodes[parentIndex].firstChild;
while (nodes[child].nextSibling>=0)
child = nodes[child].nextSibling;
nodes[child].nextSibling = i;
}
}
}
for (i=0; i<objects.size(); i++)
{
objects[i].nextSibling = -1;
objects[i].firstDecal = -1;
S32 nodeIndex = objects[i].nodeIndex;
if (nodeIndex>=0)
{
if (nodes[nodeIndex].firstObject<0)
nodes[nodeIndex].firstObject = i;
else
{
S32 objectIndex = nodes[nodeIndex].firstObject;
while (objects[objectIndex].nextSibling>=0)
objectIndex = objects[objectIndex].nextSibling;
objects[objectIndex].nextSibling = i;
}
}
}
for (i=0; i<decals.size(); i++)
{
decals[i].nextSibling = -1;
S32 objectIndex = decals[i].objectIndex;
if (objects[objectIndex].firstDecal<0)
objects[objectIndex].firstDecal = i;
else
{
S32 decalIndex = objects[objectIndex].firstDecal;
while (decals[decalIndex].nextSibling>=0)
decalIndex = decals[decalIndex].nextSibling;
decals[decalIndex].nextSibling = i;
}
}
mFlags = 0;
for (i=0; i<sequences.size(); i++)
{
if (!sequences[i].animatesScale())
continue;
U32 curVal = mFlags & AnyScale;
U32 newVal = sequences[i].flags & AnyScale;
mFlags &= ~(AnyScale);
mFlags |= getMax(curVal,newVal); // take the larger value (can only convert upwards)
}
// set up alphaIn and alphaOut vectors...
#if defined(TORQUE_LIB)
alphaIn.setSize(details.size());
alphaOut.setSize(details.size());
#endif
for (i=0; i<details.size(); i++)
{
if (details[i].size<0)
{
// we don't care...
alphaIn[i] = 0.0f;
alphaOut[i] = 0.0f;
}
else if (i+1==details.size() || details[i+1].size<0)
{
alphaIn[i] = 0.0f;
alphaOut[i] = smAlphaOutLastDetail;
}
else
{
if (details[i+1].subShapeNum<0)
{
// following detail is a billboard detail...treat special...
alphaIn[i] = smAlphaInBillboard;
alphaOut[i] = smAlphaOutBillboard;
}
else
{
// next detail is normal detail
alphaIn[i] = smAlphaInDefault;
alphaOut[i] = smAlphaOutDefault;
}
}
}
for (i=mSmallestVisibleDL-1; i>=0; i--)
{
if (i<smNumSkipLoadDetails)
{
// this detail level renders when pixel size
// is larger than our cap...zap all the meshes and decals
// associated with it and use the next detail level
// instead...
S32 ss = details[i].subShapeNum;
S32 od = details[i].objectDetailNum;
if (ss==details[i+1].subShapeNum && od==details[i+1].objectDetailNum)
// doh! already done this one (init can be called multiple times on same shape due
// to sequence importing).
continue;
details[i].subShapeNum = details[i+1].subShapeNum;
details[i].objectDetailNum = details[i+1].objectDetailNum;
}
}
for (i=0; i<details.size(); i++)
{
S32 count = 0;
S32 ss = details[i].subShapeNum;
S32 od = details[i].objectDetailNum;
if (ss<0)
{
// billboard detail...
count += 2;
continue;
}
S32 start = subShapeFirstObject[ss];
S32 end = start + subShapeNumObjects[ss];
for (j=start; j<end; j++)
{
Object & obj = objects[j];
if (od<obj.numMeshes)
{
TSMesh * mesh = meshes[obj.startMeshIndex+od];
count += mesh ? mesh->getNumPolys() : 0;
}
}
details[i].polyCount = count;
}
// Init the collision accelerator array. Note that we don't compute the
// accelerators until the app requests them
{
S32 dca;
for (dca = 0; dca < detailCollisionAccelerators.size(); dca++)
{
ConvexHullAccelerator* accel = detailCollisionAccelerators[dca];
if (accel != NULL)
{
delete [] accel->vertexList;
delete [] accel->normalList;
for (S32 j = 0; j < accel->numVerts; j++)
delete [] accel->emitStrings[j];
delete [] accel->emitStrings;
delete accel;
}
}
detailCollisionAccelerators.setSize(details.size());
for (dca = 0; dca < detailCollisionAccelerators.size(); dca++)
detailCollisionAccelerators[dca] = NULL;
}
mMergeBufferSize=0;
for (i=0; i<objects.size(); i++)
{
TSObject * obj = &objects[i];
S32 maxSize = 0;
for (S32 dl=0; dl<obj->numMeshes; dl++)
{
TSMesh * mesh = meshes[obj->startMeshIndex+dl];
if (mesh)
{
mesh->mergeBufferStart = mMergeBufferSize;
maxSize = getMax((S32)maxSize,(S32)mesh->mergeIndices.size());
}
}
mMergeBufferSize += maxSize;
}
initMaterialList();
}
void TSShape::setupBillboardDetails(TSShapeInstance *shape)
{
// set up billboard details -- only do this once, meaning that
// if we add a sequence to the shape we don't redo the billboard
// details...
S32 i;
if (billboardDetails.empty())
{
for (i=0; i<details.size(); i++)
{
if (details[i].subShapeNum>=0)
continue; // not a billboard detail
while (billboardDetails.size()<=i)
billboardDetails.push_back(NULL);
U32 props = details[i].objectDetailNum;
U32 numEquatorSteps = props & 0x7F; // bits 0..6
U32 numPolarSteps = (props>>7) & 0x3F; // bits 7..12
F32 polarAngle = 0.5f * M_PI_F * (1.0f/64.0f) * (F32) ((props>>13) & 0x3F); // bits 13..18
S32 dl = (props>>19) & 0x0F; // 19..22
S32 dim = (props>>23) & 0xFF; // 23..30
bool includePoles = (props & 0x80000000)!=0; // bit 31
billboardDetails[i] = new TSLastDetail(shape,numEquatorSteps,numPolarSteps,polarAngle,includePoles,dl,dim);
}
}
}
void TSShape::initMaterialList()
{
S32 numSubShapes = subShapeFirstObject.size();
#if defined(TORQUE_LIB)
subShapeFirstTranslucentObject.setSize(numSubShapes);
#endif
S32 i,j,k;
// for each subshape, find the first translucent object
// also, while we're at it, set mHasTranslucency
for (S32 ss = 0; ss<numSubShapes; ss++)
{
S32 start = subShapeFirstObject[ss];
S32 end = subShapeNumObjects[ss];
subShapeFirstTranslucentObject[ss] = end;
for (i=start; i<end; i++)
{
// check to see if this object has translucency
Object & obj = objects[i];
for (j=0; j<obj.numMeshes; j++)
{
TSMesh * mesh = meshes[obj.startMeshIndex+j];
if (!mesh)
continue;
for (k=0; k<mesh->primitives.size(); k++)
{
if (mesh->primitives[k].matIndex & TSDrawPrimitive::NoMaterial)
continue;
S32 flags = materialList->getFlags(mesh->primitives[k].matIndex & TSDrawPrimitive::MaterialMask);
if (flags & TSMaterialList::AuxiliaryMap)
continue;
if (flags & TSMaterialList::Translucent)
{
mFlags |= HasTranslucency;
subShapeFirstTranslucentObject[ss] = i;
break;
}
}
if (k!=mesh->primitives.size())
break;
}
if (j!=obj.numMeshes)
break;
}
if (i!=end)
break;
}
}
bool TSShape::preloadMaterialList()
{
if(materialList)
return materialList->load(MeshTexture, mSourceResource ? mSourceResource->path : "", true);
return true;
}
bool TSShape::buildConvexHull(S32 dl) const
{
AssertFatal(dl>=0 && dl<details.size(),"TSShape::buildConvexHull: detail out of range");
bool ok = true;
const Detail & detail = details[dl];
S32 ss = detail.subShapeNum;
S32 od = detail.objectDetailNum;
S32 start = subShapeFirstObject[ss];
S32 end = subShapeNumObjects[ss];
for (S32 i=start; i<end; i++)
{
TSMesh * mesh = meshes[objects[i].startMeshIndex+od];
if (!mesh)
continue;
ok &= mesh->buildConvexHull();
}
return ok;
}
Vector<MatrixF> gTempNodeTransforms(__FILE__, __LINE__);
void TSShape::computeBounds(S32 dl, Box3F & bounds) const
{
// if dl==-1, nothing to do
if (dl==-1)
return;
AssertFatal(dl>=0 && dl<details.size(),"TSShapeInstance::computeBounds");
// get subshape and object detail
const TSDetail * detail = &details[dl];
S32 ss = detail->subShapeNum;
S32 od = detail->objectDetailNum;
// set up temporary storage for non-local transforms...
S32 i;
S32 start = subShapeFirstNode[ss];
S32 end = subShapeNumNodes[ss] + start;
gTempNodeTransforms.setSize(end-start);
for (i=start; i<end; i++)
{
MatrixF mat;
QuatF q;
TSTransform::setMatrix(defaultRotations[i].getQuatF(&q),defaultTranslations[i],&mat);
if (nodes[i].parentIndex>=0)
gTempNodeTransforms[i-start].mul(gTempNodeTransforms[nodes[i].parentIndex-start],mat);
else
gTempNodeTransforms[i-start] = mat;
}
// run through objects and updating bounds as we go
bounds.min.set( 10E30f, 10E30f, 10E30f);
bounds.max.set(-10E30f,-10E30f,-10E30f);
Box3F box;
start = subShapeFirstObject[ss];
end = subShapeNumObjects[ss] + start;
for (i=start; i<end; i++)
{
const Object * object = &objects[i];
TSMesh * mesh = od<object->numMeshes ? meshes[object->startMeshIndex+od] : NULL;
if (mesh)
{
static MatrixF idMat(true);
if (object->nodeIndex<0)
mesh->computeBounds(idMat,box);
else
mesh->computeBounds(gTempNodeTransforms[object->nodeIndex-start],box);
bounds.min.setMin(box.min);
bounds.max.setMax(box.max);
}
}
}
TSShapeAlloc TSShape::alloc;
#define alloc TSShape::alloc
// messy stuff: check to see if we should "skip" meshNum
// this assumes that meshes for a given object/decal are in a row
// skipDL is the lowest detail number we keep (i.e., the # of details we skip)
bool TSShape::checkSkip(S32 meshNum, S32 & curObject, S32 & curDecal, S32 skipDL)
{
if (skipDL==0)
// easy out...
return false;
// skip detail level exists on this subShape
S32 skipSS = details[skipDL].subShapeNum;
if (curObject<objects.size())
{
S32 start = objects[curObject].startMeshIndex;
if (meshNum>=start)
{
// we are either from this object, the next object, or a decal
if (meshNum < start + objects[curObject].numMeshes)
{
// this object...
if (subShapeFirstObject[skipSS]>curObject)
// haven't reached this subshape yet
return true;
if (skipSS+1==subShapeFirstObject.size() || curObject<subShapeFirstObject[skipSS+1])
// curObject is on subshape of skip detail...make sure it's after skipDL
return (meshNum-start<details[skipDL].objectDetailNum);
// if we get here, then curObject ocurrs on subShape after skip detail (so keep it)
return false;
}
else
// advance object, try again
return checkSkip(meshNum,++curObject,curDecal,skipDL);
}
}
if (curDecal<decals.size())
{
S32 start = decals[curDecal].startMeshIndex;
if (meshNum>=start)
{
// we are either from this decal, the next decal, or error
if (meshNum < start + decals[curDecal].numMeshes)
{
// this object...
if (subShapeFirstDecal[skipSS]>curDecal)
// haven't reached this subshape yet
return true;
if (skipSS+1==subShapeFirstDecal.size() || curDecal<subShapeFirstDecal[skipSS+1])
// curDecal is on subshape of skip detail...make sure it's after skipDL
return (meshNum-start<details[skipDL].objectDetailNum);
// if we get here, then curDecal ocurrs on subShape after skip detail (so keep it)
return false;
}
else
// advance decal, try again
return checkSkip(meshNum,curObject,++curDecal,skipDL);
}
}
AssertFatal(0,"TSShape::checkSkip: assertion failed");
return false;
}
void TSShape::assembleShape()
{
S32 i,j;
// get counts...
S32 numNodes = alloc.get32();
S32 numObjects = alloc.get32();
S32 numDecals = alloc.get32();
S32 numSubShapes = alloc.get32();
S32 numIflMaterials = alloc.get32();
S32 numNodeRots;
S32 numNodeTrans;
S32 numNodeUniformScales;
S32 numNodeAlignedScales;
S32 numNodeArbitraryScales;
if (smReadVersion<22)
{
numNodeRots = numNodeTrans = alloc.get32() - numNodes;
numNodeUniformScales = numNodeAlignedScales = numNodeArbitraryScales = 0;
}
else
{
numNodeRots = alloc.get32();
numNodeTrans = alloc.get32();
numNodeUniformScales = alloc.get32();
numNodeAlignedScales = alloc.get32();
numNodeArbitraryScales = alloc.get32();
}
S32 numGroundFrames = 0;
if (smReadVersion>23)
numGroundFrames = alloc.get32();
S32 numObjectStates = alloc.get32();
S32 numDecalStates = alloc.get32();
S32 numTriggers = alloc.get32();
S32 numDetails = alloc.get32();
S32 numMeshes = alloc.get32();
S32 numSkins = 0;
if (smReadVersion<23)
// in later versions, skins are kept with other meshes
numSkins = alloc.get32();
S32 numNames = alloc.get32();
mSmallestVisibleSize = (F32)alloc.get32();
mSmallestVisibleDL = alloc.get32();
S32 skipDL = getMin(mSmallestVisibleDL,smNumSkipLoadDetails);
alloc.checkGuard();
// get bounds...
alloc.get32((S32*)&radius,1);
alloc.get32((S32*)&tubeRadius,1);
alloc.get32((S32*)&center,3);
alloc.get32((S32*)&bounds,6);
alloc.checkGuard();
// copy various vectors...
S32 * ptr32 = alloc.copyToShape32(numNodes*5);
nodes.set(ptr32,numNodes);
alloc.checkGuard();
ptr32 = alloc.copyToShape32(numObjects*6,true);
if (!ptr32)
ptr32 = alloc.allocShape32(numSkins*6); // pre v23 shapes store skins and meshes separately...no longer
else
alloc.allocShape32(numSkins*6);
objects.set(ptr32,numObjects);
alloc.checkGuard();
ptr32 = alloc.copyToShape32(numDecals*5,true);
decals.set(ptr32,numDecals);
alloc.checkGuard();
ptr32 = alloc.copyToShape32(numIflMaterials*5);
iflMaterials.set(ptr32,numIflMaterials);
alloc.checkGuard();
ptr32 = alloc.copyToShape32(numSubShapes,true);
subShapeFirstNode.set(ptr32,numSubShapes);
ptr32 = alloc.copyToShape32(numSubShapes,true);
subShapeFirstObject.set(ptr32,numSubShapes);
ptr32 = alloc.copyToShape32(numSubShapes,true);
subShapeFirstDecal.set(ptr32,numSubShapes);
alloc.checkGuard();
ptr32 = alloc.copyToShape32(numSubShapes);
subShapeNumNodes.set(ptr32,numSubShapes);
ptr32 = alloc.copyToShape32(numSubShapes);
subShapeNumObjects.set(ptr32,numSubShapes);
ptr32 = alloc.copyToShape32(numSubShapes);
subShapeNumDecals.set(ptr32,numSubShapes);
alloc.checkGuard();
ptr32 = alloc.allocShape32(numSubShapes);
subShapeFirstTranslucentObject.set(ptr32,numSubShapes);
// get meshIndexList...older shapes only
S32 meshIndexListSize = 0;
S32 * meshIndexList = NULL;
if (smReadVersion<16)
{
meshIndexListSize = alloc.get32();
meshIndexList = alloc.getPointer32(meshIndexListSize);
}
// get default translation and rotation
S16 * ptr16 = alloc.allocShape16(0);
for (i=0;i<numNodes;i++)
alloc.copyToShape16(4);
defaultRotations.set(ptr16,numNodes);
alloc.align32();
ptr32 = alloc.allocShape32(0);
for (i=0;i<numNodes;i++)
{
alloc.copyToShape32(3);
alloc.copyToShape32(sizeof(Point3F)-12); // handle alignment issues w/ point3f
}
defaultTranslations.set(ptr32,numNodes);
// get any node sequence data stored in shape
nodeTranslations.setSize(numNodeTrans);
for (i=0;i<numNodeTrans;i++)
alloc.get32((S32*)&nodeTranslations[i],3);
nodeRotations.setSize(numNodeRots);
for (i=0;i<numNodeRots;i++)
alloc.get16((S16*)&nodeRotations[i],4);
alloc.align32();
alloc.checkGuard();
if (smReadVersion>21)
{
// more node sequence data...scale
nodeUniformScales.setSize(numNodeUniformScales);
for (i=0;i<numNodeUniformScales;i++)
alloc.get32((S32*)&nodeUniformScales[i],1);
nodeAlignedScales.setSize(numNodeAlignedScales);
for (i=0;i<numNodeAlignedScales;i++)
alloc.get32((S32*)&nodeAlignedScales[i],3);
nodeArbitraryScaleFactors.setSize(numNodeArbitraryScales);
for (i=0;i<numNodeArbitraryScales;i++)
alloc.get32((S32*)&nodeArbitraryScaleFactors[i],3);
nodeArbitraryScaleRots.setSize(numNodeArbitraryScales);
for (i=0;i<numNodeArbitraryScales;i++)
alloc.get16((S16*)&nodeArbitraryScaleRots[i],4);
alloc.align32();
alloc.checkGuard();
}
// old shapes need ground transforms moved to ground arrays...but only do it once
if (smReadVersion<22 && alloc.allocShape32(0))
{
for (i=0; i<sequences.size(); i++)
{
// move ground transform data to ground vectors
Sequence & seq = sequences[i];
S32 oldSz = groundTranslations.size();
groundTranslations.setSize(oldSz+seq.numGroundFrames);
groundRotations.setSize(oldSz+seq.numGroundFrames);
for (S32 j=0;j<seq.numGroundFrames;j++)
{
groundTranslations[j+oldSz] = nodeTranslations[seq.firstGroundFrame+j-numNodes];
groundRotations[j+oldSz] = nodeRotations[seq.firstGroundFrame+j-numNodes];
}
seq.firstGroundFrame = oldSz;
seq.baseTranslation -= numNodes;
seq.baseRotation -= numNodes;
seq.baseScale = 0; // not used on older shapes...but keep it clean
}
}
// version 22 & 23 shapes accidentally had no ground transforms, and ground for
// earlier shapes is handled just above, so...
if (smReadVersion>23)
{
groundTranslations.setSize(numGroundFrames);
for (i=0;i<numGroundFrames;i++)
alloc.get32((S32*)&groundTranslations[i],3);
groundRotations.setSize(numGroundFrames);
for (i=0;i<numGroundFrames;i++)
alloc.get16((S16*)&groundRotations[i],4);
alloc.align32();
alloc.checkGuard();
}
// object states
ptr32 = alloc.copyToShape32(numObjectStates*3);
objectStates.set(ptr32,numObjectStates);
alloc.allocShape32(numSkins*3); // provide buffer after objectStates for older shapes
alloc.checkGuard();
// decal states
ptr32 = alloc.copyToShape32(numDecalStates);
decalStates.set(ptr32,numDecalStates);
alloc.checkGuard();
// frame triggers
ptr32 = alloc.getPointer32(numTriggers*2);
triggers.setSize(numTriggers);
dMemcpy(triggers.address(),ptr32,sizeof(S32)*numTriggers*2);
alloc.checkGuard();
// details
ptr32 = alloc.copyToShape32(numDetails*7,true);
details.set(ptr32,numDetails);
alloc.checkGuard();
// about to read in the meshes...first must allocate some scratch space
S32 scratchSize = getMax(numSkins,numMeshes);
TSMesh::smVertsList.setSize(scratchSize);
TSMesh::smTVertsList.setSize(scratchSize);
TSMesh::smNormsList.setSize(scratchSize);
TSMesh::smEncodedNormsList.setSize(scratchSize);
TSMesh::smDataCopied.setSize(scratchSize);
TSSkinMesh::smInitTransformList.setSize(scratchSize);
TSSkinMesh::smVertexIndexList.setSize(scratchSize);
TSSkinMesh::smBoneIndexList.setSize(scratchSize);
TSSkinMesh::smWeightList.setSize(scratchSize);
TSSkinMesh::smNodeIndexList.setSize(scratchSize);
for (i=0; i<numMeshes; i++)
{
TSMesh::smVertsList[i]=NULL;
TSMesh::smTVertsList[i]=NULL;
TSMesh::smNormsList[i]=NULL;
TSMesh::smEncodedNormsList[i]=NULL;
TSMesh::smDataCopied[i]=false;
TSSkinMesh::smInitTransformList[i] = NULL;
TSSkinMesh::smVertexIndexList[i] = NULL;
TSSkinMesh::smBoneIndexList[i] = NULL;
TSSkinMesh::smWeightList[i] = NULL;
TSSkinMesh::smNodeIndexList[i] = NULL;
}
// read in the meshes (sans skins)...
if (smReadVersion>15)
{
// straight forward read one at a time
ptr32 = alloc.allocShape32(numMeshes + numSkins*numDetails); // leave room for skins on old shapes
S32 curObject = 0, curDecal = 0; // for tracking skipped meshes
for (i=0; i<numMeshes; i++)
{
bool skip = checkSkip(i,curObject,curDecal,skipDL); // skip this mesh?
S32 meshType = alloc.get32();
TSMesh * mesh = TSMesh::assembleMesh(meshType,skip);
if (ptr32)
ptr32[i] = skip ? 0 : (S32)mesh;
// fill in location of verts, tverts, and normals for detail levels
if (mesh && meshType!=TSMesh::DecalMeshType)
{
TSMesh::smVertsList[i] = mesh->verts.address();
TSMesh::smTVertsList[i] = mesh->tverts.address();
TSMesh::smNormsList[i] = mesh->norms.address();
TSMesh::smEncodedNormsList[i] = mesh->encodedNorms.address();
TSMesh::smDataCopied[i] = !skip; // as long as we didn't skip this mesh, the data should be in shape now
if (meshType==TSMesh::SkinMeshType)
{
TSSkinMesh * skin = (TSSkinMesh*)mesh;
TSMesh::smVertsList[i] = skin->initialVerts.address();
TSMesh::smNormsList[i] = skin->initialNorms.address();
TSSkinMesh::smInitTransformList[i] = skin->initialTransforms.address();
TSSkinMesh::smVertexIndexList[i] = skin->vertexIndex.address();
TSSkinMesh::smBoneIndexList[i] = skin->boneIndex.address();
TSSkinMesh::smWeightList[i] = skin->weight.address();
TSSkinMesh::smNodeIndexList[i] = skin->nodeIndex.address();
}
}
}
meshes.set(ptr32,numMeshes);
}
else
{
// use meshIndexList to contruct mesh list...
ptr32 = alloc.allocShape32(meshIndexListSize + numSkins*numDetails);
S32 next=0;
S32 curObject = 0, curDecal = 0; // for tracking skipped meshes
for (i=0; i<meshIndexListSize; i++)
{
bool skip = checkSkip(i,curObject,curDecal,skipDL);
if (meshIndexList[i]>=0)
{
AssertFatal(meshIndexList[i]==next,"TSShape::read: assertion failed on obsolete shape");
S32 meshType = alloc.get32();
TSMesh * mesh = TSMesh::assembleMesh(meshType,skip);
if (ptr32)
ptr32[i] = skip ? 0 : (S32) mesh;
next = meshIndexList[i]+1;
// fill in location of verts, tverts, and normals for detail levels
if (mesh && meshType!=TSMesh::DecalMeshType)
{
TSMesh::smVertsList[i] = mesh->verts.address();
TSMesh::smTVertsList[i] = mesh->tverts.address();
TSMesh::smNormsList[i] = mesh->norms.address();
TSMesh::smEncodedNormsList[i] = mesh->encodedNorms.address();
TSMesh::smDataCopied[i] = !skip; // as long as we didn't skip this mesh, the data should be in shape now
if (meshType==TSMesh::SkinMeshType)
{
TSSkinMesh * skin = (TSSkinMesh*)mesh;
TSMesh::smVertsList[i] = skin->initialVerts.address();
TSMesh::smNormsList[i] = skin->initialNorms.address();
TSSkinMesh::smInitTransformList[i] = skin->initialTransforms.address();
TSSkinMesh::smVertexIndexList[i] = skin->vertexIndex.address();
TSSkinMesh::smBoneIndexList[i] = skin->boneIndex.address();
TSSkinMesh::smWeightList[i] = skin->weight.address();
TSSkinMesh::smNodeIndexList[i] = skin->nodeIndex.address();
}
}
}
else if (ptr32)
ptr32[i] = 0; // no mesh
}
meshes.set(ptr32,meshIndexListSize);
}
alloc.checkGuard();
// names
bool namesInStringTable = (StringTable!=NULL);
char * nameBufferStart = (char*)alloc.getPointer8(0);
char * name = nameBufferStart;
S32 nameBufferSize = 0;
names.setSize(numNames);
for (i=0; i<numNames; i++)
{
for (j=0; name[j]; j++)
;
if (namesInStringTable)
names[i] = StringTable->insert(name,false);
nameBufferSize += j + 1;
name += j + 1;
}
if (!namesInStringTable)
{
name = (char*)alloc.copyToShape8(nameBufferSize);
if (name) // make sure we did copy (might just be getting size of buffer)
for (i=0; i<numNames; i++)
{
for (j=0; name[j]; j++)
;
names[i] = name;
name += j + 1;
}
}
else
alloc.getPointer8(nameBufferSize);
alloc.align32();
alloc.checkGuard();
if (smReadVersion<23)
{
// get detail information about skins...
S32 * detailFirstSkin = alloc.getPointer32(numDetails);
S32 * detailNumSkins = alloc.getPointer32(numDetails);
alloc.checkGuard();
// about to read in skins...clear out scratch space...
if (numSkins)
{
TSSkinMesh::smInitTransformList.setSize(numSkins);
TSSkinMesh::smVertexIndexList.setSize(numSkins);
TSSkinMesh::smBoneIndexList.setSize(numSkins);
TSSkinMesh::smWeightList.setSize(numSkins);
TSSkinMesh::smNodeIndexList.setSize(numSkins);
}
for (i=0; i<numSkins; i++)
{
TSMesh::smVertsList[i]=NULL;
TSMesh::smTVertsList[i]=NULL;
TSMesh::smNormsList[i]=NULL;
TSMesh::smEncodedNormsList[i]=NULL;
TSMesh::smDataCopied[i]=false;
TSSkinMesh::smInitTransformList[i] = NULL;
TSSkinMesh::smVertexIndexList[i] = NULL;
TSSkinMesh::smBoneIndexList[i] = NULL;
TSSkinMesh::smWeightList[i] = NULL;
TSSkinMesh::smNodeIndexList[i] = NULL;
}
// skins
ptr32 = alloc.allocShape32(numSkins);
for (i=0; i<numSkins; i++)
{
bool skip = i<detailFirstSkin[skipDL];
TSSkinMesh * skin = (TSSkinMesh*)TSMesh::assembleMesh(TSMesh::SkinMeshType,skip);
if (meshes.address())
{
// add pointer to skin in shapes list of meshes
// we reserved room for this above...
meshes.set(meshes.address(),meshes.size()+1);
meshes[meshes.size()-1] = skip ? NULL : skin;
}
// fill in location of verts, tverts, and normals for shared detail levels
if (skin)
{
TSMesh::smVertsList[i] = skin->initialVerts.address();
TSMesh::smTVertsList[i] = skin->tverts.address();
TSMesh::smNormsList[i] = skin->initialNorms.address();
TSMesh::smEncodedNormsList[i] = skin->encodedNorms.address();
TSMesh::smDataCopied[i] = !skip; // as long as we didn't skip this mesh, the data should be in shape now
TSSkinMesh::smInitTransformList[i] = skin->initialTransforms.address();
TSSkinMesh::smVertexIndexList[i] = skin->vertexIndex.address();
TSSkinMesh::smBoneIndexList[i] = skin->boneIndex.address();
TSSkinMesh::smWeightList[i] = skin->weight.address();
TSSkinMesh::smNodeIndexList[i] = skin->nodeIndex.address();
}
}
alloc.checkGuard();
// we now have skins in mesh list...add skin objects to object list and patch things up
fixupOldSkins(numMeshes,numSkins,numDetails,detailFirstSkin,detailNumSkins);
}
// allocate storage space for some arrays (filled in during Shape::init)...
ptr32 = alloc.allocShape32(numDetails);
alphaIn.set(ptr32,numDetails);
ptr32 = alloc.allocShape32(numDetails);
alphaOut.set(ptr32,numDetails);
mPreviousMerge.setSize(numObjects);
for (i = 0; i < numObjects; ++i)
mPreviousMerge[i] = -1;
mExportMerge = smReadVersion >= 23;
}
void TSShape::disassembleShape()
{
S32 i;
// set counts...
S32 numNodes = alloc.set32(nodes.size());
S32 numObjects = alloc.set32(objects.size());
S32 numDecals = alloc.set32(decals.size());
S32 numSubShapes = alloc.set32(subShapeFirstNode.size());
S32 numIflMaterials = alloc.set32(iflMaterials.size());
S32 numNodeRotations = alloc.set32(nodeRotations.size());
S32 numNodeTranslations = alloc.set32(nodeTranslations.size());
S32 numNodeUniformScales = alloc.set32(nodeUniformScales.size());
S32 numNodeAlignedScales = alloc.set32(nodeAlignedScales.size());
S32 numNodeArbitraryScales = alloc.set32(nodeArbitraryScaleFactors.size());
S32 numGroundFrames = alloc.set32(groundTranslations.size());
S32 numObjectStates = alloc.set32(objectStates.size());
S32 numDecalStates = alloc.set32(decalStates.size());
S32 numTriggers = alloc.set32(triggers.size());
S32 numDetails = alloc.set32(details.size());
S32 numMeshes = alloc.set32(meshes.size());
S32 numNames = alloc.set32(names.size());
alloc.set32((S32)mSmallestVisibleSize);
alloc.set32(mSmallestVisibleDL);
alloc.setGuard();
// get bounds...
alloc.copyToBuffer32((S32*)&radius,1);
alloc.copyToBuffer32((S32*)&tubeRadius,1);
alloc.copyToBuffer32((S32*)&center,3);
alloc.copyToBuffer32((S32*)&bounds,6);
alloc.setGuard();
// copy various vectors...
alloc.copyToBuffer32((S32*)nodes.address(),numNodes*5);
alloc.setGuard();
alloc.copyToBuffer32((S32*)objects.address(),numObjects*6);
alloc.setGuard();
alloc.copyToBuffer32((S32*)decals.address(),numDecals*5);
alloc.setGuard();
alloc.copyToBuffer32((S32*)iflMaterials.address(),numIflMaterials*5);
alloc.setGuard();
alloc.copyToBuffer32((S32*)subShapeFirstNode.address(),numSubShapes);
alloc.copyToBuffer32((S32*)subShapeFirstObject.address(),numSubShapes);
alloc.copyToBuffer32((S32*)subShapeFirstDecal.address(),numSubShapes);
alloc.setGuard();
alloc.copyToBuffer32((S32*)subShapeNumNodes.address(),numSubShapes);
alloc.copyToBuffer32((S32*)subShapeNumObjects.address(),numSubShapes);
alloc.copyToBuffer32((S32*)subShapeNumDecals.address(),numSubShapes);
alloc.setGuard();
// default transforms...
alloc.copyToBuffer16((S16*)defaultRotations.address(),numNodes*4);
alloc.copyToBuffer32((S32*)defaultTranslations.address(),numNodes*3);
// animated transforms...
alloc.copyToBuffer16((S16*)nodeRotations.address(),numNodeRotations*4);
alloc.copyToBuffer32((S32*)nodeTranslations.address(),numNodeTranslations*3);
alloc.setGuard();
// ...with scale
alloc.copyToBuffer32((S32*)nodeUniformScales.address(),numNodeUniformScales);
alloc.copyToBuffer32((S32*)nodeAlignedScales.address(),numNodeAlignedScales*3);
alloc.copyToBuffer32((S32*)nodeArbitraryScaleFactors.address(),numNodeArbitraryScales*3);
alloc.copyToBuffer16((S16*)nodeArbitraryScaleRots.address(),numNodeArbitraryScales*4);
alloc.setGuard();
alloc.copyToBuffer32((S32*)groundTranslations.address(),3*numGroundFrames);
alloc.copyToBuffer16((S16*)groundRotations.address(),4*numGroundFrames);
alloc.setGuard();
// object states..
alloc.copyToBuffer32((S32*)objectStates.address(),numObjectStates*3);
alloc.setGuard();
// decal states...
alloc.copyToBuffer32((S32*)decalStates.address(),numDecalStates);
alloc.setGuard();
// frame triggers
alloc.copyToBuffer32((S32*)triggers.address(),numTriggers*2);
alloc.setGuard();
// details
alloc.copyToBuffer32((S32*)details.address(),numDetails*7);
alloc.setGuard();
// read in the meshes (sans skins)...
bool * isMesh = new bool[numMeshes]; // funny business because decals are pretend meshes (legacy issue)
for (i=0;i<numMeshes;i++)
isMesh[i]=false;
for (i=0; i<objects.size(); i++)
{
for (S32 j=0; j<objects[i].numMeshes; j++)
// even if an empty mesh, it's a mesh...
isMesh[objects[i].startMeshIndex+j]=true;
}
for (i=0; i<numMeshes; i++)
{
TSMesh * mesh = NULL;
TSDecalMesh * decalMesh = NULL;
if (isMesh[i])
mesh = meshes[i];
else
decalMesh = (TSDecalMesh*)meshes[i];
alloc.set32(mesh ? mesh->getMeshType() : (decalMesh ? TSMesh::DecalMeshType : TSMesh::NullMeshType));
if (mesh)
mesh->disassemble();
else if (decalMesh)
decalMesh->disassemble();
}
delete [] isMesh;
alloc.setGuard();
// names
for (i=0; i<numNames; i++)
alloc.copyToBuffer8((S8*)names[i],dStrlen(names[i])+1);
alloc.setGuard();
}
//-------------------------------------------------
// write whole shape
//-------------------------------------------------
void TSShape::write(Stream * s)
{
// write version
s->write(smVersion | (mExporterVersion<<16));
alloc.setWrite();
disassembleShape();
S32 * buffer32 = alloc.getBuffer32();
S16 * buffer16 = alloc.getBuffer16();
S8 * buffer8 = alloc.getBuffer8();
S32 size32 = alloc.getBufferSize32();
S32 size16 = alloc.getBufferSize16();
S32 size8 = alloc.getBufferSize8();
// convert sizes to dwords...
if (size16 & 1)
size16 += 2;
size16 >>= 1;
if (size8 & 3)
size8 += 4;
size8 >>= 2;
S32 sizeMemBuffer, start16, start8;
sizeMemBuffer = size32 + size16 + size8;
start16 = size32;
start8 = start16+size16;
// in dwords -- write will properly endian-flip.
s->write(sizeMemBuffer);
s->write(start16);
s->write(start8);
// endian-flip the entire write buffers.
fixEndian(buffer32,buffer16,buffer8,size32,size16,size8);
// now write buffers
s->write(size32*4,buffer32);
s->write(size16*4,buffer16);
s->write(size8 *4,buffer8);
// write sequences - write will properly endian-flip.
s->write(sequences.size());
for (S32 i=0; i<sequences.size(); i++)
sequences[i].write(s);
// write material list - write will properly endian-flip.
materialList->write(*s);
delete [] buffer32;
delete [] buffer16;
delete [] buffer8;
}
//-------------------------------------------------
// read whole shape
//-------------------------------------------------
bool TSShape::read(Stream * s)
{
// read version - read handles endian-flip
s->read(&smReadVersion);
mExporterVersion = smReadVersion >> 16;
smReadVersion &= 0xFF;
if (smReadVersion>smVersion)
{
// error -- don't support future versions yet :>
Con::errorf(ConsoleLogEntry::General,
"Error: attempt to load a version %i dts-shape, can currently only load version %i and before.",
smReadVersion,smVersion);
return false;
}
mReadVersion = smReadVersion;
S32 * memBuffer32;
S16 * memBuffer16;
S8 * memBuffer8;
S32 count32, count16, count8;
if (mReadVersion<19)
{
Con::printf("... Shape with old version.");
readOldShape(s,memBuffer32,memBuffer16,memBuffer8,count32,count16,count8);
}
else
{
S32 i;
U32 sizeMemBuffer, startU16, startU8;
// in dwords. - read handles endian-flip
s->read(&sizeMemBuffer);
s->read(&startU16);
s->read(&startU8);
if (s->getStatus()!=Stream::Ok)
{
Con::errorf(ConsoleLogEntry::General, "Error: bad shape file.");
return false;
}
S32 * tmp = new S32[sizeMemBuffer];
s->read(sizeof(S32)*sizeMemBuffer,(U8*)tmp);
memBuffer32 = tmp;
memBuffer16 = (S16*)(tmp+startU16);
memBuffer8 = (S8*)(tmp+startU8);
count32 = startU16;
count16 = startU8-startU16;
count8 = sizeMemBuffer-startU8;
// read sequences
S32 numSequences;
s->read(&numSequences);
sequences.setSize(numSequences);
for (i=0; i<numSequences; i++)
{
constructInPlace(&sequences[i]);
sequences[i].read(s);
}
// read material list
delete materialList; // just in case...
materialList = new TSMaterialList;
materialList->read(*s);
}
// since we read in the buffers, we need to endian-flip their entire contents...
fixEndian(memBuffer32,memBuffer16,memBuffer8,count32,count16,count8);
alloc.setRead(memBuffer32,memBuffer16,memBuffer8,true);
assembleShape(); // determine size of buffer needed
S32 buffSize = alloc.getSize();
alloc.doAlloc();
mMemoryBlock = alloc.getBuffer();
alloc.setRead(memBuffer32,memBuffer16,memBuffer8,false);
assembleShape(); // copy to buffer
AssertFatal(alloc.getSize()==buffSize,"TSShape::read: shape data buffer size mis-calculated");
if (smReadVersion<19)
{
delete [] memBuffer32;
delete [] memBuffer16;
delete [] memBuffer8;
}
else
delete [] memBuffer32; // this covers all the buffers
if (smInitOnRead)
init();
return true;
}
void TSShape::fixEndian(S32 * buff32, S16 * buff16, S8 *, S32 count32, S32 count16, S32)
{
// if endian-ness isn't the same, need to flip the buffer contents.
if (0x12345678!=convertLEndianToHost(0x12345678))
{
for (S32 i=0; i<count32; i++)
buff32[i]=convertLEndianToHost(buff32[i]);
for (S32 i=0; i<count16*2; i++)
buff16[i]=convertLEndianToHost(buff16[i]);
}
}
/** Read the .ifl material file
The .ifl file (output by max) is essentially a keyframe list for
the animation and contains the names of the textures along
with a duration time. This function parses the file to and appends
the textures to the shape's master material list.
*/
void TSShape::readIflMaterials(const char* shapePath)
{
if (mFlags & IflInit)
return;
for (S32 i=0; i<iflMaterials.size(); i++)
{
IflMaterial & iflMaterial = iflMaterials[i];
iflMaterial.firstFrame = materialList->size(); // next material added will be our first frame
iflMaterial.firstFrameOffTimeIndex = iflFrameOffTimes.size(); // next entry added will be our first
iflMaterial.numFrames = 0; // we'll increment as we read the file
U32 totalTime = 0;
// Fix slashes that face the wrong way
char * pName = (char*)getName(iflMaterial.nameIndex);
S32 len = dStrlen(pName);
for (S32 j=0; j<len; j++)
if (pName[j]=='\\')
pName[j]='/';
// Open the file which should be located in the same directory
// as the .dts shape.
// Tg: I cut out a some backwards compatibilty code dealing
// with the old file prefixing. If someone is having compatibilty
// problems, you may want to check the previous version of this
// file.
char nameBuffer[256];
dSprintf(nameBuffer,sizeof(nameBuffer),"%s/%s",shapePath,pName);
Stream * s = ResourceManager->openStream(nameBuffer);
if (!s || s->getStatus() != Stream::Ok)
{
iflMaterial.firstFrame = iflMaterial.materialSlot; // avoid over-runs
if (s)
ResourceManager->closeStream(s);
continue;
}
// Parse the file, creat ifl "key" frames and append
// texture names to the shape's material list.
char buffer[512];
U32 duration;
while (s->getStatus() == Stream::Ok)
{
s->readLine((U8*)buffer,512);
if (s->getStatus() == Stream::Ok || s->getStatus() == Stream::EOS)
{
char * pos = buffer;
while (*pos)
{
if (*pos=='\t')
*pos=' ';
pos++;
}
pos = buffer;
// Find the name and duration start points
while (*pos && *pos==' ')
pos++;
char * pos2 = dStrchr(pos,' ');
if (pos2)
{
*pos2='\0';
pos2++;
while (*pos2 && *pos2==' ')
pos2++;
}
// skip line with no material
if (!*pos)
continue;
// Extract frame duration
duration = pos2 ? dAtoi(pos2) : 1;
if (duration==0)
// just in case...
duration = 1;
// Tg: I cut out a some backwards compatibilty code dealing
// with the old file prefixing. If someone is having compatibilty
// problems, you may want to check the previous version of this
// file.
// Strip off texture extension first.
if ((pos2 = dStrchr(pos,'.')) != 0)
*pos2 = '\0';
materialList->push_back(pos,TSMaterialList::IflFrame);
//
totalTime += duration;
iflFrameOffTimes.push_back((1.0f/30.0f) * (F32)totalTime); // ifl units are frames (1 frame = 1/30 sec)
iflMaterial.numFrames++;
}
}
// close the file...
ResourceManager->closeStream(s);
}
initMaterialList();
mFlags |= IflInit;
}
ResourceInstance *constructTSShape(Stream &stream)
{
TSShape * ret = new TSShape;
if (!ret->read(&stream))
{
// Failed, so clean up and set to NULL so we'll return NULL.
SAFE_DELETE(ret);
}
return ret;
}
#if 1
TSShape::ConvexHullAccelerator* TSShape::getAccelerator(S32 dl)
{
AssertFatal(dl < details.size(), "Error, bad detail level!");
if (dl == -1)
return NULL;
if (detailCollisionAccelerators[dl] == NULL)
computeAccelerator(dl);
AssertFatal(detailCollisionAccelerators[dl] != NULL, "This should be non-null after computing it!");
return detailCollisionAccelerators[dl];
}
void TSShape::computeAccelerator(S32 dl)
{
AssertFatal(dl < details.size(), "Error, bad detail level!");
// Have we already computed this?
if (detailCollisionAccelerators[dl] != NULL)
return;
// Create a bogus features list...
ConvexFeature cf;
MatrixF mat(true);
Point3F n(0, 0, 1);
const TSDetail* detail = &details[dl];
S32 ss = detail->subShapeNum;
S32 od = detail->objectDetailNum;
S32 start = subShapeFirstObject[ss];
S32 end = subShapeNumObjects[ss] + start;
if (start < end)
{
// run through objects and collide
// DMMNOTE: This assumes that the transform of the collision hulls is
// identity...
U32 surfaceKey = 0;
for (S32 i = start; i < end; i++)
{
const TSObject* obj = &objects[i];
if (obj->numMeshes && od < obj->numMeshes) {
TSMesh* mesh = meshes[obj->startMeshIndex + od];
if (mesh)
mesh->getFeatures(0, mat, n, &cf, surfaceKey);
}
}
}
Vector<Point3F> fixedVerts;
VECTOR_SET_ASSOCIATION(fixedVerts);
S32 i;
for (i = 0; i < cf.mVertexList.size(); i++) {
S32 j;
bool found = false;
for (j = 0; j < cf.mFaceList.size(); j++) {
if (cf.mFaceList[j].vertex[0] == i ||
cf.mFaceList[j].vertex[1] == i ||
cf.mFaceList[j].vertex[2] == i) {
found = true;
break;
}
}
if (!found)
continue;
found = false;
for (j = 0; j < fixedVerts.size(); j++) {
if (fixedVerts[j] == cf.mVertexList[i]) {
found = true;
break;
}
}
if (found == true) {
// Ok, need to replace any references to vertex i in the facelists with
// a reference to vertex j in the fixed list
for (S32 k = 0; k < cf.mFaceList.size(); k++) {
for (S32 l = 0; l < 3; l++) {
if (cf.mFaceList[k].vertex[l] == i)
cf.mFaceList[k].vertex[l] = j;
}
}
} else {
for (S32 k = 0; k < cf.mFaceList.size(); k++) {
for (S32 l = 0; l < 3; l++) {
if (cf.mFaceList[k].vertex[l] == i)
cf.mFaceList[k].vertex[l] = fixedVerts.size();
}
}
fixedVerts.push_back(cf.mVertexList[i]);
}
}
cf.mVertexList.setSize(0);
cf.mVertexList = fixedVerts;
// Ok, so now we have a vertex list. Lets copy that out...
ConvexHullAccelerator* accel = new ConvexHullAccelerator;
detailCollisionAccelerators[dl] = accel;
accel->numVerts = cf.mVertexList.size();
accel->vertexList = new Point3F[accel->numVerts];
dMemcpy(accel->vertexList, cf.mVertexList.address(), sizeof(Point3F) * accel->numVerts);
accel->normalList = new PlaneF[cf.mFaceList.size()];
for (i = 0; i < cf.mFaceList.size(); i++)
accel->normalList[i] = cf.mFaceList[i].normal;
accel->emitStrings = new U8*[accel->numVerts];
dMemset(accel->emitStrings, 0, sizeof(U8*) * accel->numVerts);
for (i = 0; i < accel->numVerts; i++) {
S32 j;
Vector<U32> faces;
VECTOR_SET_ASSOCIATION(faces);
for (j = 0; j < cf.mFaceList.size(); j++) {
if (cf.mFaceList[j].vertex[0] == i ||
cf.mFaceList[j].vertex[1] == i ||
cf.mFaceList[j].vertex[2] == i) {
faces.push_back(j);
}
}
AssertFatal(faces.size() != 0, "Huh? Vertex unreferenced by any faces");
// Insert all faces that didn't make the first cut, but share a plane with
// a face that's on the short list.
for (j = 0; j < cf.mFaceList.size(); j++) {
bool found = false;
S32 k;
for (k = 0; k < faces.size(); k++) {
if (faces[k] == j)
found = true;
}
if (found)
continue;
found = false;
for (k = 0; k < faces.size(); k++) {
if (mDot(accel->normalList[faces[k]], accel->normalList[j]) > 0.999) {
found = true;
break;
}
}
if (found)
faces.push_back(j);
}
Vector<U32> vertRemaps;
VECTOR_SET_ASSOCIATION(vertRemaps);
for (j = 0; j < faces.size(); j++) {
for (U32 k = 0; k < 3; k++) {
U32 insert = cf.mFaceList[faces[j]].vertex[k];
bool found = false;
for (S32 l = 0; l < vertRemaps.size(); l++) {
if (insert == vertRemaps[l]) {
found = true;
break;
}
}
if (!found)
vertRemaps.push_back(insert);
}
}
Vector<Point2I> edges;
VECTOR_SET_ASSOCIATION(edges);
for (j = 0; j < faces.size(); j++) {
for (U32 k = 0; k < 3; k++) {
U32 edgeStart = cf.mFaceList[faces[j]].vertex[(k + 0) % 3];
U32 edgeEnd = cf.mFaceList[faces[j]].vertex[(k + 1) % 3];
U32 e0 = getMin(edgeStart, edgeEnd);
U32 e1 = getMax(edgeStart, edgeEnd);
bool found = false;
for (S32 l = 0; l < edges.size(); l++) {
if (edges[l].x == e0 && edges[l].y == e1) {
found = true;
break;
}
}
if (!found)
edges.push_back(Point2I(e0, e1));
}
}
AssertFatal(vertRemaps.size() < 256 && faces.size() < 256 && edges.size() < 256,
"Error, ran over the shapebase assumptions about convex hulls.");
U32 emitStringLen = 1 + vertRemaps.size() +
1 + (edges.size() * 2) +
1 + (faces.size() * 4);
accel->emitStrings[i] = new U8[emitStringLen];
U32 currPos = 0;
accel->emitStrings[i][currPos++] = vertRemaps.size();
for (j = 0; j < vertRemaps.size(); j++)
accel->emitStrings[i][currPos++] = vertRemaps[j];
accel->emitStrings[i][currPos++] = edges.size();
for (j = 0; j < edges.size(); j++) {
S32 l;
U32 old = edges[j].x;
bool found = false;
for (l = 0; l < vertRemaps.size(); l++) {
if (vertRemaps[l] == old) {
found = true;
accel->emitStrings[i][currPos++] = l;
break;
}
}
AssertFatal(found, "Error, couldn't find the remap!");
old = edges[j].y;
found = false;
for (l = 0; l < vertRemaps.size(); l++) {
if (vertRemaps[l] == old) {
found = true;
accel->emitStrings[i][currPos++] = l;
break;
}
}
AssertFatal(found, "Error, couldn't find the remap!");
}
accel->emitStrings[i][currPos++] = faces.size();
for (j = 0; j < faces.size(); j++) {
accel->emitStrings[i][currPos++] = faces[j];
for (U32 k = 0; k < 3; k++) {
U32 old = cf.mFaceList[faces[j]].vertex[k];
bool found = false;
for (S32 l = 0; l < vertRemaps.size(); l++) {
if (vertRemaps[l] == old) {
found = true;
accel->emitStrings[i][currPos++] = l;
break;
}
}
AssertFatal(found, "Error, couldn't find the remap!");
}
}
AssertFatal(currPos == emitStringLen, "Error, over/underflowed the emission string!");
}
}
#endif