//----------------------------------------------------------------------------- // Torque Game Engine // Copyright (C) GarageGames.com, Inc. //----------------------------------------------------------------------------- #include "max2dtsExporter/stripper.h" F32 Stripper::adjacencyWeight = 5; F32 Stripper::noswapWeight = 3; F32 Stripper::alreadyCachedWeight = 1; U32 Stripper::cacheSize = 16; U32 Stripper::simK = 5; Stripper::Stripper(Vector & _faces, Vector & _faceIndices) : faces(_faces), faceIndices(_faceIndices), adjacent(_faces.size(),_faces.size()) { // keep track of whether face used in a strip yet used.setSize(faces.size()); for (S32 i=0; i=0) { // make sure current face is still last 3 items in the cache TSDrawPrimitive & face = faces[addedFace]; U32 idx0 = faceIndices[face.start+0]; U32 idx1 = faceIndices[face.start+1]; U32 idx2 = faceIndices[face.start+2]; if ( idx0!=*(cache) && idx0!=*(cache-1) && idx0!=*(cache-2)) { setExportError("Assertion failed when stripping (8)"); return; } if ( idx1!=*(cache) && idx1!=*(cache-1) && idx1!=*(cache-2)) { setExportError("Assertion failed when stripping (9)"); return; } if ( idx2!=*(cache) && idx2!=*(cache-1) && idx2!=*(cache-2)) { setExportError("Assertion failed when stripping (10)"); return; } } } void Stripper::copyParams(Stripper * from) { limitStripLength = from->limitStripLength; } void Stripper::makeStrips() { // if already encountered an error, then // we'll just go through the motions if (isError()) return; // main strip loop... U32 start, end, i, sz; Vector someFaces; Vector someIndices; for (start = 0; startbestScore) { bestFace = i; bestScore = currentScore; } } } // if no face... if (bestFace<0) return false; // start the strip -- add in standard order...may be changed later strip.numElements += 3; stripIndices.push_back(faceIndices[faces[bestFace].start + 0]); addToCache(stripIndices.last()); stripIndices.push_back(faceIndices[faces[bestFace].start + 1]); addToCache(stripIndices.last()); stripIndices.push_back(faceIndices[faces[bestFace].start + 2]); addToCache(stripIndices.last()); testCache(bestFace); // adjust adjacency information for (j=startFace; j prev; addVert = -1; TSDrawPrimitive & addFace = faces[face]; U32 idx0 = faceIndices[addFace.start+0]; U32 idx1 = faceIndices[addFace.start+1]; U32 idx2 = faceIndices[addFace.start+2]; S32 * cache = &vertexCache.last(); if (idx0==*cache || idx0==*(cache-1) || idx0==*(cache-2)) prev.push_back(idx0); else addVert = idx0; if (idx1==*cache || idx1==*(cache-1) || idx1==*(cache-2)) prev.push_back(idx1); else addVert = idx1; if (idx2==*cache || idx2==*(cache-1) || idx2==*(cache-2)) prev.push_back(idx2); else addVert = idx2; if (addVert<0 || prev.size()!=2) { setExportError("Assertion failed when stripping (5)"); return; } if (idx1==addVert) { // swap order of oldVerts oldVert0 = prev[1]; oldVert1 = prev[0]; } else { // keep order oldVert0 = prev[0]; oldVert1 = prev[1]; } } // assumes start + 0,1,2 is a triangle or first 3 indices of a strip void Stripper::rotateFace(S32 start, Vector & indices) { U32 tmp = indices[start]; indices[start] = indices[start+1]; indices[start+1] = indices[start+2]; indices[start+2] = tmp; S32 * cache = &vertexCache.last(); tmp = *(cache-2); *(cache-2) = *(cache-1); *(cache-1) = *cache; *cache = tmp; testCache(currentFace); } bool Stripper::swapNeeded(S32 oldVert0, S32 oldVert1) { S32 * cache = &vertexCache.last(); return (*cache!=oldVert0 || *(cache-1)!=oldVert1) && (*cache!=oldVert1 || *(cache-1)!=oldVert0); // Long form: // if ( (*cache==oldVert0 && *(cache-1)==oldVert1) || (*cache==oldVert1 && *(cache-1)==oldVert0) ) // return false; // else // return true; } F32 Stripper::getScore(S32 face, bool ignoreOrder) { // score face depending on following criteria: // -- select face with fewest adjacencies? // -- select face that continues strip without swap? // -- select face with new vertex already in cache? // weighting of each criteria controlled by adjacencyWeight, noswapWeight, and alreadyCachedWeight // if ignoreOrder is true, don't worry about swaps if (face<0) return -100000.0f; // get the 2 verts from the last face and the 1 new vert S32 oldVert0, oldVert1, addVert; getVerts(face, oldVert0, oldVert1, addVert); F32 score = 0.0f; // fewer adjacent faces the better... score -= numAdjacent[face] * adjacencyWeight; // reward if no swap needed...don't worry about order (only same facing faces get here) if (!ignoreOrder && !swapNeeded(oldVert0,oldVert1)) score += noswapWeight; // if new vertex in the cache, add the already in cache bonus... for (S32 i=0;i0) break; } // survived the gauntlet... return true; } bool Stripper::canGo(S32 face) { if (face<0) return false; U32 idx0 = faceIndices[faces[face].start + 0]; U32 idx1 = faceIndices[faces[face].start + 1]; U32 idx2 = faceIndices[faces[face].start + 2]; U32 last = stripIndices.last(); if (last==idx0 || last==idx1 || last==idx2) return true; return false; } bool Stripper::addStrip(TSDrawPrimitive & strip, S32 startFace, S32 endFace) { // if already encountered an error, then // we'll just go through the motions if (isError()) return false; if (strip.numElements>3 && stripLongEnough(startFace,endFace)) return false; testCache(currentFace); // get unused faces adjacent to current face (choose only faces pointing same way) // if more than one face adjacent on an edge (unusual case) chooses one with lowest adjacency count S32 face0, face1, face2; getAdjacentFaces(startFace,endFace,currentFace,face0,face1,face2); // one more check -- make sure most recent vert is in face // this can happen in exceptional case where we "back up" // using common edge between previous two faces, but not the // previous face (a v-junction?) bool bad0,bad1,bad2; bad0=bad1=bad2=false; if (strip.numElements!=3 && !canGo(face0)) face0=-1; if (strip.numElements!=3 && !canGo(face1)) face1=-1; if (strip.numElements!=3 && !canGo(face2)) face2=-1; if (face0<0 && face1<0 && face2<0) return false; testCache(currentFace); // score faces, choose highest score F32 score0 = getScore(face0,strip.numElements==3); F32 score1 = getScore(face1,strip.numElements==3); F32 score2 = getScore(face2,strip.numElements==3); S32 bestFace = -1; if (score0>=score1 && score0>=score2) bestFace = face0; else if (score1>=score0 && score1>=score2) bestFace = face1; else if (score2>=score1 && score2>=score0) bestFace = face2; testCache(currentFace); // add new element S32 oldVert0, oldVert1, addVert; getVerts(bestFace,oldVert0,oldVert1,addVert); testCache(currentFace); if (strip.numElements==3) { testCache(currentFace); // special case...rotate previous element until we can add this face U32 doh=0; while (swapNeeded(oldVert0,oldVert1)) { rotateFace(strip.start,stripIndices); if (++doh==3) { setExportError("Assertion error while stripping: infinite loop"); return false; } } stripIndices.push_back(addVert); addToCache(addVert); strip.numElements++; testCache(bestFace); } else { testCache(currentFace); if (swapNeeded(oldVert0,oldVert1)) { U32 sz = stripIndices.size(); U32 dup = stripIndices[sz-3]; stripIndices.insert(sz-1); stripIndices[sz-1] = dup; addToCache(dup,1); strip.numElements++; testCache(-1); } stripIndices.push_back(addVert); strip.numElements++; addToCache(addVert); testCache(bestFace); } // add other faces to recentFaces list if (face0>=0 && face0!=bestFace) recentFaces.push_back(face0); if (face1>=0 && face1!=bestFace) recentFaces.push_back(face1); if (face2>=0 && face2!=bestFace) recentFaces.push_back(face2); // adjust adjacency information for (S32 j=startFace; j