Pyrogenesis  13997
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
mikktspace.cpp
Go to the documentation of this file.
1 // Slightly modified version of mikktspace, by Wildfire Games, for 0 A.D.
2 //
3 // Motivation for changes:
4 // * For quietness with our default warning flags, some warnings are
5 // explicitly disabled.
6 
7 #include "precompiled.h"
8 
9 #ifdef _MSC_VER
10 # pragma warning(disable:4189) // local variable is initialized but not referenced
11 #endif
12 
13 #if defined(__GNUC__)
14 # define GCC_VERSION (__GNUC__*100 + __GNUC_MINOR__)
15 
16 # if GCC_VERSION >= 402 // older GCCs don't support the diagnostic pragma at all
17 # pragma GCC diagnostic ignored "-Wunused-variable"
18 # endif
19 #endif
20 
21 /** \file mikktspace/mikktspace.c
22  * \ingroup mikktspace
23  */
24 /**
25  * Copyright (C) 2011 by Morten S. Mikkelsen
26  *
27  * This software is provided 'as-is', without any express or implied
28  * warranty. In no event will the authors be held liable for any damages
29  * arising from the use of this software.
30  *
31  * Permission is granted to anyone to use this software for any purpose,
32  * including commercial applications, and to alter it and redistribute it
33  * freely, subject to the following restrictions:
34  *
35  * 1. The origin of this software must not be misrepresented; you must not
36  * claim that you wrote the original software. If you use this software
37  * in a product, an acknowledgment in the product documentation would be
38  * appreciated but is not required.
39  * 2. Altered source versions must be plainly marked as such, and must not be
40  * misrepresented as being the original software.
41  * 3. This notice may not be removed or altered from any source distribution.
42  */
43 
44 #include <assert.h>
45 #include <stdio.h>
46 #include <math.h>
47 #include <string.h>
48 #include <float.h>
49 #include <stdlib.h>
50 
51 #include "mikktspace.h"
52 
53 #define TFALSE 0
54 #define TTRUE 1
55 
56 #ifndef M_PI
57 #define M_PI 3.1415926535897932384626433832795
58 #endif
59 
60 #define INTERNAL_RND_SORT_SEED 39871946
61 
62 // internal structure
63 typedef struct
64 {
65  float x, y, z;
66 } SVec3;
67 
68 static tbool veq( const SVec3 v1, const SVec3 v2 )
69 {
70  return (v1.x == v2.x) && (v1.y == v2.y) && (v1.z == v2.z);
71 }
72 
73 static SVec3 vadd( const SVec3 v1, const SVec3 v2 )
74 {
75  SVec3 vRes;
76 
77  vRes.x = v1.x + v2.x;
78  vRes.y = v1.y + v2.y;
79  vRes.z = v1.z + v2.z;
80 
81  return vRes;
82 }
83 
84 
85 static SVec3 vsub( const SVec3 v1, const SVec3 v2 )
86 {
87  SVec3 vRes;
88 
89  vRes.x = v1.x - v2.x;
90  vRes.y = v1.y - v2.y;
91  vRes.z = v1.z - v2.z;
92 
93  return vRes;
94 }
95 
96 static SVec3 vscale(const float fS, const SVec3 v)
97 {
98  SVec3 vRes;
99 
100  vRes.x = fS * v.x;
101  vRes.y = fS * v.y;
102  vRes.z = fS * v.z;
103 
104  return vRes;
105 }
106 
107 static float LengthSquared( const SVec3 v )
108 {
109  return v.x*v.x + v.y*v.y + v.z*v.z;
110 }
111 
112 static float Length( const SVec3 v )
113 {
114  return sqrtf(LengthSquared(v));
115 }
116 
117 static SVec3 Normalize( const SVec3 v )
118 {
119  return vscale(1 / Length(v), v);
120 }
121 
122 static float vdot( const SVec3 v1, const SVec3 v2)
123 {
124  return v1.x*v2.x + v1.y*v2.y + v1.z*v2.z;
125 }
126 
127 
128 static tbool NotZero(const float fX)
129 {
130  // could possibly use FLT_EPSILON instead
131  return fabsf(fX) > FLT_MIN;
132 }
133 
134 static tbool VNotZero(const SVec3 v)
135 {
136  // might change this to an epsilon based test
137  return NotZero(v.x) || NotZero(v.y) || NotZero(v.z);
138 }
139 
140 
141 
142 typedef struct
143 {
144  int iNrFaces;
145  int * pTriMembers;
146 } SSubGroup;
147 
148 typedef struct
149 {
150  int iNrFaces;
154 } SGroup;
155 
156 //
157 #define MARK_DEGENERATE 1
158 #define QUAD_ONE_DEGEN_TRI 2
159 #define GROUP_WITH_ANY 4
160 #define ORIENT_PRESERVING 8
161 
162 
163 
164 typedef struct
165 {
166  int FaceNeighbors[3];
167  SGroup * AssignedGroup[3];
168 
169  // normalized first order face derivatives
170  SVec3 vOs, vOt;
171  float fMagS, fMagT; // original magnitudes
172 
173  // determines if the current and the next triangle are a quad.
175  int iFlag, iTSpacesOffs;
176  unsigned char vert_num[4];
177 } STriInfo;
178 
179 typedef struct
180 {
182  float fMagS;
184  float fMagT;
185  int iCounter; // this is to average back into quads.
187 } STSpace;
188 
189 static int GenerateInitialVerticesIndexList(STriInfo pTriInfos[], int piTriList_out[], const SMikkTSpaceContext * pContext, const int iNrTrianglesIn);
190 static void GenerateSharedVerticesIndexList(int piTriList_in_and_out[], const SMikkTSpaceContext * pContext, const int iNrTrianglesIn);
191 static void InitTriInfo(STriInfo pTriInfos[], const int piTriListIn[], const SMikkTSpaceContext * pContext, const int iNrTrianglesIn);
192 static int Build4RuleGroups(STriInfo pTriInfos[], SGroup pGroups[], int piGroupTrianglesBuffer[], const int piTriListIn[], const int iNrTrianglesIn);
193 static tbool GenerateTSpaces(STSpace psTspace[], const STriInfo pTriInfos[], const SGroup pGroups[],
194  const int iNrActiveGroups, const int piTriListIn[], const float fThresCos,
195  const SMikkTSpaceContext * pContext);
196 
197 static int MakeIndex(const int iFace, const int iVert)
198 {
199  assert(iVert>=0 && iVert<4 && iFace>=0);
200  return (iFace<<2) | (iVert&0x3);
201 }
202 
203 static void IndexToData(int * piFace, int * piVert, const int iIndexIn)
204 {
205  piVert[0] = iIndexIn&0x3;
206  piFace[0] = iIndexIn>>2;
207 }
208 
209 static STSpace AvgTSpace(const STSpace * pTS0, const STSpace * pTS1)
210 {
211  STSpace ts_res;
212 
213  // this if is important. Due to floating point precision
214  // averaging when ts0==ts1 will cause a slight difference
215  // which results in tangent space splits later on
216  if (pTS0->fMagS==pTS1->fMagS && pTS0->fMagT==pTS1->fMagT &&
217  veq(pTS0->vOs,pTS1->vOs) && veq(pTS0->vOt, pTS1->vOt))
218  {
219  ts_res.fMagS = pTS0->fMagS;
220  ts_res.fMagT = pTS0->fMagT;
221  ts_res.vOs = pTS0->vOs;
222  ts_res.vOt = pTS0->vOt;
223  }
224  else
225  {
226  ts_res.fMagS = 0.5f*(pTS0->fMagS+pTS1->fMagS);
227  ts_res.fMagT = 0.5f*(pTS0->fMagT+pTS1->fMagT);
228  ts_res.vOs = vadd(pTS0->vOs,pTS1->vOs);
229  ts_res.vOt = vadd(pTS0->vOt,pTS1->vOt);
230  if ( VNotZero(ts_res.vOs) ) ts_res.vOs = Normalize(ts_res.vOs);
231  if ( VNotZero(ts_res.vOt) ) ts_res.vOt = Normalize(ts_res.vOt);
232  }
233 
234  return ts_res;
235 }
236 
237 
238 
239 static SVec3 GetPosition(const SMikkTSpaceContext * pContext, const int index);
240 static SVec3 GetNormal(const SMikkTSpaceContext * pContext, const int index);
241 static SVec3 GetTexCoord(const SMikkTSpaceContext * pContext, const int index);
242 
243 
244 // degen triangles
245 static void DegenPrologue(STriInfo pTriInfos[], int piTriList_out[], const int iNrTrianglesIn, const int iTotTris);
246 static void DegenEpilogue(STSpace psTspace[], STriInfo pTriInfos[], int piTriListIn[], const SMikkTSpaceContext * pContext, const int iNrTrianglesIn, const int iTotTris);
247 
248 
250 {
251  return genTangSpace(pContext, 180.0f);
252 }
253 
254 tbool genTangSpace(const SMikkTSpaceContext * pContext, const float fAngularThreshold)
255 {
256  // count nr_triangles
257  int * piTriListIn = NULL, * piGroupTrianglesBuffer = NULL;
258  STriInfo * pTriInfos = NULL;
259  SGroup * pGroups = NULL;
260  STSpace * psTspace = NULL;
261  int iNrTrianglesIn = 0, f=0, t=0, i=0;
262  int iNrTSPaces = 0, iTotTris = 0, iDegenTriangles = 0, iNrMaxGroups = 0;
263  int iNrActiveGroups = 0, index = 0;
264  const int iNrFaces = pContext->m_pInterface->m_getNumFaces(pContext);
265  tbool bRes = TFALSE;
266  const float fThresCos = (float) cos((fAngularThreshold*(float)M_PI)/180.0f);
267 
268  // verify all call-backs have been set
269  if ( pContext->m_pInterface->m_getNumFaces==NULL ||
270  pContext->m_pInterface->m_getNumVerticesOfFace==NULL ||
271  pContext->m_pInterface->m_getPosition==NULL ||
272  pContext->m_pInterface->m_getNormal==NULL ||
273  pContext->m_pInterface->m_getTexCoord==NULL )
274  return TFALSE;
275 
276  // count triangles on supported faces
277  for (f=0; f<iNrFaces; f++)
278  {
279  const int verts = pContext->m_pInterface->m_getNumVerticesOfFace(pContext, f);
280  if (verts==3) ++iNrTrianglesIn;
281  else if(verts==4) iNrTrianglesIn += 2;
282  }
283  if (iNrTrianglesIn<=0) return TFALSE;
284 
285  // allocate memory for an index list
286  piTriListIn = (int *) malloc(sizeof(int)*3*iNrTrianglesIn);
287  pTriInfos = (STriInfo *) malloc(sizeof(STriInfo)*iNrTrianglesIn);
288  if (piTriListIn==NULL || pTriInfos==NULL)
289  {
290  if (piTriListIn!=NULL) free(piTriListIn);
291  if (pTriInfos!=NULL) free(pTriInfos);
292  return TFALSE;
293  }
294 
295  // make an initial triangle --> face index list
296  iNrTSPaces = GenerateInitialVerticesIndexList(pTriInfos, piTriListIn, pContext, iNrTrianglesIn);
297 
298  // make a welded index list of identical positions and attributes (pos, norm, texc)
299  //printf("gen welded index list begin\n");
300  GenerateSharedVerticesIndexList(piTriListIn, pContext, iNrTrianglesIn);
301  //printf("gen welded index list end\n");
302 
303  // Mark all degenerate triangles
304  iTotTris = iNrTrianglesIn;
305  iDegenTriangles = 0;
306  for (t=0; t<iTotTris; t++)
307  {
308  const int i0 = piTriListIn[t*3+0];
309  const int i1 = piTriListIn[t*3+1];
310  const int i2 = piTriListIn[t*3+2];
311  const SVec3 p0 = GetPosition(pContext, i0);
312  const SVec3 p1 = GetPosition(pContext, i1);
313  const SVec3 p2 = GetPosition(pContext, i2);
314  if (veq(p0,p1) || veq(p0,p2) || veq(p1,p2)) // degenerate
315  {
316  pTriInfos[t].iFlag |= MARK_DEGENERATE;
317  ++iDegenTriangles;
318  }
319  }
320  iNrTrianglesIn = iTotTris - iDegenTriangles;
321 
322  // mark all triangle pairs that belong to a quad with only one
323  // good triangle. These need special treatment in DegenEpilogue().
324  // Additionally, move all good triangles to the start of
325  // pTriInfos[] and piTriListIn[] without changing order and
326  // put the degenerate triangles last.
327  DegenPrologue(pTriInfos, piTriListIn, iNrTrianglesIn, iTotTris);
328 
329 
330  // evaluate triangle level attributes and neighbor list
331  //printf("gen neighbors list begin\n");
332  InitTriInfo(pTriInfos, piTriListIn, pContext, iNrTrianglesIn);
333  //printf("gen neighbors list end\n");
334 
335 
336  // based on the 4 rules, identify groups based on connectivity
337  iNrMaxGroups = iNrTrianglesIn*3;
338  pGroups = (SGroup *) malloc(sizeof(SGroup)*iNrMaxGroups);
339  piGroupTrianglesBuffer = (int *) malloc(sizeof(int)*iNrTrianglesIn*3);
340  if (pGroups==NULL || piGroupTrianglesBuffer==NULL)
341  {
342  if (pGroups!=NULL) free(pGroups);
343  if (piGroupTrianglesBuffer!=NULL) free(piGroupTrianglesBuffer);
344  free(piTriListIn);
345  free(pTriInfos);
346  return TFALSE;
347  }
348  //printf("gen 4rule groups begin\n");
349  iNrActiveGroups =
350  Build4RuleGroups(pTriInfos, pGroups, piGroupTrianglesBuffer, piTriListIn, iNrTrianglesIn);
351  //printf("gen 4rule groups end\n");
352 
353  //
354 
355  psTspace = (STSpace *) malloc(sizeof(STSpace)*iNrTSPaces);
356  if (psTspace==NULL)
357  {
358  free(piTriListIn);
359  free(pTriInfos);
360  free(pGroups);
361  free(piGroupTrianglesBuffer);
362  return TFALSE;
363  }
364  memset(psTspace, 0, sizeof(STSpace)*iNrTSPaces);
365  for (t=0; t<iNrTSPaces; t++)
366  {
367  psTspace[t].vOs.x=1.0f; psTspace[t].vOs.y=0.0f; psTspace[t].vOs.z=0.0f; psTspace[t].fMagS = 1.0f;
368  psTspace[t].vOt.x=0.0f; psTspace[t].vOt.y=1.0f; psTspace[t].vOt.z=0.0f; psTspace[t].fMagT = 1.0f;
369  }
370 
371  // make tspaces, each group is split up into subgroups if necessary
372  // based on fAngularThreshold. Finally a tangent space is made for
373  // every resulting subgroup
374  //printf("gen tspaces begin\n");
375  bRes = GenerateTSpaces(psTspace, pTriInfos, pGroups, iNrActiveGroups, piTriListIn, fThresCos, pContext);
376  //printf("gen tspaces end\n");
377 
378  // clean up
379  free(pGroups);
380  free(piGroupTrianglesBuffer);
381 
382  if (!bRes) // if an allocation in GenerateTSpaces() failed
383  {
384  // clean up and return false
385  free(pTriInfos); free(piTriListIn); free(psTspace);
386  return TFALSE;
387  }
388 
389 
390  // degenerate quads with one good triangle will be fixed by copying a space from
391  // the good triangle to the coinciding vertex.
392  // all other degenerate triangles will just copy a space from any good triangle
393  // with the same welded index in piTriListIn[].
394  DegenEpilogue(psTspace, pTriInfos, piTriListIn, pContext, iNrTrianglesIn, iTotTris);
395 
396  free(pTriInfos); free(piTriListIn);
397 
398  index = 0;
399  for (f=0; f<iNrFaces; f++)
400  {
401  const int verts = pContext->m_pInterface->m_getNumVerticesOfFace(pContext, f);
402  if (verts!=3 && verts!=4) continue;
403 
404 
405  // I've decided to let degenerate triangles and group-with-anythings
406  // vary between left/right hand coordinate systems at the vertices.
407  // All healthy triangles on the other hand are built to always be either or.
408 
409  /*// force the coordinate system orientation to be uniform for every face.
410  // (this is already the case for good triangles but not for
411  // degenerate ones and those with bGroupWithAnything==true)
412  bool bOrient = psTspace[index].bOrient;
413  if (psTspace[index].iCounter == 0) // tspace was not derived from a group
414  {
415  // look for a space created in GenerateTSpaces() by iCounter>0
416  bool bNotFound = true;
417  int i=1;
418  while (i<verts && bNotFound)
419  {
420  if (psTspace[index+i].iCounter > 0) bNotFound=false;
421  else ++i;
422  }
423  if (!bNotFound) bOrient = psTspace[index+i].bOrient;
424  }*/
425 
426  // set data
427  for (i=0; i<verts; i++)
428  {
429  const STSpace * pTSpace = &psTspace[index];
430  float tang[] = {pTSpace->vOs.x, pTSpace->vOs.y, pTSpace->vOs.z};
431  float bitang[] = {pTSpace->vOt.x, pTSpace->vOt.y, pTSpace->vOt.z};
432  if (pContext->m_pInterface->m_setTSpace!=NULL)
433  pContext->m_pInterface->m_setTSpace(pContext, tang, bitang, pTSpace->fMagS, pTSpace->fMagT, pTSpace->bOrient, f, i);
434  if (pContext->m_pInterface->m_setTSpaceBasic!=NULL)
435  pContext->m_pInterface->m_setTSpaceBasic(pContext, tang, pTSpace->bOrient==TTRUE ? 1.0f : (-1.0f), f, i);
436 
437  ++index;
438  }
439  }
440 
441  free(psTspace);
442 
443 
444  return TTRUE;
445 }
446 
447 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
448 
449 typedef struct
450 {
451  float vert[3];
452  int index;
453 } STmpVert;
454 
455 const int g_iCells = 2048;
456 
457 #ifdef _MSC_VER
458  #define NOINLINE __declspec(noinline)
459 #else
460  #define NOINLINE __attribute__ ((noinline))
461 #endif
462 
463 // it is IMPORTANT that this function is called to evaluate the hash since
464 // inlining could potentially reorder instructions and generate different
465 // results for the same effective input value fVal.
466 NOINLINE int FindGridCell(const float fMin, const float fMax, const float fVal)
467 {
468  const float fIndex = g_iCells * ((fVal-fMin)/(fMax-fMin));
469  const int iIndex = fIndex<0?0:((int)fIndex);
470  return iIndex<g_iCells?iIndex:(g_iCells-1);
471 }
472 
473 static void MergeVertsFast(int piTriList_in_and_out[], STmpVert pTmpVert[], const SMikkTSpaceContext * pContext, const int iL_in, const int iR_in);
474 static void MergeVertsSlow(int piTriList_in_and_out[], const SMikkTSpaceContext * pContext, const int pTable[], const int iEntries);
475 static void GenerateSharedVerticesIndexListSlow(int piTriList_in_and_out[], const SMikkTSpaceContext * pContext, const int iNrTrianglesIn);
476 
477 static void GenerateSharedVerticesIndexList(int piTriList_in_and_out[], const SMikkTSpaceContext * pContext, const int iNrTrianglesIn)
478 {
479 
480  // Generate bounding box
481  int * piHashTable=NULL, * piHashCount=NULL, * piHashOffsets=NULL, * piHashCount2=NULL;
482  STmpVert * pTmpVert = NULL;
483  int i=0, iChannel=0, k=0, e=0;
484  int iMaxCount=0;
485  SVec3 vMin = GetPosition(pContext, 0), vMax = vMin, vDim;
486  float fMin, fMax;
487  for (i=1; i<(iNrTrianglesIn*3); i++)
488  {
489  const int index = piTriList_in_and_out[i];
490 
491  const SVec3 vP = GetPosition(pContext, index);
492  if (vMin.x > vP.x) vMin.x = vP.x;
493  else if(vMax.x < vP.x) vMax.x = vP.x;
494  if (vMin.y > vP.y) vMin.y = vP.y;
495  else if(vMax.y < vP.y) vMax.y = vP.y;
496  if (vMin.z > vP.z) vMin.z = vP.z;
497  else if(vMax.z < vP.z) vMax.z = vP.z;
498  }
499 
500  vDim = vsub(vMax,vMin);
501  iChannel = 0;
502  fMin = vMin.x; fMax=vMax.x;
503  if (vDim.y>vDim.x && vDim.y>vDim.z)
504  {
505  iChannel=1;
506  fMin = vMin.y, fMax=vMax.y;
507  }
508  else if(vDim.z>vDim.x)
509  {
510  iChannel=2;
511  fMin = vMin.z, fMax=vMax.z;
512  }
513 
514  // make allocations
515  piHashTable = (int *) malloc(sizeof(int)*iNrTrianglesIn*3);
516  piHashCount = (int *) malloc(sizeof(int)*g_iCells);
517  piHashOffsets = (int *) malloc(sizeof(int)*g_iCells);
518  piHashCount2 = (int *) malloc(sizeof(int)*g_iCells);
519 
520  if (piHashTable==NULL || piHashCount==NULL || piHashOffsets==NULL || piHashCount2==NULL)
521  {
522  if (piHashTable!=NULL) free(piHashTable);
523  if (piHashCount!=NULL) free(piHashCount);
524  if (piHashOffsets!=NULL) free(piHashOffsets);
525  if (piHashCount2!=NULL) free(piHashCount2);
526  GenerateSharedVerticesIndexListSlow(piTriList_in_and_out, pContext, iNrTrianglesIn);
527  return;
528  }
529  memset(piHashCount, 0, sizeof(int)*g_iCells);
530  memset(piHashCount2, 0, sizeof(int)*g_iCells);
531 
532  // count amount of elements in each cell unit
533  for (i=0; i<(iNrTrianglesIn*3); i++)
534  {
535  const int index = piTriList_in_and_out[i];
536  const SVec3 vP = GetPosition(pContext, index);
537  const float fVal = iChannel==0 ? vP.x : (iChannel==1 ? vP.y : vP.z);
538  const int iCell = FindGridCell(fMin, fMax, fVal);
539  ++piHashCount[iCell];
540  }
541 
542  // evaluate start index of each cell.
543  piHashOffsets[0]=0;
544  for (k=1; k<g_iCells; k++)
545  piHashOffsets[k]=piHashOffsets[k-1]+piHashCount[k-1];
546 
547  // insert vertices
548  for (i=0; i<(iNrTrianglesIn*3); i++)
549  {
550  const int index = piTriList_in_and_out[i];
551  const SVec3 vP = GetPosition(pContext, index);
552  const float fVal = iChannel==0 ? vP.x : (iChannel==1 ? vP.y : vP.z);
553  const int iCell = FindGridCell(fMin, fMax, fVal);
554  int * pTable = NULL;
555 
556  assert(piHashCount2[iCell]<piHashCount[iCell]);
557  pTable = &piHashTable[piHashOffsets[iCell]];
558  pTable[piHashCount2[iCell]] = i; // vertex i has been inserted.
559  ++piHashCount2[iCell];
560  }
561  for (k=0; k<g_iCells; k++)
562  assert(piHashCount2[k] == piHashCount[k]); // verify the count
563  free(piHashCount2);
564 
565  // find maximum amount of entries in any hash entry
566  iMaxCount = piHashCount[0];
567  for (k=1; k<g_iCells; k++)
568  if (iMaxCount<piHashCount[k])
569  iMaxCount=piHashCount[k];
570  pTmpVert = (STmpVert *) malloc(sizeof(STmpVert)*iMaxCount);
571 
572 
573  // complete the merge
574  for (k=0; k<g_iCells; k++)
575  {
576  // extract table of cell k and amount of entries in it
577  int * pTable = &piHashTable[piHashOffsets[k]];
578  const int iEntries = piHashCount[k];
579  if (iEntries < 2) continue;
580 
581  if (pTmpVert!=NULL)
582  {
583  for (e=0; e<iEntries; e++)
584  {
585  int i = pTable[e];
586  const SVec3 vP = GetPosition(pContext, piTriList_in_and_out[i]);
587  pTmpVert[e].vert[0] = vP.x; pTmpVert[e].vert[1] = vP.y;
588  pTmpVert[e].vert[2] = vP.z; pTmpVert[e].index = i;
589  }
590  MergeVertsFast(piTriList_in_and_out, pTmpVert, pContext, 0, iEntries-1);
591  }
592  else
593  MergeVertsSlow(piTriList_in_and_out, pContext, pTable, iEntries);
594  }
595 
596  if (pTmpVert!=NULL) { free(pTmpVert); }
597  free(piHashTable);
598  free(piHashCount);
599  free(piHashOffsets);
600 }
601 
602 static void MergeVertsFast(int piTriList_in_and_out[], STmpVert pTmpVert[], const SMikkTSpaceContext * pContext, const int iL_in, const int iR_in)
603 {
604  // make bbox
605  int c=0, l=0, channel=0;
606  float fvMin[3], fvMax[3];
607  float dx=0, dy=0, dz=0, fSep=0;
608  for (c=0; c<3; c++)
609  { fvMin[c]=pTmpVert[iL_in].vert[c]; fvMax[c]=fvMin[c]; }
610  for (l=(iL_in+1); l<=iR_in; l++)
611  for (c=0; c<3; c++)
612  if (fvMin[c]>pTmpVert[l].vert[c]) fvMin[c]=pTmpVert[l].vert[c];
613  else if(fvMax[c]<pTmpVert[l].vert[c]) fvMax[c]=pTmpVert[l].vert[c];
614 
615  dx = fvMax[0]-fvMin[0];
616  dy = fvMax[1]-fvMin[1];
617  dz = fvMax[2]-fvMin[2];
618 
619  channel = 0;
620  if (dy>dx && dy>dz) channel=1;
621  else if(dz>dx) channel=2;
622 
623  fSep = 0.5f*(fvMax[channel]+fvMin[channel]);
624 
625  // terminate recursion when the separation/average value
626  // is no longer strictly between fMin and fMax values.
627  if (fSep>=fvMax[channel] || fSep<=fvMin[channel])
628  {
629  // complete the weld
630  for (l=iL_in; l<=iR_in; l++)
631  {
632  int i = pTmpVert[l].index;
633  const int index = piTriList_in_and_out[i];
634  const SVec3 vP = GetPosition(pContext, index);
635  const SVec3 vN = GetNormal(pContext, index);
636  const SVec3 vT = GetTexCoord(pContext, index);
637 
638  tbool bNotFound = TTRUE;
639  int l2=iL_in, i2rec=-1;
640  while (l2<l && bNotFound)
641  {
642  const int i2 = pTmpVert[l2].index;
643  const int index2 = piTriList_in_and_out[i2];
644  const SVec3 vP2 = GetPosition(pContext, index2);
645  const SVec3 vN2 = GetNormal(pContext, index2);
646  const SVec3 vT2 = GetTexCoord(pContext, index2);
647  i2rec=i2;
648 
649  //if(vP==vP2 && vN==vN2 && vT==vT2)
650  if (vP.x==vP2.x && vP.y==vP2.y && vP.z==vP2.z &&
651  vN.x==vN2.x && vN.y==vN2.y && vN.z==vN2.z &&
652  vT.x==vT2.x && vT.y==vT2.y && vT.z==vT2.z)
653  bNotFound = TFALSE;
654  else
655  ++l2;
656  }
657 
658  // merge if previously found
659  if (!bNotFound)
660  piTriList_in_and_out[i] = piTriList_in_and_out[i2rec];
661  }
662  }
663  else
664  {
665  int iL=iL_in, iR=iR_in;
666  assert((iR_in-iL_in)>0); // at least 2 entries
667 
668  // separate (by fSep) all points between iL_in and iR_in in pTmpVert[]
669  while (iL < iR)
670  {
671  tbool bReadyLeftSwap = TFALSE, bReadyRightSwap = TFALSE;
672  while ((!bReadyLeftSwap) && iL<iR)
673  {
674  assert(iL>=iL_in && iL<=iR_in);
675  bReadyLeftSwap = !(pTmpVert[iL].vert[channel]<fSep);
676  if (!bReadyLeftSwap) ++iL;
677  }
678  while ((!bReadyRightSwap) && iL<iR)
679  {
680  assert(iR>=iL_in && iR<=iR_in);
681  bReadyRightSwap = pTmpVert[iR].vert[channel]<fSep;
682  if (!bReadyRightSwap) --iR;
683  }
684  assert( (iL<iR) || !(bReadyLeftSwap && bReadyRightSwap) );
685 
686  if (bReadyLeftSwap && bReadyRightSwap)
687  {
688  const STmpVert sTmp = pTmpVert[iL];
689  assert(iL<iR);
690  pTmpVert[iL] = pTmpVert[iR];
691  pTmpVert[iR] = sTmp;
692  ++iL; --iR;
693  }
694  }
695 
696  assert(iL==(iR+1) || (iL==iR));
697  if (iL==iR)
698  {
699  const tbool bReadyRightSwap = pTmpVert[iR].vert[channel]<fSep;
700  if (bReadyRightSwap) ++iL;
701  else --iR;
702  }
703 
704  // only need to weld when there is more than 1 instance of the (x,y,z)
705  if (iL_in < iR)
706  MergeVertsFast(piTriList_in_and_out, pTmpVert, pContext, iL_in, iR); // weld all left of fSep
707  if (iL < iR_in)
708  MergeVertsFast(piTriList_in_and_out, pTmpVert, pContext, iL, iR_in); // weld all right of (or equal to) fSep
709  }
710 }
711 
712 static void MergeVertsSlow(int piTriList_in_and_out[], const SMikkTSpaceContext * pContext, const int pTable[], const int iEntries)
713 {
714  // this can be optimized further using a tree structure or more hashing.
715  int e=0;
716  for (e=0; e<iEntries; e++)
717  {
718  int i = pTable[e];
719  const int index = piTriList_in_and_out[i];
720  const SVec3 vP = GetPosition(pContext, index);
721  const SVec3 vN = GetNormal(pContext, index);
722  const SVec3 vT = GetTexCoord(pContext, index);
723 
724  tbool bNotFound = TTRUE;
725  int e2=0, i2rec=-1;
726  while (e2<e && bNotFound)
727  {
728  const int i2 = pTable[e2];
729  const int index2 = piTriList_in_and_out[i2];
730  const SVec3 vP2 = GetPosition(pContext, index2);
731  const SVec3 vN2 = GetNormal(pContext, index2);
732  const SVec3 vT2 = GetTexCoord(pContext, index2);
733  i2rec = i2;
734 
735  if (veq(vP,vP2) && veq(vN,vN2) && veq(vT,vT2))
736  bNotFound = TFALSE;
737  else
738  ++e2;
739  }
740 
741  // merge if previously found
742  if (!bNotFound)
743  piTriList_in_and_out[i] = piTriList_in_and_out[i2rec];
744  }
745 }
746 
747 static void GenerateSharedVerticesIndexListSlow(int piTriList_in_and_out[], const SMikkTSpaceContext * pContext, const int iNrTrianglesIn)
748 {
749  int iNumUniqueVerts = 0, t=0, i=0;
750  for (t=0; t<iNrTrianglesIn; t++)
751  {
752  for (i=0; i<3; i++)
753  {
754  const int offs = t*3 + i;
755  const int index = piTriList_in_and_out[offs];
756 
757  const SVec3 vP = GetPosition(pContext, index);
758  const SVec3 vN = GetNormal(pContext, index);
759  const SVec3 vT = GetTexCoord(pContext, index);
760 
761  tbool bFound = TFALSE;
762  int t2=0, index2rec=-1;
763  while (!bFound && t2<=t)
764  {
765  int j=0;
766  while (!bFound && j<3)
767  {
768  const int index2 = piTriList_in_and_out[t2*3 + j];
769  const SVec3 vP2 = GetPosition(pContext, index2);
770  const SVec3 vN2 = GetNormal(pContext, index2);
771  const SVec3 vT2 = GetTexCoord(pContext, index2);
772 
773  if (veq(vP,vP2) && veq(vN,vN2) && veq(vT,vT2))
774  bFound = TTRUE;
775  else
776  ++j;
777  }
778  if (!bFound) ++t2;
779  }
780 
781  assert(bFound);
782  // if we found our own
783  if (index2rec == index) { ++iNumUniqueVerts; }
784 
785  piTriList_in_and_out[offs] = index2rec;
786  }
787  }
788 }
789 
790 static int GenerateInitialVerticesIndexList(STriInfo pTriInfos[], int piTriList_out[], const SMikkTSpaceContext * pContext, const int iNrTrianglesIn)
791 {
792  int iTSpacesOffs = 0, f=0, t=0;
793  int iDstTriIndex = 0;
794  for (f=0; f<pContext->m_pInterface->m_getNumFaces(pContext); f++)
795  {
796  const int verts = pContext->m_pInterface->m_getNumVerticesOfFace(pContext, f);
797  if (verts!=3 && verts!=4) continue;
798 
799  pTriInfos[iDstTriIndex].iOrgFaceNumber = f;
800  pTriInfos[iDstTriIndex].iTSpacesOffs = iTSpacesOffs;
801 
802  if (verts==3)
803  {
804  unsigned char * pVerts = pTriInfos[iDstTriIndex].vert_num;
805  pVerts[0]=0; pVerts[1]=1; pVerts[2]=2;
806  piTriList_out[iDstTriIndex*3+0] = MakeIndex(f, 0);
807  piTriList_out[iDstTriIndex*3+1] = MakeIndex(f, 1);
808  piTriList_out[iDstTriIndex*3+2] = MakeIndex(f, 2);
809  ++iDstTriIndex; // next
810  }
811  else
812  {
813  {
814  pTriInfos[iDstTriIndex+1].iOrgFaceNumber = f;
815  pTriInfos[iDstTriIndex+1].iTSpacesOffs = iTSpacesOffs;
816  }
817 
818  {
819  // need an order independent way to evaluate
820  // tspace on quads. This is done by splitting
821  // along the shortest diagonal.
822  const int i0 = MakeIndex(f, 0);
823  const int i1 = MakeIndex(f, 1);
824  const int i2 = MakeIndex(f, 2);
825  const int i3 = MakeIndex(f, 3);
826  const SVec3 T0 = GetTexCoord(pContext, i0);
827  const SVec3 T1 = GetTexCoord(pContext, i1);
828  const SVec3 T2 = GetTexCoord(pContext, i2);
829  const SVec3 T3 = GetTexCoord(pContext, i3);
830  const float distSQ_02 = LengthSquared(vsub(T2,T0));
831  const float distSQ_13 = LengthSquared(vsub(T3,T1));
832  tbool bQuadDiagIs_02;
833  if (distSQ_02<distSQ_13)
834  bQuadDiagIs_02 = TTRUE;
835  else if(distSQ_13<distSQ_02)
836  bQuadDiagIs_02 = TFALSE;
837  else
838  {
839  const SVec3 P0 = GetPosition(pContext, i0);
840  const SVec3 P1 = GetPosition(pContext, i1);
841  const SVec3 P2 = GetPosition(pContext, i2);
842  const SVec3 P3 = GetPosition(pContext, i3);
843  const float distSQ_02 = LengthSquared(vsub(P2,P0));
844  const float distSQ_13 = LengthSquared(vsub(P3,P1));
845 
846  bQuadDiagIs_02 = distSQ_13<distSQ_02 ? TFALSE : TTRUE;
847  }
848 
849  if (bQuadDiagIs_02)
850  {
851  {
852  unsigned char * pVerts_A = pTriInfos[iDstTriIndex].vert_num;
853  pVerts_A[0]=0; pVerts_A[1]=1; pVerts_A[2]=2;
854  }
855  piTriList_out[iDstTriIndex*3+0] = i0;
856  piTriList_out[iDstTriIndex*3+1] = i1;
857  piTriList_out[iDstTriIndex*3+2] = i2;
858  ++iDstTriIndex; // next
859  {
860  unsigned char * pVerts_B = pTriInfos[iDstTriIndex].vert_num;
861  pVerts_B[0]=0; pVerts_B[1]=2; pVerts_B[2]=3;
862  }
863  piTriList_out[iDstTriIndex*3+0] = i0;
864  piTriList_out[iDstTriIndex*3+1] = i2;
865  piTriList_out[iDstTriIndex*3+2] = i3;
866  ++iDstTriIndex; // next
867  }
868  else
869  {
870  {
871  unsigned char * pVerts_A = pTriInfos[iDstTriIndex].vert_num;
872  pVerts_A[0]=0; pVerts_A[1]=1; pVerts_A[2]=3;
873  }
874  piTriList_out[iDstTriIndex*3+0] = i0;
875  piTriList_out[iDstTriIndex*3+1] = i1;
876  piTriList_out[iDstTriIndex*3+2] = i3;
877  ++iDstTriIndex; // next
878  {
879  unsigned char * pVerts_B = pTriInfos[iDstTriIndex].vert_num;
880  pVerts_B[0]=1; pVerts_B[1]=2; pVerts_B[2]=3;
881  }
882  piTriList_out[iDstTriIndex*3+0] = i1;
883  piTriList_out[iDstTriIndex*3+1] = i2;
884  piTriList_out[iDstTriIndex*3+2] = i3;
885  ++iDstTriIndex; // next
886  }
887  }
888  }
889 
890  iTSpacesOffs += verts;
891  assert(iDstTriIndex<=iNrTrianglesIn);
892  }
893 
894  for (t=0; t<iNrTrianglesIn; t++)
895  pTriInfos[t].iFlag = 0;
896 
897  // return total amount of tspaces
898  return iTSpacesOffs;
899 }
900 
901 static SVec3 GetPosition(const SMikkTSpaceContext * pContext, const int index)
902 {
903  int iF, iI;
904  SVec3 res; float pos[3];
905  IndexToData(&iF, &iI, index);
906  pContext->m_pInterface->m_getPosition(pContext, pos, iF, iI);
907  res.x=pos[0]; res.y=pos[1]; res.z=pos[2];
908  return res;
909 }
910 
911 static SVec3 GetNormal(const SMikkTSpaceContext * pContext, const int index)
912 {
913  int iF, iI;
914  SVec3 res; float norm[3];
915  IndexToData(&iF, &iI, index);
916  pContext->m_pInterface->m_getNormal(pContext, norm, iF, iI);
917  res.x=norm[0]; res.y=norm[1]; res.z=norm[2];
918  return res;
919 }
920 
921 static SVec3 GetTexCoord(const SMikkTSpaceContext * pContext, const int index)
922 {
923  int iF, iI;
924  SVec3 res; float texc[2];
925  IndexToData(&iF, &iI, index);
926  pContext->m_pInterface->m_getTexCoord(pContext, texc, iF, iI);
927  res.x=texc[0]; res.y=texc[1]; res.z=1.0f;
928  return res;
929 }
930 
931 /////////////////////////////////////////////////////////////////////////////////////////////////////
932 /////////////////////////////////////////////////////////////////////////////////////////////////////
933 
934 typedef union
935 {
936  struct
937  {
938  int i0, i1, f;
939  };
940  int array[3];
941 } SEdge;
942 
943 static void BuildNeighborsFast(STriInfo pTriInfos[], SEdge * pEdges, const int piTriListIn[], const int iNrTrianglesIn);
944 static void BuildNeighborsSlow(STriInfo pTriInfos[], const int piTriListIn[], const int iNrTrianglesIn);
945 
946 // returns the texture area times 2
947 static float CalcTexArea(const SMikkTSpaceContext * pContext, const int indices[])
948 {
949  const SVec3 t1 = GetTexCoord(pContext, indices[0]);
950  const SVec3 t2 = GetTexCoord(pContext, indices[1]);
951  const SVec3 t3 = GetTexCoord(pContext, indices[2]);
952 
953  const float t21x = t2.x-t1.x;
954  const float t21y = t2.y-t1.y;
955  const float t31x = t3.x-t1.x;
956  const float t31y = t3.y-t1.y;
957 
958  const float fSignedAreaSTx2 = t21x*t31y - t21y*t31x;
959 
960  return fSignedAreaSTx2<0 ? (-fSignedAreaSTx2) : fSignedAreaSTx2;
961 }
962 
963 static void InitTriInfo(STriInfo pTriInfos[], const int piTriListIn[], const SMikkTSpaceContext * pContext, const int iNrTrianglesIn)
964 {
965  int f=0, i=0, t=0;
966  // pTriInfos[f].iFlag is cleared in GenerateInitialVerticesIndexList() which is called before this function.
967 
968  // generate neighbor info list
969  for (f=0; f<iNrTrianglesIn; f++)
970  for (i=0; i<3; i++)
971  {
972  pTriInfos[f].FaceNeighbors[i] = -1;
973  pTriInfos[f].AssignedGroup[i] = NULL;
974 
975  pTriInfos[f].vOs.x=0.0f; pTriInfos[f].vOs.y=0.0f; pTriInfos[f].vOs.z=0.0f;
976  pTriInfos[f].vOt.x=0.0f; pTriInfos[f].vOt.y=0.0f; pTriInfos[f].vOt.z=0.0f;
977  pTriInfos[f].fMagS = 0;
978  pTriInfos[f].fMagT = 0;
979 
980  // assumed bad
981  pTriInfos[f].iFlag |= GROUP_WITH_ANY;
982  }
983 
984  // evaluate first order derivatives
985  for (f=0; f<iNrTrianglesIn; f++)
986  {
987  // initial values
988  const SVec3 v1 = GetPosition(pContext, piTriListIn[f*3+0]);
989  const SVec3 v2 = GetPosition(pContext, piTriListIn[f*3+1]);
990  const SVec3 v3 = GetPosition(pContext, piTriListIn[f*3+2]);
991  const SVec3 t1 = GetTexCoord(pContext, piTriListIn[f*3+0]);
992  const SVec3 t2 = GetTexCoord(pContext, piTriListIn[f*3+1]);
993  const SVec3 t3 = GetTexCoord(pContext, piTriListIn[f*3+2]);
994 
995  const float t21x = t2.x-t1.x;
996  const float t21y = t2.y-t1.y;
997  const float t31x = t3.x-t1.x;
998  const float t31y = t3.y-t1.y;
999  const SVec3 d1 = vsub(v2,v1);
1000  const SVec3 d2 = vsub(v3,v1);
1001 
1002  const float fSignedAreaSTx2 = t21x*t31y - t21y*t31x;
1003  //assert(fSignedAreaSTx2!=0);
1004  SVec3 vOs = vsub(vscale(t31y,d1), vscale(t21y,d2)); // eq 18
1005  SVec3 vOt = vadd(vscale(-t31x,d1), vscale(t21x,d2)); // eq 19
1006 
1007  pTriInfos[f].iFlag |= (fSignedAreaSTx2>0 ? ORIENT_PRESERVING : 0);
1008 
1009  if ( NotZero(fSignedAreaSTx2) )
1010  {
1011  const float fAbsArea = fabsf(fSignedAreaSTx2);
1012  const float fLenOs = Length(vOs);
1013  const float fLenOt = Length(vOt);
1014  const float fS = (pTriInfos[f].iFlag&ORIENT_PRESERVING)==0 ? (-1.0f) : 1.0f;
1015  if ( NotZero(fLenOs) ) pTriInfos[f].vOs = vscale(fS/fLenOs, vOs);
1016  if ( NotZero(fLenOt) ) pTriInfos[f].vOt = vscale(fS/fLenOt, vOt);
1017 
1018  // evaluate magnitudes prior to normalization of vOs and vOt
1019  pTriInfos[f].fMagS = fLenOs / fAbsArea;
1020  pTriInfos[f].fMagT = fLenOt / fAbsArea;
1021 
1022  // if this is a good triangle
1023  if ( NotZero(pTriInfos[f].fMagS) && NotZero(pTriInfos[f].fMagT))
1024  pTriInfos[f].iFlag &= (~GROUP_WITH_ANY);
1025  }
1026  }
1027 
1028  // force otherwise healthy quads to a fixed orientation
1029  while (t<(iNrTrianglesIn-1))
1030  {
1031  const int iFO_a = pTriInfos[t].iOrgFaceNumber;
1032  const int iFO_b = pTriInfos[t+1].iOrgFaceNumber;
1033  if (iFO_a==iFO_b) // this is a quad
1034  {
1035  const tbool bIsDeg_a = (pTriInfos[t].iFlag&MARK_DEGENERATE)!=0 ? TTRUE : TFALSE;
1036  const tbool bIsDeg_b = (pTriInfos[t+1].iFlag&MARK_DEGENERATE)!=0 ? TTRUE : TFALSE;
1037 
1038  // bad triangles should already have been removed by
1039  // DegenPrologue(), but just in case check bIsDeg_a and bIsDeg_a are false
1040  if ((bIsDeg_a||bIsDeg_b)==TFALSE)
1041  {
1042  const tbool bOrientA = (pTriInfos[t].iFlag&ORIENT_PRESERVING)!=0 ? TTRUE : TFALSE;
1043  const tbool bOrientB = (pTriInfos[t+1].iFlag&ORIENT_PRESERVING)!=0 ? TTRUE : TFALSE;
1044  // if this happens the quad has extremely bad mapping!!
1045  if (bOrientA!=bOrientB)
1046  {
1047  //printf("found quad with bad mapping\n");
1048  tbool bChooseOrientFirstTri = TFALSE;
1049  if ((pTriInfos[t+1].iFlag&GROUP_WITH_ANY)!=0) bChooseOrientFirstTri = TTRUE;
1050  else if( CalcTexArea(pContext, &piTriListIn[t*3+0]) >= CalcTexArea(pContext, &piTriListIn[(t+1)*3+0]) )
1051  bChooseOrientFirstTri = TTRUE;
1052 
1053  // force match
1054  {
1055  const int t0 = bChooseOrientFirstTri ? t : (t+1);
1056  const int t1 = bChooseOrientFirstTri ? (t+1) : t;
1057  pTriInfos[t1].iFlag &= (~ORIENT_PRESERVING); // clear first
1058  pTriInfos[t1].iFlag |= (pTriInfos[t0].iFlag&ORIENT_PRESERVING); // copy bit
1059  }
1060  }
1061  }
1062  t += 2;
1063  }
1064  else
1065  ++t;
1066  }
1067 
1068  // match up edge pairs
1069  {
1070  SEdge * pEdges = (SEdge *) malloc(sizeof(SEdge)*iNrTrianglesIn*3);
1071  if (pEdges==NULL)
1072  BuildNeighborsSlow(pTriInfos, piTriListIn, iNrTrianglesIn);
1073  else
1074  {
1075  BuildNeighborsFast(pTriInfos, pEdges, piTriListIn, iNrTrianglesIn);
1076 
1077  free(pEdges);
1078  }
1079  }
1080 }
1081 
1082 /////////////////////////////////////////////////////////////////////////////////////////////////////
1083 /////////////////////////////////////////////////////////////////////////////////////////////////////
1084 
1085 static tbool AssignRecur(const int piTriListIn[], STriInfo psTriInfos[], const int iMyTriIndex, SGroup * pGroup);
1086 static void AddTriToGroup(SGroup * pGroup, const int iTriIndex);
1087 
1088 static int Build4RuleGroups(STriInfo pTriInfos[], SGroup pGroups[], int piGroupTrianglesBuffer[], const int piTriListIn[], const int iNrTrianglesIn)
1089 {
1090  const int iNrMaxGroups = iNrTrianglesIn*3;
1091  int iNrActiveGroups = 0;
1092  int iOffset = 0, f=0, i=0;
1093  for (f=0; f<iNrTrianglesIn; f++)
1094  {
1095  for (i=0; i<3; i++)
1096  {
1097  // if not assigned to a group
1098  if ((pTriInfos[f].iFlag&GROUP_WITH_ANY)==0 && pTriInfos[f].AssignedGroup[i]==NULL)
1099  {
1100  tbool bOrPre;
1101  int neigh_indexL, neigh_indexR;
1102  const int vert_index = piTriListIn[f*3+i];
1103  assert(iNrActiveGroups<iNrMaxGroups);
1104  pTriInfos[f].AssignedGroup[i] = &pGroups[iNrActiveGroups];
1105  pTriInfos[f].AssignedGroup[i]->iVertexRepresentitive = vert_index;
1106  pTriInfos[f].AssignedGroup[i]->bOrientPreservering = (pTriInfos[f].iFlag&ORIENT_PRESERVING)!=0;
1107  pTriInfos[f].AssignedGroup[i]->iNrFaces = 0;
1108  pTriInfos[f].AssignedGroup[i]->pFaceIndices = &piGroupTrianglesBuffer[iOffset];
1109  ++iNrActiveGroups;
1110 
1111  AddTriToGroup(pTriInfos[f].AssignedGroup[i], f);
1112  bOrPre = (pTriInfos[f].iFlag&ORIENT_PRESERVING)!=0 ? TTRUE : TFALSE;
1113  neigh_indexL = pTriInfos[f].FaceNeighbors[i];
1114  neigh_indexR = pTriInfos[f].FaceNeighbors[i>0?(i-1):2];
1115  if (neigh_indexL>=0) // neighbor
1116  {
1117  const tbool bAnswer =
1118  AssignRecur(piTriListIn, pTriInfos, neigh_indexL,
1119  pTriInfos[f].AssignedGroup[i] );
1120 
1121  const tbool bOrPre2 = (pTriInfos[neigh_indexL].iFlag&ORIENT_PRESERVING)!=0 ? TTRUE : TFALSE;
1122  const tbool bDiff = bOrPre!=bOrPre2 ? TTRUE : TFALSE;
1123  assert(bAnswer || bDiff);
1124  }
1125  if (neigh_indexR>=0) // neighbor
1126  {
1127  const tbool bAnswer =
1128  AssignRecur(piTriListIn, pTriInfos, neigh_indexR,
1129  pTriInfos[f].AssignedGroup[i] );
1130 
1131  const tbool bOrPre2 = (pTriInfos[neigh_indexR].iFlag&ORIENT_PRESERVING)!=0 ? TTRUE : TFALSE;
1132  const tbool bDiff = bOrPre!=bOrPre2 ? TTRUE : TFALSE;
1133  assert(bAnswer || bDiff);
1134  }
1135 
1136  // update offset
1137  iOffset += pTriInfos[f].AssignedGroup[i]->iNrFaces;
1138  // since the groups are disjoint a triangle can never
1139  // belong to more than 3 groups. Subsequently something
1140  // is completely screwed if this assertion ever hits.
1141  assert(iOffset <= iNrMaxGroups);
1142  }
1143  }
1144  }
1145 
1146  return iNrActiveGroups;
1147 }
1148 
1149 static void AddTriToGroup(SGroup * pGroup, const int iTriIndex)
1150 {
1151  pGroup->pFaceIndices[pGroup->iNrFaces] = iTriIndex;
1152  ++pGroup->iNrFaces;
1153 }
1154 
1155 static tbool AssignRecur(const int piTriListIn[], STriInfo psTriInfos[],
1156  const int iMyTriIndex, SGroup * pGroup)
1157 {
1158  STriInfo * pMyTriInfo = &psTriInfos[iMyTriIndex];
1159 
1160  // track down vertex
1161  const int iVertRep = pGroup->iVertexRepresentitive;
1162  const int * pVerts = &piTriListIn[3*iMyTriIndex+0];
1163  int i=-1;
1164  if (pVerts[0]==iVertRep) i=0;
1165  else if(pVerts[1]==iVertRep) i=1;
1166  else if(pVerts[2]==iVertRep) i=2;
1167  assert(i>=0 && i<3);
1168 
1169  // early out
1170  if (pMyTriInfo->AssignedGroup[i] == pGroup) return TTRUE;
1171  else if(pMyTriInfo->AssignedGroup[i]!=NULL) return TFALSE;
1172  if ((pMyTriInfo->iFlag&GROUP_WITH_ANY)!=0)
1173  {
1174  // first to group with a group-with-anything triangle
1175  // determines it's orientation.
1176  // This is the only existing order dependency in the code!!
1177  if ( pMyTriInfo->AssignedGroup[0] == NULL &&
1178  pMyTriInfo->AssignedGroup[1] == NULL &&
1179  pMyTriInfo->AssignedGroup[2] == NULL )
1180  {
1181  pMyTriInfo->iFlag &= (~ORIENT_PRESERVING);
1182  pMyTriInfo->iFlag |= (pGroup->bOrientPreservering ? ORIENT_PRESERVING : 0);
1183  }
1184  }
1185  {
1186  const tbool bOrient = (pMyTriInfo->iFlag&ORIENT_PRESERVING)!=0 ? TTRUE : TFALSE;
1187  if (bOrient != pGroup->bOrientPreservering) return TFALSE;
1188  }
1189 
1190  AddTriToGroup(pGroup, iMyTriIndex);
1191  pMyTriInfo->AssignedGroup[i] = pGroup;
1192 
1193  {
1194  const int neigh_indexL = pMyTriInfo->FaceNeighbors[i];
1195  const int neigh_indexR = pMyTriInfo->FaceNeighbors[i>0?(i-1):2];
1196  if (neigh_indexL>=0)
1197  AssignRecur(piTriListIn, psTriInfos, neigh_indexL, pGroup);
1198  if (neigh_indexR>=0)
1199  AssignRecur(piTriListIn, psTriInfos, neigh_indexR, pGroup);
1200  }
1201 
1202 
1203 
1204  return TTRUE;
1205 }
1206 
1207 /////////////////////////////////////////////////////////////////////////////////////////////////////
1208 /////////////////////////////////////////////////////////////////////////////////////////////////////
1209 
1210 static tbool CompareSubGroups(const SSubGroup * pg1, const SSubGroup * pg2);
1211 static void QuickSort(int* pSortBuffer, int iLeft, int iRight, unsigned int uSeed);
1212 static STSpace EvalTspace(int face_indices[], const int iFaces, const int piTriListIn[], const STriInfo pTriInfos[], const SMikkTSpaceContext * pContext, const int iVertexRepresentitive);
1213 
1214 static tbool GenerateTSpaces(STSpace psTspace[], const STriInfo pTriInfos[], const SGroup pGroups[],
1215  const int iNrActiveGroups, const int piTriListIn[], const float fThresCos,
1216  const SMikkTSpaceContext * pContext)
1217 {
1218  STSpace * pSubGroupTspace = NULL;
1219  SSubGroup * pUniSubGroups = NULL;
1220  int * pTmpMembers = NULL;
1221  int iMaxNrFaces=0, iUniqueTspaces=0, g=0, i=0;
1222  for (g=0; g<iNrActiveGroups; g++)
1223  if (iMaxNrFaces < pGroups[g].iNrFaces)
1224  iMaxNrFaces = pGroups[g].iNrFaces;
1225 
1226  if (iMaxNrFaces == 0) return TTRUE;
1227 
1228  // make initial allocations
1229  pSubGroupTspace = (STSpace *) malloc(sizeof(STSpace)*iMaxNrFaces);
1230  pUniSubGroups = (SSubGroup *) malloc(sizeof(SSubGroup)*iMaxNrFaces);
1231  pTmpMembers = (int *) malloc(sizeof(int)*iMaxNrFaces);
1232  if (pSubGroupTspace==NULL || pUniSubGroups==NULL || pTmpMembers==NULL)
1233  {
1234  if (pSubGroupTspace!=NULL) free(pSubGroupTspace);
1235  if (pUniSubGroups!=NULL) free(pUniSubGroups);
1236  if (pTmpMembers!=NULL) free(pTmpMembers);
1237  return TFALSE;
1238  }
1239 
1240 
1241  iUniqueTspaces = 0;
1242  for (g=0; g<iNrActiveGroups; g++)
1243  {
1244  const SGroup * pGroup = &pGroups[g];
1245  int iUniqueSubGroups = 0, s=0;
1246 
1247  for (i=0; i<pGroup->iNrFaces; i++) // triangles
1248  {
1249  const int f = pGroup->pFaceIndices[i]; // triangle number
1250  int index=-1, iVertIndex=-1, iOF_1=-1, iMembers=0, j=0, l=0;
1251  SSubGroup tmp_group;
1252  tbool bFound;
1253  SVec3 n, vOs, vOt;
1254  if (pTriInfos[f].AssignedGroup[0]==pGroup) index=0;
1255  else if(pTriInfos[f].AssignedGroup[1]==pGroup) index=1;
1256  else if(pTriInfos[f].AssignedGroup[2]==pGroup) index=2;
1257  assert(index>=0 && index<3);
1258 
1259  iVertIndex = piTriListIn[f*3+index];
1260  assert(iVertIndex==pGroup->iVertexRepresentitive);
1261 
1262  // is normalized already
1263  n = GetNormal(pContext, iVertIndex);
1264 
1265  // project
1266  vOs = vsub(pTriInfos[f].vOs, vscale(vdot(n,pTriInfos[f].vOs), n));
1267  vOt = vsub(pTriInfos[f].vOt, vscale(vdot(n,pTriInfos[f].vOt), n));
1268  if ( VNotZero(vOs) ) vOs = Normalize(vOs);
1269  if ( VNotZero(vOt) ) vOt = Normalize(vOt);
1270 
1271  // original face number
1272  iOF_1 = pTriInfos[f].iOrgFaceNumber;
1273 
1274  iMembers = 0;
1275  for (j=0; j<pGroup->iNrFaces; j++)
1276  {
1277  const int t = pGroup->pFaceIndices[j]; // triangle number
1278  const int iOF_2 = pTriInfos[t].iOrgFaceNumber;
1279 
1280  // project
1281  SVec3 vOs2 = vsub(pTriInfos[t].vOs, vscale(vdot(n,pTriInfos[t].vOs), n));
1282  SVec3 vOt2 = vsub(pTriInfos[t].vOt, vscale(vdot(n,pTriInfos[t].vOt), n));
1283  if ( VNotZero(vOs2) ) vOs2 = Normalize(vOs2);
1284  if ( VNotZero(vOt2) ) vOt2 = Normalize(vOt2);
1285 
1286  {
1287  const tbool bAny = ( (pTriInfos[f].iFlag | pTriInfos[t].iFlag) & GROUP_WITH_ANY )!=0 ? TTRUE : TFALSE;
1288  // make sure triangles which belong to the same quad are joined.
1289  const tbool bSameOrgFace = iOF_1==iOF_2 ? TTRUE : TFALSE;
1290 
1291  const float fCosS = vdot(vOs,vOs2);
1292  const float fCosT = vdot(vOt,vOt2);
1293 
1294  assert(f!=t || bSameOrgFace); // sanity check
1295  if (bAny || bSameOrgFace || (fCosS>fThresCos && fCosT>fThresCos))
1296  pTmpMembers[iMembers++] = t;
1297  }
1298  }
1299 
1300  // sort pTmpMembers
1301  tmp_group.iNrFaces = iMembers;
1302  tmp_group.pTriMembers = pTmpMembers;
1303  if (iMembers>1)
1304  {
1305  unsigned int uSeed = INTERNAL_RND_SORT_SEED; // could replace with a random seed?
1306  QuickSort(pTmpMembers, 0, iMembers-1, uSeed);
1307  }
1308 
1309  // look for an existing match
1310  bFound = TFALSE;
1311  l=0;
1312  while (l<iUniqueSubGroups && !bFound)
1313  {
1314  bFound = CompareSubGroups(&tmp_group, &pUniSubGroups[l]);
1315  if (!bFound) ++l;
1316  }
1317 
1318  // assign tangent space index
1319  assert(bFound || l==iUniqueSubGroups);
1320  //piTempTangIndices[f*3+index] = iUniqueTspaces+l;
1321 
1322  // if no match was found we allocate a new subgroup
1323  if (!bFound)
1324  {
1325  // insert new subgroup
1326  int * pIndices = (int *) malloc(sizeof(int)*iMembers);
1327  if (pIndices==NULL)
1328  {
1329  // clean up and return false
1330  int s=0;
1331  for (s=0; s<iUniqueSubGroups; s++)
1332  free(pUniSubGroups[s].pTriMembers);
1333  free(pUniSubGroups);
1334  free(pTmpMembers);
1335  free(pSubGroupTspace);
1336  return TFALSE;
1337  }
1338  pUniSubGroups[iUniqueSubGroups].iNrFaces = iMembers;
1339  pUniSubGroups[iUniqueSubGroups].pTriMembers = pIndices;
1340  memcpy(pIndices, tmp_group.pTriMembers, iMembers*sizeof(int));
1341  pSubGroupTspace[iUniqueSubGroups] =
1342  EvalTspace(tmp_group.pTriMembers, iMembers, piTriListIn, pTriInfos, pContext, pGroup->iVertexRepresentitive);
1343  ++iUniqueSubGroups;
1344  }
1345 
1346  // output tspace
1347  {
1348  const int iOffs = pTriInfos[f].iTSpacesOffs;
1349  const int iVert = pTriInfos[f].vert_num[index];
1350  STSpace * pTS_out = &psTspace[iOffs+iVert];
1351  assert(pTS_out->iCounter<2);
1352  assert(((pTriInfos[f].iFlag&ORIENT_PRESERVING)!=0) == pGroup->bOrientPreservering);
1353  if (pTS_out->iCounter==1)
1354  {
1355  *pTS_out = AvgTSpace(pTS_out, &pSubGroupTspace[l]);
1356  pTS_out->iCounter = 2; // update counter
1357  pTS_out->bOrient = pGroup->bOrientPreservering;
1358  }
1359  else
1360  {
1361  assert(pTS_out->iCounter==0);
1362  *pTS_out = pSubGroupTspace[l];
1363  pTS_out->iCounter = 1; // update counter
1364  pTS_out->bOrient = pGroup->bOrientPreservering;
1365  }
1366  }
1367  }
1368 
1369  // clean up and offset iUniqueTspaces
1370  for (s=0; s<iUniqueSubGroups; s++)
1371  free(pUniSubGroups[s].pTriMembers);
1372  iUniqueTspaces += iUniqueSubGroups;
1373  }
1374 
1375  // clean up
1376  free(pUniSubGroups);
1377  free(pTmpMembers);
1378  free(pSubGroupTspace);
1379 
1380  return TTRUE;
1381 }
1382 
1383 static STSpace EvalTspace(int face_indices[], const int iFaces, const int piTriListIn[], const STriInfo pTriInfos[],
1384  const SMikkTSpaceContext * pContext, const int iVertexRepresentitive)
1385 {
1386  STSpace res;
1387  float fAngleSum = 0;
1388  int face=0;
1389  res.vOs.x=0.0f; res.vOs.y=0.0f; res.vOs.z=0.0f;
1390  res.vOt.x=0.0f; res.vOt.y=0.0f; res.vOt.z=0.0f;
1391  res.fMagS = 0; res.fMagT = 0;
1392 
1393  for (face=0; face<iFaces; face++)
1394  {
1395  const int f = face_indices[face];
1396 
1397  // only valid triangles get to add their contribution
1398  if ( (pTriInfos[f].iFlag&GROUP_WITH_ANY)==0 )
1399  {
1400  SVec3 n, vOs, vOt, p0, p1, p2, v1, v2;
1401  float fCos, fAngle, fMagS, fMagT;
1402  int i=-1, index=-1, i0=-1, i1=-1, i2=-1;
1403  if (piTriListIn[3*f+0]==iVertexRepresentitive) i=0;
1404  else if(piTriListIn[3*f+1]==iVertexRepresentitive) i=1;
1405  else if(piTriListIn[3*f+2]==iVertexRepresentitive) i=2;
1406  assert(i>=0 && i<3);
1407 
1408  // project
1409  index = piTriListIn[3*f+i];
1410  n = GetNormal(pContext, index);
1411  vOs = vsub(pTriInfos[f].vOs, vscale(vdot(n,pTriInfos[f].vOs), n));
1412  vOt = vsub(pTriInfos[f].vOt, vscale(vdot(n,pTriInfos[f].vOt), n));
1413  if ( VNotZero(vOs) ) vOs = Normalize(vOs);
1414  if ( VNotZero(vOt) ) vOt = Normalize(vOt);
1415 
1416  i2 = piTriListIn[3*f + (i<2?(i+1):0)];
1417  i1 = piTriListIn[3*f + i];
1418  i0 = piTriListIn[3*f + (i>0?(i-1):2)];
1419 
1420  p0 = GetPosition(pContext, i0);
1421  p1 = GetPosition(pContext, i1);
1422  p2 = GetPosition(pContext, i2);
1423  v1 = vsub(p0,p1);
1424  v2 = vsub(p2,p1);
1425 
1426  // project
1427  v1 = vsub(v1, vscale(vdot(n,v1),n)); if( VNotZero(v1) ) v1 = Normalize(v1);
1428  v2 = vsub(v2, vscale(vdot(n,v2),n)); if( VNotZero(v2) ) v2 = Normalize(v2);
1429 
1430  // weight contribution by the angle
1431  // between the two edge vectors
1432  fCos = vdot(v1,v2); fCos=fCos>1?1:(fCos<(-1) ? (-1) : fCos);
1433  fAngle = (float) acos(fCos);
1434  fMagS = pTriInfos[f].fMagS;
1435  fMagT = pTriInfos[f].fMagT;
1436 
1437  res.vOs=vadd(res.vOs, vscale(fAngle,vOs));
1438  res.vOt=vadd(res.vOt,vscale(fAngle,vOt));
1439  res.fMagS+=(fAngle*fMagS);
1440  res.fMagT+=(fAngle*fMagT);
1441  fAngleSum += fAngle;
1442  }
1443  }
1444 
1445  // normalize
1446  if ( VNotZero(res.vOs) ) res.vOs = Normalize(res.vOs);
1447  if ( VNotZero(res.vOt) ) res.vOt = Normalize(res.vOt);
1448  if (fAngleSum>0)
1449  {
1450  res.fMagS /= fAngleSum;
1451  res.fMagT /= fAngleSum;
1452  }
1453 
1454  return res;
1455 }
1456 
1457 static tbool CompareSubGroups(const SSubGroup * pg1, const SSubGroup * pg2)
1458 {
1459  tbool bStillSame=TTRUE;
1460  int i=0;
1461  if (pg1->iNrFaces!=pg2->iNrFaces) return TFALSE;
1462  while (i<pg1->iNrFaces && bStillSame)
1463  {
1464  bStillSame = pg1->pTriMembers[i]==pg2->pTriMembers[i] ? TTRUE : TFALSE;
1465  if (bStillSame) ++i;
1466  }
1467  return bStillSame;
1468 }
1469 
1470 static void QuickSort(int* pSortBuffer, int iLeft, int iRight, unsigned int uSeed)
1471 {
1472  int iL, iR, n, index, iMid, iTmp;
1473 
1474  // Random
1475  unsigned int t=uSeed&31;
1476  t=(uSeed<<t)|(uSeed>>(32-t));
1477  uSeed=uSeed+t+3;
1478  // Random end
1479 
1480  iL=iLeft; iR=iRight;
1481  n = (iR-iL)+1;
1482  assert(n>=0);
1483  index = (int) (uSeed%n);
1484 
1485  iMid=pSortBuffer[index + iL];
1486 
1487 
1488  do
1489  {
1490  while (pSortBuffer[iL] < iMid)
1491  ++iL;
1492  while (pSortBuffer[iR] > iMid)
1493  --iR;
1494 
1495  if (iL <= iR)
1496  {
1497  iTmp = pSortBuffer[iL];
1498  pSortBuffer[iL] = pSortBuffer[iR];
1499  pSortBuffer[iR] = iTmp;
1500  ++iL; --iR;
1501  }
1502  }
1503  while (iL <= iR);
1504 
1505  if (iLeft < iR)
1506  QuickSort(pSortBuffer, iLeft, iR, uSeed);
1507  if (iL < iRight)
1508  QuickSort(pSortBuffer, iL, iRight, uSeed);
1509 }
1510 
1511 /////////////////////////////////////////////////////////////////////////////////////////////
1512 /////////////////////////////////////////////////////////////////////////////////////////////
1513 
1514 static void QuickSortEdges(SEdge * pSortBuffer, int iLeft, int iRight, const int channel, unsigned int uSeed);
1515 static void GetEdge(int * i0_out, int * i1_out, int * edgenum_out, const int indices[], const int i0_in, const int i1_in);
1516 
1517 static void BuildNeighborsFast(STriInfo pTriInfos[], SEdge * pEdges, const int piTriListIn[], const int iNrTrianglesIn)
1518 {
1519  // build array of edges
1520  unsigned int uSeed = INTERNAL_RND_SORT_SEED; // could replace with a random seed?
1521  int iEntries=0, iCurStartIndex=-1, f=0, i=0;
1522  for (f=0; f<iNrTrianglesIn; f++)
1523  for (i=0; i<3; i++)
1524  {
1525  const int i0 = piTriListIn[f*3+i];
1526  const int i1 = piTriListIn[f*3+(i<2?(i+1):0)];
1527  pEdges[f*3+i].i0 = i0 < i1 ? i0 : i1; // put minimum index in i0
1528  pEdges[f*3+i].i1 = !(i0 < i1) ? i0 : i1; // put maximum index in i1
1529  pEdges[f*3+i].f = f; // record face number
1530  }
1531 
1532  // sort over all edges by i0, this is the pricy one.
1533  QuickSortEdges(pEdges, 0, iNrTrianglesIn*3-1, 0, uSeed); // sort channel 0 which is i0
1534 
1535  // sub sort over i1, should be fast.
1536  // could replace this with a 64 bit int sort over (i0,i1)
1537  // with i0 as msb in the quicksort call above.
1538  iEntries = iNrTrianglesIn*3;
1539  iCurStartIndex = 0;
1540  for (i=1; i<iEntries; i++)
1541  {
1542  if (pEdges[iCurStartIndex].i0 != pEdges[i].i0)
1543  {
1544  const int iL = iCurStartIndex;
1545  const int iR = i-1;
1546  //const int iElems = i-iL;
1547  iCurStartIndex = i;
1548  QuickSortEdges(pEdges, iL, iR, 1, uSeed); // sort channel 1 which is i1
1549  }
1550  }
1551 
1552  // sub sort over f, which should be fast.
1553  // this step is to remain compliant with BuildNeighborsSlow() when
1554  // more than 2 triangles use the same edge (such as a butterfly topology).
1555  iCurStartIndex = 0;
1556  for (i=1; i<iEntries; i++)
1557  {
1558  if (pEdges[iCurStartIndex].i0 != pEdges[i].i0 || pEdges[iCurStartIndex].i1 != pEdges[i].i1)
1559  {
1560  const int iL = iCurStartIndex;
1561  const int iR = i-1;
1562  //const int iElems = i-iL;
1563  iCurStartIndex = i;
1564  QuickSortEdges(pEdges, iL, iR, 2, uSeed); // sort channel 2 which is f
1565  }
1566  }
1567 
1568  // pair up, adjacent triangles
1569  for (i=0; i<iEntries; i++)
1570  {
1571  const int i0=pEdges[i].i0;
1572  const int i1=pEdges[i].i1;
1573  const int f = pEdges[i].f;
1574  tbool bUnassigned_A;
1575 
1576  int i0_A, i1_A;
1577  int edgenum_A, edgenum_B=0; // 0,1 or 2
1578  GetEdge(&i0_A, &i1_A, &edgenum_A, &piTriListIn[f*3], i0, i1); // resolve index ordering and edge_num
1579  bUnassigned_A = pTriInfos[f].FaceNeighbors[edgenum_A] == -1 ? TTRUE : TFALSE;
1580 
1581  if (bUnassigned_A)
1582  {
1583  // get true index ordering
1584  int j=i+1, t;
1585  tbool bNotFound = TTRUE;
1586  while (j<iEntries && i0==pEdges[j].i0 && i1==pEdges[j].i1 && bNotFound)
1587  {
1588  tbool bUnassigned_B;
1589  int i0_B, i1_B;
1590  t = pEdges[j].f;
1591  // flip i0_B and i1_B
1592  GetEdge(&i1_B, &i0_B, &edgenum_B, &piTriListIn[t*3], pEdges[j].i0, pEdges[j].i1); // resolve index ordering and edge_num
1593  //assert(!(i0_A==i1_B && i1_A==i0_B));
1594  bUnassigned_B = pTriInfos[t].FaceNeighbors[edgenum_B]==-1 ? TTRUE : TFALSE;
1595  if (i0_A==i0_B && i1_A==i1_B && bUnassigned_B)
1596  bNotFound = TFALSE;
1597  else
1598  ++j;
1599  }
1600 
1601  if (!bNotFound)
1602  {
1603  int t = pEdges[j].f;
1604  pTriInfos[f].FaceNeighbors[edgenum_A] = t;
1605  //assert(pTriInfos[t].FaceNeighbors[edgenum_B]==-1);
1606  pTriInfos[t].FaceNeighbors[edgenum_B] = f;
1607  }
1608  }
1609  }
1610 }
1611 
1612 static void BuildNeighborsSlow(STriInfo pTriInfos[], const int piTriListIn[], const int iNrTrianglesIn)
1613 {
1614  int f=0, i=0;
1615  for (f=0; f<iNrTrianglesIn; f++)
1616  {
1617  for (i=0; i<3; i++)
1618  {
1619  // if unassigned
1620  if (pTriInfos[f].FaceNeighbors[i] == -1)
1621  {
1622  const int i0_A = piTriListIn[f*3+i];
1623  const int i1_A = piTriListIn[f*3+(i<2?(i+1):0)];
1624 
1625  // search for a neighbor
1626  tbool bFound = TFALSE;
1627  int t=0, j=0;
1628  while (!bFound && t<iNrTrianglesIn)
1629  {
1630  if (t!=f)
1631  {
1632  j=0;
1633  while (!bFound && j<3)
1634  {
1635  // in rev order
1636  const int i1_B = piTriListIn[t*3+j];
1637  const int i0_B = piTriListIn[t*3+(j<2?(j+1):0)];
1638  //assert(!(i0_A==i1_B && i1_A==i0_B));
1639  if (i0_A==i0_B && i1_A==i1_B)
1640  bFound = TTRUE;
1641  else
1642  ++j;
1643  }
1644  }
1645 
1646  if (!bFound) ++t;
1647  }
1648 
1649  // assign neighbors
1650  if (bFound)
1651  {
1652  pTriInfos[f].FaceNeighbors[i] = t;
1653  //assert(pTriInfos[t].FaceNeighbors[j]==-1);
1654  pTriInfos[t].FaceNeighbors[j] = f;
1655  }
1656  }
1657  }
1658  }
1659 }
1660 
1661 static void QuickSortEdges(SEdge * pSortBuffer, int iLeft, int iRight, const int channel, unsigned int uSeed)
1662 {
1663  unsigned int t;
1664  int iL, iR, n, index, iMid;
1665 
1666  // early out
1667  SEdge sTmp;
1668  const int iElems = iRight-iLeft+1;
1669  if (iElems<2) return;
1670  else if(iElems==2)
1671  {
1672  if (pSortBuffer[iLeft].array[channel] > pSortBuffer[iRight].array[channel])
1673  {
1674  sTmp = pSortBuffer[iLeft];
1675  pSortBuffer[iLeft] = pSortBuffer[iRight];
1676  pSortBuffer[iRight] = sTmp;
1677  }
1678  return;
1679  }
1680 
1681  // Random
1682  t=uSeed&31;
1683  t=(uSeed<<t)|(uSeed>>(32-t));
1684  uSeed=uSeed+t+3;
1685  // Random end
1686 
1687  iL=iLeft, iR=iRight;
1688  n = (iR-iL)+1;
1689  assert(n>=0);
1690  index = (int) (uSeed%n);
1691 
1692  iMid=pSortBuffer[index + iL].array[channel];
1693 
1694  do
1695  {
1696  while (pSortBuffer[iL].array[channel] < iMid)
1697  ++iL;
1698  while (pSortBuffer[iR].array[channel] > iMid)
1699  --iR;
1700 
1701  if (iL <= iR)
1702  {
1703  sTmp = pSortBuffer[iL];
1704  pSortBuffer[iL] = pSortBuffer[iR];
1705  pSortBuffer[iR] = sTmp;
1706  ++iL; --iR;
1707  }
1708  }
1709  while (iL <= iR);
1710 
1711  if (iLeft < iR)
1712  QuickSortEdges(pSortBuffer, iLeft, iR, channel, uSeed);
1713  if (iL < iRight)
1714  QuickSortEdges(pSortBuffer, iL, iRight, channel, uSeed);
1715 }
1716 
1717 // resolve ordering and edge number
1718 static void GetEdge(int * i0_out, int * i1_out, int * edgenum_out, const int indices[], const int i0_in, const int i1_in)
1719 {
1720  *edgenum_out = -1;
1721 
1722  // test if first index is on the edge
1723  if (indices[0]==i0_in || indices[0]==i1_in)
1724  {
1725  // test if second index is on the edge
1726  if (indices[1]==i0_in || indices[1]==i1_in)
1727  {
1728  edgenum_out[0]=0; // first edge
1729  i0_out[0]=indices[0];
1730  i1_out[0]=indices[1];
1731  }
1732  else
1733  {
1734  edgenum_out[0]=2; // third edge
1735  i0_out[0]=indices[2];
1736  i1_out[0]=indices[0];
1737  }
1738  }
1739  else
1740  {
1741  // only second and third index is on the edge
1742  edgenum_out[0]=1; // second edge
1743  i0_out[0]=indices[1];
1744  i1_out[0]=indices[2];
1745  }
1746 }
1747 
1748 
1749 /////////////////////////////////////////////////////////////////////////////////////////////
1750 /////////////////////////////////// Degenerate triangles ////////////////////////////////////
1751 
1752 static void DegenPrologue(STriInfo pTriInfos[], int piTriList_out[], const int iNrTrianglesIn, const int iTotTris)
1753 {
1754  int iNextGoodTriangleSearchIndex=-1;
1755  tbool bStillFindingGoodOnes;
1756 
1757  // locate quads with only one good triangle
1758  int t=0;
1759  while (t<(iTotTris-1))
1760  {
1761  const int iFO_a = pTriInfos[t].iOrgFaceNumber;
1762  const int iFO_b = pTriInfos[t+1].iOrgFaceNumber;
1763  if (iFO_a==iFO_b) // this is a quad
1764  {
1765  const tbool bIsDeg_a = (pTriInfos[t].iFlag&MARK_DEGENERATE)!=0 ? TTRUE : TFALSE;
1766  const tbool bIsDeg_b = (pTriInfos[t+1].iFlag&MARK_DEGENERATE)!=0 ? TTRUE : TFALSE;
1767  if ((bIsDeg_a^bIsDeg_b)!=0)
1768  {
1769  pTriInfos[t].iFlag |= QUAD_ONE_DEGEN_TRI;
1770  pTriInfos[t+1].iFlag |= QUAD_ONE_DEGEN_TRI;
1771  }
1772  t += 2;
1773  }
1774  else
1775  ++t;
1776  }
1777 
1778  // reorder list so all degen triangles are moved to the back
1779  // without reordering the good triangles
1780  iNextGoodTriangleSearchIndex = 1;
1781  t=0;
1782  bStillFindingGoodOnes = TTRUE;
1783  while (t<iNrTrianglesIn && bStillFindingGoodOnes)
1784  {
1785  const tbool bIsGood = (pTriInfos[t].iFlag&MARK_DEGENERATE)==0 ? TTRUE : TFALSE;
1786  if (bIsGood)
1787  {
1788  if (iNextGoodTriangleSearchIndex < (t+2))
1789  iNextGoodTriangleSearchIndex = t+2;
1790  }
1791  else
1792  {
1793  int t0, t1;
1794  // search for the first good triangle.
1795  tbool bJustADegenerate = TTRUE;
1796  while (bJustADegenerate && iNextGoodTriangleSearchIndex<iTotTris)
1797  {
1798  const tbool bIsGood = (pTriInfos[iNextGoodTriangleSearchIndex].iFlag&MARK_DEGENERATE)==0 ? TTRUE : TFALSE;
1799  if (bIsGood) bJustADegenerate=TFALSE;
1800  else ++iNextGoodTriangleSearchIndex;
1801  }
1802 
1803  t0 = t;
1804  t1 = iNextGoodTriangleSearchIndex;
1805  ++iNextGoodTriangleSearchIndex;
1806  assert(iNextGoodTriangleSearchIndex > (t+1));
1807 
1808  // swap triangle t0 and t1
1809  if (!bJustADegenerate)
1810  {
1811  int i=0;
1812  for (i=0; i<3; i++)
1813  {
1814  const int index = piTriList_out[t0*3+i];
1815  piTriList_out[t0*3+i] = piTriList_out[t1*3+i];
1816  piTriList_out[t1*3+i] = index;
1817  }
1818  {
1819  const STriInfo tri_info = pTriInfos[t0];
1820  pTriInfos[t0] = pTriInfos[t1];
1821  pTriInfos[t1] = tri_info;
1822  }
1823  }
1824  else
1825  bStillFindingGoodOnes = TFALSE; // this is not supposed to happen
1826  }
1827 
1828  if (bStillFindingGoodOnes) ++t;
1829  }
1830 
1831  assert(bStillFindingGoodOnes); // code will still work.
1832  assert(iNrTrianglesIn == t);
1833 }
1834 
1835 static void DegenEpilogue(STSpace psTspace[], STriInfo pTriInfos[], int piTriListIn[], const SMikkTSpaceContext * pContext, const int iNrTrianglesIn, const int iTotTris)
1836 {
1837  int t=0, i=0;
1838  // deal with degenerate triangles
1839  // punishment for degenerate triangles is O(N^2)
1840  for (t=iNrTrianglesIn; t<iTotTris; t++)
1841  {
1842  // degenerate triangles on a quad with one good triangle are skipped
1843  // here but processed in the next loop
1844  const tbool bSkip = (pTriInfos[t].iFlag&QUAD_ONE_DEGEN_TRI)!=0 ? TTRUE : TFALSE;
1845 
1846  if (!bSkip)
1847  {
1848  for (i=0; i<3; i++)
1849  {
1850  const int index1 = piTriListIn[t*3+i];
1851  // search through the good triangles
1852  tbool bNotFound = TTRUE;
1853  int j=0;
1854  while (bNotFound && j<(3*iNrTrianglesIn))
1855  {
1856  const int index2 = piTriListIn[j];
1857  if (index1==index2) bNotFound=TFALSE;
1858  else ++j;
1859  }
1860 
1861  if (!bNotFound)
1862  {
1863  const int iTri = j/3;
1864  const int iVert = j%3;
1865  const int iSrcVert=pTriInfos[iTri].vert_num[iVert];
1866  const int iSrcOffs=pTriInfos[iTri].iTSpacesOffs;
1867  const int iDstVert=pTriInfos[t].vert_num[i];
1868  const int iDstOffs=pTriInfos[t].iTSpacesOffs;
1869 
1870  // copy tspace
1871  psTspace[iDstOffs+iDstVert] = psTspace[iSrcOffs+iSrcVert];
1872  }
1873  }
1874  }
1875  }
1876 
1877  // deal with degenerate quads with one good triangle
1878  for (t=0; t<iNrTrianglesIn; t++)
1879  {
1880  // this triangle belongs to a quad where the
1881  // other triangle is degenerate
1882  if ( (pTriInfos[t].iFlag&QUAD_ONE_DEGEN_TRI)!=0 )
1883  {
1884  SVec3 vDstP;
1885  int iOrgF=-1, i=0;
1886  tbool bNotFound;
1887  unsigned char * pV = pTriInfos[t].vert_num;
1888  int iFlag = (1<<pV[0]) | (1<<pV[1]) | (1<<pV[2]);
1889  int iMissingIndex = 0;
1890  if ((iFlag&2)==0) iMissingIndex=1;
1891  else if((iFlag&4)==0) iMissingIndex=2;
1892  else if((iFlag&8)==0) iMissingIndex=3;
1893 
1894  iOrgF = pTriInfos[t].iOrgFaceNumber;
1895  vDstP = GetPosition(pContext, MakeIndex(iOrgF, iMissingIndex));
1896  bNotFound = TTRUE;
1897  i=0;
1898  while (bNotFound && i<3)
1899  {
1900  const int iVert = pV[i];
1901  const SVec3 vSrcP = GetPosition(pContext, MakeIndex(iOrgF, iVert));
1902  if (veq(vSrcP, vDstP)==TTRUE)
1903  {
1904  const int iOffs = pTriInfos[t].iTSpacesOffs;
1905  psTspace[iOffs+iMissingIndex] = psTspace[iOffs+iVert];
1906  bNotFound=TFALSE;
1907  }
1908  else
1909  ++i;
1910  }
1911  assert(!bNotFound);
1912  }
1913  }
1914 }
float z
Definition: mikktspace.cpp:65
tbool bOrient
Definition: mikktspace.cpp:186
#define T0(z, i)
int iTSpacesOffs
Definition: mikktspace.cpp:175
const int g_iCells
Definition: mikktspace.cpp:455
float vert[3]
Definition: mikktspace.cpp:451
#define TTRUE
Definition: mikktspace.cpp:54
SMikkTSpaceInterface * m_pInterface
Definition: mikktspace.h:109
float fMagT
Definition: mikktspace.cpp:171
#define TFALSE
Copyright (C) 2011 by Morten S.
Definition: mikktspace.cpp:53
static void InitTriInfo(STriInfo pTriInfos[], const int piTriListIn[], const SMikkTSpaceContext *pContext, const int iNrTrianglesIn)
Definition: mikktspace.cpp:963
int i0
Definition: mikktspace.cpp:938
static SVec3 vscale(const float fS, const SVec3 v)
Definition: mikktspace.cpp:96
static tbool AssignRecur(const int piTriListIn[], STriInfo psTriInfos[], const int iMyTriIndex, SGroup *pGroup)
int iOrgFaceNumber
Definition: mikktspace.cpp:174
float vdot(float *va, float *vb)
Return dot product of length 3 vectors va and vb.
Definition: Decompose.cpp:55
static SVec3 GetTexCoord(const SMikkTSpaceContext *pContext, const int index)
Definition: mikktspace.cpp:921
int(* m_getNumVerticesOfFace)(const SMikkTSpaceContext *pContext, const int iFace)
Definition: mikktspace.h:72
int i1
Definition: mikktspace.cpp:938
static void MergeVertsFast(int piTriList_in_and_out[], STmpVert pTmpVert[], const SMikkTSpaceContext *pContext, const int iL_in, const int iR_in)
Definition: mikktspace.cpp:602
static float CalcTexArea(const SMikkTSpaceContext *pContext, const int indices[])
Definition: mikktspace.cpp:947
void(* m_setTSpace)(const SMikkTSpaceContext *pContext, const float fvTangent[], const float fvBiTangent[], const float fMagS, const float fMagT, const tbool bIsOrientationPreserving, const int iFace, const int iVert)
Definition: mikktspace.h:103
int * pFaceIndices
Definition: mikktspace.cpp:151
void(* m_getNormal)(const SMikkTSpaceContext *pContext, float fvNormOut[], const int iFace, const int iVert)
Definition: mikktspace.h:77
static void BuildNeighborsFast(STriInfo pTriInfos[], SEdge *pEdges, const int piTriListIn[], const int iNrTrianglesIn)
int FaceNeighbors[3]
Definition: mikktspace.cpp:166
float y
Definition: mikktspace.cpp:65
#define MARK_DEGENERATE
Definition: mikktspace.cpp:157
float fMagS
Definition: mikktspace.cpp:171
static SVec3 vsub(const SVec3 v1, const SVec3 v2)
Definition: mikktspace.cpp:85
static void GenerateSharedVerticesIndexList(int piTriList_in_and_out[], const SMikkTSpaceContext *pContext, const int iNrTrianglesIn)
Definition: mikktspace.cpp:477
static int GenerateInitialVerticesIndexList(STriInfo pTriInfos[], int piTriList_out[], const SMikkTSpaceContext *pContext, const int iNrTrianglesIn)
Definition: mikktspace.cpp:790
static void GetEdge(int *i0_out, int *i1_out, int *edgenum_out, const int indices[], const int i0_in, const int i1_in)
#define INTERNAL_RND_SORT_SEED
Definition: mikktspace.cpp:60
SVec3 vOt
Definition: mikktspace.cpp:183
static SVec3 GetNormal(const SMikkTSpaceContext *pContext, const int index)
Definition: mikktspace.cpp:911
static int MakeIndex(const int iFace, const int iVert)
Definition: mikktspace.cpp:197
tbool bOrientPreservering
Definition: mikktspace.cpp:153
static SVec3 vadd(const SVec3 v1, const SVec3 v2)
Definition: mikktspace.cpp:73
static void IndexToData(int *piFace, int *piVert, const int iIndexIn)
Definition: mikktspace.cpp:203
static tbool GenerateTSpaces(STSpace psTspace[], const STriInfo pTriInfos[], const SGroup pGroups[], const int iNrActiveGroups, const int piTriListIn[], const float fThresCos, const SMikkTSpaceContext *pContext)
static void QuickSortEdges(SEdge *pSortBuffer, int iLeft, int iRight, const int channel, unsigned int uSeed)
static tbool CompareSubGroups(const SSubGroup *pg1, const SSubGroup *pg2)
static void QuickSort(int *pSortBuffer, int iLeft, int iRight, unsigned int uSeed)
static void MergeVertsSlow(int piTriList_in_and_out[], const SMikkTSpaceContext *pContext, const int pTable[], const int iEntries)
Definition: mikktspace.cpp:712
float x
Definition: mikktspace.cpp:65
#define NOINLINE
Definition: mikktspace.cpp:460
SVec3 vOt
Definition: mikktspace.cpp:170
static STSpace AvgTSpace(const STSpace *pTS0, const STSpace *pTS1)
Definition: mikktspace.cpp:209
int(* m_getNumFaces)(const SMikkTSpaceContext *pContext)
Definition: mikktspace.h:68
tbool genTangSpaceDefault(const SMikkTSpaceContext *pContext)
Definition: mikktspace.cpp:249
SGroup * AssignedGroup[3]
Definition: mikktspace.cpp:167
static void DegenPrologue(STriInfo pTriInfos[], int piTriList_out[], const int iNrTrianglesIn, const int iTotTris)
static tbool VNotZero(const SVec3 v)
Definition: mikktspace.cpp:134
void(* m_setTSpaceBasic)(const SMikkTSpaceContext *pContext, const float fvTangent[], const float fSign, const int iFace, const int iVert)
Definition: mikktspace.h:90
#define M_PI
Definition: mikktspace.cpp:57
static int Build4RuleGroups(STriInfo pTriInfos[], SGroup pGroups[], int piGroupTrianglesBuffer[], const int piTriListIn[], const int iNrTrianglesIn)
SVec3 vOs
Definition: mikktspace.cpp:181
void(* m_getPosition)(const SMikkTSpaceContext *pContext, float fvPosOut[], const int iFace, const int iVert)
Definition: mikktspace.h:76
static void GenerateSharedVerticesIndexListSlow(int piTriList_in_and_out[], const SMikkTSpaceContext *pContext, const int iNrTrianglesIn)
Definition: mikktspace.cpp:747
tbool genTangSpace(const SMikkTSpaceContext *pContext, const float fAngularThreshold)
Definition: mikktspace.cpp:254
static SVec3 GetPosition(const SMikkTSpaceContext *pContext, const int index)
Definition: mikktspace.cpp:901
static tbool NotZero(const float fX)
Definition: mikktspace.cpp:128
float fMagT
Definition: mikktspace.cpp:184
int array[3]
Definition: mikktspace.cpp:940
void(* m_getTexCoord)(const SMikkTSpaceContext *pContext, float fvTexcOut[], const int iFace, const int iVert)
Definition: mikktspace.h:78
float fMagS
Definition: mikktspace.cpp:182
static float LengthSquared(const SVec3 v)
Definition: mikktspace.cpp:107
unsigned char vert_num[4]
Definition: mikktspace.cpp:176
int iNrFaces
Definition: mikktspace.cpp:150
int iVertexRepresentitive
Definition: mikktspace.cpp:152
static tbool veq(const SVec3 v1, const SVec3 v2)
Definition: mikktspace.cpp:68
NOINLINE int FindGridCell(const float fMin, const float fMax, const float fVal)
Definition: mikktspace.cpp:466
static SVec3 Normalize(const SVec3 v)
Definition: mikktspace.cpp:117
int iCounter
Definition: mikktspace.cpp:185
int tbool
Copyright (C) 2011 by Morten S.
Definition: mikktspace.h:62
static float Length(const SVec3 v)
Definition: mikktspace.cpp:112
static STSpace EvalTspace(int face_indices[], const int iFaces, const int piTriListIn[], const STriInfo pTriInfos[], const SMikkTSpaceContext *pContext, const int iVertexRepresentitive)
static void DegenEpilogue(STSpace psTspace[], STriInfo pTriInfos[], int piTriListIn[], const SMikkTSpaceContext *pContext, const int iNrTrianglesIn, const int iTotTris)
static void AddTriToGroup(SGroup *pGroup, const int iTriIndex)
#define QUAD_ONE_DEGEN_TRI
Definition: mikktspace.cpp:158
SVec3 vOs
Definition: mikktspace.cpp:170
int * pTriMembers
Definition: mikktspace.cpp:145
#define GROUP_WITH_ANY
Definition: mikktspace.cpp:159
static void BuildNeighborsSlow(STriInfo pTriInfos[], const int piTriListIn[], const int iNrTrianglesIn)
#define ORIENT_PRESERVING
Definition: mikktspace.cpp:160