Blender V2.61 - r43446
|
00001 00024 #include <assert.h> 00025 #include <stdio.h> 00026 #include <math.h> 00027 #include <string.h> 00028 #include <float.h> 00029 #include <stdlib.h> 00030 00031 #include "mikktspace.h" 00032 00033 #define TFALSE 0 00034 #define TTRUE 1 00035 00036 #ifndef M_PI 00037 #define M_PI 3.1415926535897932384626433832795 00038 #endif 00039 00040 #define INTERNAL_RND_SORT_SEED 39871946 00041 00042 // internal structure 00043 typedef struct 00044 { 00045 float x, y, z; 00046 } SVec3; 00047 00048 static tbool veq( const SVec3 v1, const SVec3 v2 ) 00049 { 00050 return (v1.x == v2.x) && (v1.y == v2.y) && (v1.z == v2.z); 00051 } 00052 00053 static SVec3 vadd( const SVec3 v1, const SVec3 v2 ) 00054 { 00055 SVec3 vRes; 00056 00057 vRes.x = v1.x + v2.x; 00058 vRes.y = v1.y + v2.y; 00059 vRes.z = v1.z + v2.z; 00060 00061 return vRes; 00062 } 00063 00064 00065 static SVec3 vsub( const SVec3 v1, const SVec3 v2 ) 00066 { 00067 SVec3 vRes; 00068 00069 vRes.x = v1.x - v2.x; 00070 vRes.y = v1.y - v2.y; 00071 vRes.z = v1.z - v2.z; 00072 00073 return vRes; 00074 } 00075 00076 static SVec3 vscale(const float fS, const SVec3 v) 00077 { 00078 SVec3 vRes; 00079 00080 vRes.x = fS * v.x; 00081 vRes.y = fS * v.y; 00082 vRes.z = fS * v.z; 00083 00084 return vRes; 00085 } 00086 00087 static float LengthSquared( const SVec3 v ) 00088 { 00089 return v.x*v.x + v.y*v.y + v.z*v.z; 00090 } 00091 00092 static float Length( const SVec3 v ) 00093 { 00094 return sqrtf(LengthSquared(v)); 00095 } 00096 00097 static SVec3 Normalize( const SVec3 v ) 00098 { 00099 return vscale(1 / Length(v), v); 00100 } 00101 00102 static float vdot( const SVec3 v1, const SVec3 v2) 00103 { 00104 return v1.x*v2.x + v1.y*v2.y + v1.z*v2.z; 00105 } 00106 00107 00108 static tbool NotZero(const float fX) 00109 { 00110 // could possibly use FLT_EPSILON instead 00111 return fabsf(fX) > FLT_MIN; 00112 } 00113 00114 static tbool VNotZero(const SVec3 v) 00115 { 00116 // might change this to an epsilon based test 00117 return NotZero(v.x) || NotZero(v.y) || NotZero(v.z); 00118 } 00119 00120 00121 00122 typedef struct 00123 { 00124 int iNrFaces; 00125 int * pTriMembers; 00126 } SSubGroup; 00127 00128 typedef struct 00129 { 00130 int iNrFaces; 00131 int * pFaceIndices; 00132 int iVertexRepresentitive; 00133 tbool bOrientPreservering; 00134 } SGroup; 00135 00136 // 00137 #define MARK_DEGENERATE 1 00138 #define QUAD_ONE_DEGEN_TRI 2 00139 #define GROUP_WITH_ANY 4 00140 #define ORIENT_PRESERVING 8 00141 00142 00143 00144 typedef struct 00145 { 00146 int FaceNeighbors[3]; 00147 SGroup * AssignedGroup[3]; 00148 00149 // normalized first order face derivatives 00150 SVec3 vOs, vOt; 00151 float fMagS, fMagT; // original magnitudes 00152 00153 // determines if the current and the next triangle are a quad. 00154 int iOrgFaceNumber; 00155 int iFlag, iTSpacesOffs; 00156 unsigned char vert_num[4]; 00157 } STriInfo; 00158 00159 typedef struct 00160 { 00161 SVec3 vOs; 00162 float fMagS; 00163 SVec3 vOt; 00164 float fMagT; 00165 int iCounter; // this is to average back into quads. 00166 tbool bOrient; 00167 } STSpace; 00168 00169 static int GenerateInitialVerticesIndexList(STriInfo pTriInfos[], int piTriList_out[], const SMikkTSpaceContext * pContext, const int iNrTrianglesIn); 00170 static void GenerateSharedVerticesIndexList(int piTriList_in_and_out[], const SMikkTSpaceContext * pContext, const int iNrTrianglesIn); 00171 static void InitTriInfo(STriInfo pTriInfos[], const int piTriListIn[], const SMikkTSpaceContext * pContext, const int iNrTrianglesIn); 00172 static int Build4RuleGroups(STriInfo pTriInfos[], SGroup pGroups[], int piGroupTrianglesBuffer[], const int piTriListIn[], const int iNrTrianglesIn); 00173 static tbool GenerateTSpaces(STSpace psTspace[], const STriInfo pTriInfos[], const SGroup pGroups[], 00174 const int iNrActiveGroups, const int piTriListIn[], const float fThresCos, 00175 const SMikkTSpaceContext * pContext); 00176 00177 static int MakeIndex(const int iFace, const int iVert) 00178 { 00179 assert(iVert>=0 && iVert<4 && iFace>=0); 00180 return (iFace<<2) | (iVert&0x3); 00181 } 00182 00183 static void IndexToData(int * piFace, int * piVert, const int iIndexIn) 00184 { 00185 piVert[0] = iIndexIn&0x3; 00186 piFace[0] = iIndexIn>>2; 00187 } 00188 00189 static STSpace AvgTSpace(const STSpace * pTS0, const STSpace * pTS1) 00190 { 00191 STSpace ts_res; 00192 00193 // this if is important. Due to floating point precision 00194 // averaging when ts0==ts1 will cause a slight difference 00195 // which results in tangent space splits later on 00196 if(pTS0->fMagS==pTS1->fMagS && pTS0->fMagT==pTS1->fMagT && 00197 veq(pTS0->vOs,pTS1->vOs) && veq(pTS0->vOt, pTS1->vOt)) 00198 { 00199 ts_res.fMagS = pTS0->fMagS; 00200 ts_res.fMagT = pTS0->fMagT; 00201 ts_res.vOs = pTS0->vOs; 00202 ts_res.vOt = pTS0->vOt; 00203 } 00204 else 00205 { 00206 ts_res.fMagS = 0.5f*(pTS0->fMagS+pTS1->fMagS); 00207 ts_res.fMagT = 0.5f*(pTS0->fMagT+pTS1->fMagT); 00208 ts_res.vOs = vadd(pTS0->vOs,pTS1->vOs); 00209 ts_res.vOt = vadd(pTS0->vOt,pTS1->vOt); 00210 if( VNotZero(ts_res.vOs) ) ts_res.vOs = Normalize(ts_res.vOs); 00211 if( VNotZero(ts_res.vOt) ) ts_res.vOt = Normalize(ts_res.vOt); 00212 } 00213 00214 return ts_res; 00215 } 00216 00217 00218 00219 static SVec3 GetPosition(const SMikkTSpaceContext * pContext, const int index); 00220 static SVec3 GetNormal(const SMikkTSpaceContext * pContext, const int index); 00221 static SVec3 GetTexCoord(const SMikkTSpaceContext * pContext, const int index); 00222 00223 00224 // degen triangles 00225 static void DegenPrologue(STriInfo pTriInfos[], int piTriList_out[], const int iNrTrianglesIn, const int iTotTris); 00226 static void DegenEpilogue(STSpace psTspace[], STriInfo pTriInfos[], int piTriListIn[], const SMikkTSpaceContext * pContext, const int iNrTrianglesIn, const int iTotTris); 00227 00228 00229 tbool genTangSpaceDefault(const SMikkTSpaceContext * pContext) 00230 { 00231 return genTangSpace(pContext, 180.0f); 00232 } 00233 00234 tbool genTangSpace(const SMikkTSpaceContext * pContext, const float fAngularThreshold) 00235 { 00236 // count nr_triangles 00237 int * piTriListIn = NULL, * piGroupTrianglesBuffer = NULL; 00238 STriInfo * pTriInfos = NULL; 00239 SGroup * pGroups = NULL; 00240 STSpace * psTspace = NULL; 00241 int iNrTrianglesIn = 0, f=0, t=0, i=0; 00242 int iNrTSPaces = 0, iTotTris = 0, iDegenTriangles = 0, iNrMaxGroups = 0; 00243 int iNrActiveGroups = 0, index = 0; 00244 const int iNrFaces = pContext->m_pInterface->m_getNumFaces(pContext); 00245 tbool bRes = TFALSE; 00246 const float fThresCos = (float) cos((fAngularThreshold*(float)M_PI)/180.0f); 00247 00248 // verify all call-backs have been set 00249 if( pContext->m_pInterface->m_getNumFaces==NULL || 00250 pContext->m_pInterface->m_getNumVerticesOfFace==NULL || 00251 pContext->m_pInterface->m_getPosition==NULL || 00252 pContext->m_pInterface->m_getNormal==NULL || 00253 pContext->m_pInterface->m_getTexCoord==NULL ) 00254 return TFALSE; 00255 00256 // count triangles on supported faces 00257 for(f=0; f<iNrFaces; f++) 00258 { 00259 const int verts = pContext->m_pInterface->m_getNumVerticesOfFace(pContext, f); 00260 if(verts==3) ++iNrTrianglesIn; 00261 else if(verts==4) iNrTrianglesIn += 2; 00262 } 00263 if(iNrTrianglesIn<=0) return TFALSE; 00264 00265 // allocate memory for an index list 00266 piTriListIn = (int *) malloc(sizeof(int)*3*iNrTrianglesIn); 00267 pTriInfos = (STriInfo *) malloc(sizeof(STriInfo)*iNrTrianglesIn); 00268 if(piTriListIn==NULL || pTriInfos==NULL) 00269 { 00270 if(piTriListIn!=NULL) free(piTriListIn); 00271 if(pTriInfos!=NULL) free(pTriInfos); 00272 return TFALSE; 00273 } 00274 00275 // make an initial triangle --> face index list 00276 iNrTSPaces = GenerateInitialVerticesIndexList(pTriInfos, piTriListIn, pContext, iNrTrianglesIn); 00277 00278 // make a welded index list of identical positions and attributes (pos, norm, texc) 00279 //printf("gen welded index list begin\n"); 00280 GenerateSharedVerticesIndexList(piTriListIn, pContext, iNrTrianglesIn); 00281 //printf("gen welded index list end\n"); 00282 00283 // Mark all degenerate triangles 00284 iTotTris = iNrTrianglesIn; 00285 iDegenTriangles = 0; 00286 for(t=0; t<iTotTris; t++) 00287 { 00288 const int i0 = piTriListIn[t*3+0]; 00289 const int i1 = piTriListIn[t*3+1]; 00290 const int i2 = piTriListIn[t*3+2]; 00291 const SVec3 p0 = GetPosition(pContext, i0); 00292 const SVec3 p1 = GetPosition(pContext, i1); 00293 const SVec3 p2 = GetPosition(pContext, i2); 00294 if(veq(p0,p1) || veq(p0,p2) || veq(p1,p2)) // degenerate 00295 { 00296 pTriInfos[t].iFlag |= MARK_DEGENERATE; 00297 ++iDegenTriangles; 00298 } 00299 } 00300 iNrTrianglesIn = iTotTris - iDegenTriangles; 00301 00302 // mark all triangle pairs that belong to a quad with only one 00303 // good triangle. These need special treatment in DegenEpilogue(). 00304 // Additionally, move all good triangles to the start of 00305 // pTriInfos[] and piTriListIn[] without changing order and 00306 // put the degenerate triangles last. 00307 DegenPrologue(pTriInfos, piTriListIn, iNrTrianglesIn, iTotTris); 00308 00309 00310 // evaluate triangle level attributes and neighbor list 00311 //printf("gen neighbors list begin\n"); 00312 InitTriInfo(pTriInfos, piTriListIn, pContext, iNrTrianglesIn); 00313 //printf("gen neighbors list end\n"); 00314 00315 00316 // based on the 4 rules, identify groups based on connectivity 00317 iNrMaxGroups = iNrTrianglesIn*3; 00318 pGroups = (SGroup *) malloc(sizeof(SGroup)*iNrMaxGroups); 00319 piGroupTrianglesBuffer = (int *) malloc(sizeof(int)*iNrTrianglesIn*3); 00320 if(pGroups==NULL || piGroupTrianglesBuffer==NULL) 00321 { 00322 if(pGroups!=NULL) free(pGroups); 00323 if(piGroupTrianglesBuffer!=NULL) free(piGroupTrianglesBuffer); 00324 free(piTriListIn); 00325 free(pTriInfos); 00326 return TFALSE; 00327 } 00328 //printf("gen 4rule groups begin\n"); 00329 iNrActiveGroups = 00330 Build4RuleGroups(pTriInfos, pGroups, piGroupTrianglesBuffer, piTriListIn, iNrTrianglesIn); 00331 //printf("gen 4rule groups end\n"); 00332 00333 // 00334 00335 psTspace = (STSpace *) malloc(sizeof(STSpace)*iNrTSPaces); 00336 if(psTspace==NULL) 00337 { 00338 free(piTriListIn); 00339 free(pTriInfos); 00340 free(pGroups); 00341 free(piGroupTrianglesBuffer); 00342 return TFALSE; 00343 } 00344 memset(psTspace, 0, sizeof(STSpace)*iNrTSPaces); 00345 for(t=0; t<iNrTSPaces; t++) 00346 { 00347 psTspace[t].vOs.x=1.0f; psTspace[t].vOs.y=0.0f; psTspace[t].vOs.z=0.0f; psTspace[t].fMagS = 1.0f; 00348 psTspace[t].vOt.x=0.0f; psTspace[t].vOt.y=1.0f; psTspace[t].vOt.z=0.0f; psTspace[t].fMagT = 1.0f; 00349 } 00350 00351 // make tspaces, each group is split up into subgroups if necessary 00352 // based on fAngularThreshold. Finally a tangent space is made for 00353 // every resulting subgroup 00354 //printf("gen tspaces begin\n"); 00355 bRes = GenerateTSpaces(psTspace, pTriInfos, pGroups, iNrActiveGroups, piTriListIn, fThresCos, pContext); 00356 //printf("gen tspaces end\n"); 00357 00358 // clean up 00359 free(pGroups); 00360 free(piGroupTrianglesBuffer); 00361 00362 if(!bRes) // if an allocation in GenerateTSpaces() failed 00363 { 00364 // clean up and return false 00365 free(pTriInfos); free(piTriListIn); free(psTspace); 00366 return TFALSE; 00367 } 00368 00369 00370 // degenerate quads with one good triangle will be fixed by copying a space from 00371 // the good triangle to the coinciding vertex. 00372 // all other degenerate triangles will just copy a space from any good triangle 00373 // with the same welded index in piTriListIn[]. 00374 DegenEpilogue(psTspace, pTriInfos, piTriListIn, pContext, iNrTrianglesIn, iTotTris); 00375 00376 free(pTriInfos); free(piTriListIn); 00377 00378 index = 0; 00379 for(f=0; f<iNrFaces; f++) 00380 { 00381 const int verts = pContext->m_pInterface->m_getNumVerticesOfFace(pContext, f); 00382 if(verts!=3 && verts!=4) continue; 00383 00384 00385 // I've decided to let degenerate triangles and group-with-anythings 00386 // vary between left/right hand coordinate systems at the vertices. 00387 // All healthy triangles on the other hand are built to always be either or. 00388 00389 /*// force the coordinate system orientation to be uniform for every face. 00390 // (this is already the case for good triangles but not for 00391 // degenerate ones and those with bGroupWithAnything==true) 00392 bool bOrient = psTspace[index].bOrient; 00393 if(psTspace[index].iCounter == 0) // tspace was not derived from a group 00394 { 00395 // look for a space created in GenerateTSpaces() by iCounter>0 00396 bool bNotFound = true; 00397 int i=1; 00398 while(i<verts && bNotFound) 00399 { 00400 if(psTspace[index+i].iCounter > 0) bNotFound=false; 00401 else ++i; 00402 } 00403 if(!bNotFound) bOrient = psTspace[index+i].bOrient; 00404 }*/ 00405 00406 // set data 00407 for(i=0; i<verts; i++) 00408 { 00409 const STSpace * pTSpace = &psTspace[index]; 00410 float tang[] = {pTSpace->vOs.x, pTSpace->vOs.y, pTSpace->vOs.z}; 00411 float bitang[] = {pTSpace->vOt.x, pTSpace->vOt.y, pTSpace->vOt.z}; 00412 if(pContext->m_pInterface->m_setTSpace!=NULL) 00413 pContext->m_pInterface->m_setTSpace(pContext, tang, bitang, pTSpace->fMagS, pTSpace->fMagT, pTSpace->bOrient, f, i); 00414 if(pContext->m_pInterface->m_setTSpaceBasic!=NULL) 00415 pContext->m_pInterface->m_setTSpaceBasic(pContext, tang, pTSpace->bOrient==TTRUE ? 1.0f : (-1.0f), f, i); 00416 00417 ++index; 00418 } 00419 } 00420 00421 free(psTspace); 00422 00423 00424 return TTRUE; 00425 } 00426 00428 00429 typedef struct 00430 { 00431 float vert[3]; 00432 int index; 00433 } STmpVert; 00434 00435 const int g_iCells = 2048; 00436 00437 #ifdef _MSC_VER 00438 #define NOINLINE __declspec(noinline) 00439 #else 00440 #define NOINLINE __attribute__ ((noinline)) 00441 #endif 00442 00443 // it is IMPORTANT that this function is called to evaluate the hash since 00444 // inlining could potentially reorder instructions and generate different 00445 // results for the same effective input value fVal. 00446 NOINLINE int FindGridCell(const float fMin, const float fMax, const float fVal) 00447 { 00448 const float fIndex = g_iCells * ((fVal-fMin)/(fMax-fMin)); 00449 const int iIndex = fIndex<0?0:((int)fIndex); 00450 return iIndex<g_iCells?iIndex:(g_iCells-1); 00451 } 00452 00453 static void MergeVertsFast(int piTriList_in_and_out[], STmpVert pTmpVert[], const SMikkTSpaceContext * pContext, const int iL_in, const int iR_in); 00454 static void MergeVertsSlow(int piTriList_in_and_out[], const SMikkTSpaceContext * pContext, const int pTable[], const int iEntries); 00455 static void GenerateSharedVerticesIndexListSlow(int piTriList_in_and_out[], const SMikkTSpaceContext * pContext, const int iNrTrianglesIn); 00456 00457 static void GenerateSharedVerticesIndexList(int piTriList_in_and_out[], const SMikkTSpaceContext * pContext, const int iNrTrianglesIn) 00458 { 00459 00460 // Generate bounding box 00461 int * piHashTable=NULL, * piHashCount=NULL, * piHashOffsets=NULL, * piHashCount2=NULL; 00462 STmpVert * pTmpVert = NULL; 00463 int i=0, iChannel=0, k=0, e=0; 00464 int iMaxCount=0; 00465 SVec3 vMin = GetPosition(pContext, 0), vMax = vMin, vDim; 00466 float fMin, fMax; 00467 for(i=1; i<(iNrTrianglesIn*3); i++) 00468 { 00469 const int index = piTriList_in_and_out[i]; 00470 00471 const SVec3 vP = GetPosition(pContext, index); 00472 if(vMin.x > vP.x) vMin.x = vP.x; 00473 else if(vMax.x < vP.x) vMax.x = vP.x; 00474 if(vMin.y > vP.y) vMin.y = vP.y; 00475 else if(vMax.y < vP.y) vMax.y = vP.y; 00476 if(vMin.z > vP.z) vMin.z = vP.z; 00477 else if(vMax.z < vP.z) vMax.z = vP.z; 00478 } 00479 00480 vDim = vsub(vMax,vMin); 00481 iChannel = 0; 00482 fMin = vMin.x; fMax=vMax.x; 00483 if(vDim.y>vDim.x && vDim.y>vDim.z) 00484 { 00485 iChannel=1; 00486 fMin = vMin.y, fMax=vMax.y; 00487 } 00488 else if(vDim.z>vDim.x) 00489 { 00490 iChannel=2; 00491 fMin = vMin.z, fMax=vMax.z; 00492 } 00493 00494 // make allocations 00495 piHashTable = (int *) malloc(sizeof(int)*iNrTrianglesIn*3); 00496 piHashCount = (int *) malloc(sizeof(int)*g_iCells); 00497 piHashOffsets = (int *) malloc(sizeof(int)*g_iCells); 00498 piHashCount2 = (int *) malloc(sizeof(int)*g_iCells); 00499 00500 if(piHashTable==NULL || piHashCount==NULL || piHashOffsets==NULL || piHashCount2==NULL) 00501 { 00502 if(piHashTable!=NULL) free(piHashTable); 00503 if(piHashCount!=NULL) free(piHashCount); 00504 if(piHashOffsets!=NULL) free(piHashOffsets); 00505 if(piHashCount2!=NULL) free(piHashCount2); 00506 GenerateSharedVerticesIndexListSlow(piTriList_in_and_out, pContext, iNrTrianglesIn); 00507 return; 00508 } 00509 memset(piHashCount, 0, sizeof(int)*g_iCells); 00510 memset(piHashCount2, 0, sizeof(int)*g_iCells); 00511 00512 // count amount of elements in each cell unit 00513 for(i=0; i<(iNrTrianglesIn*3); i++) 00514 { 00515 const int index = piTriList_in_and_out[i]; 00516 const SVec3 vP = GetPosition(pContext, index); 00517 const float fVal = iChannel==0 ? vP.x : (iChannel==1 ? vP.y : vP.z); 00518 const int iCell = FindGridCell(fMin, fMax, fVal); 00519 ++piHashCount[iCell]; 00520 } 00521 00522 // evaluate start index of each cell. 00523 piHashOffsets[0]=0; 00524 for(k=1; k<g_iCells; k++) 00525 piHashOffsets[k]=piHashOffsets[k-1]+piHashCount[k-1]; 00526 00527 // insert vertices 00528 for(i=0; i<(iNrTrianglesIn*3); i++) 00529 { 00530 const int index = piTriList_in_and_out[i]; 00531 const SVec3 vP = GetPosition(pContext, index); 00532 const float fVal = iChannel==0 ? vP.x : (iChannel==1 ? vP.y : vP.z); 00533 const int iCell = FindGridCell(fMin, fMax, fVal); 00534 int * pTable = NULL; 00535 00536 assert(piHashCount2[iCell]<piHashCount[iCell]); 00537 pTable = &piHashTable[piHashOffsets[iCell]]; 00538 pTable[piHashCount2[iCell]] = i; // vertex i has been inserted. 00539 ++piHashCount2[iCell]; 00540 } 00541 for(k=0; k<g_iCells; k++) 00542 assert(piHashCount2[k] == piHashCount[k]); // verify the count 00543 free(piHashCount2); 00544 00545 // find maximum amount of entries in any hash entry 00546 iMaxCount = piHashCount[0]; 00547 for(k=1; k<g_iCells; k++) 00548 if(iMaxCount<piHashCount[k]) 00549 iMaxCount=piHashCount[k]; 00550 pTmpVert = (STmpVert *) malloc(sizeof(STmpVert)*iMaxCount); 00551 00552 00553 // complete the merge 00554 for(k=0; k<g_iCells; k++) 00555 { 00556 // extract table of cell k and amount of entries in it 00557 int * pTable = &piHashTable[piHashOffsets[k]]; 00558 const int iEntries = piHashCount[k]; 00559 if(iEntries < 2) continue; 00560 00561 if(pTmpVert!=NULL) 00562 { 00563 for(e=0; e<iEntries; e++) 00564 { 00565 int i = pTable[e]; 00566 const SVec3 vP = GetPosition(pContext, piTriList_in_and_out[i]); 00567 pTmpVert[e].vert[0] = vP.x; pTmpVert[e].vert[1] = vP.y; 00568 pTmpVert[e].vert[2] = vP.z; pTmpVert[e].index = i; 00569 } 00570 MergeVertsFast(piTriList_in_and_out, pTmpVert, pContext, 0, iEntries-1); 00571 } 00572 else 00573 MergeVertsSlow(piTriList_in_and_out, pContext, pTable, iEntries); 00574 } 00575 00576 if(pTmpVert!=NULL) { free(pTmpVert); } 00577 free(piHashTable); 00578 free(piHashCount); 00579 free(piHashOffsets); 00580 } 00581 00582 static void MergeVertsFast(int piTriList_in_and_out[], STmpVert pTmpVert[], const SMikkTSpaceContext * pContext, const int iL_in, const int iR_in) 00583 { 00584 // make bbox 00585 int c=0, l=0, channel=0; 00586 float fvMin[3], fvMax[3]; 00587 float dx=0, dy=0, dz=0, fSep=0; 00588 for(c=0; c<3; c++) 00589 { fvMin[c]=pTmpVert[iL_in].vert[c]; fvMax[c]=fvMin[c]; } 00590 for(l=(iL_in+1); l<=iR_in; l++) 00591 for(c=0; c<3; c++) 00592 if(fvMin[c]>pTmpVert[l].vert[c]) fvMin[c]=pTmpVert[l].vert[c]; 00593 else if(fvMax[c]<pTmpVert[l].vert[c]) fvMax[c]=pTmpVert[l].vert[c]; 00594 00595 dx = fvMax[0]-fvMin[0]; 00596 dy = fvMax[1]-fvMin[1]; 00597 dz = fvMax[2]-fvMin[2]; 00598 00599 channel = 0; 00600 if(dy>dx && dy>dz) channel=1; 00601 else if(dz>dx) channel=2; 00602 00603 fSep = 0.5f*(fvMax[channel]+fvMin[channel]); 00604 00605 // terminate recursion when the separation/average value 00606 // is no longer strictly between fMin and fMax values. 00607 if(fSep>=fvMax[channel] || fSep<=fvMin[channel]) 00608 { 00609 // complete the weld 00610 for(l=iL_in; l<=iR_in; l++) 00611 { 00612 int i = pTmpVert[l].index; 00613 const int index = piTriList_in_and_out[i]; 00614 const SVec3 vP = GetPosition(pContext, index); 00615 const SVec3 vN = GetNormal(pContext, index); 00616 const SVec3 vT = GetTexCoord(pContext, index); 00617 00618 tbool bNotFound = TTRUE; 00619 int l2=iL_in, i2rec=-1; 00620 while(l2<l && bNotFound) 00621 { 00622 const int i2 = pTmpVert[l2].index; 00623 const int index2 = piTriList_in_and_out[i2]; 00624 const SVec3 vP2 = GetPosition(pContext, index2); 00625 const SVec3 vN2 = GetNormal(pContext, index2); 00626 const SVec3 vT2 = GetTexCoord(pContext, index2); 00627 i2rec=i2; 00628 00629 //if(vP==vP2 && vN==vN2 && vT==vT2) 00630 if(vP.x==vP2.x && vP.y==vP2.y && vP.z==vP2.z && 00631 vN.x==vN2.x && vN.y==vN2.y && vN.z==vN2.z && 00632 vT.x==vT2.x && vT.y==vT2.y && vT.z==vT2.z) 00633 bNotFound = TFALSE; 00634 else 00635 ++l2; 00636 } 00637 00638 // merge if previously found 00639 if(!bNotFound) 00640 piTriList_in_and_out[i] = piTriList_in_and_out[i2rec]; 00641 } 00642 } 00643 else 00644 { 00645 int iL=iL_in, iR=iR_in; 00646 assert((iR_in-iL_in)>0); // at least 2 entries 00647 00648 // separate (by fSep) all points between iL_in and iR_in in pTmpVert[] 00649 while(iL < iR) 00650 { 00651 tbool bReadyLeftSwap = TFALSE, bReadyRightSwap = TFALSE; 00652 while((!bReadyLeftSwap) && iL<iR) 00653 { 00654 assert(iL>=iL_in && iL<=iR_in); 00655 bReadyLeftSwap = !(pTmpVert[iL].vert[channel]<fSep); 00656 if(!bReadyLeftSwap) ++iL; 00657 } 00658 while((!bReadyRightSwap) && iL<iR) 00659 { 00660 assert(iR>=iL_in && iR<=iR_in); 00661 bReadyRightSwap = pTmpVert[iR].vert[channel]<fSep; 00662 if(!bReadyRightSwap) --iR; 00663 } 00664 assert( (iL<iR) || !(bReadyLeftSwap && bReadyRightSwap) ); 00665 00666 if(bReadyLeftSwap && bReadyRightSwap) 00667 { 00668 const STmpVert sTmp = pTmpVert[iL]; 00669 assert(iL<iR); 00670 pTmpVert[iL] = pTmpVert[iR]; 00671 pTmpVert[iR] = sTmp; 00672 ++iL; --iR; 00673 } 00674 } 00675 00676 assert(iL==(iR+1) || (iL==iR)); 00677 if(iL==iR) 00678 { 00679 const tbool bReadyRightSwap = pTmpVert[iR].vert[channel]<fSep; 00680 if(bReadyRightSwap) ++iL; 00681 else --iR; 00682 } 00683 00684 // only need to weld when there is more than 1 instance of the (x,y,z) 00685 if(iL_in < iR) 00686 MergeVertsFast(piTriList_in_and_out, pTmpVert, pContext, iL_in, iR); // weld all left of fSep 00687 if(iL < iR_in) 00688 MergeVertsFast(piTriList_in_and_out, pTmpVert, pContext, iL, iR_in); // weld all right of (or equal to) fSep 00689 } 00690 } 00691 00692 static void MergeVertsSlow(int piTriList_in_and_out[], const SMikkTSpaceContext * pContext, const int pTable[], const int iEntries) 00693 { 00694 // this can be optimized further using a tree structure or more hashing. 00695 int e=0; 00696 for(e=0; e<iEntries; e++) 00697 { 00698 int i = pTable[e]; 00699 const int index = piTriList_in_and_out[i]; 00700 const SVec3 vP = GetPosition(pContext, index); 00701 const SVec3 vN = GetNormal(pContext, index); 00702 const SVec3 vT = GetTexCoord(pContext, index); 00703 00704 tbool bNotFound = TTRUE; 00705 int e2=0, i2rec=-1; 00706 while(e2<e && bNotFound) 00707 { 00708 const int i2 = pTable[e2]; 00709 const int index2 = piTriList_in_and_out[i2]; 00710 const SVec3 vP2 = GetPosition(pContext, index2); 00711 const SVec3 vN2 = GetNormal(pContext, index2); 00712 const SVec3 vT2 = GetTexCoord(pContext, index2); 00713 i2rec = i2; 00714 00715 if(veq(vP,vP2) && veq(vN,vN2) && veq(vT,vT2)) 00716 bNotFound = TFALSE; 00717 else 00718 ++e2; 00719 } 00720 00721 // merge if previously found 00722 if(!bNotFound) 00723 piTriList_in_and_out[i] = piTriList_in_and_out[i2rec]; 00724 } 00725 } 00726 00727 static void GenerateSharedVerticesIndexListSlow(int piTriList_in_and_out[], const SMikkTSpaceContext * pContext, const int iNrTrianglesIn) 00728 { 00729 int iNumUniqueVerts = 0, t=0, i=0; 00730 for(t=0; t<iNrTrianglesIn; t++) 00731 { 00732 for(i=0; i<3; i++) 00733 { 00734 const int offs = t*3 + i; 00735 const int index = piTriList_in_and_out[offs]; 00736 00737 const SVec3 vP = GetPosition(pContext, index); 00738 const SVec3 vN = GetNormal(pContext, index); 00739 const SVec3 vT = GetTexCoord(pContext, index); 00740 00741 tbool bFound = TFALSE; 00742 int t2=0, index2rec=-1; 00743 while(!bFound && t2<=t) 00744 { 00745 int j=0; 00746 while(!bFound && j<3) 00747 { 00748 const int index2 = piTriList_in_and_out[t2*3 + j]; 00749 const SVec3 vP2 = GetPosition(pContext, index2); 00750 const SVec3 vN2 = GetNormal(pContext, index2); 00751 const SVec3 vT2 = GetTexCoord(pContext, index2); 00752 00753 if(veq(vP,vP2) && veq(vN,vN2) && veq(vT,vT2)) 00754 bFound = TTRUE; 00755 else 00756 ++j; 00757 } 00758 if(!bFound) ++t2; 00759 } 00760 00761 assert(bFound); 00762 // if we found our own 00763 if(index2rec == index) { ++iNumUniqueVerts; } 00764 00765 piTriList_in_and_out[offs] = index2rec; 00766 } 00767 } 00768 } 00769 00770 static int GenerateInitialVerticesIndexList(STriInfo pTriInfos[], int piTriList_out[], const SMikkTSpaceContext * pContext, const int iNrTrianglesIn) 00771 { 00772 int iTSpacesOffs = 0, f=0, t=0; 00773 int iDstTriIndex = 0; 00774 for(f=0; f<pContext->m_pInterface->m_getNumFaces(pContext); f++) 00775 { 00776 const int verts = pContext->m_pInterface->m_getNumVerticesOfFace(pContext, f); 00777 if(verts!=3 && verts!=4) continue; 00778 00779 pTriInfos[iDstTriIndex].iOrgFaceNumber = f; 00780 pTriInfos[iDstTriIndex].iTSpacesOffs = iTSpacesOffs; 00781 00782 if(verts==3) 00783 { 00784 unsigned char * pVerts = pTriInfos[iDstTriIndex].vert_num; 00785 pVerts[0]=0; pVerts[1]=1; pVerts[2]=2; 00786 piTriList_out[iDstTriIndex*3+0] = MakeIndex(f, 0); 00787 piTriList_out[iDstTriIndex*3+1] = MakeIndex(f, 1); 00788 piTriList_out[iDstTriIndex*3+2] = MakeIndex(f, 2); 00789 ++iDstTriIndex; // next 00790 } 00791 else 00792 { 00793 { 00794 pTriInfos[iDstTriIndex+1].iOrgFaceNumber = f; 00795 pTriInfos[iDstTriIndex+1].iTSpacesOffs = iTSpacesOffs; 00796 } 00797 00798 { 00799 // need an order independent way to evaluate 00800 // tspace on quads. This is done by splitting 00801 // along the shortest diagonal. 00802 const int i0 = MakeIndex(f, 0); 00803 const int i1 = MakeIndex(f, 1); 00804 const int i2 = MakeIndex(f, 2); 00805 const int i3 = MakeIndex(f, 3); 00806 const SVec3 T0 = GetTexCoord(pContext, i0); 00807 const SVec3 T1 = GetTexCoord(pContext, i1); 00808 const SVec3 T2 = GetTexCoord(pContext, i2); 00809 const SVec3 T3 = GetTexCoord(pContext, i3); 00810 const float distSQ_02 = LengthSquared(vsub(T2,T0)); 00811 const float distSQ_13 = LengthSquared(vsub(T3,T1)); 00812 tbool bQuadDiagIs_02; 00813 if(distSQ_02<distSQ_13) 00814 bQuadDiagIs_02 = TTRUE; 00815 else if(distSQ_13<distSQ_02) 00816 bQuadDiagIs_02 = TFALSE; 00817 else 00818 { 00819 const SVec3 P0 = GetPosition(pContext, i0); 00820 const SVec3 P1 = GetPosition(pContext, i1); 00821 const SVec3 P2 = GetPosition(pContext, i2); 00822 const SVec3 P3 = GetPosition(pContext, i3); 00823 const float distSQ_02 = LengthSquared(vsub(P2,P0)); 00824 const float distSQ_13 = LengthSquared(vsub(P3,P1)); 00825 00826 bQuadDiagIs_02 = distSQ_13<distSQ_02 ? TFALSE : TTRUE; 00827 } 00828 00829 if(bQuadDiagIs_02) 00830 { 00831 { 00832 unsigned char * pVerts_A = pTriInfos[iDstTriIndex].vert_num; 00833 pVerts_A[0]=0; pVerts_A[1]=1; pVerts_A[2]=2; 00834 } 00835 piTriList_out[iDstTriIndex*3+0] = i0; 00836 piTriList_out[iDstTriIndex*3+1] = i1; 00837 piTriList_out[iDstTriIndex*3+2] = i2; 00838 ++iDstTriIndex; // next 00839 { 00840 unsigned char * pVerts_B = pTriInfos[iDstTriIndex].vert_num; 00841 pVerts_B[0]=0; pVerts_B[1]=2; pVerts_B[2]=3; 00842 } 00843 piTriList_out[iDstTriIndex*3+0] = i0; 00844 piTriList_out[iDstTriIndex*3+1] = i2; 00845 piTriList_out[iDstTriIndex*3+2] = i3; 00846 ++iDstTriIndex; // next 00847 } 00848 else 00849 { 00850 { 00851 unsigned char * pVerts_A = pTriInfos[iDstTriIndex].vert_num; 00852 pVerts_A[0]=0; pVerts_A[1]=1; pVerts_A[2]=3; 00853 } 00854 piTriList_out[iDstTriIndex*3+0] = i0; 00855 piTriList_out[iDstTriIndex*3+1] = i1; 00856 piTriList_out[iDstTriIndex*3+2] = i3; 00857 ++iDstTriIndex; // next 00858 { 00859 unsigned char * pVerts_B = pTriInfos[iDstTriIndex].vert_num; 00860 pVerts_B[0]=1; pVerts_B[1]=2; pVerts_B[2]=3; 00861 } 00862 piTriList_out[iDstTriIndex*3+0] = i1; 00863 piTriList_out[iDstTriIndex*3+1] = i2; 00864 piTriList_out[iDstTriIndex*3+2] = i3; 00865 ++iDstTriIndex; // next 00866 } 00867 } 00868 } 00869 00870 iTSpacesOffs += verts; 00871 assert(iDstTriIndex<=iNrTrianglesIn); 00872 } 00873 00874 for(t=0; t<iNrTrianglesIn; t++) 00875 pTriInfos[t].iFlag = 0; 00876 00877 // return total amount of tspaces 00878 return iTSpacesOffs; 00879 } 00880 00881 static SVec3 GetPosition(const SMikkTSpaceContext * pContext, const int index) 00882 { 00883 int iF, iI; 00884 SVec3 res; float pos[3]; 00885 IndexToData(&iF, &iI, index); 00886 pContext->m_pInterface->m_getPosition(pContext, pos, iF, iI); 00887 res.x=pos[0]; res.y=pos[1]; res.z=pos[2]; 00888 return res; 00889 } 00890 00891 static SVec3 GetNormal(const SMikkTSpaceContext * pContext, const int index) 00892 { 00893 int iF, iI; 00894 SVec3 res; float norm[3]; 00895 IndexToData(&iF, &iI, index); 00896 pContext->m_pInterface->m_getNormal(pContext, norm, iF, iI); 00897 res.x=norm[0]; res.y=norm[1]; res.z=norm[2]; 00898 return res; 00899 } 00900 00901 static SVec3 GetTexCoord(const SMikkTSpaceContext * pContext, const int index) 00902 { 00903 int iF, iI; 00904 SVec3 res; float texc[2]; 00905 IndexToData(&iF, &iI, index); 00906 pContext->m_pInterface->m_getTexCoord(pContext, texc, iF, iI); 00907 res.x=texc[0]; res.y=texc[1]; res.z=1.0f; 00908 return res; 00909 } 00910 00913 00914 typedef union 00915 { 00916 struct 00917 { 00918 int i0, i1, f; 00919 }; 00920 int array[3]; 00921 } SEdge; 00922 00923 static void BuildNeighborsFast(STriInfo pTriInfos[], SEdge * pEdges, const int piTriListIn[], const int iNrTrianglesIn); 00924 static void BuildNeighborsSlow(STriInfo pTriInfos[], const int piTriListIn[], const int iNrTrianglesIn); 00925 00926 // returns the texture area times 2 00927 static float CalcTexArea(const SMikkTSpaceContext * pContext, const int indices[]) 00928 { 00929 const SVec3 t1 = GetTexCoord(pContext, indices[0]); 00930 const SVec3 t2 = GetTexCoord(pContext, indices[1]); 00931 const SVec3 t3 = GetTexCoord(pContext, indices[2]); 00932 00933 const float t21x = t2.x-t1.x; 00934 const float t21y = t2.y-t1.y; 00935 const float t31x = t3.x-t1.x; 00936 const float t31y = t3.y-t1.y; 00937 00938 const float fSignedAreaSTx2 = t21x*t31y - t21y*t31x; 00939 00940 return fSignedAreaSTx2<0 ? (-fSignedAreaSTx2) : fSignedAreaSTx2; 00941 } 00942 00943 static void InitTriInfo(STriInfo pTriInfos[], const int piTriListIn[], const SMikkTSpaceContext * pContext, const int iNrTrianglesIn) 00944 { 00945 int f=0, i=0, t=0; 00946 // pTriInfos[f].iFlag is cleared in GenerateInitialVerticesIndexList() which is called before this function. 00947 00948 // generate neighbor info list 00949 for(f=0; f<iNrTrianglesIn; f++) 00950 for(i=0; i<3; i++) 00951 { 00952 pTriInfos[f].FaceNeighbors[i] = -1; 00953 pTriInfos[f].AssignedGroup[i] = NULL; 00954 00955 pTriInfos[f].vOs.x=0.0f; pTriInfos[f].vOs.y=0.0f; pTriInfos[f].vOs.z=0.0f; 00956 pTriInfos[f].vOt.x=0.0f; pTriInfos[f].vOt.y=0.0f; pTriInfos[f].vOt.z=0.0f; 00957 pTriInfos[f].fMagS = 0; 00958 pTriInfos[f].fMagT = 0; 00959 00960 // assumed bad 00961 pTriInfos[f].iFlag |= GROUP_WITH_ANY; 00962 } 00963 00964 // evaluate first order derivatives 00965 for(f=0; f<iNrTrianglesIn; f++) 00966 { 00967 // initial values 00968 const SVec3 v1 = GetPosition(pContext, piTriListIn[f*3+0]); 00969 const SVec3 v2 = GetPosition(pContext, piTriListIn[f*3+1]); 00970 const SVec3 v3 = GetPosition(pContext, piTriListIn[f*3+2]); 00971 const SVec3 t1 = GetTexCoord(pContext, piTriListIn[f*3+0]); 00972 const SVec3 t2 = GetTexCoord(pContext, piTriListIn[f*3+1]); 00973 const SVec3 t3 = GetTexCoord(pContext, piTriListIn[f*3+2]); 00974 00975 const float t21x = t2.x-t1.x; 00976 const float t21y = t2.y-t1.y; 00977 const float t31x = t3.x-t1.x; 00978 const float t31y = t3.y-t1.y; 00979 const SVec3 d1 = vsub(v2,v1); 00980 const SVec3 d2 = vsub(v3,v1); 00981 00982 const float fSignedAreaSTx2 = t21x*t31y - t21y*t31x; 00983 //assert(fSignedAreaSTx2!=0); 00984 SVec3 vOs = vsub(vscale(t31y,d1), vscale(t21y,d2)); // eq 18 00985 SVec3 vOt = vadd(vscale(-t31x,d1), vscale(t21x,d2)); // eq 19 00986 00987 pTriInfos[f].iFlag |= (fSignedAreaSTx2>0 ? ORIENT_PRESERVING : 0); 00988 00989 if( NotZero(fSignedAreaSTx2) ) 00990 { 00991 const float fAbsArea = fabsf(fSignedAreaSTx2); 00992 const float fLenOs = Length(vOs); 00993 const float fLenOt = Length(vOt); 00994 const float fS = (pTriInfos[f].iFlag&ORIENT_PRESERVING)==0 ? (-1.0f) : 1.0f; 00995 if( NotZero(fLenOs) ) pTriInfos[f].vOs = vscale(fS/fLenOs, vOs); 00996 if( NotZero(fLenOt) ) pTriInfos[f].vOt = vscale(fS/fLenOt, vOt); 00997 00998 // evaluate magnitudes prior to normalization of vOs and vOt 00999 pTriInfos[f].fMagS = fLenOs / fAbsArea; 01000 pTriInfos[f].fMagT = fLenOt / fAbsArea; 01001 01002 // if this is a good triangle 01003 if( NotZero(pTriInfos[f].fMagS) && NotZero(pTriInfos[f].fMagT)) 01004 pTriInfos[f].iFlag &= (~GROUP_WITH_ANY); 01005 } 01006 } 01007 01008 // force otherwise healthy quads to a fixed orientation 01009 while(t<(iNrTrianglesIn-1)) 01010 { 01011 const int iFO_a = pTriInfos[t].iOrgFaceNumber; 01012 const int iFO_b = pTriInfos[t+1].iOrgFaceNumber; 01013 if(iFO_a==iFO_b) // this is a quad 01014 { 01015 const tbool bIsDeg_a = (pTriInfos[t].iFlag&MARK_DEGENERATE)!=0 ? TTRUE : TFALSE; 01016 const tbool bIsDeg_b = (pTriInfos[t+1].iFlag&MARK_DEGENERATE)!=0 ? TTRUE : TFALSE; 01017 01018 // bad triangles should already have been removed by 01019 // DegenPrologue(), but just in case check bIsDeg_a and bIsDeg_a are false 01020 if((bIsDeg_a||bIsDeg_b)==TFALSE) 01021 { 01022 const tbool bOrientA = (pTriInfos[t].iFlag&ORIENT_PRESERVING)!=0 ? TTRUE : TFALSE; 01023 const tbool bOrientB = (pTriInfos[t+1].iFlag&ORIENT_PRESERVING)!=0 ? TTRUE : TFALSE; 01024 // if this happens the quad has extremely bad mapping!! 01025 if(bOrientA!=bOrientB) 01026 { 01027 //printf("found quad with bad mapping\n"); 01028 tbool bChooseOrientFirstTri = TFALSE; 01029 if((pTriInfos[t+1].iFlag&GROUP_WITH_ANY)!=0) bChooseOrientFirstTri = TTRUE; 01030 else if( CalcTexArea(pContext, &piTriListIn[t*3+0]) >= CalcTexArea(pContext, &piTriListIn[(t+1)*3+0]) ) 01031 bChooseOrientFirstTri = TTRUE; 01032 01033 // force match 01034 { 01035 const int t0 = bChooseOrientFirstTri ? t : (t+1); 01036 const int t1 = bChooseOrientFirstTri ? (t+1) : t; 01037 pTriInfos[t1].iFlag &= (~ORIENT_PRESERVING); // clear first 01038 pTriInfos[t1].iFlag |= (pTriInfos[t0].iFlag&ORIENT_PRESERVING); // copy bit 01039 } 01040 } 01041 } 01042 t += 2; 01043 } 01044 else 01045 ++t; 01046 } 01047 01048 // match up edge pairs 01049 { 01050 SEdge * pEdges = (SEdge *) malloc(sizeof(SEdge)*iNrTrianglesIn*3); 01051 if(pEdges==NULL) 01052 BuildNeighborsSlow(pTriInfos, piTriListIn, iNrTrianglesIn); 01053 else 01054 { 01055 BuildNeighborsFast(pTriInfos, pEdges, piTriListIn, iNrTrianglesIn); 01056 01057 free(pEdges); 01058 } 01059 } 01060 } 01061 01064 01065 static tbool AssignRecur(const int piTriListIn[], STriInfo psTriInfos[], const int iMyTriIndex, SGroup * pGroup); 01066 static void AddTriToGroup(SGroup * pGroup, const int iTriIndex); 01067 01068 static int Build4RuleGroups(STriInfo pTriInfos[], SGroup pGroups[], int piGroupTrianglesBuffer[], const int piTriListIn[], const int iNrTrianglesIn) 01069 { 01070 const int iNrMaxGroups = iNrTrianglesIn*3; 01071 int iNrActiveGroups = 0; 01072 int iOffset = 0, f=0, i=0; 01073 for(f=0; f<iNrTrianglesIn; f++) 01074 { 01075 for(i=0; i<3; i++) 01076 { 01077 // if not assigned to a group 01078 if((pTriInfos[f].iFlag&GROUP_WITH_ANY)==0 && pTriInfos[f].AssignedGroup[i]==NULL) 01079 { 01080 tbool bOrPre; 01081 int neigh_indexL, neigh_indexR; 01082 const int vert_index = piTriListIn[f*3+i]; 01083 assert(iNrActiveGroups<iNrMaxGroups); 01084 pTriInfos[f].AssignedGroup[i] = &pGroups[iNrActiveGroups]; 01085 pTriInfos[f].AssignedGroup[i]->iVertexRepresentitive = vert_index; 01086 pTriInfos[f].AssignedGroup[i]->bOrientPreservering = (pTriInfos[f].iFlag&ORIENT_PRESERVING)!=0; 01087 pTriInfos[f].AssignedGroup[i]->iNrFaces = 0; 01088 pTriInfos[f].AssignedGroup[i]->pFaceIndices = &piGroupTrianglesBuffer[iOffset]; 01089 ++iNrActiveGroups; 01090 01091 AddTriToGroup(pTriInfos[f].AssignedGroup[i], f); 01092 bOrPre = (pTriInfos[f].iFlag&ORIENT_PRESERVING)!=0 ? TTRUE : TFALSE; 01093 neigh_indexL = pTriInfos[f].FaceNeighbors[i]; 01094 neigh_indexR = pTriInfos[f].FaceNeighbors[i>0?(i-1):2]; 01095 if(neigh_indexL>=0) // neighbor 01096 { 01097 const tbool bAnswer = 01098 AssignRecur(piTriListIn, pTriInfos, neigh_indexL, 01099 pTriInfos[f].AssignedGroup[i] ); 01100 01101 const tbool bOrPre2 = (pTriInfos[neigh_indexL].iFlag&ORIENT_PRESERVING)!=0 ? TTRUE : TFALSE; 01102 const tbool bDiff = bOrPre!=bOrPre2 ? TTRUE : TFALSE; 01103 assert(bAnswer || bDiff); 01104 } 01105 if(neigh_indexR>=0) // neighbor 01106 { 01107 const tbool bAnswer = 01108 AssignRecur(piTriListIn, pTriInfos, neigh_indexR, 01109 pTriInfos[f].AssignedGroup[i] ); 01110 01111 const tbool bOrPre2 = (pTriInfos[neigh_indexR].iFlag&ORIENT_PRESERVING)!=0 ? TTRUE : TFALSE; 01112 const tbool bDiff = bOrPre!=bOrPre2 ? TTRUE : TFALSE; 01113 assert(bAnswer || bDiff); 01114 } 01115 01116 // update offset 01117 iOffset += pTriInfos[f].AssignedGroup[i]->iNrFaces; 01118 // since the groups are disjoint a triangle can never 01119 // belong to more than 3 groups. Subsequently something 01120 // is completely screwed if this assertion ever hits. 01121 assert(iOffset <= iNrMaxGroups); 01122 } 01123 } 01124 } 01125 01126 return iNrActiveGroups; 01127 } 01128 01129 static void AddTriToGroup(SGroup * pGroup, const int iTriIndex) 01130 { 01131 pGroup->pFaceIndices[pGroup->iNrFaces] = iTriIndex; 01132 ++pGroup->iNrFaces; 01133 } 01134 01135 static tbool AssignRecur(const int piTriListIn[], STriInfo psTriInfos[], 01136 const int iMyTriIndex, SGroup * pGroup) 01137 { 01138 STriInfo * pMyTriInfo = &psTriInfos[iMyTriIndex]; 01139 01140 // track down vertex 01141 const int iVertRep = pGroup->iVertexRepresentitive; 01142 const int * pVerts = &piTriListIn[3*iMyTriIndex+0]; 01143 int i=-1; 01144 if(pVerts[0]==iVertRep) i=0; 01145 else if(pVerts[1]==iVertRep) i=1; 01146 else if(pVerts[2]==iVertRep) i=2; 01147 assert(i>=0 && i<3); 01148 01149 // early out 01150 if(pMyTriInfo->AssignedGroup[i] == pGroup) return TTRUE; 01151 else if(pMyTriInfo->AssignedGroup[i]!=NULL) return TFALSE; 01152 if((pMyTriInfo->iFlag&GROUP_WITH_ANY)!=0) 01153 { 01154 // first to group with a group-with-anything triangle 01155 // determines it's orientation. 01156 // This is the only existing order dependency in the code!! 01157 if( pMyTriInfo->AssignedGroup[0] == NULL && 01158 pMyTriInfo->AssignedGroup[1] == NULL && 01159 pMyTriInfo->AssignedGroup[2] == NULL ) 01160 { 01161 pMyTriInfo->iFlag &= (~ORIENT_PRESERVING); 01162 pMyTriInfo->iFlag |= (pGroup->bOrientPreservering ? ORIENT_PRESERVING : 0); 01163 } 01164 } 01165 { 01166 const tbool bOrient = (pMyTriInfo->iFlag&ORIENT_PRESERVING)!=0 ? TTRUE : TFALSE; 01167 if(bOrient != pGroup->bOrientPreservering) return TFALSE; 01168 } 01169 01170 AddTriToGroup(pGroup, iMyTriIndex); 01171 pMyTriInfo->AssignedGroup[i] = pGroup; 01172 01173 { 01174 const int neigh_indexL = pMyTriInfo->FaceNeighbors[i]; 01175 const int neigh_indexR = pMyTriInfo->FaceNeighbors[i>0?(i-1):2]; 01176 if(neigh_indexL>=0) 01177 AssignRecur(piTriListIn, psTriInfos, neigh_indexL, pGroup); 01178 if(neigh_indexR>=0) 01179 AssignRecur(piTriListIn, psTriInfos, neigh_indexR, pGroup); 01180 } 01181 01182 01183 01184 return TTRUE; 01185 } 01186 01189 01190 static tbool CompareSubGroups(const SSubGroup * pg1, const SSubGroup * pg2); 01191 static void QuickSort(int* pSortBuffer, int iLeft, int iRight, unsigned int uSeed); 01192 static STSpace EvalTspace(int face_indices[], const int iFaces, const int piTriListIn[], const STriInfo pTriInfos[], const SMikkTSpaceContext * pContext, const int iVertexRepresentitive); 01193 01194 static tbool GenerateTSpaces(STSpace psTspace[], const STriInfo pTriInfos[], const SGroup pGroups[], 01195 const int iNrActiveGroups, const int piTriListIn[], const float fThresCos, 01196 const SMikkTSpaceContext * pContext) 01197 { 01198 STSpace * pSubGroupTspace = NULL; 01199 SSubGroup * pUniSubGroups = NULL; 01200 int * pTmpMembers = NULL; 01201 int iMaxNrFaces=0, iUniqueTspaces=0, g=0, i=0; 01202 for(g=0; g<iNrActiveGroups; g++) 01203 if(iMaxNrFaces < pGroups[g].iNrFaces) 01204 iMaxNrFaces = pGroups[g].iNrFaces; 01205 01206 if(iMaxNrFaces == 0) return TTRUE; 01207 01208 // make initial allocations 01209 pSubGroupTspace = (STSpace *) malloc(sizeof(STSpace)*iMaxNrFaces); 01210 pUniSubGroups = (SSubGroup *) malloc(sizeof(SSubGroup)*iMaxNrFaces); 01211 pTmpMembers = (int *) malloc(sizeof(int)*iMaxNrFaces); 01212 if(pSubGroupTspace==NULL || pUniSubGroups==NULL || pTmpMembers==NULL) 01213 { 01214 if(pSubGroupTspace!=NULL) free(pSubGroupTspace); 01215 if(pUniSubGroups!=NULL) free(pUniSubGroups); 01216 if(pTmpMembers!=NULL) free(pTmpMembers); 01217 return TFALSE; 01218 } 01219 01220 01221 iUniqueTspaces = 0; 01222 for(g=0; g<iNrActiveGroups; g++) 01223 { 01224 const SGroup * pGroup = &pGroups[g]; 01225 int iUniqueSubGroups = 0, s=0; 01226 01227 for(i=0; i<pGroup->iNrFaces; i++) // triangles 01228 { 01229 const int f = pGroup->pFaceIndices[i]; // triangle number 01230 int index=-1, iVertIndex=-1, iOF_1=-1, iMembers=0, j=0, l=0; 01231 SSubGroup tmp_group; 01232 tbool bFound; 01233 SVec3 n, vOs, vOt; 01234 if(pTriInfos[f].AssignedGroup[0]==pGroup) index=0; 01235 else if(pTriInfos[f].AssignedGroup[1]==pGroup) index=1; 01236 else if(pTriInfos[f].AssignedGroup[2]==pGroup) index=2; 01237 assert(index>=0 && index<3); 01238 01239 iVertIndex = piTriListIn[f*3+index]; 01240 assert(iVertIndex==pGroup->iVertexRepresentitive); 01241 01242 // is normalized already 01243 n = GetNormal(pContext, iVertIndex); 01244 01245 // project 01246 vOs = vsub(pTriInfos[f].vOs, vscale(vdot(n,pTriInfos[f].vOs), n)); 01247 vOt = vsub(pTriInfos[f].vOt, vscale(vdot(n,pTriInfos[f].vOt), n)); 01248 if( VNotZero(vOs) ) vOs = Normalize(vOs); 01249 if( VNotZero(vOt) ) vOt = Normalize(vOt); 01250 01251 // original face number 01252 iOF_1 = pTriInfos[f].iOrgFaceNumber; 01253 01254 iMembers = 0; 01255 for(j=0; j<pGroup->iNrFaces; j++) 01256 { 01257 const int t = pGroup->pFaceIndices[j]; // triangle number 01258 const int iOF_2 = pTriInfos[t].iOrgFaceNumber; 01259 01260 // project 01261 SVec3 vOs2 = vsub(pTriInfos[t].vOs, vscale(vdot(n,pTriInfos[t].vOs), n)); 01262 SVec3 vOt2 = vsub(pTriInfos[t].vOt, vscale(vdot(n,pTriInfos[t].vOt), n)); 01263 if( VNotZero(vOs2) ) vOs2 = Normalize(vOs2); 01264 if( VNotZero(vOt2) ) vOt2 = Normalize(vOt2); 01265 01266 { 01267 const tbool bAny = ( (pTriInfos[f].iFlag | pTriInfos[t].iFlag) & GROUP_WITH_ANY )!=0 ? TTRUE : TFALSE; 01268 // make sure triangles which belong to the same quad are joined. 01269 const tbool bSameOrgFace = iOF_1==iOF_2 ? TTRUE : TFALSE; 01270 01271 const float fCosS = vdot(vOs,vOs2); 01272 const float fCosT = vdot(vOt,vOt2); 01273 01274 assert(f!=t || bSameOrgFace); // sanity check 01275 if(bAny || bSameOrgFace || (fCosS>fThresCos && fCosT>fThresCos)) 01276 pTmpMembers[iMembers++] = t; 01277 } 01278 } 01279 01280 // sort pTmpMembers 01281 tmp_group.iNrFaces = iMembers; 01282 tmp_group.pTriMembers = pTmpMembers; 01283 if(iMembers>1) 01284 { 01285 unsigned int uSeed = INTERNAL_RND_SORT_SEED; // could replace with a random seed? 01286 QuickSort(pTmpMembers, 0, iMembers-1, uSeed); 01287 } 01288 01289 // look for an existing match 01290 bFound = TFALSE; 01291 l=0; 01292 while(l<iUniqueSubGroups && !bFound) 01293 { 01294 bFound = CompareSubGroups(&tmp_group, &pUniSubGroups[l]); 01295 if(!bFound) ++l; 01296 } 01297 01298 // assign tangent space index 01299 assert(bFound || l==iUniqueSubGroups); 01300 //piTempTangIndices[f*3+index] = iUniqueTspaces+l; 01301 01302 // if no match was found we allocate a new subgroup 01303 if(!bFound) 01304 { 01305 // insert new subgroup 01306 int * pIndices = (int *) malloc(sizeof(int)*iMembers); 01307 if(pIndices==NULL) 01308 { 01309 // clean up and return false 01310 int s=0; 01311 for(s=0; s<iUniqueSubGroups; s++) 01312 free(pUniSubGroups[s].pTriMembers); 01313 free(pUniSubGroups); 01314 free(pTmpMembers); 01315 free(pSubGroupTspace); 01316 return TFALSE; 01317 } 01318 pUniSubGroups[iUniqueSubGroups].iNrFaces = iMembers; 01319 pUniSubGroups[iUniqueSubGroups].pTriMembers = pIndices; 01320 memcpy(pIndices, tmp_group.pTriMembers, iMembers*sizeof(int)); 01321 pSubGroupTspace[iUniqueSubGroups] = 01322 EvalTspace(tmp_group.pTriMembers, iMembers, piTriListIn, pTriInfos, pContext, pGroup->iVertexRepresentitive); 01323 ++iUniqueSubGroups; 01324 } 01325 01326 // output tspace 01327 { 01328 const int iOffs = pTriInfos[f].iTSpacesOffs; 01329 const int iVert = pTriInfos[f].vert_num[index]; 01330 STSpace * pTS_out = &psTspace[iOffs+iVert]; 01331 assert(pTS_out->iCounter<2); 01332 assert(((pTriInfos[f].iFlag&ORIENT_PRESERVING)!=0) == pGroup->bOrientPreservering); 01333 if(pTS_out->iCounter==1) 01334 { 01335 *pTS_out = AvgTSpace(pTS_out, &pSubGroupTspace[l]); 01336 pTS_out->iCounter = 2; // update counter 01337 pTS_out->bOrient = pGroup->bOrientPreservering; 01338 } 01339 else 01340 { 01341 assert(pTS_out->iCounter==0); 01342 *pTS_out = pSubGroupTspace[l]; 01343 pTS_out->iCounter = 1; // update counter 01344 pTS_out->bOrient = pGroup->bOrientPreservering; 01345 } 01346 } 01347 } 01348 01349 // clean up and offset iUniqueTspaces 01350 for(s=0; s<iUniqueSubGroups; s++) 01351 free(pUniSubGroups[s].pTriMembers); 01352 iUniqueTspaces += iUniqueSubGroups; 01353 } 01354 01355 // clean up 01356 free(pUniSubGroups); 01357 free(pTmpMembers); 01358 free(pSubGroupTspace); 01359 01360 return TTRUE; 01361 } 01362 01363 static STSpace EvalTspace(int face_indices[], const int iFaces, const int piTriListIn[], const STriInfo pTriInfos[], 01364 const SMikkTSpaceContext * pContext, const int iVertexRepresentitive) 01365 { 01366 STSpace res; 01367 float fAngleSum = 0; 01368 int face=0; 01369 res.vOs.x=0.0f; res.vOs.y=0.0f; res.vOs.z=0.0f; 01370 res.vOt.x=0.0f; res.vOt.y=0.0f; res.vOt.z=0.0f; 01371 res.fMagS = 0; res.fMagT = 0; 01372 01373 for(face=0; face<iFaces; face++) 01374 { 01375 const int f = face_indices[face]; 01376 01377 // only valid triangles get to add their contribution 01378 if( (pTriInfos[f].iFlag&GROUP_WITH_ANY)==0 ) 01379 { 01380 SVec3 n, vOs, vOt, p0, p1, p2, v1, v2; 01381 float fCos, fAngle, fMagS, fMagT; 01382 int i=-1, index=-1, i0=-1, i1=-1, i2=-1; 01383 if(piTriListIn[3*f+0]==iVertexRepresentitive) i=0; 01384 else if(piTriListIn[3*f+1]==iVertexRepresentitive) i=1; 01385 else if(piTriListIn[3*f+2]==iVertexRepresentitive) i=2; 01386 assert(i>=0 && i<3); 01387 01388 // project 01389 index = piTriListIn[3*f+i]; 01390 n = GetNormal(pContext, index); 01391 vOs = vsub(pTriInfos[f].vOs, vscale(vdot(n,pTriInfos[f].vOs), n)); 01392 vOt = vsub(pTriInfos[f].vOt, vscale(vdot(n,pTriInfos[f].vOt), n)); 01393 if( VNotZero(vOs) ) vOs = Normalize(vOs); 01394 if( VNotZero(vOt) ) vOt = Normalize(vOt); 01395 01396 i2 = piTriListIn[3*f + (i<2?(i+1):0)]; 01397 i1 = piTriListIn[3*f + i]; 01398 i0 = piTriListIn[3*f + (i>0?(i-1):2)]; 01399 01400 p0 = GetPosition(pContext, i0); 01401 p1 = GetPosition(pContext, i1); 01402 p2 = GetPosition(pContext, i2); 01403 v1 = vsub(p0,p1); 01404 v2 = vsub(p2,p1); 01405 01406 // project 01407 v1 = vsub(v1, vscale(vdot(n,v1),n)); if( VNotZero(v1) ) v1 = Normalize(v1); 01408 v2 = vsub(v2, vscale(vdot(n,v2),n)); if( VNotZero(v2) ) v2 = Normalize(v2); 01409 01410 // weight contribution by the angle 01411 // between the two edge vectors 01412 fCos = vdot(v1,v2); fCos=fCos>1?1:(fCos<(-1) ? (-1) : fCos); 01413 fAngle = (float) acos(fCos); 01414 fMagS = pTriInfos[f].fMagS; 01415 fMagT = pTriInfos[f].fMagT; 01416 01417 res.vOs=vadd(res.vOs, vscale(fAngle,vOs)); 01418 res.vOt=vadd(res.vOt,vscale(fAngle,vOt)); 01419 res.fMagS+=(fAngle*fMagS); 01420 res.fMagT+=(fAngle*fMagT); 01421 fAngleSum += fAngle; 01422 } 01423 } 01424 01425 // normalize 01426 if( VNotZero(res.vOs) ) res.vOs = Normalize(res.vOs); 01427 if( VNotZero(res.vOt) ) res.vOt = Normalize(res.vOt); 01428 if(fAngleSum>0) 01429 { 01430 res.fMagS /= fAngleSum; 01431 res.fMagT /= fAngleSum; 01432 } 01433 01434 return res; 01435 } 01436 01437 static tbool CompareSubGroups(const SSubGroup * pg1, const SSubGroup * pg2) 01438 { 01439 tbool bStillSame=TTRUE; 01440 int i=0; 01441 if(pg1->iNrFaces!=pg2->iNrFaces) return TFALSE; 01442 while(i<pg1->iNrFaces && bStillSame) 01443 { 01444 bStillSame = pg1->pTriMembers[i]==pg2->pTriMembers[i] ? TTRUE : TFALSE; 01445 if(bStillSame) ++i; 01446 } 01447 return bStillSame; 01448 } 01449 01450 static void QuickSort(int* pSortBuffer, int iLeft, int iRight, unsigned int uSeed) 01451 { 01452 int iL, iR, n, index, iMid, iTmp; 01453 01454 // Random 01455 unsigned int t=uSeed&31; 01456 t=(uSeed<<t)|(uSeed>>(32-t)); 01457 uSeed=uSeed+t+3; 01458 // Random end 01459 01460 iL=iLeft; iR=iRight; 01461 n = (iR-iL)+1; 01462 assert(n>=0); 01463 index = (int) (uSeed%n); 01464 01465 iMid=pSortBuffer[index + iL]; 01466 01467 01468 do 01469 { 01470 while(pSortBuffer[iL] < iMid) 01471 ++iL; 01472 while(pSortBuffer[iR] > iMid) 01473 --iR; 01474 01475 if(iL <= iR) 01476 { 01477 iTmp = pSortBuffer[iL]; 01478 pSortBuffer[iL] = pSortBuffer[iR]; 01479 pSortBuffer[iR] = iTmp; 01480 ++iL; --iR; 01481 } 01482 } 01483 while(iL <= iR); 01484 01485 if(iLeft < iR) 01486 QuickSort(pSortBuffer, iLeft, iR, uSeed); 01487 if(iL < iRight) 01488 QuickSort(pSortBuffer, iL, iRight, uSeed); 01489 } 01490 01493 01494 static void QuickSortEdges(SEdge * pSortBuffer, int iLeft, int iRight, const int channel, unsigned int uSeed); 01495 static void GetEdge(int * i0_out, int * i1_out, int * edgenum_out, const int indices[], const int i0_in, const int i1_in); 01496 01497 static void BuildNeighborsFast(STriInfo pTriInfos[], SEdge * pEdges, const int piTriListIn[], const int iNrTrianglesIn) 01498 { 01499 // build array of edges 01500 unsigned int uSeed = INTERNAL_RND_SORT_SEED; // could replace with a random seed? 01501 int iEntries=0, iCurStartIndex=-1, f=0, i=0; 01502 for(f=0; f<iNrTrianglesIn; f++) 01503 for(i=0; i<3; i++) 01504 { 01505 const int i0 = piTriListIn[f*3+i]; 01506 const int i1 = piTriListIn[f*3+(i<2?(i+1):0)]; 01507 pEdges[f*3+i].i0 = i0 < i1 ? i0 : i1; // put minimum index in i0 01508 pEdges[f*3+i].i1 = !(i0 < i1) ? i0 : i1; // put maximum index in i1 01509 pEdges[f*3+i].f = f; // record face number 01510 } 01511 01512 // sort over all edges by i0, this is the pricy one. 01513 QuickSortEdges(pEdges, 0, iNrTrianglesIn*3-1, 0, uSeed); // sort channel 0 which is i0 01514 01515 // sub sort over i1, should be fast. 01516 // could replace this with a 64 bit int sort over (i0,i1) 01517 // with i0 as msb in the quicksort call above. 01518 iEntries = iNrTrianglesIn*3; 01519 iCurStartIndex = 0; 01520 for(i=1; i<iEntries; i++) 01521 { 01522 if(pEdges[iCurStartIndex].i0 != pEdges[i].i0) 01523 { 01524 const int iL = iCurStartIndex; 01525 const int iR = i-1; 01526 //const int iElems = i-iL; 01527 iCurStartIndex = i; 01528 QuickSortEdges(pEdges, iL, iR, 1, uSeed); // sort channel 1 which is i1 01529 } 01530 } 01531 01532 // sub sort over f, which should be fast. 01533 // this step is to remain compliant with BuildNeighborsSlow() when 01534 // more than 2 triangles use the same edge (such as a butterfly topology). 01535 iCurStartIndex = 0; 01536 for(i=1; i<iEntries; i++) 01537 { 01538 if(pEdges[iCurStartIndex].i0 != pEdges[i].i0 || pEdges[iCurStartIndex].i1 != pEdges[i].i1) 01539 { 01540 const int iL = iCurStartIndex; 01541 const int iR = i-1; 01542 //const int iElems = i-iL; 01543 iCurStartIndex = i; 01544 QuickSortEdges(pEdges, iL, iR, 2, uSeed); // sort channel 2 which is f 01545 } 01546 } 01547 01548 // pair up, adjacent triangles 01549 for(i=0; i<iEntries; i++) 01550 { 01551 const int i0=pEdges[i].i0; 01552 const int i1=pEdges[i].i1; 01553 const int f = pEdges[i].f; 01554 tbool bUnassigned_A; 01555 01556 int i0_A, i1_A; 01557 int edgenum_A, edgenum_B=0; // 0,1 or 2 01558 GetEdge(&i0_A, &i1_A, &edgenum_A, &piTriListIn[f*3], i0, i1); // resolve index ordering and edge_num 01559 bUnassigned_A = pTriInfos[f].FaceNeighbors[edgenum_A] == -1 ? TTRUE : TFALSE; 01560 01561 if(bUnassigned_A) 01562 { 01563 // get true index ordering 01564 int j=i+1, t; 01565 tbool bNotFound = TTRUE; 01566 while(j<iEntries && i0==pEdges[j].i0 && i1==pEdges[j].i1 && bNotFound) 01567 { 01568 tbool bUnassigned_B; 01569 int i0_B, i1_B; 01570 t = pEdges[j].f; 01571 // flip i0_B and i1_B 01572 GetEdge(&i1_B, &i0_B, &edgenum_B, &piTriListIn[t*3], pEdges[j].i0, pEdges[j].i1); // resolve index ordering and edge_num 01573 //assert(!(i0_A==i1_B && i1_A==i0_B)); 01574 bUnassigned_B = pTriInfos[t].FaceNeighbors[edgenum_B]==-1 ? TTRUE : TFALSE; 01575 if(i0_A==i0_B && i1_A==i1_B && bUnassigned_B) 01576 bNotFound = TFALSE; 01577 else 01578 ++j; 01579 } 01580 01581 if(!bNotFound) 01582 { 01583 int t = pEdges[j].f; 01584 pTriInfos[f].FaceNeighbors[edgenum_A] = t; 01585 //assert(pTriInfos[t].FaceNeighbors[edgenum_B]==-1); 01586 pTriInfos[t].FaceNeighbors[edgenum_B] = f; 01587 } 01588 } 01589 } 01590 } 01591 01592 static void BuildNeighborsSlow(STriInfo pTriInfos[], const int piTriListIn[], const int iNrTrianglesIn) 01593 { 01594 int f=0, i=0; 01595 for(f=0; f<iNrTrianglesIn; f++) 01596 { 01597 for(i=0; i<3; i++) 01598 { 01599 // if unassigned 01600 if(pTriInfos[f].FaceNeighbors[i] == -1) 01601 { 01602 const int i0_A = piTriListIn[f*3+i]; 01603 const int i1_A = piTriListIn[f*3+(i<2?(i+1):0)]; 01604 01605 // search for a neighbor 01606 tbool bFound = TFALSE; 01607 int t=0, j=0; 01608 while(!bFound && t<iNrTrianglesIn) 01609 { 01610 if(t!=f) 01611 { 01612 j=0; 01613 while(!bFound && j<3) 01614 { 01615 // in rev order 01616 const int i1_B = piTriListIn[t*3+j]; 01617 const int i0_B = piTriListIn[t*3+(j<2?(j+1):0)]; 01618 //assert(!(i0_A==i1_B && i1_A==i0_B)); 01619 if(i0_A==i0_B && i1_A==i1_B) 01620 bFound = TTRUE; 01621 else 01622 ++j; 01623 } 01624 } 01625 01626 if(!bFound) ++t; 01627 } 01628 01629 // assign neighbors 01630 if(bFound) 01631 { 01632 pTriInfos[f].FaceNeighbors[i] = t; 01633 //assert(pTriInfos[t].FaceNeighbors[j]==-1); 01634 pTriInfos[t].FaceNeighbors[j] = f; 01635 } 01636 } 01637 } 01638 } 01639 } 01640 01641 static void QuickSortEdges(SEdge * pSortBuffer, int iLeft, int iRight, const int channel, unsigned int uSeed) 01642 { 01643 unsigned int t; 01644 int iL, iR, n, index, iMid; 01645 01646 // early out 01647 SEdge sTmp; 01648 const int iElems = iRight-iLeft+1; 01649 if(iElems<2) return; 01650 else if(iElems==2) 01651 { 01652 if(pSortBuffer[iLeft].array[channel] > pSortBuffer[iRight].array[channel]) 01653 { 01654 sTmp = pSortBuffer[iLeft]; 01655 pSortBuffer[iLeft] = pSortBuffer[iRight]; 01656 pSortBuffer[iRight] = sTmp; 01657 } 01658 return; 01659 } 01660 01661 // Random 01662 t=uSeed&31; 01663 t=(uSeed<<t)|(uSeed>>(32-t)); 01664 uSeed=uSeed+t+3; 01665 // Random end 01666 01667 iL=iLeft, iR=iRight; 01668 n = (iR-iL)+1; 01669 assert(n>=0); 01670 index = (int) (uSeed%n); 01671 01672 iMid=pSortBuffer[index + iL].array[channel]; 01673 01674 do 01675 { 01676 while(pSortBuffer[iL].array[channel] < iMid) 01677 ++iL; 01678 while(pSortBuffer[iR].array[channel] > iMid) 01679 --iR; 01680 01681 if(iL <= iR) 01682 { 01683 sTmp = pSortBuffer[iL]; 01684 pSortBuffer[iL] = pSortBuffer[iR]; 01685 pSortBuffer[iR] = sTmp; 01686 ++iL; --iR; 01687 } 01688 } 01689 while(iL <= iR); 01690 01691 if(iLeft < iR) 01692 QuickSortEdges(pSortBuffer, iLeft, iR, channel, uSeed); 01693 if(iL < iRight) 01694 QuickSortEdges(pSortBuffer, iL, iRight, channel, uSeed); 01695 } 01696 01697 // resolve ordering and edge number 01698 static void GetEdge(int * i0_out, int * i1_out, int * edgenum_out, const int indices[], const int i0_in, const int i1_in) 01699 { 01700 *edgenum_out = -1; 01701 01702 // test if first index is on the edge 01703 if(indices[0]==i0_in || indices[0]==i1_in) 01704 { 01705 // test if second index is on the edge 01706 if(indices[1]==i0_in || indices[1]==i1_in) 01707 { 01708 edgenum_out[0]=0; // first edge 01709 i0_out[0]=indices[0]; 01710 i1_out[0]=indices[1]; 01711 } 01712 else 01713 { 01714 edgenum_out[0]=2; // third edge 01715 i0_out[0]=indices[2]; 01716 i1_out[0]=indices[0]; 01717 } 01718 } 01719 else 01720 { 01721 // only second and third index is on the edge 01722 edgenum_out[0]=1; // second edge 01723 i0_out[0]=indices[1]; 01724 i1_out[0]=indices[2]; 01725 } 01726 } 01727 01728 01731 01732 static void DegenPrologue(STriInfo pTriInfos[], int piTriList_out[], const int iNrTrianglesIn, const int iTotTris) 01733 { 01734 int iNextGoodTriangleSearchIndex=-1; 01735 tbool bStillFindingGoodOnes; 01736 01737 // locate quads with only one good triangle 01738 int t=0; 01739 while(t<(iTotTris-1)) 01740 { 01741 const int iFO_a = pTriInfos[t].iOrgFaceNumber; 01742 const int iFO_b = pTriInfos[t+1].iOrgFaceNumber; 01743 if(iFO_a==iFO_b) // this is a quad 01744 { 01745 const tbool bIsDeg_a = (pTriInfos[t].iFlag&MARK_DEGENERATE)!=0 ? TTRUE : TFALSE; 01746 const tbool bIsDeg_b = (pTriInfos[t+1].iFlag&MARK_DEGENERATE)!=0 ? TTRUE : TFALSE; 01747 if((bIsDeg_a^bIsDeg_b)!=0) 01748 { 01749 pTriInfos[t].iFlag |= QUAD_ONE_DEGEN_TRI; 01750 pTriInfos[t+1].iFlag |= QUAD_ONE_DEGEN_TRI; 01751 } 01752 t += 2; 01753 } 01754 else 01755 ++t; 01756 } 01757 01758 // reorder list so all degen triangles are moved to the back 01759 // without reordering the good triangles 01760 iNextGoodTriangleSearchIndex = 1; 01761 t=0; 01762 bStillFindingGoodOnes = TTRUE; 01763 while(t<iNrTrianglesIn && bStillFindingGoodOnes) 01764 { 01765 const tbool bIsGood = (pTriInfos[t].iFlag&MARK_DEGENERATE)==0 ? TTRUE : TFALSE; 01766 if(bIsGood) 01767 { 01768 if(iNextGoodTriangleSearchIndex < (t+2)) 01769 iNextGoodTriangleSearchIndex = t+2; 01770 } 01771 else 01772 { 01773 int t0, t1; 01774 // search for the first good triangle. 01775 tbool bJustADegenerate = TTRUE; 01776 while(bJustADegenerate && iNextGoodTriangleSearchIndex<iTotTris) 01777 { 01778 const tbool bIsGood = (pTriInfos[iNextGoodTriangleSearchIndex].iFlag&MARK_DEGENERATE)==0 ? TTRUE : TFALSE; 01779 if(bIsGood) bJustADegenerate=TFALSE; 01780 else ++iNextGoodTriangleSearchIndex; 01781 } 01782 01783 t0 = t; 01784 t1 = iNextGoodTriangleSearchIndex; 01785 ++iNextGoodTriangleSearchIndex; 01786 assert(iNextGoodTriangleSearchIndex > (t+1)); 01787 01788 // swap triangle t0 and t1 01789 if(!bJustADegenerate) 01790 { 01791 int i=0; 01792 for(i=0; i<3; i++) 01793 { 01794 const int index = piTriList_out[t0*3+i]; 01795 piTriList_out[t0*3+i] = piTriList_out[t1*3+i]; 01796 piTriList_out[t1*3+i] = index; 01797 } 01798 { 01799 const STriInfo tri_info = pTriInfos[t0]; 01800 pTriInfos[t0] = pTriInfos[t1]; 01801 pTriInfos[t1] = tri_info; 01802 } 01803 } 01804 else 01805 bStillFindingGoodOnes = TFALSE; // this is not supposed to happen 01806 } 01807 01808 if(bStillFindingGoodOnes) ++t; 01809 } 01810 01811 assert(bStillFindingGoodOnes); // code will still work. 01812 assert(iNrTrianglesIn == t); 01813 } 01814 01815 static void DegenEpilogue(STSpace psTspace[], STriInfo pTriInfos[], int piTriListIn[], const SMikkTSpaceContext * pContext, const int iNrTrianglesIn, const int iTotTris) 01816 { 01817 int t=0, i=0; 01818 // deal with degenerate triangles 01819 // punishment for degenerate triangles is O(N^2) 01820 for(t=iNrTrianglesIn; t<iTotTris; t++) 01821 { 01822 // degenerate triangles on a quad with one good triangle are skipped 01823 // here but processed in the next loop 01824 const tbool bSkip = (pTriInfos[t].iFlag&QUAD_ONE_DEGEN_TRI)!=0 ? TTRUE : TFALSE; 01825 01826 if(!bSkip) 01827 { 01828 for(i=0; i<3; i++) 01829 { 01830 const int index1 = piTriListIn[t*3+i]; 01831 // search through the good triangles 01832 tbool bNotFound = TTRUE; 01833 int j=0; 01834 while(bNotFound && j<(3*iNrTrianglesIn)) 01835 { 01836 const int index2 = piTriListIn[j]; 01837 if(index1==index2) bNotFound=TFALSE; 01838 else ++j; 01839 } 01840 01841 if(!bNotFound) 01842 { 01843 const int iTri = j/3; 01844 const int iVert = j%3; 01845 const int iSrcVert=pTriInfos[iTri].vert_num[iVert]; 01846 const int iSrcOffs=pTriInfos[iTri].iTSpacesOffs; 01847 const int iDstVert=pTriInfos[t].vert_num[i]; 01848 const int iDstOffs=pTriInfos[t].iTSpacesOffs; 01849 01850 // copy tspace 01851 psTspace[iDstOffs+iDstVert] = psTspace[iSrcOffs+iSrcVert]; 01852 } 01853 } 01854 } 01855 } 01856 01857 // deal with degenerate quads with one good triangle 01858 for(t=0; t<iNrTrianglesIn; t++) 01859 { 01860 // this triangle belongs to a quad where the 01861 // other triangle is degenerate 01862 if( (pTriInfos[t].iFlag&QUAD_ONE_DEGEN_TRI)!=0 ) 01863 { 01864 SVec3 vDstP; 01865 int iOrgF=-1, i=0; 01866 tbool bNotFound; 01867 unsigned char * pV = pTriInfos[t].vert_num; 01868 int iFlag = (1<<pV[0]) | (1<<pV[1]) | (1<<pV[2]); 01869 int iMissingIndex = 0; 01870 if((iFlag&2)==0) iMissingIndex=1; 01871 else if((iFlag&4)==0) iMissingIndex=2; 01872 else if((iFlag&8)==0) iMissingIndex=3; 01873 01874 iOrgF = pTriInfos[t].iOrgFaceNumber; 01875 vDstP = GetPosition(pContext, MakeIndex(iOrgF, iMissingIndex)); 01876 bNotFound = TTRUE; 01877 i=0; 01878 while(bNotFound && i<3) 01879 { 01880 const int iVert = pV[i]; 01881 const SVec3 vSrcP = GetPosition(pContext, MakeIndex(iOrgF, iVert)); 01882 if(veq(vSrcP, vDstP)==TTRUE) 01883 { 01884 const int iOffs = pTriInfos[t].iTSpacesOffs; 01885 psTspace[iOffs+iMissingIndex] = psTspace[iOffs+iVert]; 01886 bNotFound=TFALSE; 01887 } 01888 else 01889 ++i; 01890 } 01891 assert(!bNotFound); 01892 } 01893 } 01894 }