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