#include #include #include #include "stl_opt.h" static int s_used[8192]; // same as MD3_MAX_TRIANGLES /* ** FindNextTriangleInStrip ** ** Given a surface and triangle this tries to find the next triangle ** in the strip that would continue the strip. The next triangle in ** the strip should have the same winding as this triangle. */ static int FindNextTriangleInStripOrFan( int mesh[][3], int tri, int orientation, int numTris, int odd ) { int t; int sum = 0; int currentTri[3]; int side; int a, b, c; int refa, refb; #define USE_STL_OPTIMISATION #ifdef USE_STL_OPTIMISATION int iAbuttingCount; const int* pAbuttingTris; int iAbuttingTriIndex; #endif currentTri[0] = mesh[tri][(0+orientation)%3]; currentTri[1] = mesh[tri][(1+orientation)%3]; currentTri[2] = mesh[tri][(2+orientation)%3]; if ( odd ) { refa = currentTri[1]; refb = currentTri[2]; } else { refa = currentTri[2]; refb = currentTri[0]; } // go through all triangles and look for sides that match // this triangle's #ifdef USE_STL_OPTIMISATION // STL-optimised method... // pAbuttingTris = TriangleAbutList_Query(tri, &iAbuttingCount); for (iAbuttingTriIndex = 0; iAbuttingTriIndex < iAbuttingCount; iAbuttingTriIndex++) { t = pAbuttingTris[iAbuttingTriIndex]; #else // old brute-force method... // for ( t = 0; t < numTris; t++ ) { #endif // don't check against self or against previously used triangles if ( t == tri ) continue; if ( s_used[t] ) continue; // check all three sides of the candidate triangle for ( side = 0; side < 3; side++ ) { // check only the second (abutting) side if ( ( refa == mesh[t][(side+1)%3] ) && ( refb == mesh[t][side] ) ) { a = mesh[t][0]; b = mesh[t][1]; c = mesh[t][2]; // rotate the candidate triangle to align it properly in the strip if ( side == 1 ) { mesh[t][0] = b; mesh[t][1] = c; mesh[t][2] = a; } else if ( side == 2 ) { mesh[t][0] = c; mesh[t][1] = a; mesh[t][2] = b; } return t; } /* else { Error( "fans not implemented yet" ); // check only the third (abutting) side if ( ( currentTri[2] == pSurf->baseTriangles[t].v[side].index ) && ( currentTri[0] == pSurf->baseTriangles[t].v[(side+1)%3].index ) ) { return t; } } */ } } return -1; } /* ** StripLength */ static int StripLength( int mesh[][3], int strip[][3], int tri, int orientation, int numInputTris, int fillNo ) { int stripIndex = 0; int next; int odd = 1; strip[stripIndex][0] = mesh[tri][(0+orientation)%3]; strip[stripIndex][1] = mesh[tri][(1+orientation)%3]; strip[stripIndex][2] = mesh[tri][(2+orientation)%3]; s_used[tri] = fillNo; stripIndex++; next = tri; while ( ( next = FindNextTriangleInStripOrFan( mesh, next, orientation, numInputTris, odd ) ) != -1 ) { s_used[next] = fillNo; odd = !odd; strip[stripIndex][0] = mesh[next][0]; strip[stripIndex][1] = mesh[next][1]; strip[stripIndex][2] = mesh[next][2]; stripIndex++; // all iterations after first need to be with an unrotated reference triangle orientation = 0; } return stripIndex; } /* ** BuildOptimizedList ** ** Attempts to build the longest strip/fan possible. Does not adhere ** to pure strip or fan, will intermix between the two so long as some ** type of connectivity can be maintained. */ #define MAX_ORIENTATIONS 3 #define MAX_MATCHED_SIDES 4 #define MAX_SEED_TRIANGLES 16 static int BuildOptimizedList( int mesh[][3], int strip[][3], int numInputTris ) { int t; int stripLen = 0; int startTri = -1; int bestTri = -1, bestLength = 0, bestOrientation = -1; int matchedSides = 0; int orientation = 0; int seedTriangles[MAX_MATCHED_SIDES][MAX_SEED_TRIANGLES]; int seedLengths[MAX_ORIENTATIONS][MAX_MATCHED_SIDES][MAX_SEED_TRIANGLES]; int numSeeds[MAX_MATCHED_SIDES] = { 0, 0, 0 }; int i; // build a ranked list of candidate seed triangles based on // number of offshoot strips. Precedence goes to orphans, // then corners, then edges, and interiors. memset( seedTriangles, 0xff, sizeof( seedTriangles ) ); memset( seedLengths, 0xff, sizeof( seedLengths ) ); for ( i = 0; i < MAX_MATCHED_SIDES; i++ ) { // find the triangle with lowest number of child strips for ( t = 0; t < numInputTris; t++ ) { int orientation; int n; if ( s_used[t] ) continue; // try the candidate triangle in three different orientations matchedSides = 0; for ( orientation = 0; orientation < 3; orientation++ ) { if ( ( n = FindNextTriangleInStripOrFan( mesh, t, orientation, numInputTris, 1 ) ) != -1 ) { matchedSides++; } } if ( matchedSides == i ) { seedTriangles[i][numSeeds[i]] = t; numSeeds[i]++; if ( numSeeds[i] == MAX_SEED_TRIANGLES ) break; } } } // we have a list of potential seed triangles, so we now go through each // potential candidate and look to see which produces the longest strip // and select our startTri based on this for ( i = 0; i < MAX_MATCHED_SIDES; i++ ) { int j; for ( j = 0; j < numSeeds[i]; j++ ) { for ( orientation = 0; orientation < 3; orientation++ ) { int k; seedLengths[orientation][i][j] = StripLength( mesh, strip, seedTriangles[i][j], orientation, numInputTris, 2 ); if ( seedLengths[orientation][i][j] > bestLength ) { bestTri = seedTriangles[i][j]; bestLength = seedLengths[orientation][i][j]; bestOrientation = orientation; } for ( k = 0; k < numInputTris; k++ ) { if ( s_used[k] == 2 ) s_used[k] = 0; } } } if ( bestTri != -1 ) { break; } } // build the strip for real if ( bestTri != -1 ) { stripLen = StripLength( mesh, strip, bestTri, bestOrientation, numInputTris, 1 ); } return stripLen; } /* ** OrderMesh ** ** Given an input mesh and an output mesh, this routine will reorder ** the triangles within the mesh into strips/fans. */ // note, the 'maxVerts' param is purely a hint as to how big to make the STL optimise arrays so they don't need to // be expanded at run-time... // void OrderMesh( int input[][3], int output[][3], int numTris, int maxVerts ) { int i; int sumStrippedTriangles = 0; int strippedTriangles; int totalStrips = 0; int strip[8192][3]; // could dump directly into 'output', but // this helps with debugging memset( s_used, 0, sizeof( s_used ) ); TriangleAbutList_Gen(input, numTris, maxVerts); #if 0 FILE *fp = fopen( "strip.txt", "wt" ); for ( i = 0; i < numTris; i++ ) { fprintf( fp, "%4d: %3d %3d %3d\n", i, input[i][0], input[i][1], input[i][2] ); } fclose( fp ); #endif // while there are still triangles that are not part of a strip while ( sumStrippedTriangles < numTris ) { printf("%c%d%%",0x0D,(sumStrippedTriangles*100)/numTris); // build a strip strippedTriangles = BuildOptimizedList( input, strip, numTris ); for ( i = 0; i < strippedTriangles; i++ ) { output[sumStrippedTriangles+i][0] = strip[i][0]; output[sumStrippedTriangles+i][1] = strip[i][1]; output[sumStrippedTriangles+i][2] = strip[i][2]; } sumStrippedTriangles += strippedTriangles; totalStrips++; } printf("\n"); printf( "Triangles on surface: %d\n", sumStrippedTriangles ); printf( "Total strips from surface: %d\n", totalStrips ); printf( "Average strip length: %f\n", ( float ) sumStrippedTriangles / totalStrips ); }