//----------------------------------------------------------------------------- // Torque Game Engine // Copyright (C) GarageGames.com, Inc. //----------------------------------------------------------------------------- #include "max2dtsExporter/translucentSort.h" #include "ts/tsMesh.h" U32 * doh = NULL; void crash() { *doh=123; } F32 epsilon = 0.0001f; // hold indices and sizes of biggest faces...these are marked as higher priority for splitting with Vector bigFaces; Vector bigFaceSizes; // planes in this list are hidden because we are on the other side of them at the time... Vector noAddNormals; TranslucentSort::TranslucentSort(Vector & faces, Vector & indices, Vector & verts, Vector & norms, Vector & tverts, S32 numBigFaces, S32 maxDepth, bool zLayerUp, bool zLayerDown) : mFaces(faces), mIndices(indices), mVerts(verts), mNorms(norms), mTVerts(tverts) { mNumBigFaces = numBigFaces; mMaxDepth = maxDepth; mZLayerUp = zLayerUp; mZLayerDown = zLayerDown; bigFaces.clear(); bigFaceSizes.clear(); initFaces(); frontSort = backSort = NULL; currentDepth = 0; } TranslucentSort::TranslucentSort(TranslucentSort * from) : mFaces(from->mFaces), mIndices(from->mIndices), mVerts(from->mVerts), mNorms(from->mNorms), mTVerts(from->mTVerts) { faceInfoList.setSize(mFaces.size()); for (S32 i=0; ifaceInfoList[i]; } currentDepth = from->currentDepth + 1; mMaxDepth = from->mMaxDepth; mZLayerUp = from->mZLayerUp; mZLayerDown = from->mZLayerDown; mNumBigFaces = 0; // never used... frontSort = backSort = NULL; } TranslucentSort::~TranslucentSort() { delete frontSort; delete backSort; S32 i; for (i=0; iused = false; } for (i=0; iused) break; if (i==faceInfoList.size()) return; // no unused faces... while (1) { // 1. select faces with no one behind them -- these guys get drawn first backClusters.increment(); backClusters.last() = new TSIntegerSet; // backClusters.last()->setSize(mFaces.size()); for (i=0; iused && !faceInfoList[i]->isBehindMe.testAll() && !faceInfoList[i]->isCutByMe.testAll()) { backClusters.last()->set(i); faceInfoList[i]->used = true; // select as used so we don't grab it below } // 2. select faces with no one in front of them -- these guys get drawn last frontClusters.insert(0); frontClusters.first() = new TSIntegerSet; // frontClusters.first()->setSize(mFaces.size()); for (i=0; iused && !faceInfoList[i]->isInFrontOfMe.testAll() && !faceInfoList[i]->isCutByMe.testAll()) { frontClusters.first()->set(i); faceInfoList[i]->used = true; // this won't have any effect, but it's here to parallel above } // 3. clear above faces and repeat 1&2 until no more faces found in either step TSIntegerSet removeThese = *backClusters.last(); removeThese.overlap(*frontClusters.first()); if (!removeThese.testAll()) // didn't remove anything... break; clearFaces(removeThese); } // 4. pick face cutting fewest other faces and resulting in most balanced split, call this cutFace S32 fewestCuts; S32 balance; S32 priority = 0; S32 cutFace = -1; for (i=0; iused) continue; S32 cut = 0, front = 0, back = 0; for (S32 j=0; jused) continue; if (faceInfoList[i]->isCutByMe.test(j)) cut++; if (faceInfoList[i]->isInFrontOfMe.test(j)) front++; if (faceInfoList[i]->isBehindMe.test(j)) back++; } if (cutFace>=0) { if (faceInfoList[i]->priority < priority) continue; if (faceInfoList[i]->priority == priority) { if ( (cut>fewestCuts) || (cut==fewestCuts && mAbs(front-back)>=balance) ) continue; } } // if we get this far, this is our new cutFace cutFace = i; fewestCuts = cut; priority = faceInfoList[i]->priority; balance = mAbs(front-back); } if (cutFace>=0 && currentDepthisCutByMe.testAll()) { S32 startSize = mFaces.size(); // won't need to split beyond here, even though more faces added for (i=0; iused && faceInfoList[cutFace]->isCutByMe.test(i)) splitFace(i,faceInfoList[cutFace]->normal,faceInfoList[cutFace]->k); // may be new faces and some old faces may have been disabled, recompute face info for (i=0; iused) setFaceInfo(mFaces[i],*faceInfoList[i]); } S32 startNumFaces = mFaces.size(); TSIntegerSet disableSet; // 6. branch into two orders depending on which side of cutFace camera is, perform translucent sort on each // back backSort = new TranslucentSort(this); // disableSet.setSize(mFaces.size()); disableSet.clearAll(); for (i=0; ifaceInfoList[i]->used || backSort->faceInfoList[cutFace]->isBehindMe.test(i)) continue; if (backSort->faceInfoList[cutFace]->isCutByMe.test(i)) crash(); // doh, perform hard assert :(... if (backSort->faceInfoList[cutFace]->isCoplanarWithMe.test(i) || cutFace==i) { if (mDot(backSort->faceInfoList[cutFace]->normal,backSort->faceInfoList[i]->normal)<0.0f) continue; } else if (!backSort->faceInfoList[cutFace]->isInFrontOfMe.test(i) && cutFace!=i) crash(); disableSet.set(i); } if (!disableSet.testAll()) crash(); backSort->clearFaces(disableSet); backSort->sort(); if (backSort->backSort==NULL && backSort->frontSort==NULL && backSort->frontClusters.empty() && backSort->backClusters.empty()) { // empty, no reason to keep backSort delete backSort; backSort = NULL; } // create faceInfo entry for any faces that got added (set used=true) faceInfoList.setSize(mFaces.size()); for (i=startNumFaces; iused = true; } // front frontSort = new TranslucentSort(this); // disableSet.setSize(mFaces.size()); disableSet.clearAll(); for (i=0; ifaceInfoList[i]->used || frontSort->faceInfoList[cutFace]->isInFrontOfMe.test(i)) continue; if (frontSort->faceInfoList[cutFace]->isCutByMe.test(i)) crash(); // doh, perform hard assert... if (frontSort->faceInfoList[cutFace]->isCoplanarWithMe.test(i) || cutFace==i) { if (mDot(frontSort->faceInfoList[cutFace]->normal,frontSort->faceInfoList[i]->normal)>0.0f) continue; } else if (!frontSort->faceInfoList[cutFace]->isBehindMe.test(i) && i!=cutFace) crash(); disableSet.set(i); } if (!disableSet.testAll()) crash(); frontSort->clearFaces(disableSet); frontSort->sort(); if (frontSort->backSort==NULL && frontSort->frontSort==NULL && frontSort->frontClusters.empty() && frontSort->backClusters.empty()) { // empty, no reason to keep backSort delete backSort; backSort = NULL; } // setup cut plane splitNormal = faceInfoList[cutFace]->normal; splitK = faceInfoList[cutFace]->k; } else if (cutFace>=0) { // we've gotten too deep, just dump the remaing faces -- but dump in best order we can if (mZLayerUp) layerSort(middleCluster,true); else if (mZLayerDown) layerSort(middleCluster,false); else copeSort(middleCluster); } } // called copeSort because we can't really get a perfect order, we'll do the best we can void TranslucentSort::copeSort(Vector & cluster) { Vector frontOrderedCluster, backOrderedCluster; // restore after following loop saveFaceInfo(); while (1) { S32 bestFace = -1; S32 bestCount = 0x7FFFFFFF; bool front; // we need to find face with fewest polys behind or in front (cut implies both) for (S32 i=0; iused) continue; S32 frontCount = 0; S32 backCount = 0; for (S32 j=0; jused) continue; if (faceInfoList[i]->isInFrontOfMe.test(j)) frontCount++; else if (faceInfoList[i]->isBehindMe.test(j)) backCount++; else if (faceInfoList[i]->isCutByMe.test(j)) { frontCount++; backCount++; } } if (backCount < bestCount || bestFace<0) { bestCount = backCount; bestFace = i; front = false; } if (frontCount==0 && frontCount < bestCount) { bestCount = frontCount; bestFace = i; front = true; } } if (bestFace>=0) { if (front) frontOrderedCluster.push_front(bestFace); else backOrderedCluster.push_back(bestFace); TSIntegerSet disableSet; // disableSet.setSize(mFaces.size()); disableSet.set(bestFace); clearFaces(disableSet); } else break; } cluster = backOrderedCluster; cluster.merge(frontOrderedCluster); // we need face info back... restoreFaceInfo(); // we have a good ordering...but see if we can make some local optimizations for (S32 i=0; i & cluster, bool pointUp) { // sort up-pointing faces from bottom to top and down-pointing faces from top to bottom Vector upCluster, downCluster; Vector upZ, downZ; // go through each face, decide which list to add it to and where for (S32 i=0; iused) continue; TSDrawPrimitive & face = mFaces[i]; S32 idx0 = mIndices[face.start + 0]; S32 idx1 = mIndices[face.start + 1]; S32 idx2 = mIndices[face.start + 2]; Point3F & v0 = mVerts[idx0]; Point3F & v1 = mVerts[idx1]; Point3F & v2 = mVerts[idx2]; // find smallest z F32 smallZ = v0.z < v1.z ? v0.z : v1.z; smallZ = smallZ < v2.z ? smallZ : v2.z; // find largets Z F32 bigZ = v0.z > v1.z ? v0.z : v1.z; bigZ = bigZ > v2.z ? bigZ : v2.z; F32 sortBy = pointUp ? smallZ : bigZ; S32 j; if (faceInfoList[i]->normal.z>0.0f) { // we face up if (upCluster.empty()) { upCluster.push_back(i); upZ.push_back(sortBy); } else { // keep sorted in order of increasing z (so bottom faces are first) for (j=0; jdownZ[j]) break; downZ.insert(j); downZ[j] = sortBy; downCluster.insert(j); downCluster[j] = i; } } } if (pointUp) { cluster = upCluster; cluster.merge(downCluster); } else { cluster = downCluster; cluster.merge(upCluster); } } void TranslucentSort::saveFaceInfo() { if (saveFaceInfoList.size()) { for (S32 i=0; i=mDot(edge12,edge12) && mDot(edge01,edge01)>=mDot(edge20,edge20)) { // edge01 biggest gap mCross(edge12,edge20,&normal); normal *= -1.0f; } else if (mDot(edge12,edge12)>=mDot(edge20,edge20) && mDot(edge12,edge12)>=mDot(edge01,edge01)) { // edge12 biggest gap mCross(edge20,edge01,&normal); normal *= -1.0f; } else { // edge20 biggest gap mCross(edge01,edge12,&normal); normal *= -1.0f; } normal.normalize(); faceInfo.k = mDot(normal,vert0); if (setPriority) { faceInfo.priority = 0; F32 maxExtent = mDot(edge01,edge01); if (maxExtentbigFaceSizes[i]) { bigFaceSizes.insert(i); bigFaces.insert(i); bigFaceSizes[i] = maxExtent; bigFaces[i] = (S32)(&face-mFaces.address()); for (; ipriority = imNumBigFaces) { bigFaceSizes.decrement(); bigFaces.decrement(); } break; } } } } void TranslucentSort::setFaceInfo(TSDrawPrimitive & face, FaceInfo & faceInfo) { // faceInfo.isInFrontOfMe.setSize(mFaces.size()); // faceInfo.isBehindMe.setSize(mFaces.size()); // faceInfo.isCutByMe.setSize(mFaces.size()); // faceInfo.isCoplanarWithMe.setSize(mFaces.size()); faceInfo.isInFrontOfMe.clearAll(); faceInfo.isBehindMe.clearAll(); faceInfo.isCutByMe.clearAll(); faceInfo.isCoplanarWithMe.clearAll(); Point3F & normal = faceInfo.normal; F32 & k = faceInfo.k; S32 myIndex = (S32)(&face-mFaces.address()); for (S32 i=0; iused) continue; TSDrawPrimitive & otherFace = mFaces[i]; S32 idx0 = mIndices[otherFace.start + 0]; S32 idx1 = mIndices[otherFace.start + 1]; S32 idx2 = mIndices[otherFace.start + 2]; Point3F & v0 = mVerts[idx0]; Point3F & v1 = mVerts[idx1]; Point3F & v2 = mVerts[idx2]; bool hasFrontVert = false, hasBackVert = false; if (mDot(normal,v0) > k + epsilon) hasFrontVert = true; else if (mDot(normal,v0) < k - epsilon) hasBackVert = true; if (mDot(normal,v1) > k + epsilon) hasFrontVert = true; else if (mDot(normal,v1) < k - epsilon) hasBackVert = true; if (mDot(normal,v2) > k + epsilon) hasFrontVert = true; else if (mDot(normal,v2) < k - epsilon) hasBackVert = true; if (hasFrontVert && !hasBackVert) faceInfo.isInFrontOfMe.set(i); else if (!hasFrontVert && hasBackVert) faceInfo.isBehindMe.set(i); else if (hasFrontVert && hasBackVert) faceInfo.isCutByMe.set(i); else if (!hasFrontVert && !hasBackVert) faceInfo.isCoplanarWithMe.set(i); /* TODO: need to keep record of order still so following optimization doesn't work...add noContest set? // if I'm in front of someone who is in front of me, then treat as if neither of us are in front of the other if (faceInfo.isInFrontOfMe.test(i) && faceInfoList[i]->isInFrontOfMe.test(myIndex)) { faceInfo.isInFrontOfMe.clear(i); faceInfoList[i]->isInFrontOfMe.clear(myIndex); } // ditto for behind... if (faceInfo.isBehindMe.test(i) && faceInfoList[i]->isBehindMe.test(myIndex)) { faceInfo.isBehindMe.clear(i); faceInfoList[i]->isBehindMe.clear(myIndex); } */ } } bool TranslucentSort::anyInFrontOfPlane(Point3F normal, F32 k) { // make sure no face in use is in front of plane for (S32 i=0; iused) continue; S32 idx0 = mIndices[mFaces[i].start + 0]; S32 idx1 = mIndices[mFaces[i].start + 1]; S32 idx2 = mIndices[mFaces[i].start + 2]; if (mDot(normal,mVerts[idx0]) > k + epsilon) return true; if (mDot(normal,mVerts[idx1]) > k + epsilon) return true; if (mDot(normal,mVerts[idx2]) > k + epsilon) return true; } return false; } bool TranslucentSort::anyBehindPlane(Point3F normal, F32 k) { // make sure no face in use is behind plane for (S32 i=0; iused) continue; S32 idx0 = mIndices[mFaces[i].start + 0]; S32 idx1 = mIndices[mFaces[i].start + 1]; S32 idx2 = mIndices[mFaces[i].start + 2]; if (mDot(normal,mVerts[idx0]) < k - epsilon) return true; if (mDot(normal,mVerts[idx1]) < k - epsilon) return true; if (mDot(normal,mVerts[idx2]) < k - epsilon) return true; } return false; } void getMinMaxExtents(Point3F & x, Point3F & v0, Point3F & v1, Point3F & v2, F32 & xmin, F32 & xmax) { xmin = xmax = mDot(x,v0); F32 dot = mDot(x,v1); if (xmin>dot) xmin=dot; else if (xmaxdot) xmin=dot; else if (xmaxused = true; faceInfoList[faceIndex]->childFace1 = numFaces-3; faceInfoList[faceIndex]->childFace2 = numFaces-2; faceInfoList[faceIndex]->childFace3 = numFaces-1; initFaceInfo(mFaces[numFaces-3],*faceInfoList[numFaces-3],false); initFaceInfo(mFaces[numFaces-2],*faceInfoList[numFaces-2],false); initFaceInfo(mFaces[numFaces-1],*faceInfoList[numFaces-1],false); faceInfoList[numFaces-3]->priority = faceInfoList[faceIndex]->priority; faceInfoList[numFaces-2]->priority = faceInfoList[faceIndex]->priority; faceInfoList[numFaces-1]->priority = faceInfoList[faceIndex]->priority; faceInfoList[numFaces-3]->parentFace = faceIndex; faceInfoList[numFaces-2]->parentFace = faceIndex; faceInfoList[numFaces-1]->parentFace = faceIndex; } void TranslucentSort::splitFace2(S32 faceIndex, Point3F normal, F32 k) { S32 idx0 = mIndices[mFaces[faceIndex].start + 0]; S32 idx1 = mIndices[mFaces[faceIndex].start + 1]; S32 idx2 = mIndices[mFaces[faceIndex].start + 2]; Point3F v0 = mVerts[idx0]; Point3F v1 = mVerts[idx1]; Point3F v2 = mVerts[idx2]; F32 k0 = mDot(normal,v0); F32 k1 = mDot(normal,v1); F32 k2 = mDot(normal,v2); // make sure we got here legitimately if (mFabs(k0-k) >= epsilon && mFabs(k1-k) >= epsilon && mFabs(k2-k) >= epsilon) crash(); // find the odd man out (the vertex that is on the plane) S32 rogue; if (mFabs(k0-k) < epsilon) rogue = 0; else if (mFabs(k1-k) < epsilon) rogue = 1; else if (mFabs(k2-k) < epsilon) rogue = 2; else crash(); // re-order verts so that rogue vert is first vert idx0 = mIndices[mFaces[faceIndex].start + ((rogue+0)%3)]; idx1 = mIndices[mFaces[faceIndex].start + ((rogue+1)%3)]; idx2 = mIndices[mFaces[faceIndex].start + ((rogue+2)%3)]; v0 = mVerts[idx0]; v1 = mVerts[idx1]; v2 = mVerts[idx2]; k0 = mDot(normal,v0); k1 = mDot(normal,v1); k2 = mDot(normal,v2); Point2F tv0 = mTVerts[idx0]; Point2F tv1 = mTVerts[idx1]; Point2F tv2 = mTVerts[idx2]; Point3F n0 = mNorms[idx0]; Point3F n1 = mNorms[idx1]; Point3F n2 = mNorms[idx2]; // find intersection of edges and plane F32 a12 = (k-k1)/(k2-k1); Point3F v12 = v2-v1; v12 *= a12; v12 += v1; Point2F tv12 = tv2-tv1; tv12 *= a12; tv12 += tv1; // interpolate the normals too (we'll just linearly interpolate...perhaps slerp if later) Point3F n12 = n2-n1; n12 *= a12; n12 += n1; n12.normalize(); // add new vert S32 idx12 = mVerts.size(); mVerts.increment(); mTVerts.increment(); mNorms.increment(); mVerts[idx12] = v12; mNorms[idx12] = n12; mTVerts[idx12] = tv12; // add two faces mFaces.increment(); mFaces.increment(); TSDrawPrimitive & face0 = mFaces[mFaces.size()-2]; TSDrawPrimitive & face1 = mFaces[mFaces.size()-1]; // add idx0, idx2, idx12 face0.start = mIndices.size(); face0.numElements = 3; face0.matIndex = mFaces[faceIndex].matIndex; mIndices.push_back(idx0); mIndices.push_back(idx2); mIndices.push_back(idx12); // add idx0, idx12, idx1 face1.start = mIndices.size(); face1.numElements = 3; face1.matIndex = mFaces[faceIndex].matIndex; mIndices.push_back(idx0); mIndices.push_back(idx12); mIndices.push_back(idx1); // finally, set faceInfo S32 numFaces = mFaces.size(); faceInfoList.increment(); faceInfoList.last() = new FaceInfo; faceInfoList.increment(); faceInfoList.last() = new FaceInfo; faceInfoList[faceIndex]->used = true; faceInfoList[faceIndex]->childFace1 = numFaces-2; faceInfoList[faceIndex]->childFace2 = numFaces-1; faceInfoList[faceIndex]->childFace3 = -1; initFaceInfo(mFaces[numFaces-2],*faceInfoList[numFaces-2],false); initFaceInfo(mFaces[numFaces-1],*faceInfoList[numFaces-1],false); faceInfoList[numFaces-2]->priority = faceInfoList[faceIndex]->priority; faceInfoList[numFaces-1]->priority = faceInfoList[faceIndex]->priority; faceInfoList[numFaces-2]->parentFace = faceIndex; faceInfoList[numFaces-1]->parentFace = faceIndex; } void TranslucentSort::addFaces(Vector & addClusters, Vector & faces, Vector & indices, bool continueLast) { S32 startFaces = faces.size(); for (S32 i=0; i & faces, Vector & indices, bool continueLast) { TSIntegerSet toAdd = *addCluster; bool startNewFace = !continueLast || faces.empty(); while (toAdd.testAll()) { // for (S32 i=0; inormal) > 0.99f) toAdd.clear(i); } if (!toAdd.test(i)) continue; // add this face... if (startNewFace) { faces.increment(); faces.last().start = indices.size(); faces.last().numElements = 0; faces.last().matIndex = mFaces[i].matIndex; startNewFace = false; } faces.last().numElements += 3; indices.push_back(mIndices[mFaces[i].start+0]); indices.push_back(mIndices[mFaces[i].start+1]); indices.push_back(mIndices[mFaces[i].start+2]); toAdd.clear(i); } startNewFace = true; } } void TranslucentSort::addOrderedFaces(Vector & orderedCluster, Vector & faces, Vector & indices, bool continueLast) { Vector toAdd = orderedCluster; bool startNewFace = !continueLast || faces.empty(); while (!toAdd.empty()) { for (S32 i=0; inormal) > 0.99f) { toAdd.erase(i); i--; break; } } if (k!=noAddNormals.size()) continue; if (!startNewFace && mFaces[toAdd[i]].matIndex != faces.last().matIndex) continue; if (startNewFace) { faces.increment(); faces.last().start = indices.size(); faces.last().numElements = 0; faces.last().matIndex = mFaces[toAdd[i]].matIndex; startNewFace = false; } faces.last().numElements += 3; indices.push_back(mIndices[mFaces[toAdd[i]].start+0]); indices.push_back(mIndices[mFaces[toAdd[i]].start+1]); indices.push_back(mIndices[mFaces[toAdd[i]].start+2]); toAdd.erase(i); i--; } startNewFace = true; } } void TranslucentSort::generateClusters(Vector & clusters, Vector & faces, Vector & indices, S32 retIndex) { S32 idx = clusters.size(); clusters.increment(); clusters.increment(); // add back faces clusters[idx].startPrimitive = faces.size(); addFaces(backClusters,faces,indices); clusters[idx].endPrimitive = faces.size(); clusters[idx].normal = splitNormal; clusters[idx].k = splitK; if (frontSort && backSort) { // Note: below there are some lines dealing with the variable "noAddNormal" scattered in. Kind of a hack. // Here is what it does: it is an optimization. Any face with a normal matching an entry in that list will // not be added to the mesh. This is desired if we know we are on one side of a plane (then we don't want // to bother adding faces that face the opposite direction). // back then front -- but add in opp. order because we know where to return from frontSort but not backSort S32 frontSide = clusters.size(); frontSort->generateClusters(clusters,faces,indices,idx+1); clusters[idx].frontCluster = clusters.size(); noAddNormals.increment(); noAddNormals.last() = -splitNormal; backSort->generateClusters(clusters,faces,indices,frontSide); noAddNormals.decrement(); clusters[idx].backCluster = clusters[idx].frontCluster; // front then back -- but add in opp. order because we know where to return from backSort but not frontSort S32 backSide = clusters.size(); backSort->generateClusters(clusters,faces,indices,idx+1); clusters[idx].backCluster = clusters.size(); noAddNormals.increment(); noAddNormals.last() = splitNormal; frontSort->generateClusters(clusters,faces,indices,backSide); noAddNormals.decrement(); } else if (frontSort) { clusters[idx].frontCluster = clusters[idx].backCluster = clusters.size(); frontSort->generateClusters(clusters,faces,indices,idx+1); } else if (backSort) { clusters[idx].frontCluster = clusters[idx].backCluster = clusters.size(); backSort->generateClusters(clusters,faces,indices,idx+1); } else { addOrderedFaces(middleCluster,faces,indices,clusters[idx].startPrimitive!=faces.size()); addFaces(frontClusters,faces,indices,clusters[idx].startPrimitive!=faces.size()); clusters[idx].endPrimitive = faces.size(); clusters[idx].frontCluster = clusters[idx].backCluster = retIndex; } if (frontSort || backSort) { clusters[idx+1].normal.set(0.0f,0.0f,0.0f); clusters[idx+1].k = 0.0f; clusters[idx+1].startPrimitive = faces.size(); addFaces(frontClusters,faces,indices); clusters[idx+1].endPrimitive = faces.size(); clusters[idx+1].frontCluster = clusters[idx+1].backCluster = retIndex; } else clusters.decrement(); } void TranslucentSort::generateSortedMesh(TSSortedMesh * mesh, S32 numBigFaces, S32 maxDepth, bool zLayerUp, bool zLayerDown) { // on entry, mesh is organized like a standard mesh... // numFrames, numMatFrames, & vertsPerFrame describe what // is held in verts, norms, and tverts arrays // primitives and indices vectors describe the faces (same // faces on each frame/matFrame) // we want to convert this over to the structure that will be // used by TSSortedMesh...we also want to sort the faces, of course... Vector meshFaces; Vector meshIndices; Vector meshVerts; Vector meshNorms; Vector meshTVerts; Vector meshClusters; S32 i,j; for (i=0; inumFrames; i++) { for (j=0; jnumMatFrames; j++) { Vector faces = mesh->primitives; Vector indices = mesh->indices; Vector verts; Vector norms; Vector tverts; Vector clusters; verts.setSize(mesh->vertsPerFrame); dMemcpy(verts.address(),&mesh->verts[i*mesh->vertsPerFrame],sizeof(Point3F)*mesh->vertsPerFrame); norms.setSize(mesh->vertsPerFrame); dMemcpy(norms.address(),&mesh->norms[i*mesh->vertsPerFrame],sizeof(Point3F)*mesh->vertsPerFrame); tverts.setSize(mesh->vertsPerFrame); dMemcpy(tverts.address(),&mesh->tverts[i*mesh->vertsPerFrame],sizeof(Point2F)*mesh->vertsPerFrame); TranslucentSort sort(faces,indices,verts,norms,tverts,numBigFaces,maxDepth,zLayerUp,zLayerDown); sort.sort(); Vector newFaces; Vector newIndices; sort.generateClusters(clusters,newFaces,newIndices); S32 k; for (k=0; kk) clusters[l].frontCluster--; if (clusters[l].backCluster==k) clusters[l].backCluster = clusters[k].backCluster; if (clusters[l].backCluster>k) clusters[l].backCluster--; } clusters.erase(k); k = -1; // start over, our parent may now be useless... } } if (j==0) { mesh->startCluster.push_back(meshClusters.size()); mesh->firstVerts.push_back(meshVerts.size()); mesh->numVerts.push_back(verts.size()); } // TODO: if tverts same as some previous frame, use that frame number // o.w. mesh->firstTVerts.push_back(meshTVerts.size()); // adjust startPrimitive, endPrimitive, frontCluster, & backCluster on list of clusters just generated for (k=0; kfirstTVerts.last()==meshTVerts.size()) meshTVerts.merge(tverts); meshNorms.merge(norms); meshIndices.merge(newIndices); meshFaces.merge(newFaces); meshClusters.merge(clusters); } } mesh->clusters = meshClusters; mesh->primitives = meshFaces; mesh->indices = meshIndices; mesh->verts = meshVerts; mesh->norms = meshNorms; mesh->tverts = meshTVerts; }