//----------------------------------------------------------------------------- // 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; ivertexList; 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=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=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=0) decalIndex = decals[decalIndex].nextSibling; decals[decalIndex].nextSibling = i; } } mFlags = 0; for (i=0; i=0; i--) { if (igetNumPolys() : 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; inumMeshes; 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=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; ssprimitives.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 && dlbuildConvexHull(); } return ok; } Vector gTempNodeTransforms(__FILE__, __LINE__); void TSShape::computeBounds(S32 dl, Box3F & bounds) const { // if dl==-1, nothing to do if (dl==-1) return; AssertFatal(dl>=0 && dlsubShapeNum; 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=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; inumMeshes ? 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=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=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() || curDecal23) 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;i21) { // more node sequence data...scale nodeUniformScales.setSize(numNodeUniformScales); for (i=0;i23) { groundTranslations.setSize(numGroundFrames); for (i=0;i15) { // 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; iverts.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=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; iinsert(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; iinitialVerts.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;igetMeshType() : (decalMesh ? TSMesh::DecalMeshType : TSMesh::NullMeshType)); if (mesh) mesh->disassemble(); else if (decalMesh) decalMesh->disassemble(); } delete [] isMesh; alloc.setGuard(); // names for (i=0; iwrite(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; iwrite(*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; iread(*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; isize(); // 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; jopenStream(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 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 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 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 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