Blender  V2.59
mikktspace.c
Go to the documentation of this file.
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 int GenerateInitialVerticesIndexList(STriInfo pTriInfos[], int piTriList_out[], const SMikkTSpaceContext * pContext, const int iNrTrianglesIn);
00170 void GenerateSharedVerticesIndexList(int piTriList_in_and_out[], const SMikkTSpaceContext * pContext, const int iNrTrianglesIn);
00171 void InitTriInfo(STriInfo pTriInfos[], const int piTriListIn[], const SMikkTSpaceContext * pContext, const int iNrTrianglesIn);
00172 int Build4RuleGroups(STriInfo pTriInfos[], SGroup pGroups[], int piGroupTrianglesBuffer[], const int piTriListIn[], const int iNrTrianglesIn);
00173 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 SVec3 GetPosition(const SMikkTSpaceContext * pContext, const int index);
00220 SVec3 GetNormal(const SMikkTSpaceContext * pContext, const int index);
00221 SVec3 GetTexCoord(const SMikkTSpaceContext * pContext, const int index);
00222 
00223 
00224 // degen triangles
00225 void DegenPrologue(STriInfo pTriInfos[], int piTriList_out[], const int iNrTrianglesIn, const int iTotTris);
00226 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 void MergeVertsFast(int piTriList_in_and_out[], STmpVert pTmpVert[], const SMikkTSpaceContext * pContext, const int iL_in, const int iR_in);
00454 void MergeVertsSlow(int piTriList_in_and_out[], const SMikkTSpaceContext * pContext, const int pTable[], const int iEntries);
00455 void GenerateSharedVerticesIndexListSlow(int piTriList_in_and_out[], const SMikkTSpaceContext * pContext, const int iNrTrianglesIn);
00456 
00457 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 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 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 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 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 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 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 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 void BuildNeighborsFast(STriInfo pTriInfos[], SEdge * pEdges, const int piTriListIn[], const int iNrTrianglesIn);
00924 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 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 tbool AssignRecur(const int piTriListIn[], STriInfo psTriInfos[], const int iMyTriIndex, SGroup * pGroup);
01066 void AddTriToGroup(SGroup * pGroup, const int iTriIndex);
01067 
01068 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 void AddTriToGroup(SGroup * pGroup, const int iTriIndex)
01130 {
01131         pGroup->pFaceIndices[pGroup->iNrFaces] = iTriIndex;
01132         ++pGroup->iNrFaces;
01133 }
01134 
01135 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 tbool CompareSubGroups(const SSubGroup * pg1, const SSubGroup * pg2);
01191 void QuickSort(int* pSortBuffer, int iLeft, int iRight, unsigned int uSeed);
01192 STSpace EvalTspace(int face_indices[], const int iFaces, const int piTriListIn[], const STriInfo pTriInfos[], const SMikkTSpaceContext * pContext, const int iVertexRepresentitive);
01193 
01194 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                 iUniqueSubGroups = 0;
01354         }
01355 
01356         // clean up
01357         free(pUniSubGroups);
01358         free(pTmpMembers);
01359         free(pSubGroupTspace);
01360 
01361         return TTRUE;
01362 }
01363 
01364 STSpace EvalTspace(int face_indices[], const int iFaces, const int piTriListIn[], const STriInfo pTriInfos[],
01365                                    const SMikkTSpaceContext * pContext, const int iVertexRepresentitive)
01366 {
01367         STSpace res;
01368         float fAngleSum = 0;
01369         int face=0;
01370         res.vOs.x=0.0f; res.vOs.y=0.0f; res.vOs.z=0.0f;
01371         res.vOt.x=0.0f; res.vOt.y=0.0f; res.vOt.z=0.0f;
01372         res.fMagS = 0; res.fMagT = 0;
01373 
01374         for(face=0; face<iFaces; face++)
01375         {
01376                 const int f = face_indices[face];
01377 
01378                 // only valid triangles get to add their contribution
01379                 if( (pTriInfos[f].iFlag&GROUP_WITH_ANY)==0 )
01380                 {
01381                         SVec3 n, vOs, vOt, p0, p1, p2, v1, v2;
01382                         float fCos, fAngle, fMagS, fMagT;
01383                         int i=-1, index=-1, i0=-1, i1=-1, i2=-1;
01384                         if(piTriListIn[3*f+0]==iVertexRepresentitive) i=0;
01385                         else if(piTriListIn[3*f+1]==iVertexRepresentitive) i=1;
01386                         else if(piTriListIn[3*f+2]==iVertexRepresentitive) i=2;
01387                         assert(i>=0 && i<3);
01388 
01389                         // project
01390                         index = piTriListIn[3*f+i];
01391                         n = GetNormal(pContext, index);
01392                         vOs = vsub(pTriInfos[f].vOs, vscale(vdot(n,pTriInfos[f].vOs), n));
01393                         vOt = vsub(pTriInfos[f].vOt, vscale(vdot(n,pTriInfos[f].vOt), n));
01394                         if( VNotZero(vOs) ) vOs = Normalize(vOs);
01395                         if( VNotZero(vOt) ) vOt = Normalize(vOt);
01396 
01397                         i2 = piTriListIn[3*f + (i<2?(i+1):0)];
01398                         i1 = piTriListIn[3*f + i];
01399                         i0 = piTriListIn[3*f + (i>0?(i-1):2)];
01400 
01401                         p0 = GetPosition(pContext, i0);
01402                         p1 = GetPosition(pContext, i1);
01403                         p2 = GetPosition(pContext, i2);
01404                         v1 = vsub(p0,p1);
01405                         v2 = vsub(p2,p1);
01406 
01407                         // project
01408                         v1 = vsub(v1, vscale(vdot(n,v1),n)); if( VNotZero(v1) ) v1 = Normalize(v1);
01409                         v2 = vsub(v2, vscale(vdot(n,v2),n)); if( VNotZero(v2) ) v2 = Normalize(v2);
01410 
01411                         // weight contribution by the angle
01412                         // between the two edge vectors
01413                         fCos = vdot(v1,v2); fCos=fCos>1?1:(fCos<(-1) ? (-1) : fCos);
01414                         fAngle = (float) acos(fCos);
01415                         fMagS = pTriInfos[f].fMagS;
01416                         fMagT = pTriInfos[f].fMagT;
01417 
01418                         res.vOs=vadd(res.vOs, vscale(fAngle,vOs));
01419                         res.vOt=vadd(res.vOt,vscale(fAngle,vOt));
01420                         res.fMagS+=(fAngle*fMagS);
01421                         res.fMagT+=(fAngle*fMagT);
01422                         fAngleSum += fAngle;
01423                 }
01424         }
01425 
01426         // normalize
01427         if( VNotZero(res.vOs) ) res.vOs = Normalize(res.vOs);
01428         if( VNotZero(res.vOt) ) res.vOt = Normalize(res.vOt);
01429         if(fAngleSum>0)
01430         {
01431                 res.fMagS /= fAngleSum;
01432                 res.fMagT /= fAngleSum;
01433         }
01434 
01435         return res;
01436 }
01437 
01438 tbool CompareSubGroups(const SSubGroup * pg1, const SSubGroup * pg2)
01439 {
01440         tbool bStillSame=TTRUE;
01441         int i=0;
01442         if(pg1->iNrFaces!=pg2->iNrFaces) return TFALSE;
01443         while(i<pg1->iNrFaces && bStillSame)
01444         {
01445                 bStillSame = pg1->pTriMembers[i]==pg2->pTriMembers[i] ? TTRUE : TFALSE;
01446                 if(bStillSame) ++i;
01447         }
01448         return bStillSame;
01449 }
01450 
01451 void QuickSort(int* pSortBuffer, int iLeft, int iRight, unsigned int uSeed)
01452 {
01453         int iL, iR, n, index, iMid, iTmp;
01454 
01455         // Random
01456         unsigned int t=uSeed&31;
01457         t=(uSeed<<t)|(uSeed>>(32-t));
01458         uSeed=uSeed+t+3;
01459         // Random end
01460 
01461         iL=iLeft; iR=iRight;
01462         n = (iR-iL)+1;
01463         assert(n>=0);
01464         index = (int) (uSeed%n);
01465 
01466         iMid=pSortBuffer[index + iL];
01467 
01468 
01469         do
01470         {
01471                 while(pSortBuffer[iL] < iMid)
01472                         ++iL;
01473                 while(pSortBuffer[iR] > iMid)
01474                         --iR;
01475 
01476                 if(iL <= iR)
01477                 {
01478                         iTmp = pSortBuffer[iL];
01479                         pSortBuffer[iL] = pSortBuffer[iR];
01480                         pSortBuffer[iR] = iTmp;
01481                         ++iL; --iR;
01482                 }
01483         }
01484         while(iL <= iR);
01485 
01486         if(iLeft < iR)
01487                 QuickSort(pSortBuffer, iLeft, iR, uSeed);
01488         if(iL < iRight)
01489                 QuickSort(pSortBuffer, iL, iRight, uSeed);
01490 }
01491 
01494 
01495 void QuickSortEdges(SEdge * pSortBuffer, int iLeft, int iRight, const int channel, unsigned int uSeed);
01496 void GetEdge(int * i0_out, int * i1_out, int * edgenum_out, const int indices[], const int i0_in, const int i1_in);
01497 
01498 void BuildNeighborsFast(STriInfo pTriInfos[], SEdge * pEdges, const int piTriListIn[], const int iNrTrianglesIn)
01499 {
01500         // build array of edges
01501         unsigned int uSeed = INTERNAL_RND_SORT_SEED;                            // could replace with a random seed?
01502         int iEntries=0, iCurStartIndex=-1, f=0, i=0;
01503         for(f=0; f<iNrTrianglesIn; f++)
01504                 for(i=0; i<3; i++)
01505                 {
01506                         const int i0 = piTriListIn[f*3+i];
01507                         const int i1 = piTriListIn[f*3+(i<2?(i+1):0)];
01508                         pEdges[f*3+i].i0 = i0 < i1 ? i0 : i1;                   // put minimum index in i0
01509                         pEdges[f*3+i].i1 = !(i0 < i1) ? i0 : i1;                // put maximum index in i1
01510                         pEdges[f*3+i].f = f;                                                    // record face number
01511                 }
01512 
01513         // sort over all edges by i0, this is the pricy one.
01514         QuickSortEdges(pEdges, 0, iNrTrianglesIn*3-1, 0, uSeed);        // sort channel 0 which is i0
01515 
01516         // sub sort over i1, should be fast.
01517         // could replace this with a 64 bit int sort over (i0,i1)
01518         // with i0 as msb in the quicksort call above.
01519         iEntries = iNrTrianglesIn*3;
01520         iCurStartIndex = 0;
01521         for(i=1; i<iEntries; i++)
01522         {
01523                 if(pEdges[iCurStartIndex].i0 != pEdges[i].i0)
01524                 {
01525                         const int iL = iCurStartIndex;
01526                         const int iR = i-1;
01527                         //const int iElems = i-iL;
01528                         iCurStartIndex = i;
01529                         QuickSortEdges(pEdges, iL, iR, 1, uSeed);       // sort channel 1 which is i1
01530                 }
01531         }
01532 
01533         // sub sort over f, which should be fast.
01534         // this step is to remain compliant with BuildNeighborsSlow() when
01535         // more than 2 triangles use the same edge (such as a butterfly topology).
01536         iCurStartIndex = 0;
01537         for(i=1; i<iEntries; i++)
01538         {
01539                 if(pEdges[iCurStartIndex].i0 != pEdges[i].i0 || pEdges[iCurStartIndex].i1 != pEdges[i].i1)
01540                 {
01541                         const int iL = iCurStartIndex;
01542                         const int iR = i-1;
01543                         //const int iElems = i-iL;
01544                         iCurStartIndex = i;
01545                         QuickSortEdges(pEdges, iL, iR, 2, uSeed);       // sort channel 2 which is f
01546                 }
01547         }
01548 
01549         // pair up, adjacent triangles
01550         for(i=0; i<iEntries; i++)
01551         {
01552                 const int i0=pEdges[i].i0;
01553                 const int i1=pEdges[i].i1;
01554                 const int f = pEdges[i].f;
01555                 tbool bUnassigned_A;
01556 
01557                 int i0_A, i1_A;
01558                 int edgenum_A, edgenum_B=0;     // 0,1 or 2
01559                 GetEdge(&i0_A, &i1_A, &edgenum_A, &piTriListIn[f*3], i0, i1);   // resolve index ordering and edge_num
01560                 bUnassigned_A = pTriInfos[f].FaceNeighbors[edgenum_A] == -1 ? TTRUE : TFALSE;
01561 
01562                 if(bUnassigned_A)
01563                 {
01564                         // get true index ordering
01565                         int j=i+1, t;
01566                         tbool bNotFound = TTRUE;
01567                         while(j<iEntries && i0==pEdges[j].i0 && i1==pEdges[j].i1 && bNotFound)
01568                         {
01569                                 tbool bUnassigned_B;
01570                                 int i0_B, i1_B;
01571                                 t = pEdges[j].f;
01572                                 // flip i0_B and i1_B
01573                                 GetEdge(&i1_B, &i0_B, &edgenum_B, &piTriListIn[t*3], pEdges[j].i0, pEdges[j].i1);       // resolve index ordering and edge_num
01574                                 //assert(!(i0_A==i1_B && i1_A==i0_B));
01575                                 bUnassigned_B =  pTriInfos[t].FaceNeighbors[edgenum_B]==-1 ? TTRUE : TFALSE;
01576                                 if(i0_A==i0_B && i1_A==i1_B && bUnassigned_B)
01577                                         bNotFound = TFALSE;
01578                                 else
01579                                         ++j;
01580                         }
01581 
01582                         if(!bNotFound)
01583                         {
01584                                 int t = pEdges[j].f;
01585                                 pTriInfos[f].FaceNeighbors[edgenum_A] = t;
01586                                 //assert(pTriInfos[t].FaceNeighbors[edgenum_B]==-1);
01587                                 pTriInfos[t].FaceNeighbors[edgenum_B] = f;
01588                         }
01589                 }
01590         }
01591 }
01592 
01593 void BuildNeighborsSlow(STriInfo pTriInfos[], const int piTriListIn[], const int iNrTrianglesIn)
01594 {
01595         int f=0, i=0;
01596         for(f=0; f<iNrTrianglesIn; f++)
01597         {
01598                 for(i=0; i<3; i++)
01599                 {
01600                         // if unassigned
01601                         if(pTriInfos[f].FaceNeighbors[i] == -1)
01602                         {
01603                                 const int i0_A = piTriListIn[f*3+i];
01604                                 const int i1_A = piTriListIn[f*3+(i<2?(i+1):0)];
01605 
01606                                 // search for a neighbor
01607                                 tbool bFound = TFALSE;
01608                                 int t=0, j=0;
01609                                 while(!bFound && t<iNrTrianglesIn)
01610                                 {
01611                                         if(t!=f)
01612                                         {
01613                                                 j=0;
01614                                                 while(!bFound && j<3)
01615                                                 {
01616                                                         // in rev order
01617                                                         const int i1_B = piTriListIn[t*3+j];
01618                                                         const int i0_B = piTriListIn[t*3+(j<2?(j+1):0)];
01619                                                         //assert(!(i0_A==i1_B && i1_A==i0_B));
01620                                                         if(i0_A==i0_B && i1_A==i1_B)
01621                                                                 bFound = TTRUE;
01622                                                         else
01623                                                                 ++j;
01624                                                 }
01625                                         }
01626                                         
01627                                         if(!bFound) ++t;
01628                                 }
01629 
01630                                 // assign neighbors
01631                                 if(bFound)
01632                                 {
01633                                         pTriInfos[f].FaceNeighbors[i] = t;
01634                                         //assert(pTriInfos[t].FaceNeighbors[j]==-1);
01635                                         pTriInfos[t].FaceNeighbors[j] = f;
01636                                 }
01637                         }
01638                 }
01639         }
01640 }
01641 
01642 void QuickSortEdges(SEdge * pSortBuffer, int iLeft, int iRight, const int channel, unsigned int uSeed)
01643 {
01644         unsigned int t;
01645         int iL, iR, n, index, iMid;
01646 
01647         // early out
01648         SEdge sTmp;
01649         const int iElems = iRight-iLeft+1;
01650         if(iElems<2) return;
01651         else if(iElems==2)
01652         {
01653                 if(pSortBuffer[iLeft].array[channel] > pSortBuffer[iRight].array[channel])
01654                 {
01655                         sTmp = pSortBuffer[iLeft];
01656                         pSortBuffer[iLeft] = pSortBuffer[iRight];
01657                         pSortBuffer[iRight] = sTmp;
01658                 }
01659                 return;
01660         }
01661 
01662         // Random
01663         t=uSeed&31;
01664         t=(uSeed<<t)|(uSeed>>(32-t));
01665         uSeed=uSeed+t+3;
01666         // Random end
01667 
01668         iL=iLeft, iR=iRight;
01669         n = (iR-iL)+1;
01670         assert(n>=0);
01671         index = (int) (uSeed%n);
01672 
01673         iMid=pSortBuffer[index + iL].array[channel];
01674 
01675         do
01676         {
01677                 while(pSortBuffer[iL].array[channel] < iMid)
01678                         ++iL;
01679                 while(pSortBuffer[iR].array[channel] > iMid)
01680                         --iR;
01681 
01682                 if(iL <= iR)
01683                 {
01684                         sTmp = pSortBuffer[iL];
01685                         pSortBuffer[iL] = pSortBuffer[iR];
01686                         pSortBuffer[iR] = sTmp;
01687                         ++iL; --iR;
01688                 }
01689         }
01690         while(iL <= iR);
01691 
01692         if(iLeft < iR)
01693                 QuickSortEdges(pSortBuffer, iLeft, iR, channel, uSeed);
01694         if(iL < iRight)
01695                 QuickSortEdges(pSortBuffer, iL, iRight, channel, uSeed);
01696 }
01697 
01698 // resolve ordering and edge number
01699 void GetEdge(int * i0_out, int * i1_out, int * edgenum_out, const int indices[], const int i0_in, const int i1_in)
01700 {
01701         *edgenum_out = -1;
01702         
01703         // test if first index is on the edge
01704         if(indices[0]==i0_in || indices[0]==i1_in)
01705         {
01706                 // test if second index is on the edge
01707                 if(indices[1]==i0_in || indices[1]==i1_in)
01708                 {
01709                         edgenum_out[0]=0;       // first edge
01710                         i0_out[0]=indices[0];
01711                         i1_out[0]=indices[1];
01712                 }
01713                 else
01714                 {
01715                         edgenum_out[0]=2;       // third edge
01716                         i0_out[0]=indices[2];
01717                         i1_out[0]=indices[0];
01718                 }
01719         }
01720         else
01721         {
01722                 // only second and third index is on the edge
01723                 edgenum_out[0]=1;       // second edge
01724                 i0_out[0]=indices[1];
01725                 i1_out[0]=indices[2];
01726         }
01727 }
01728 
01729 
01732 
01733 void DegenPrologue(STriInfo pTriInfos[], int piTriList_out[], const int iNrTrianglesIn, const int iTotTris)
01734 {
01735         int iNextGoodTriangleSearchIndex=-1;
01736         tbool bStillFindingGoodOnes;
01737 
01738         // locate quads with only one good triangle
01739         int t=0;
01740         while(t<(iTotTris-1))
01741         {
01742                 const int iFO_a = pTriInfos[t].iOrgFaceNumber;
01743                 const int iFO_b = pTriInfos[t+1].iOrgFaceNumber;
01744                 if(iFO_a==iFO_b)        // this is a quad
01745                 {
01746                         const tbool bIsDeg_a = (pTriInfos[t].iFlag&MARK_DEGENERATE)!=0 ? TTRUE : TFALSE;
01747                         const tbool bIsDeg_b = (pTriInfos[t+1].iFlag&MARK_DEGENERATE)!=0 ? TTRUE : TFALSE;
01748                         if((bIsDeg_a^bIsDeg_b)!=0)
01749                         {
01750                                 pTriInfos[t].iFlag |= QUAD_ONE_DEGEN_TRI;
01751                                 pTriInfos[t+1].iFlag |= QUAD_ONE_DEGEN_TRI;
01752                         }
01753                         t += 2;
01754                 }
01755                 else
01756                         ++t;
01757         }
01758 
01759         // reorder list so all degen triangles are moved to the back
01760         // without reordering the good triangles
01761         iNextGoodTriangleSearchIndex = 1;
01762         t=0;
01763         bStillFindingGoodOnes = TTRUE;
01764         while(t<iNrTrianglesIn && bStillFindingGoodOnes)
01765         {
01766                 const tbool bIsGood = (pTriInfos[t].iFlag&MARK_DEGENERATE)==0 ? TTRUE : TFALSE;
01767                 if(bIsGood)
01768                 {
01769                         if(iNextGoodTriangleSearchIndex < (t+2))
01770                                 iNextGoodTriangleSearchIndex = t+2;
01771                 }
01772                 else
01773                 {
01774                         int t0, t1;
01775                         // search for the first good triangle.
01776                         tbool bJustADegenerate = TTRUE;
01777                         while(bJustADegenerate && iNextGoodTriangleSearchIndex<iTotTris)
01778                         {
01779                                 const tbool bIsGood = (pTriInfos[iNextGoodTriangleSearchIndex].iFlag&MARK_DEGENERATE)==0 ? TTRUE : TFALSE;
01780                                 if(bIsGood) bJustADegenerate=TFALSE;
01781                                 else ++iNextGoodTriangleSearchIndex;
01782                         }
01783 
01784                         t0 = t;
01785                         t1 = iNextGoodTriangleSearchIndex;
01786                         ++iNextGoodTriangleSearchIndex;
01787                         assert(iNextGoodTriangleSearchIndex > (t+1));
01788 
01789                         // swap triangle t0 and t1
01790                         if(!bJustADegenerate)
01791                         {
01792                                 int i=0;
01793                                 for(i=0; i<3; i++)
01794                                 {
01795                                         const int index = piTriList_out[t0*3+i];
01796                                         piTriList_out[t0*3+i] = piTriList_out[t1*3+i];
01797                                         piTriList_out[t1*3+i] = index;
01798                                 }
01799                                 {
01800                                         const STriInfo tri_info = pTriInfos[t0];
01801                                         pTriInfos[t0] = pTriInfos[t1];
01802                                         pTriInfos[t1] = tri_info;
01803                                 }
01804                         }
01805                         else
01806                                 bStillFindingGoodOnes = TFALSE; // this is not supposed to happen
01807                 }
01808 
01809                 if(bStillFindingGoodOnes) ++t;
01810         }
01811 
01812         assert(bStillFindingGoodOnes);  // code will still work.
01813         assert(iNrTrianglesIn == t);
01814 }
01815 
01816 void DegenEpilogue(STSpace psTspace[], STriInfo pTriInfos[], int piTriListIn[], const SMikkTSpaceContext * pContext, const int iNrTrianglesIn, const int iTotTris)
01817 {
01818         int t=0, i=0;
01819         // deal with degenerate triangles
01820         // punishment for degenerate triangles is O(N^2)
01821         for(t=iNrTrianglesIn; t<iTotTris; t++)
01822         {
01823                 // degenerate triangles on a quad with one good triangle are skipped
01824                 // here but processed in the next loop
01825                 const tbool bSkip = (pTriInfos[t].iFlag&QUAD_ONE_DEGEN_TRI)!=0 ? TTRUE : TFALSE;
01826 
01827                 if(!bSkip)
01828                 {
01829                         for(i=0; i<3; i++)
01830                         {
01831                                 const int index1 = piTriListIn[t*3+i];
01832                                 // search through the good triangles
01833                                 tbool bNotFound = TTRUE;
01834                                 int j=0;
01835                                 while(bNotFound && j<(3*iNrTrianglesIn))
01836                                 {
01837                                         const int index2 = piTriListIn[j];
01838                                         if(index1==index2) bNotFound=TFALSE;
01839                                         else ++j;
01840                                 }
01841 
01842                                 if(!bNotFound)
01843                                 {
01844                                         const int iTri = j/3;
01845                                         const int iVert = j%3;
01846                                         const int iSrcVert=pTriInfos[iTri].vert_num[iVert];
01847                                         const int iSrcOffs=pTriInfos[iTri].iTSpacesOffs;
01848                                         const int iDstVert=pTriInfos[t].vert_num[i];
01849                                         const int iDstOffs=pTriInfos[t].iTSpacesOffs;
01850                                         
01851                                         // copy tspace
01852                                         psTspace[iDstOffs+iDstVert] = psTspace[iSrcOffs+iSrcVert];
01853                                 }
01854                         }
01855                 }
01856         }
01857 
01858         // deal with degenerate quads with one good triangle
01859         for(t=0; t<iNrTrianglesIn; t++)
01860         {
01861                 // this triangle belongs to a quad where the
01862                 // other triangle is degenerate
01863                 if( (pTriInfos[t].iFlag&QUAD_ONE_DEGEN_TRI)!=0 )
01864                 {
01865                         SVec3 vDstP;
01866                         int iOrgF=-1, i=0;
01867                         tbool bNotFound;
01868                         unsigned char * pV = pTriInfos[t].vert_num;
01869                         int iFlag = (1<<pV[0]) | (1<<pV[1]) | (1<<pV[2]);
01870                         int iMissingIndex = 0;
01871                         if((iFlag&2)==0) iMissingIndex=1;
01872                         else if((iFlag&4)==0) iMissingIndex=2;
01873                         else if((iFlag&8)==0) iMissingIndex=3;
01874 
01875                         iOrgF = pTriInfos[t].iOrgFaceNumber;
01876                         vDstP = GetPosition(pContext, MakeIndex(iOrgF, iMissingIndex));
01877                         bNotFound = TTRUE;
01878                         i=0;
01879                         while(bNotFound && i<3)
01880                         {
01881                                 const int iVert = pV[i];
01882                                 const SVec3 vSrcP = GetPosition(pContext, MakeIndex(iOrgF, iVert));
01883                                 if(veq(vSrcP, vDstP)==TTRUE)
01884                                 {
01885                                         const int iOffs = pTriInfos[t].iTSpacesOffs;
01886                                         psTspace[iOffs+iMissingIndex] = psTspace[iOffs+iVert];
01887                                         bNotFound=TFALSE;
01888                                 }
01889                                 else
01890                                         ++i;
01891                         }
01892                         assert(!bNotFound);
01893                 }
01894         }
01895 }