1715 lines
55 KiB
C++
Executable File
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*)¢er,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*)¢er,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
|
|
|