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
 | |
| 
 | 
