@woosh/meep-engine 2.42.0 → 2.42.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/core/assert.js +2 -2
  2. package/core/collection/RingBuffer.js +15 -0
  3. package/core/collection/array/array_swap.js +3 -3
  4. package/core/collection/map/AsyncLoadingCache.js +47 -0
  5. package/core/geom/3d/aabb/aabb3_compute_distance_above_plane_max.js +1 -1
  6. package/core/math/statistics/computeSampleSize_Cochran.js +3 -3
  7. package/editor/ecs/component/editors/geom/QuaternionEditor.js +12 -5
  8. package/engine/Engine.js +6 -1
  9. package/engine/EngineBootstrapper.js +2 -1
  10. package/engine/EngineHarness.js +5 -1
  11. package/engine/asset/AssetManager.js +97 -7
  12. package/engine/development/performance/AbstractMetric.js +3 -1
  13. package/engine/development/performance/RingBufferMetric.js +39 -4
  14. package/engine/ecs/EntityBuilder.js +29 -4
  15. package/engine/graphics/ecs/camera/topdown/TopDownCameraControllerSystem.js +17 -1
  16. package/engine/graphics/ecs/decal/v2/FPDecalSystem.js +18 -30
  17. package/engine/graphics/ecs/decal/v2/prototypeDecalSystem.js +57 -16
  18. package/engine/graphics/ecs/mesh-v2/allocate_v3.js +37 -0
  19. package/engine/graphics/geometry/MikkT/AddTriToGroup.js +10 -0
  20. package/engine/graphics/geometry/MikkT/AssignRecur.js +84 -0
  21. package/engine/graphics/geometry/MikkT/AvgTSpace.js +38 -0
  22. package/engine/graphics/geometry/MikkT/Build4RuleGroups.js +96 -0
  23. package/engine/graphics/geometry/MikkT/BuildNeighborsFast.js +137 -0
  24. package/engine/graphics/geometry/MikkT/CalcTexArea.js +31 -0
  25. package/engine/graphics/geometry/MikkT/CompareSubGroups.js +26 -0
  26. package/engine/graphics/geometry/MikkT/DegenEpilogue.js +220 -0
  27. package/engine/graphics/geometry/MikkT/DegenPrologue.js +115 -0
  28. package/engine/graphics/geometry/MikkT/EvalTspace.js +128 -0
  29. package/engine/graphics/geometry/MikkT/GenerateInitialVerticesIndexList.js +48 -0
  30. package/engine/graphics/geometry/MikkT/GenerateSharedVerticesIndexList.js +184 -0
  31. package/engine/graphics/geometry/MikkT/GenerateTSpaces.js +226 -0
  32. package/engine/graphics/geometry/MikkT/GetEdge.js +45 -0
  33. package/engine/graphics/geometry/MikkT/GetNormal.js +16 -0
  34. package/engine/graphics/geometry/MikkT/GetPosition.js +25 -0
  35. package/engine/graphics/geometry/MikkT/GetTexCoord.js +18 -0
  36. package/engine/graphics/geometry/MikkT/InitTriInfo.js +180 -0
  37. package/engine/graphics/geometry/MikkT/Length.js +10 -0
  38. package/engine/graphics/geometry/MikkT/MakeIndex.js +18 -0
  39. package/engine/graphics/geometry/MikkT/MikkTSpace.js +165 -2197
  40. package/engine/graphics/geometry/MikkT/NormalizeSafe.js +21 -0
  41. package/engine/graphics/geometry/MikkT/NotZero.js +10 -0
  42. package/engine/graphics/geometry/MikkT/QuickSort.js +54 -0
  43. package/engine/graphics/geometry/MikkT/QuickSortEdges.js +71 -0
  44. package/engine/graphics/geometry/MikkT/SSubGroup.js +15 -0
  45. package/engine/graphics/geometry/MikkT/STSpace.js +36 -0
  46. package/engine/graphics/geometry/MikkT/constants/GROUP_WITH_ANY.js +1 -0
  47. package/engine/graphics/geometry/MikkT/constants/INTERNAL_RND_SORT_SEED.js +1 -0
  48. package/engine/graphics/geometry/MikkT/constants/MARK_DEGENERATE.js +1 -0
  49. package/engine/graphics/geometry/MikkT/constants/ORIENT_PRESERVING.js +1 -0
  50. package/engine/graphics/geometry/MikkT/constants/QUAD_ONE_DEGEN_TRI.js +1 -0
  51. package/engine/graphics/geometry/MikkT/m_getNormal.js +16 -0
  52. package/engine/graphics/geometry/MikkT/m_getNumFaces.js +8 -0
  53. package/engine/graphics/geometry/MikkT/m_getNumVerticesOfFace.js +11 -0
  54. package/engine/graphics/geometry/MikkT/m_getPosition.js +20 -0
  55. package/engine/graphics/geometry/MikkT/m_getTexCoord.js +16 -0
  56. package/engine/graphics/geometry/MikkT/m_setTSpace.js +35 -0
  57. package/engine/graphics/geometry/MikkT/m_setTSpaceBasic.js +22 -0
  58. package/engine/graphics/geometry/MikkT/malloc.js +16 -0
  59. package/engine/graphics/geometry/MikkT/v3_scale_dot_sub_normalize.js +52 -0
  60. package/engine/graphics/impostors/octahedral/ImpostorBaker.js +3 -2
  61. package/engine/graphics/impostors/octahedral/prototypeBaker.js +5 -5
  62. package/engine/graphics/render/forward_plus/LightManager.js +16 -6
  63. package/engine/graphics/render/forward_plus/data/TextureBackedMemoryRegion.js +7 -1
  64. package/engine/graphics/render/forward_plus/materials/FP_SHADER_CHUNK_ACCUMULATION.js +13 -3
  65. package/engine/graphics/render/forward_plus/materials/FP_SHADER_CHUNK_PREAMBLE.js +2 -1
  66. package/engine/graphics/render/forward_plus/plugin/MaterialTransformer.js +14 -2
  67. package/engine/graphics/render/forward_plus/query/query_bvh_frustum_from_texture.js +2 -2
  68. package/engine/intelligence/behavior/util/RotationBehavior.js +69 -0
  69. package/generation/example/filters/SampleGroundMoistureFilter.js +5 -5
  70. package/package.json +1 -1
@@ -1,8 +1,19 @@
1
- import { assert } from "../../../../core/assert.js";
2
- import { EPSILON } from "../../../../core/math/MathUtils.js";
3
- import { array_copy } from "../../../../core/collection/array/copyArray.js";
4
- import { fabsf } from "../../../../core/math/fabsf.js";
5
- import { v3_distance_sqr } from "../../../../core/geom/v3_distance_sqr.js";
1
+ import { vec3 } from "gl-matrix";
2
+ import { m_setTSpace } from "./m_setTSpace.js";
3
+ import { GetPosition } from "./GetPosition.js";
4
+ import { malloc } from "./malloc.js";
5
+ import { STSpace } from "./STSpace.js";
6
+ import { MARK_DEGENERATE } from "./constants/MARK_DEGENERATE.js";
7
+ import { InitTriInfo } from "./InitTriInfo.js";
8
+ import { GenerateTSpaces } from "./GenerateTSpaces.js";
9
+ import { DegenEpilogue } from "./DegenEpilogue.js";
10
+ import { Build4RuleGroups } from "./Build4RuleGroups.js";
11
+ import { allocate_v3 } from "../../ecs/mesh-v2/allocate_v3.js";
12
+ import { m_getNumFaces } from "./m_getNumFaces.js";
13
+ import { GenerateInitialVerticesIndexList } from "./GenerateInitialVerticesIndexList.js";
14
+ import { DegenPrologue } from "./DegenPrologue.js";
15
+ import { GenerateSharedVerticesIndexList } from "./GenerateSharedVerticesIndexList.js";
16
+ import { DEG_TO_RAD } from "../../../../core/math/DEG_TO_RAD.js";
6
17
 
7
18
 
8
19
  /*
@@ -30,137 +41,12 @@ import { v3_distance_sqr } from "../../../../core/geom/v3_distance_sqr.js";
30
41
  sampling/exporting or by using the order-independent choice of diagonal for splitting quads suggested earlier.
31
42
  However, this must be used both by the sampler and your tools/rendering pipeline.
32
43
 
33
- @see https://github.com/mmikk/MikkTSpace/blob/master/mikktspace.c
44
+ @see https://github.com/mmikk/MikkTSpace/blob/master/mikktspace.c (the original)
45
+ @see https://github.com/blender/blender/blob/594f47ecd2d5367ca936cf6fc6ec8168c2b360d0/intern/mikktspace/mikktspace.c (looks like best-optimized version)
46
+ @see https://github.com/undefinist/mikktspacehx/blob/master/src/mikktspace/Mikktspace.hx (Haxe port)
47
+ @see https://github.com/gltf-rs/mikktspace/blob/6275cc4f15cff8be29819fb34ae8be3b9129dae1/src/generated.rs (Rust port)
34
48
  */
35
49
 
36
-
37
- /**
38
- * @extends {number[]}
39
- */
40
- class SVec3 {
41
- constructor() {
42
- this.x = 0;
43
- this.y = 0;
44
- this.z = 0;
45
- }
46
-
47
- get 0() {
48
- return this.x;
49
- }
50
-
51
- set 0(v) {
52
- this.x = v;
53
- }
54
-
55
- get 1() {
56
- return this.y;
57
- }
58
-
59
-
60
- set 1(v) {
61
- this.y = v;
62
- }
63
-
64
- get 2() {
65
- return this.z;
66
- }
67
-
68
- set 2(v) {
69
- this.z = v;
70
- }
71
-
72
- copy(other) {
73
- this.x = other.x;
74
- this.y = other.y;
75
- this.z = other.z;
76
- }
77
-
78
- }
79
-
80
- class SEdge {
81
- constructor() {
82
- /**
83
- * int
84
- * @type {number}
85
- */
86
- this.i0 = 0;
87
- /**
88
- * int
89
- * @type {number}
90
- */
91
- this.i1 = 0;
92
- /**
93
- * int
94
- * @type {number}
95
- */
96
- this.f = 0;
97
- }
98
-
99
- get 0() {
100
- return this.i0;
101
- }
102
-
103
- set 0(v) {
104
- this.i0 = v;
105
- }
106
-
107
- get 1() {
108
- return this.i1;
109
- }
110
-
111
- set 1(v) {
112
- this.i1 = v;
113
- }
114
-
115
- get 2() {
116
- return this.f;
117
- }
118
-
119
- set 2(v) {
120
- this.f = v;
121
- }
122
- }
123
-
124
- class STSpace {
125
- constructor() {
126
- this.vOs = new SVec3();
127
- this.fMagS = 0;
128
- this.vOt = new SVec3();
129
- this.fMagT = 0;
130
- this.iCounter = 0; // this is to average back into quads.
131
- this.bOrient = false;
132
- }
133
-
134
- /**
135
- *
136
- * @param {STSpace} other
137
- */
138
- copy(other) {
139
- this.vOs.copy(other.vOs);
140
- this.fMagS = other.fMagS;
141
- this.vOt.copy(other.vOt);
142
- this.fMagT = other.fMagT;
143
- this.iCounter = other.iCounter;
144
- this.bOrient = other.bOrient;
145
- }
146
- }
147
-
148
- class SSubGroup {
149
- constructor() {
150
- /**
151
- *
152
- * @type {number}
153
- */
154
- this.iNrFaces = 0;
155
-
156
- /**
157
- * *int
158
- * @type {Int32Array|null}
159
- */
160
- this.pTriMembers = null;
161
- }
162
- }
163
-
164
50
  class SGroup {
165
51
  constructor() {
166
52
  this.iNrFaces = 0;
@@ -189,8 +75,15 @@ class STriInfo {
189
75
  this.AssignedGroup = [];
190
76
 
191
77
  // normalized first order face derivatives
192
- this.vOs = new SVec3();
193
- this.vOt = new SVec3();
78
+
79
+ /**
80
+ * @type {vec3|Float32Array}
81
+ */
82
+ this.vOs = allocate_v3();
83
+ /**
84
+ * @type {vec3|Float32Array}
85
+ */
86
+ this.vOt = allocate_v3();
194
87
 
195
88
  // original magnitudes
196
89
  this.fMagS = 0;
@@ -212,7 +105,7 @@ class STriInfo {
212
105
  * int[4]
213
106
  * @type {number[]}
214
107
  */
215
- this.vert_num = [];
108
+ this.vert_num = new Uint8Array(4);
216
109
 
217
110
  /**
218
111
  *
@@ -258,2114 +151,189 @@ export class SMikkTSpaceContext {
258
151
  }
259
152
  }
260
153
 
261
- /**
262
- * Returns the number of faces (triangles/quads) on the mesh to be processed.
263
- * @param {SMikkTSpaceContext} pContext
264
- * @returns {number}
265
- */
266
- function m_getNumFaces(pContext) {
267
- return pContext.geometry_buffer_index.length / 3;
268
- }
269
-
270
- /**
271
- * Returns the number of vertices on face number iFace
272
- * iFace is a number in the range {0, 1, ..., getNumFaces()-1}
273
- * @param {SMikkTSpaceContext} pContext
274
- * @param {number} iFace
275
- * @returns {number}
276
- */
277
- function m_getNumVerticesOfFace(pContext, iFace) {
278
- // only support triangles
279
- return 3;
280
- }
281
-
282
154
  // returns the position/normal/texcoord of the referenced face of vertex number iVert.
283
155
  // iVert is in the range {0,1,2} for triangles and {0,1,2,3} for quads.
284
156
 
285
- /**
286
- *
287
- * @param {SMikkTSpaceContext} pContext
288
- * @param {number[]|{0:number,1:number,2:number}} fvPosOut
289
- * @param {number} iFace
290
- * @param {number} iVert
291
- * @returns {void}
292
- */
293
- function m_getPosition(pContext, fvPosOut, iFace, iVert) {
294
- // figure out which vertex it is
295
- const vertex_index = pContext.geometry_buffer_index[iFace * 3 + iVert];
296
-
297
- array_copy(pContext.geometry_buffer_vertex_position, vertex_index * 3, fvPosOut, 0, 3);
298
- }
299
157
 
300
158
  /**
301
159
  *
302
160
  * @param {SMikkTSpaceContext} pContext
303
- * @param {number[]|{0:number,1:number,2:number}} fvNormOut
304
- * @param {number} iFace
305
- * @param {number} iVert
306
- * @returns {void}
161
+ * @param {number} fAngularThreshold
162
+ * @return {boolean}
307
163
  */
308
- function m_getNormal(pContext, fvNormOut, iFace, iVert) {
309
- // figure out which vertex it is
310
- const vertex_index = pContext.geometry_buffer_index[iFace * 3 + iVert];
164
+ export function genTangSpace(pContext, fAngularThreshold = 180) {
165
+ // count nr_triangles
166
+ let iNrTSPaces = 0, iNrMaxGroups = 0;
167
+ let iNrActiveGroups = 0;
311
168
 
312
- array_copy(pContext.geometry_buffer_vertex_normal, vertex_index * 3, fvNormOut, 0, 3);
313
- }
169
+ const iNrFaces = m_getNumFaces(pContext);
170
+ const fThresCos = Math.cos(fAngularThreshold * DEG_TO_RAD);
314
171
 
315
- /**
316
- *
317
- * @param {SMikkTSpaceContext} pContext
318
- * @param {number[]|{0:number,1:number}} fvTexcOut
319
- * @param {number} iFace
320
- * @param {number} iVert
321
- * @returns {void}
322
- */
323
- function m_getTexCoord(pContext, fvTexcOut, iFace, iVert) {
324
- // figure out which vertex it is
325
- const vertex_index = pContext.geometry_buffer_index[iFace * 3 + iVert];
326
172
 
327
- array_copy(pContext.geometry_buffer_vertex_uv, vertex_index * 2, fvTexcOut, 0, 3);
328
- }
173
+ // count triangles on supported faces
174
+ // AlexGoldring: since we only support triangles, the number of triangles is going to equal number of faces
175
+ let iNrTrianglesIn = iNrFaces;
329
176
 
330
- /**
331
- * either (or both) of the two setTSpace callbacks can be set.
332
- * The call-back m_setTSpaceBasic() is sufficient for basic normal mapping.
333
- *
334
- * This function is used to return the tangent and fSign to the application.
335
- * fvTangent is a unit length vector.
336
- * For normal maps it is sufficient to use the following simplified version of the bitangent which is generated at pixel/vertex level.
337
- * bitangent = fSign * cross(vN, tangent);
338
- * Note that the results are returned unindexed. It is possible to generate a new index list
339
- * But averaging/overwriting tangent spaces by using an already existing index list WILL produce INCRORRECT results.
340
- * DO NOT! use an already existing index list.
341
- *
342
- * @param {SMikkTSpaceContext} pContext
343
- * @param {number[]} fvTangent
344
- * @param {number} fSign
345
- * @param {number} iFace
346
- * @param {number} iVert
347
- * @returns {void}
348
- */
349
- function m_setTSpaceBasic(pContext, fvTangent, fSign, iFace, iVert) {
350
- throw new Error('Not supported');
351
- }
177
+ if (iNrTrianglesIn <= 0) {
178
+ return false;
179
+ }
352
180
 
353
- /**
354
- *
355
- * @param {SMikkTSpaceContext} pContext
356
- * @param {number[]} fvTangent
357
- * @param {number[]} fvBiTangent
358
- * @param {number} fMagS
359
- * @param {number} fMagT
360
- * @param {boolean} bIsOrientationPreserving
361
- * @param {number} iFace
362
- * @param {number} iVert
363
- * @returns {void}
364
- */
365
- function m_setTSpace(pContext, fvTangent, fvBiTangent, fMagS, fMagT, bIsOrientationPreserving, iFace, iVert) {
366
- // figure out which vertex it is
367
- const vertex_index = pContext.geometry_buffer_index[iFace * 3 + iVert];
181
+ // allocate memory for an index list
182
+ const piTriListIn = new Int32Array(3 * iNrTrianglesIn);
183
+ const pTriInfos = malloc(STriInfo, iNrTrianglesIn);
368
184
 
369
- const tangent_destination = pContext.geometry_buffer_vertex_tangent;
370
- const tangent_address = vertex_index * 4;
185
+ // make an initial triangle --> face index list
186
+ iNrTSPaces = GenerateInitialVerticesIndexList(pTriInfos, piTriListIn, pContext, iNrTrianglesIn);
371
187
 
372
- // for logic explanation, see https://github.com/gltf-rs/mikktspace/blob/6275cc4f15cff8be29819fb34ae8be3b9129dae1/src/lib.rs#L33
373
- tangent_destination[tangent_address] = fvTangent[0];
374
- tangent_destination[tangent_address + 1] = fvTangent[1];
375
- tangent_destination[tangent_address + 2] = fvTangent[2];
376
- tangent_destination[tangent_address + 3] = bIsOrientationPreserving ? 1 : -1;
377
- }
188
+ // make a welded index list of identical positions and attributes (pos, norm, texc)
189
+ //printf("gen welded index list begin\n");
190
+ GenerateSharedVerticesIndexList(piTriListIn, pContext, iNrTrianglesIn);
191
+ //printf("gen welded index list end\n");
378
192
 
379
- const MARK_DEGENERATE = 1;
380
- const QUAD_ONE_DEGEN_TRI = 2;
381
- const GROUP_WITH_ANY = 4;
382
- const ORIENT_PRESERVING = 8;
193
+ // Mark all degenerate triangles
194
+ const iTotTris = iNrTrianglesIn;
195
+ let iDegenTriangles = 0;
383
196
 
384
- const INTERNAL_RND_SORT_SEED = 39871946;
197
+ const p0 = vec3.create();
198
+ const p1 = vec3.create();
199
+ const p2 = vec3.create();
385
200
 
386
- /**
387
- *
388
- * @param {SVec3} v1
389
- * @param {SVec3} v2
390
- * @returns {boolean}
391
- */
392
- function veq(v1, v2) {
393
- return (v1.x === v2.x) && (v1.y === v2.y) && (v1.z === v2.z);
394
- }
201
+ for (let t = 0; t < iTotTris; t++) {
395
202
 
396
- /**
397
- *
398
- * @param {SVec3} res
399
- * @param {SVec3} v1
400
- * @param {SVec3} v2
401
- */
402
- function vadd(res, v1, v2) {
203
+ const t3 = t * 3;
403
204
 
404
- const x = v1.x + v2.x;
405
- const y = v1.y + v2.y;
406
- const z = v1.z + v2.z;
205
+ const i0 = piTriListIn[t3 ];
206
+ const i1 = piTriListIn[t3 + 1];
207
+ const i2 = piTriListIn[t3 + 2];
407
208
 
408
- res.x = x;
409
- res.y = y;
410
- res.z = z;
209
+ GetPosition(p0, 0, pContext, i0);
210
+ GetPosition(p1, 0, pContext, i1);
211
+ GetPosition(p2, 0, pContext, i2);
411
212
 
412
- }
213
+ if (
214
+ vec3.exactEquals(p0, p1) ||
215
+ vec3.exactEquals(p0, p2) ||
216
+ vec3.exactEquals(p1, p2)
217
+ ) {
218
+ // degenerate
219
+ pTriInfos[t].iFlag |= MARK_DEGENERATE;
220
+ ++iDegenTriangles;
221
+ }
222
+ }
413
223
 
414
- /**
415
- *
416
- * @param {SVec3} res
417
- * @param {SVec3} v1
418
- * @param {SVec3} v2
419
- */
420
- function vsub(res, v1, v2) {
224
+ iNrTrianglesIn = iTotTris - iDegenTriangles;
421
225
 
422
- const x = v1.x - v2.x;
423
- const y = v1.y - v2.y;
424
- const z = v1.z - v2.z;
226
+ // mark all triangle pairs that belong to a quad with only one
227
+ // good triangle. These need special treatment in DegenEpilogue().
228
+ // Additionally, move all good triangles to the start of
229
+ // pTriInfos[] and piTriListIn[] without changing order and
230
+ // put the degenerate triangles last.
231
+ DegenPrologue(pTriInfos, piTriListIn, iNrTrianglesIn, iTotTris);
425
232
 
426
- res.x = x;
427
- res.y = y;
428
- res.z = z;
429
233
 
430
- }
234
+ // evaluate triangle level attributes and neighbor list
235
+ //printf("gen neighbors list begin\n");
236
+ InitTriInfo(pTriInfos, piTriListIn, pContext, iNrTrianglesIn);
237
+ //printf("gen neighbors list end\n");
431
238
 
432
- /**
433
- *
434
- * @param {number} fS
435
- * @param {SVec3} v
436
- * @returns {SVec3}
437
- */
438
- function vscale(fS, v) {
439
- const res = new SVec3();
440
239
 
441
- res.x = v.x * fS;
442
- res.y = v.y * fS;
443
- res.z = v.z * fS;
240
+ // based on the 4 rules, identify groups based on connectivity
241
+ iNrMaxGroups = iNrTrianglesIn * 3;
242
+ const pGroups = malloc(SGroup, iNrMaxGroups);
243
+ const piGroupTrianglesBuffer = new Int32Array(iNrTrianglesIn * 3);
444
244
 
445
- return res;
446
- }
245
+ //printf("gen 4rule groups begin\n");
246
+ iNrActiveGroups = Build4RuleGroups(
247
+ pTriInfos,
248
+ pGroups,
249
+ piGroupTrianglesBuffer,
250
+ piTriListIn,
251
+ iNrTrianglesIn
252
+ );
253
+ //printf("gen 4rule groups end\n");
447
254
 
448
- /**
449
- *
450
- * @param {SVec3} v
451
- * @returns {number}
452
- */
453
- function LengthSquared(v) {
454
- return v.x * v.x + v.y * v.y + v.z * v.z;
455
- }
255
+ //
456
256
 
457
- /**
458
- *
459
- * @param {SVec3} v0
460
- * @param {SVec3} v1
461
- * @returns {number}
462
- */
463
- function DistanceSquared(v0, v1) {
464
- return v3_distance_sqr(v0.x, v0.y, v0.z, v1.x, v1.y, v1.z);
465
- }
257
+ /**
258
+ *
259
+ * @type {STSpace[]}
260
+ */
261
+ const psTspace = malloc(STSpace, iNrTSPaces);
466
262
 
467
- /**
468
- *
469
- * @param {SVec3} v
470
- * @returns {number}
471
- */
472
- function Length(v) {
473
- return Math.sqrt(LengthSquared(v));
474
- }
263
+ // memset not requried in JS
264
+ // memset(psTspace, 0, sizeof(STSpace)*iNrTSPaces);
475
265
 
266
+ for (let t = 0; t < iNrTSPaces; t++) {
267
+ const stSpace = psTspace[t];
476
268
 
477
- /**
478
- *
479
- * @param {SVec3} out
480
- * @param {SVec3} input
481
- */
482
- function NormalizeSafe(out, input) {
269
+ stSpace.vOs[0] = 1.0;
270
+ stSpace.vOs[1] = 0.0;
271
+ stSpace.vOs[2] = 0.0;
483
272
 
484
- const len = Length(input);
273
+ stSpace.fMagS = 1.0;
485
274
 
486
- if (len !== 0) {
487
- const m = 1 / len;
275
+ stSpace.vOt[0] = 0.0;
276
+ stSpace.vOt[1] = 1.0;
277
+ stSpace.vOt[2] = 0.0;
488
278
 
489
- out.x = input.x * m;
490
- out.y = input.y * m;
491
- out.z = input.z * m;
492
- } else {
493
- out.copy(input);
279
+ stSpace.fMagT = 1.0;
494
280
  }
495
- }
496
-
497
- /**
498
- *
499
- * @param {SVec3} v1
500
- * @param {SVec3} v2
501
- * @returns {number}
502
- */
503
- function vdot(v1, v2) {
504
- return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z;
505
- }
506
-
507
- /**
508
- *
509
- * @param {number} v
510
- * @returns {boolean}
511
- */
512
- function NotZero(v) {
513
- return Math.abs(v) > EPSILON;
514
- }
515
-
516
- /**
517
- *
518
- * @param {SVec3} v
519
- * @returns {boolean}
520
- */
521
- function VNotZero(v) {
522
- return NotZero(v.x) || NotZero(v.y) || NotZero(v.z);
523
- }
524
281
 
525
- /**
526
- *
527
- * @param {number} iFace
528
- * @param {number} iVert
529
- * @returns {number}
530
- */
531
- function MakeIndex(iFace, iVert) {
532
- return (iFace << 2) | (iVert & 0x3);
533
- }
282
+ // make tspaces, each group is split up into subgroups if necessary
283
+ // based on fAngularThreshold. Finally, a tangent space is made for
284
+ // every resulting subgroup
285
+ //printf("gen tspaces begin\n");
286
+ GenerateTSpaces(
287
+ psTspace,
288
+ pTriInfos,
289
+ pGroups,
290
+ iNrActiveGroups,
291
+ piTriListIn,
292
+ fThresCos,
293
+ pContext
294
+ );
295
+ //printf("gen tspaces end\n");
534
296
 
535
- /**
536
- *
537
- * @param {STSpace} ts_res
538
- * @param {STSpace} pTS0
539
- * @param {STSpace} pTS1
540
- * @returns {void}
541
- */
542
- function AvgTSpace(ts_res, pTS0, pTS1) {
543
297
 
544
- // this if is important. Due to floating point precision
545
- // averaging when ts0==ts1 will cause a slight difference
546
- // which results in tangent space splits later on
547
- if (
548
- pTS0.fMagS === pTS1.fMagS && pTS0.fMagT === pTS1.fMagT &&
549
- veq(pTS0.vOs, pTS1.vOs) && veq(pTS0.vOt, pTS1.vOt)
550
- ) {
298
+ // degenerate quads with one good triangle will be fixed by copying a space from
299
+ // the good triangle to the coinciding vertex.
300
+ // all other degenerate triangles will just copy a space from any good triangle
301
+ // with the same welded index in piTriListIn[].
302
+ DegenEpilogue(
303
+ psTspace,
304
+ pTriInfos,
305
+ piTriListIn,
306
+ pContext,
307
+ iNrTrianglesIn,
308
+ iTotTris
309
+ );
551
310
 
552
- ts_res.fMagS = pTS0.fMagS;
553
- ts_res.fMagT = pTS0.fMagT;
554
- ts_res.vOs = pTS0.vOs;
555
- ts_res.vOt = pTS0.vOt;
556
311
 
557
- } else {
558
- ts_res.fMagS = 0.5 * (pTS0.fMagS + pTS1.fMagS);
559
- ts_res.fMagT = 0.5 * (pTS0.fMagT + pTS1.fMagT);
312
+ for (let f = 0; f < iNrFaces; f++) {
560
313
 
561
- vadd(ts_res.vOs, pTS0.vOs, pTS1.vOs);
562
- vadd(ts_res.vOt, pTS0.vOt, pTS1.vOt);
314
+ // set data
315
+ for (let i = 0; i < 3; i++) {
316
+ const index = f * 3 + i
317
+ const pTSpace = psTspace[index];
563
318
 
564
- NormalizeSafe(ts_res.vOs, ts_res.vOs);
565
- NormalizeSafe(ts_res.vOt, ts_res.vOt);
319
+ const tangent = pTSpace.vOs;
320
+ const bitangent = pTSpace.vOt;
321
+
322
+ m_setTSpace(
323
+ pContext,
324
+ tangent,
325
+ bitangent,
326
+ pTSpace.fMagS,
327
+ pTSpace.fMagT,
328
+ pTSpace.bOrient,
329
+ f,
330
+ i
331
+ );
332
+ }
566
333
  }
567
334
 
568
- }
569
-
570
-
571
- /**
572
- *
573
- * @param {SMikkTSpaceContext} pContext
574
- * @param {number} index
575
- * @returns {SVec3}
576
- */
577
- function GetPosition(pContext, index) {
578
- const res = new SVec3();
579
-
580
- // inlined : IndexToData(&iF, &iI, index);
581
- const iF = index & 0x3;
582
- const iI = index >> 2;
583
-
584
- m_getPosition(pContext, res, iF, iI);
585
-
586
- return res;
587
- }
588
-
589
- /**
590
- *
591
- * @param {SMikkTSpaceContext} pContext
592
- * @param {number} index
593
- * @returns {SVec3}
594
- */
595
- function GetNormal(pContext, index) {
596
- const res = new SVec3();
597
335
 
598
- // inlined : IndexToData(&iF, &iI, index);
599
- const iF = index & 0x3;
600
- const iI = index >> 2;
601
-
602
- m_getNormal(pContext, res, iF, iI);
603
-
604
- return res;
605
- }
606
-
607
- /**
608
- * TODO doesn't need to be V3, can save memory
609
- * @param {SVec3} res
610
- * @param {SMikkTSpaceContext} pContext
611
- * @param {number} index
612
- * @returns {SVec3}
613
- */
614
- function GetTexCoord(res, pContext, index) {
615
-
616
- // inlined : IndexToData(&iF, &iI, index);
617
- const iF = index & 0x3;
618
- const iI = index >> 2;
619
-
620
- m_getTexCoord(pContext, res, iF, iI);
621
-
622
- res.z = 1;
623
-
624
- return res;
336
+ return true;
625
337
  }
626
338
 
627
- /**
628
- *
629
- * @param {SEdge[]} pSortBuffer
630
- * @param {number} iLeft
631
- * @param {number} iRight
632
- * @param {number} channel
633
- * @param {number} uSeed
634
- * @returns {void}
635
- */
636
- function QuickSortEdges(pSortBuffer, iLeft, iRight, channel, uSeed) {
637
- let t;
638
- let iL, iR, n, index, iMid;
639
339
 
640
- // early out
641
- let sTmp;
642
- const iElems = iRight - iLeft + 1;
643
- if (iElems < 2) return;
644
- else if (iElems === 2) {
645
- if (pSortBuffer[iLeft][channel] > pSortBuffer[iRight][channel]) {
646
- sTmp = pSortBuffer[iLeft];
647
- pSortBuffer[iLeft] = pSortBuffer[iRight];
648
- pSortBuffer[iRight] = sTmp;
649
- }
650
- return;
651
- }
652
-
653
- // Random
654
- t = uSeed & 31;
655
- t = (uSeed << t) | (uSeed >> (32 - t));
656
- uSeed = uSeed + t + 3;
657
-
658
- uSeed = uSeed >>> 0; // make unsigned
659
- // Random end
660
-
661
- iL = iLeft;
662
- iR = iRight;
663
- n = (iR - iL) + 1;
664
- assert(n >= 0);
665
- index = (uSeed % n);
666
-
667
- iMid = pSortBuffer[index + iL][channel];
668
-
669
- do {
670
- while (pSortBuffer[iL][channel] < iMid)
671
- ++iL;
672
- while (pSortBuffer[iR][channel] > iMid)
673
- --iR;
674
-
675
- if (iL <= iR) {
676
- sTmp = pSortBuffer[iL];
677
- pSortBuffer[iL] = pSortBuffer[iR];
678
- pSortBuffer[iR] = sTmp;
679
- ++iL;
680
- --iR;
681
- }
682
- }
683
- while (iL <= iR);
684
-
685
- if (iLeft < iR)
686
- QuickSortEdges(pSortBuffer, iLeft, iR, channel, uSeed);
687
- if (iL < iRight)
688
- QuickSortEdges(pSortBuffer, iL, iRight, channel, uSeed);
689
- }
690
-
691
- /**
692
- * resolve ordering and edge number
693
- * @param {SEdge} out
694
- * @param {number[]} indices
695
- * @param {number} indices_offset
696
- * @param {number} i0_in
697
- * @param {number} i1_in
698
- * @returns {void}
699
- */
700
- function GetEdge(out, indices, indices_offset, i0_in, i1_in) {
701
-
702
- // test if first index is on the edge
703
- const index_0 = indices[indices_offset + 0];
704
- const index_1 = indices[indices_offset + 1];
705
- const index_2 = indices[indices_offset + 2];
706
-
707
- if (index_0 === i0_in || index_0 === i1_in) {
708
- // test if second index is on the edge
709
- if (index_1 === i0_in || index_1 === i1_in) {
710
- out.f = 0; // first edge
711
- out.i0 = index_0;
712
- out.i1 = index_1;
713
- } else {
714
- out.f = 2; // third edge
715
- out.i0 = index_2;
716
- out.i1 = index_0;
717
- }
718
- } else {
719
- // only second and third index is on the edge
720
- out.f = 1; // second edge
721
- out.i0 = index_1;
722
- out.i1 = index_2;
723
- }
724
- }
725
-
726
- /**
727
- * returns the texture area times 2
728
- * @param {SMikkTSpaceContext } pContext
729
- * @param {number[]} indices
730
- * @param {number} indices_offset
731
- * @returns {number}
732
- */
733
- function CalcTexArea(pContext, indices, indices_offset) {
734
-
735
- const t1 = new SVec3();
736
- const t2 = new SVec3();
737
- const t3 = new SVec3();
738
-
739
- GetTexCoord(t1, pContext, indices[indices_offset + 0]);
740
- GetTexCoord(t2, pContext, indices[indices_offset + 1]);
741
- GetTexCoord(t3, pContext, indices[indices_offset + 2]);
742
-
743
- const t21x = t2.x - t1.x;
744
- const t21y = t2.y - t1.y;
745
- const t31x = t3.x - t1.x;
746
- const t31y = t3.y - t1.y;
747
-
748
- const fSignedAreaSTx2 = t21x * t31y - t21y * t31x;
749
-
750
- return fSignedAreaSTx2 < 0 ? (-fSignedAreaSTx2) : fSignedAreaSTx2;
751
- }
752
-
753
- /**
754
- * Mimics C malloc somewhat, allocates an array of class instances of fixed size
755
- * @template T
756
- * @param {constructor<T>} Klass
757
- * @param {number} count
758
- * @returns {T[]}
759
- */
760
- function malloc(Klass, count) {
761
- const r = new Array(count);
762
-
763
- for (let i = 0; i < count; i++) {
764
- r[i] = new Klass();
765
- }
766
-
767
- return r;
768
- }
769
-
770
- /**
771
- *
772
- * @param {number[]} face_indices
773
- * @param {number} iFaces
774
- * @param {number[]} piTriListIn
775
- * @param {STriInfo[]} pTriInfos
776
- * @param {SMikkTSpaceContext} pContext
777
- * @param {number} iVertexRepresentitive
778
- * @returns {STSpace}
779
- */
780
- function EvalTspace(face_indices, iFaces, piTriListIn, pTriInfos, pContext, iVertexRepresentitive) {
781
- const res = new STSpace();
782
-
783
- let fAngleSum = 0;
784
- let face = 0;
785
-
786
- res.vOs.x = 0.0;
787
- res.vOs.y = 0.0;
788
- res.vOs.z = 0.0;
789
-
790
- res.vOt.x = 0.0;
791
- res.vOt.y = 0.0;
792
- res.vOt.z = 0.0;
793
-
794
- res.fMagS = 0;
795
- res.fMagT = 0;
796
-
797
- const vOs = new SVec3();
798
- const vOt = new SVec3();
799
-
800
- for (face = 0; face < iFaces; face++) {
801
- const f = face_indices[face];
802
- const tri_info = pTriInfos[f];
803
-
804
- // only valid triangles get to add their contribution
805
- if ((tri_info.iFlag & GROUP_WITH_ANY) === 0) {
806
- /**
807
- * @type {SVec3}
808
- */
809
- let n, p0, p1, p2, v1, v2;
810
- /**
811
- * @type {number}
812
- */
813
- let fCos, fAngle, fMagS, fMagT;
814
- let i = -1, index = -1, i0 = -1, i1 = -1, i2 = -1;
815
-
816
- const f3 = 3 * f;
817
-
818
- if (piTriListIn[f3] === iVertexRepresentitive) {
819
- i = 0;
820
- } else if (piTriListIn[f3 + 1] === iVertexRepresentitive) {
821
- i = 1;
822
- } else if (piTriListIn[f3 + 2] === iVertexRepresentitive) {
823
- i = 2;
824
- }
825
-
826
- assert(i >= 0 && i < 3);
827
-
828
- // project
829
- index = piTriListIn[f3 + i];
830
- n = GetNormal(pContext, index);
831
-
832
- vsub(vOs, tri_info.vOs, vscale(vdot(n, tri_info.vOs), n));
833
-
834
- vsub(vOt, tri_info.vOt, vscale(vdot(n, tri_info.vOt), n));
835
-
836
- NormalizeSafe(vOs, vOs);
837
- NormalizeSafe(vOt, vOt);
838
-
839
- i2 = piTriListIn[f3 + (i < 2 ? (i + 1) : 0)];
840
- i1 = piTriListIn[f3 + i];
841
- i0 = piTriListIn[f3 + (i > 0 ? (i - 1) : 2)];
842
-
843
- p0 = GetPosition(pContext, i0);
844
- p1 = GetPosition(pContext, i1);
845
- p2 = GetPosition(pContext, i2);
846
-
847
- v1 = new SVec3();
848
- vsub(v1, p0, p1);
849
- v2 = new SVec3();
850
- vsub(v2, p2, p1);
851
-
852
- // project
853
- vsub(v1, v1, vscale(vdot(n, v1), n));
854
- NormalizeSafe(v1, v1);
855
-
856
- vsub(v2, v2, vscale(vdot(n, v2), n));
857
- NormalizeSafe(v2, v2);
858
-
859
- // weight contribution by the angle
860
- // between the two edge vectors
861
- fCos = vdot(v1, v2);
862
- fCos = fCos > 1 ? 1 : (fCos < (-1) ? (-1) : fCos);
863
- fAngle = Math.acos(fCos);
864
- fMagS = tri_info.fMagS;
865
- fMagT = tri_info.fMagT;
866
-
867
- vadd(res.vOs, res.vOs, vscale(fAngle, vOs));
868
- vadd(res.vOt, res.vOt, vscale(fAngle, vOt));
869
-
870
- res.fMagS += (fAngle * fMagS);
871
- res.fMagT += (fAngle * fMagT);
872
- fAngleSum += fAngle;
873
- }
874
- }
875
-
876
- // normalize
877
- NormalizeSafe(res.vOs, res.vOs);
878
- NormalizeSafe(res.vOt, res.vOt);
879
-
880
- if (fAngleSum > 0) {
881
- res.fMagS /= fAngleSum;
882
- res.fMagT /= fAngleSum;
883
- }
884
-
885
- return res;
886
- }
887
-
888
- /**
889
- *
890
- * @param {SSubGroup} pg1
891
- * @param {SSubGroup } pg2
892
- * @returns {boolean}
893
- */
894
- function CompareSubGroups(pg1, pg2) {
895
- let bStillSame = true;
896
- let i = 0;
897
-
898
- if (pg1.iNrFaces !== pg2.iNrFaces) {
899
- return false;
900
- }
901
-
902
- while (i < pg1.iNrFaces && bStillSame) {
903
-
904
- bStillSame = pg1.pTriMembers[i] === pg2.pTriMembers[i];
905
-
906
- if (bStillSame) {
907
- ++i;
908
- }
909
-
910
- }
911
-
912
- return bStillSame;
913
- }
914
-
915
-
916
- /**
917
- *
918
- * @param {number[]|ArrayLike<number>|Uint32Array|Int32Array} pSortBuffer
919
- * @param {number} iLeft
920
- * @param {number} iRight
921
- * @param {number} uSeed
922
- * @returns {void}
923
- */
924
- function QuickSort(pSortBuffer, iLeft, iRight, uSeed) {
925
- let iL, iR, n, index, iMid, iTmp;
926
-
927
- // Random
928
- let t = uSeed & 31;
929
- t = (uSeed << t) | (uSeed >> (32 - t));
930
- uSeed = uSeed + t + 3;
931
- uSeed = uSeed >>> 0; // make unsigned
932
- // Random end
933
-
934
- iL = iLeft;
935
- iR = iRight;
936
- n = (iR - iL) + 1;
937
- assert(n >= 0);
938
- index = (uSeed % n);
939
-
940
- iMid = pSortBuffer[index + iL];
941
-
942
-
943
- do {
944
- while (pSortBuffer[iL] < iMid)
945
- ++iL;
946
- while (pSortBuffer[iR] > iMid)
947
- --iR;
948
-
949
- if (iL <= iR) {
950
- iTmp = pSortBuffer[iL];
951
- pSortBuffer[iL] = pSortBuffer[iR];
952
- pSortBuffer[iR] = iTmp;
953
- ++iL;
954
- --iR;
955
- }
956
- }
957
- while (iL <= iR);
958
-
959
- if (iLeft < iR)
960
- QuickSort(pSortBuffer, iLeft, iR, uSeed);
961
- if (iL < iRight)
962
- QuickSort(pSortBuffer, iL, iRight, uSeed);
963
- }
964
-
965
- /**
966
- *
967
- * @param {STriInfo[]} pTriInfos
968
- * @param {SEdge[]} pEdges
969
- * @param {number[]} piTriListIn
970
- * @param {number} iNrTrianglesIn
971
- * @returns {void}
972
- */
973
- function BuildNeighborsFast(pTriInfos, pEdges, piTriListIn, iNrTrianglesIn) {
974
- // build array of edges
975
- const uSeed = INTERNAL_RND_SORT_SEED; // could replace with a random seed?
976
- let iEntries = 0, iCurStartIndex = -1, f = 0, i = 0;
977
-
978
- for (f = 0; f < iNrTrianglesIn; f++)
979
-
980
- for (i = 0; i < 3; i++) {
981
- const i0 = piTriListIn[f * 3 + i];
982
- const i1 = piTriListIn[f * 3 + (i < 2 ? (i + 1) : 0)];
983
-
984
- const edge = pEdges[f * 3 + i];
985
-
986
- edge.i0 = i0 < i1 ? i0 : i1; // put minimum index in i0
987
- edge.i1 = !(i0 < i1) ? i0 : i1; // put maximum index in i1
988
- edge.f = f; // record face number
989
- }
990
-
991
- // sort over all edges by i0, this is the pricy one.
992
- QuickSortEdges(pEdges, 0, iNrTrianglesIn * 3 - 1, 0, uSeed); // sort channel 0 which is i0
993
-
994
- // sub sort over i1, should be fast.
995
- // could replace this with a 64 bit int sort over (i0,i1)
996
- // with i0 as msb in the quicksort call above.
997
- iEntries = iNrTrianglesIn * 3;
998
- iCurStartIndex = 0;
999
-
1000
- for (i = 1; i < iEntries; i++) {
1001
- if (pEdges[iCurStartIndex].i0 !== pEdges[i].i0) {
1002
- const iL = iCurStartIndex;
1003
- const iR = i - 1;
1004
- //const int iElems = i-iL;
1005
- iCurStartIndex = i;
1006
- QuickSortEdges(pEdges, iL, iR, 1, uSeed); // sort channel 1 which is i1
1007
- }
1008
- }
1009
-
1010
- // sub sort over f, which should be fast.
1011
- // this step is to remain compliant with BuildNeighborsSlow() when
1012
- // more than 2 triangles use the same edge (such as a butterfly topology).
1013
- iCurStartIndex = 0;
1014
- for (i = 1; i < iEntries; i++) {
1015
- if (pEdges[iCurStartIndex].i0 !== pEdges[i].i0 || pEdges[iCurStartIndex].i1 !== pEdges[i].i1) {
1016
- const iL = iCurStartIndex;
1017
- const iR = i - 1;
1018
- //const int iElems = i-iL;
1019
- iCurStartIndex = i;
1020
- QuickSortEdges(pEdges, iL, iR, 2, uSeed); // sort channel 2 which is f
1021
- }
1022
- }
1023
-
1024
- const temp_edge_A = new SEdge();
1025
- const temp_edge_B = new SEdge();
1026
-
1027
- // pair up, adjacent triangles
1028
- for (i = 0; i < iEntries; i++) {
1029
- const i0 = pEdges[i].i0;
1030
- const i1 = pEdges[i].i1;
1031
- const f = pEdges[i].f;
1032
- let bUnassigned_A = false;
1033
-
1034
- let i0_A, i1_A;
1035
- let edgenum_A, edgenum_B = 0; // 0,1 or 2
1036
-
1037
- GetEdge(temp_edge_A, piTriListIn, f * 3, i0, i1); // resolve index ordering and edge_num
1038
- i0_A = temp_edge_A.i0;
1039
- i1_A = temp_edge_A.i1;
1040
- edgenum_A = temp_edge_A.f;
1041
-
1042
- bUnassigned_A = pTriInfos[f].FaceNeighbors[edgenum_A] === -1;
1043
-
1044
- if (bUnassigned_A) {
1045
- // get true index ordering
1046
- let j = i + 1, t;
1047
- let bNotFound = true;
1048
- while (j < iEntries && i0 === pEdges[j].i0 && i1 === pEdges[j].i1 && bNotFound) {
1049
- let bUnassigned_B = false;
1050
- let i0_B, i1_B;
1051
- t = pEdges[j].f;
1052
- // flip i0_B and i1_B
1053
- GetEdge(temp_edge_B, piTriListIn, t * 3, pEdges[j].i0, pEdges[j].i1); // resolve index ordering and edge_num
1054
-
1055
- i0_B = temp_edge_B.i0;
1056
- i1_B = temp_edge_B.i1;
1057
- edgenum_B = temp_edge_B.f;
1058
-
1059
- //assert(!(i0_A==i1_B && i1_A==i0_B));
1060
- bUnassigned_B = pTriInfos[t].FaceNeighbors[edgenum_B] === -1;
1061
- if (i0_A === i0_B && i1_A === i1_B && bUnassigned_B)
1062
- bNotFound = false;
1063
- else
1064
- ++j;
1065
- }
1066
-
1067
- if (!bNotFound) {
1068
- let t = pEdges[j].f;
1069
- pTriInfos[f].FaceNeighbors[edgenum_A] = t;
1070
- //assert(pTriInfos[t].FaceNeighbors[edgenum_B]==-1);
1071
- pTriInfos[t].FaceNeighbors[edgenum_B] = f;
1072
- }
1073
- }
1074
- }
1075
- }
1076
-
1077
- /**
1078
- *
1079
- * @param {STriInfo[]} pTriInfos
1080
- * @param {number} piTriListIn
1081
- * @param {number} iNrTrianglesIn
1082
- * @returns {void}
1083
- */
1084
- function BuildNeighborsSlow(pTriInfos, piTriListIn, iNrTrianglesIn) {
1085
- let f = 0, i = 0;
1086
- for (f = 0; f < iNrTrianglesIn; f++) {
1087
- for (i = 0; i < 3; i++) {
1088
- // if unassigned
1089
- if (pTriInfos[f].FaceNeighbors[i] === -1) {
1090
- const i0_A = piTriListIn[f * 3 + i];
1091
- const i1_A = piTriListIn[f * 3 + (i < 2 ? (i + 1) : 0)];
1092
-
1093
- // search for a neighbor
1094
- let bFound = false;
1095
- let t = 0, j = 0;
1096
- while (!bFound && t < iNrTrianglesIn) {
1097
- if (t !== f) {
1098
- j = 0;
1099
- while (!bFound && j < 3) {
1100
- // in rev order
1101
- const i1_B = piTriListIn[t * 3 + j];
1102
- const i0_B = piTriListIn[t * 3 + (j < 2 ? (j + 1) : 0)];
1103
- //assert(!(i0_A==i1_B && i1_A==i0_B));
1104
- if (i0_A === i0_B && i1_A === i1_B)
1105
- bFound = true;
1106
- else
1107
- ++j;
1108
- }
1109
- }
1110
-
1111
- if (!bFound) ++t;
1112
- }
1113
-
1114
- // assign neighbors
1115
- if (bFound) {
1116
- pTriInfos[f].FaceNeighbors[i] = t;
1117
- //assert(pTriInfos[t].FaceNeighbors[j]==-1);
1118
- pTriInfos[t].FaceNeighbors[j] = f;
1119
- }
1120
- }
1121
- }
1122
- }
1123
- }
1124
-
1125
- /**
1126
- *
1127
- * @param {STriInfo[]} pTriInfos
1128
- * @param {number[]|Int32Array} piTriListIn
1129
- * @param {SMikkTSpaceContext} pContext
1130
- * @param {number} iNrTrianglesIn
1131
- * @returns {void}
1132
- */
1133
- function InitTriInfo(pTriInfos, piTriListIn, pContext, iNrTrianglesIn) {
1134
- let f = 0, i = 0, t = 0;
1135
- // pTriInfos[f].iFlag is cleared in GenerateInitialVerticesIndexList() which is called before this function.
1136
-
1137
- // generate neighbor info list
1138
- for (f = 0; f < iNrTrianglesIn; f++) {
1139
- for (i = 0; i < 3; i++) {
1140
- const tri = pTriInfos[f];
1141
-
1142
- tri.FaceNeighbors[i] = -1;
1143
- tri.AssignedGroup[i] = null;
1144
-
1145
- tri.vOs.x = 0.0;
1146
- tri.vOs.y = 0.0;
1147
- tri.vOs.z = 0.0;
1148
-
1149
- tri.vOt.x = 0.0;
1150
- tri.vOt.y = 0.0;
1151
- tri.vOt.z = 0.0;
1152
-
1153
- tri.fMagS = 0;
1154
- tri.fMagT = 0;
1155
-
1156
- // assumed bad
1157
- tri.iFlag |= GROUP_WITH_ANY;
1158
- }
1159
- }
1160
-
1161
- const t1 = new SVec3();
1162
- const t2 = new SVec3();
1163
- const t3 = new SVec3();
1164
-
1165
- const d1 = new SVec3();
1166
- const d2 = new SVec3();
1167
-
1168
- const vOs = new SVec3();
1169
- const vOt = new SVec3();
1170
-
1171
- // evaluate first order derivatives
1172
- for (f = 0; f < iNrTrianglesIn; f++) {
1173
- // initial values
1174
- const f3 = f * 3;
1175
-
1176
- const v1 = GetPosition(pContext, piTriListIn[f3 + 0]);
1177
- const v2 = GetPosition(pContext, piTriListIn[f3 + 1]);
1178
- const v3 = GetPosition(pContext, piTriListIn[f3 + 2]);
1179
-
1180
- GetTexCoord(t1, pContext, piTriListIn[f3 + 0]);
1181
- GetTexCoord(t2, pContext, piTriListIn[f3 + 1]);
1182
- GetTexCoord(t3, pContext, piTriListIn[f3 + 2]);
1183
-
1184
- const t21x = t2.x - t1.x;
1185
- const t21y = t2.y - t1.y;
1186
-
1187
- const t31x = t3.x - t1.x;
1188
- const t31y = t3.y - t1.y;
1189
-
1190
- vsub(d1, v2, v1);
1191
- vsub(d2, v3, v1);
1192
-
1193
- const fSignedAreaSTx2 = t21x * t31y - t21y * t31x;
1194
- //assert(fSignedAreaSTx2!=0);
1195
- vsub(vOs, vscale(t31y, d1), vscale(t21y, d2)); // eq 18
1196
-
1197
- vadd(vOt, vscale(-t31x, d1), vscale(t21x, d2)); // eq 19
1198
-
1199
- pTriInfos[f].iFlag |= (fSignedAreaSTx2 > 0 ? ORIENT_PRESERVING : 0);
1200
-
1201
- if (NotZero(fSignedAreaSTx2)) {
1202
- const fAbsArea = fabsf(fSignedAreaSTx2);
1203
- const fLenOs = Length(vOs);
1204
- const fLenOt = Length(vOt);
1205
- const fS = (pTriInfos[f].iFlag & ORIENT_PRESERVING) === 0 ? (-1.0) : 1.0;
1206
- if (NotZero(fLenOs)) {
1207
- pTriInfos[f].vOs = vscale(fS / fLenOs, vOs);
1208
- }
1209
- if (NotZero(fLenOt)) {
1210
- pTriInfos[f].vOt = vscale(fS / fLenOt, vOt);
1211
- }
1212
-
1213
- // evaluate magnitudes prior to normalization of vOs and vOt
1214
- pTriInfos[f].fMagS = fLenOs / fAbsArea;
1215
- pTriInfos[f].fMagT = fLenOt / fAbsArea;
1216
-
1217
- // if this is a good triangle
1218
- if (NotZero(pTriInfos[f].fMagS) && NotZero(pTriInfos[f].fMagT)) {
1219
- pTriInfos[f].iFlag &= (~GROUP_WITH_ANY);
1220
- }
1221
- }
1222
- }
1223
-
1224
- // force otherwise healthy quads to a fixed orientation
1225
- while (t < (iNrTrianglesIn - 1)) {
1226
- const iFO_a = pTriInfos[t].iOrgFaceNumber;
1227
- const iFO_b = pTriInfos[t + 1].iOrgFaceNumber;
1228
- if (iFO_a === iFO_b) // this is a quad
1229
- {
1230
- const bIsDeg_a = (pTriInfos[t].iFlag & MARK_DEGENERATE) !== 0;
1231
- const bIsDeg_b = (pTriInfos[t + 1].iFlag & MARK_DEGENERATE) !== 0;
1232
-
1233
- // bad triangles should already have been removed by
1234
- // DegenPrologue(), but just in case check bIsDeg_a and bIsDeg_a are false
1235
- if ((bIsDeg_a || bIsDeg_b) === false) {
1236
- const bOrientA = (pTriInfos[t].iFlag & ORIENT_PRESERVING) !== 0;
1237
- const bOrientB = (pTriInfos[t + 1].iFlag & ORIENT_PRESERVING) !== 0;
1238
- // if this happens the quad has extremely bad mapping!!
1239
- if (bOrientA !== bOrientB) {
1240
- //printf("found quad with bad mapping\n");
1241
- let bChooseOrientFirstTri = false;
1242
- if ((pTriInfos[t + 1].iFlag & GROUP_WITH_ANY) !== 0) bChooseOrientFirstTri = true;
1243
- else if (CalcTexArea(pContext, piTriListIn, t * 3) >= CalcTexArea(pContext, piTriListIn, (t + 1) * 3 + 0))
1244
- bChooseOrientFirstTri = true;
1245
-
1246
- // force match
1247
- {
1248
- const t0 = bChooseOrientFirstTri ? t : (t + 1);
1249
- const t1 = bChooseOrientFirstTri ? (t + 1) : t;
1250
- pTriInfos[t1].iFlag &= (~ORIENT_PRESERVING); // clear first
1251
- pTriInfos[t1].iFlag |= (pTriInfos[t0].iFlag & ORIENT_PRESERVING); // copy bit
1252
- }
1253
- }
1254
- }
1255
- t += 2;
1256
- } else
1257
- ++t;
1258
- }
1259
-
1260
- // match up edge pairs
1261
- {
1262
- const pEdges = malloc(SEdge, iNrTrianglesIn * 3);
1263
- BuildNeighborsFast(pTriInfos, pEdges, piTriListIn, iNrTrianglesIn);
1264
- }
1265
- }
1266
-
1267
-
1268
- /**
1269
- *
1270
- * @param {SMikkTSpaceContext} pContext
1271
- * @param {number} fAngularThreshold
1272
- * @return {boolean}
1273
- */
1274
- export function genTangSpace(pContext, fAngularThreshold = 180) {
1275
- // count nr_triangles
1276
- let iNrTrianglesIn = 0, f = 0, t = 0, i = 0;
1277
- let iNrTSPaces = 0, iTotTris = 0, iDegenTriangles = 0, iNrMaxGroups = 0;
1278
- let iNrActiveGroups = 0, index = 0;
1279
- const iNrFaces = m_getNumFaces(pContext);
1280
- let bRes = false;
1281
- const fThresCos = Math.cos((fAngularThreshold * Math.PI) / 180.0);
1282
-
1283
-
1284
- // count triangles on supported faces
1285
- for (f = 0; f < iNrFaces; f++) {
1286
- const verts = m_getNumVerticesOfFace(pContext, f);
1287
- if (verts === 3) ++iNrTrianglesIn;
1288
- else if (verts === 4) iNrTrianglesIn += 2;
1289
- }
1290
- if (iNrTrianglesIn <= 0) return false;
1291
-
1292
- // allocate memory for an index list
1293
- const piTriListIn = new Int32Array(3 * iNrTrianglesIn);
1294
- const pTriInfos = malloc(STriInfo, iNrTrianglesIn);
1295
-
1296
- // make an initial triangle -. face index list
1297
- iNrTSPaces = GenerateInitialVerticesIndexList(pTriInfos, piTriListIn, pContext, iNrTrianglesIn);
1298
-
1299
- // make a welded index list of identical positions and attributes (pos, norm, texc)
1300
- //printf("gen welded index list begin\n");
1301
- GenerateSharedVerticesIndexList(piTriListIn, pContext, iNrTrianglesIn);
1302
- //printf("gen welded index list end\n");
1303
-
1304
- // Mark all degenerate triangles
1305
- iTotTris = iNrTrianglesIn;
1306
- iDegenTriangles = 0;
1307
- for (t = 0; t < iTotTris; t++) {
1308
- const i0 = piTriListIn[t * 3 + 0];
1309
- const i1 = piTriListIn[t * 3 + 1];
1310
- const i2 = piTriListIn[t * 3 + 2];
1311
- const p0 = GetPosition(pContext, i0);
1312
- const p1 = GetPosition(pContext, i1);
1313
- const p2 = GetPosition(pContext, i2);
1314
- if (veq(p0, p1) || veq(p0, p2) || veq(p1, p2)) // degenerate
1315
- {
1316
- pTriInfos[t].iFlag |= MARK_DEGENERATE;
1317
- ++iDegenTriangles;
1318
- }
1319
- }
1320
- iNrTrianglesIn = iTotTris - iDegenTriangles;
1321
-
1322
- // mark all triangle pairs that belong to a quad with only one
1323
- // good triangle. These need special treatment in DegenEpilogue().
1324
- // Additionally, move all good triangles to the start of
1325
- // pTriInfos[] and piTriListIn[] without changing order and
1326
- // put the degenerate triangles last.
1327
- DegenPrologue(pTriInfos, piTriListIn, iNrTrianglesIn, iTotTris);
1328
-
1329
-
1330
- // evaluate triangle level attributes and neighbor list
1331
- //printf("gen neighbors list begin\n");
1332
- InitTriInfo(pTriInfos, piTriListIn, pContext, iNrTrianglesIn);
1333
- //printf("gen neighbors list end\n");
1334
-
1335
-
1336
- // based on the 4 rules, identify groups based on connectivity
1337
- iNrMaxGroups = iNrTrianglesIn * 3;
1338
- const pGroups = malloc(SGroup, iNrMaxGroups);
1339
- const piGroupTrianglesBuffer = new Int32Array(iNrTrianglesIn * 3);
1340
-
1341
- //printf("gen 4rule groups begin\n");
1342
- iNrActiveGroups =
1343
- Build4RuleGroups(pTriInfos, pGroups, piGroupTrianglesBuffer, piTriListIn, iNrTrianglesIn);
1344
- //printf("gen 4rule groups end\n");
1345
-
1346
- //
1347
-
1348
- /**
1349
- *
1350
- * @type {STSpace[]}
1351
- */
1352
- const psTspace = malloc(STSpace, iNrTSPaces);
1353
-
1354
- // memset not requried in JS
1355
- // memset(psTspace, 0, sizeof(STSpace)*iNrTSPaces);
1356
-
1357
- for (t = 0; t < iNrTSPaces; t++) {
1358
- psTspace[t].vOs.x = 1.0;
1359
- psTspace[t].vOs.y = 0.0;
1360
- psTspace[t].vOs.z = 0.0;
1361
- psTspace[t].fMagS = 1.0;
1362
- psTspace[t].vOt.x = 0.0;
1363
- psTspace[t].vOt.y = 1.0;
1364
- psTspace[t].vOt.z = 0.0;
1365
- psTspace[t].fMagT = 1.0;
1366
- }
1367
-
1368
- // make tspaces, each group is split up into subgroups if necessary
1369
- // based on fAngularThreshold. Finally a tangent space is made for
1370
- // every resulting subgroup
1371
- //printf("gen tspaces begin\n");
1372
- bRes = GenerateTSpaces(psTspace, pTriInfos, pGroups, iNrActiveGroups, piTriListIn, fThresCos, pContext);
1373
- //printf("gen tspaces end\n");
1374
-
1375
- if (!bRes) // if an allocation in GenerateTSpaces() failed
1376
- {
1377
- // clean up and return false
1378
- return false;
1379
- }
1380
-
1381
-
1382
- // degenerate quads with one good triangle will be fixed by copying a space from
1383
- // the good triangle to the coinciding vertex.
1384
- // all other degenerate triangles will just copy a space from any good triangle
1385
- // with the same welded index in piTriListIn[].
1386
- DegenEpilogue(psTspace, pTriInfos, piTriListIn, pContext, iNrTrianglesIn, iTotTris);
1387
-
1388
- index = 0;
1389
- for (f = 0; f < iNrFaces; f++) {
1390
- const verts = m_getNumVerticesOfFace(pContext, f);
1391
- if (verts !== 3 && verts !== 4) continue;
1392
-
1393
-
1394
- // I've decided to let degenerate triangles and group-with-anythings
1395
- // vary between left/right hand coordinate systems at the vertices.
1396
- // All healthy triangles on the other hand are built to always be either or.
1397
-
1398
- /*// force the coordinate system orientation to be uniform for every face.
1399
- // (this is already the case for good triangles but not for
1400
- // degenerate ones and those with bGroupWithAnything==true)
1401
- bool bOrient = psTspace[index].bOrient;
1402
- if (psTspace[index].iCounter == 0) // tspace was not derived from a group
1403
- {
1404
- // look for a space created in GenerateTSpaces() by iCounter>0
1405
- bool bNotFound = true;
1406
- int i=1;
1407
- while (i<verts && bNotFound)
1408
- {
1409
- if (psTspace[index+i].iCounter > 0) bNotFound=false;
1410
- else ++i;
1411
- }
1412
- if (!bNotFound) bOrient = psTspace[index+i].bOrient;
1413
- }*/
1414
-
1415
- // set data
1416
- for (i = 0; i < verts; i++) {
1417
- const pTSpace = psTspace[index];
1418
- const tang = [pTSpace.vOs.x, pTSpace.vOs.y, pTSpace.vOs.z];
1419
- const bitang = [pTSpace.vOt.x, pTSpace.vOt.y, pTSpace.vOt.z];
1420
-
1421
- m_setTSpace(pContext, tang, bitang, pTSpace.fMagS, pTSpace.fMagT, pTSpace.bOrient, f, i);
1422
-
1423
- ++index;
1424
- }
1425
- }
1426
-
1427
-
1428
- return true;
1429
- }
1430
-
1431
- class STmpVert {
1432
- constructor() {
1433
- this.vert = [0, 0, 0];
1434
- this.index = 0;
1435
- }
1436
- }
1437
-
1438
- /**
1439
- *
1440
- * @type {number}
1441
- */
1442
- const g_iCells = 2048;
1443
-
1444
-
1445
- /**
1446
- *
1447
- * @param {number} fMin
1448
- * @param {number} fMax
1449
- * @param {number} fVal
1450
- * @return {number}
1451
- */
1452
- function FindGridCell(fMin, fMax, fVal) {
1453
- const fIndex = g_iCells * ((fVal - fMin) / (fMax - fMin));
1454
- const iIndex = fIndex >> 0;
1455
- return iIndex < g_iCells ? (iIndex >= 0 ? iIndex : 0) : (g_iCells - 1);
1456
- }
1457
-
1458
- /**
1459
- *
1460
- * @param {number[]|Int32Array} piTriList_in_and_out
1461
- * @param {SMikkTSpaceContext} pContext
1462
- * @param {number} iNrTrianglesIn
1463
- * @returns {void}
1464
- */
1465
- function GenerateSharedVerticesIndexList(piTriList_in_and_out, pContext, iNrTrianglesIn) {
1466
-
1467
- // Generate bounding box
1468
- let piHashCount = null, piHashOffsets = null, piHashCount2 = null;
1469
- let pTmpVert = null;
1470
- let i = 0, iChannel = 0, k = 0, e = 0;
1471
- let iMaxCount = 0;
1472
- let vMin = GetPosition(pContext, 0), vMax = vMin, vDim;
1473
- let fMin, fMax;
1474
- for (i = 1; i < (iNrTrianglesIn * 3); i++) {
1475
- const index = piTriList_in_and_out[i];
1476
-
1477
- const vP = GetPosition(pContext, index);
1478
- if (vMin.x > vP.x) vMin.x = vP.x;
1479
- else if (vMax.x < vP.x) vMax.x = vP.x;
1480
- if (vMin.y > vP.y) vMin.y = vP.y;
1481
- else if (vMax.y < vP.y) vMax.y = vP.y;
1482
- if (vMin.z > vP.z) vMin.z = vP.z;
1483
- else if (vMax.z < vP.z) vMax.z = vP.z;
1484
- }
1485
-
1486
- vDim = new SVec3();
1487
-
1488
- vsub(vDim, vMax, vMin);
1489
-
1490
- iChannel = 0;
1491
- fMin = vMin.x;
1492
- fMax = vMax.x;
1493
- if (vDim.y > vDim.x && vDim.y > vDim.z) {
1494
- iChannel = 1;
1495
- fMin = vMin.y;
1496
- fMax = vMax.y;
1497
- } else if (vDim.z > vDim.x) {
1498
- iChannel = 2;
1499
- fMin = vMin.z;
1500
- fMax = vMax.z;
1501
- }
1502
-
1503
- // make allocations
1504
- const piHashTable = new Int32Array(iNrTrianglesIn * 3);
1505
- piHashCount = new Int32Array(g_iCells);
1506
- piHashOffsets = new Int32Array(g_iCells);
1507
- piHashCount2 = new Int32Array(g_iCells);
1508
-
1509
- // memset is not needed in JS, as arrays are initialized with 0 per spec
1510
- // memset(piHashCount, 0, sizeof(int)*g_iCells);
1511
- // memset(piHashCount2, 0, sizeof(int)*g_iCells);
1512
-
1513
- // count amount of elements in each cell unit
1514
- for (i = 0; i < (iNrTrianglesIn * 3); i++) {
1515
- const index = piTriList_in_and_out[i];
1516
- const vP = GetPosition(pContext, index);
1517
- const fVal = iChannel === 0 ? vP.x : (iChannel === 1 ? vP.y : vP.z);
1518
- const iCell = FindGridCell(fMin, fMax, fVal);
1519
- ++piHashCount[iCell];
1520
- }
1521
-
1522
- // evaluate start index of each cell.
1523
- piHashOffsets[0] = 0;
1524
- for (k = 1; k < g_iCells; k++)
1525
- piHashOffsets[k] = piHashOffsets[k - 1] + piHashCount[k - 1];
1526
-
1527
- // insert vertices
1528
- for (i = 0; i < (iNrTrianglesIn * 3); i++) {
1529
- const index = piTriList_in_and_out[i];
1530
- const vP = GetPosition(pContext, index);
1531
- const fVal = iChannel === 0 ? vP.x : (iChannel === 1 ? vP.y : vP.z);
1532
- const iCell = FindGridCell(fMin, fMax, fVal);
1533
-
1534
- assert(piHashCount2[iCell] < piHashCount[iCell]);
1535
- const table_offset = piHashOffsets[iCell] + piHashCount2[iCell];
1536
- piHashTable[table_offset] = i; // vertex i has been inserted.
1537
- ++piHashCount2[iCell];
1538
- }
1539
- for (k = 0; k < g_iCells; k++)
1540
- assert(piHashCount2[k] === piHashCount[k]); // verify the count
1541
-
1542
- // find maximum amount of entries in any hash entry
1543
- iMaxCount = piHashCount[0];
1544
- for (k = 1; k < g_iCells; k++)
1545
- if (iMaxCount < piHashCount[k])
1546
- iMaxCount = piHashCount[k];
1547
- pTmpVert = malloc(STmpVert, iMaxCount);
1548
-
1549
-
1550
- // complete the merge
1551
- for (k = 0; k < g_iCells; k++) {
1552
- // extract table of cell k and amount of entries in it
1553
- const pTable = piHashTable[piHashOffsets[k]];
1554
- const iEntries = piHashCount[k];
1555
- if (iEntries < 2) continue;
1556
-
1557
- for (e = 0; e < iEntries; e++) {
1558
- const i = pTable[e];
1559
- const vP = GetPosition(pContext, piTriList_in_and_out[i]);
1560
- pTmpVert[e].vert[0] = vP.x;
1561
- pTmpVert[e].vert[1] = vP.y;
1562
- pTmpVert[e].vert[2] = vP.z;
1563
- pTmpVert[e].index = i;
1564
- }
1565
- MergeVertsFast(piTriList_in_and_out, pTmpVert, pContext, 0, iEntries - 1);
1566
- }
1567
-
1568
- }
1569
-
1570
- /**
1571
- *
1572
- * @param {number[]} piTriList_in_and_out
1573
- * @param {STmpVert[]} pTmpVert
1574
- * @param {SMikkTSpaceContext} pContext
1575
- * @param {number} iL_in
1576
- * @param {number} iR_in
1577
- * @returns {void}
1578
- */
1579
- function MergeVertsFast(piTriList_in_and_out, pTmpVert, pContext, iL_in, iR_in) {
1580
- // make bbox
1581
- let c = 0, l = 0, channel = 0;
1582
- let fvMin = [], fvMax = [];
1583
- let dx = 0, dy = 0, dz = 0, fSep = 0;
1584
-
1585
- for (c = 0; c < 3; c++) {
1586
- fvMin[c] = pTmpVert[iL_in].vert[c];
1587
- fvMax[c] = fvMin[c];
1588
- }
1589
-
1590
- for (l = (iL_in + 1); l <= iR_in; l++) {
1591
- const temp_vert = pTmpVert[l];
1592
-
1593
- for (c = 0; c < 3; c++) {
1594
- const coordinate = temp_vert.vert[c];
1595
-
1596
- if (fvMin[c] > coordinate) {
1597
- fvMin[c] = coordinate;
1598
- }
1599
-
1600
- if (fvMax[c] < coordinate) {
1601
- fvMax[c] = coordinate;
1602
- }
1603
- }
1604
- }
1605
-
1606
- dx = fvMax[0] - fvMin[0];
1607
- dy = fvMax[1] - fvMin[1];
1608
- dz = fvMax[2] - fvMin[2];
1609
-
1610
- channel = 0;
1611
- if (dy > dx && dy > dz) channel = 1;
1612
- else if (dz > dx) channel = 2;
1613
-
1614
- fSep = 0.5 * (fvMax[channel] + fvMin[channel]);
1615
-
1616
- // stop if all vertices are NaNs
1617
- if (!Number.isFinite(fSep)) {
1618
- return;
1619
- }
1620
-
1621
- // terminate recursion when the separation/average value
1622
- // is no longer strictly between fMin and fMax values.
1623
- if (fSep >= fvMax[channel] || fSep <= fvMin[channel]) {
1624
- // complete the weld
1625
- for (l = iL_in; l <= iR_in; l++) {
1626
- let i = pTmpVert[l].index;
1627
- const index = piTriList_in_and_out[i];
1628
- const vP = GetPosition(pContext, index);
1629
- const vN = GetNormal(pContext, index);
1630
- const vT = new SVec3();
1631
- GetTexCoord(vT, pContext, index);
1632
-
1633
- let bNotFound = true;
1634
- let l2 = iL_in, i2rec = -1;
1635
- while (l2 < l && bNotFound) {
1636
- const i2 = pTmpVert[l2].index;
1637
- const index2 = piTriList_in_and_out[i2];
1638
- const vP2 = GetPosition(pContext, index2);
1639
- const vN2 = GetNormal(pContext, index2);
1640
-
1641
- const vT2 = new SVec3();
1642
- GetTexCoord(vT2, pContext, index2);
1643
-
1644
- i2rec = i2;
1645
-
1646
- //if (vP==vP2 && vN==vN2 && vT==vT2)
1647
- if (
1648
- vP.x === vP2.x && vP.y === vP2.y && vP.z === vP2.z &&
1649
- vN.x === vN2.x && vN.y === vN2.y && vN.z === vN2.z &&
1650
- vT.x === vT2.x && vT.y === vT2.y && vT.z === vT2.z
1651
- ) {
1652
- bNotFound = false;
1653
- } else {
1654
- ++l2;
1655
- }
1656
- }
1657
-
1658
- // merge if previously found
1659
- if (!bNotFound)
1660
- piTriList_in_and_out[i] = piTriList_in_and_out[i2rec];
1661
- }
1662
- } else {
1663
- let iL = iL_in, iR = iR_in;
1664
- assert((iR_in - iL_in) > 0); // at least 2 entries
1665
-
1666
- // separate (by fSep) all points between iL_in and iR_in in pTmpVert[]
1667
- while (iL < iR) {
1668
- let bReadyLeftSwap = false, bReadyRightSwap = false;
1669
- while ((!bReadyLeftSwap) && iL < iR) {
1670
- assert(iL >= iL_in && iL <= iR_in);
1671
- bReadyLeftSwap = !(pTmpVert[iL].vert[channel] < fSep);
1672
- if (!bReadyLeftSwap) ++iL;
1673
- }
1674
- while ((!bReadyRightSwap) && iL < iR) {
1675
- assert(iR >= iL_in && iR <= iR_in);
1676
- bReadyRightSwap = pTmpVert[iR].vert[channel] < fSep;
1677
- if (!bReadyRightSwap) --iR;
1678
- }
1679
- assert((iL < iR) || !(bReadyLeftSwap && bReadyRightSwap));
1680
-
1681
- if (bReadyLeftSwap && bReadyRightSwap) {
1682
- const sTmp = pTmpVert[iL];
1683
- assert(iL < iR);
1684
- pTmpVert[iL] = pTmpVert[iR];
1685
- pTmpVert[iR] = sTmp;
1686
- ++iL;
1687
- --iR;
1688
- }
1689
- }
1690
-
1691
- assert(iL === (iR + 1) || (iL === iR));
1692
- if (iL === iR) {
1693
- const bReadyRightSwap = pTmpVert[iR].vert[channel] < fSep;
1694
- if (bReadyRightSwap) ++iL;
1695
- else --iR;
1696
- }
1697
-
1698
- // only need to weld when there is more than 1 instance of the (x,y,z)
1699
- if (iL_in < iR)
1700
- MergeVertsFast(piTriList_in_and_out, pTmpVert, pContext, iL_in, iR); // weld all left of fSep
1701
- if (iL < iR_in)
1702
- MergeVertsFast(piTriList_in_and_out, pTmpVert, pContext, iL, iR_in); // weld all right of (or equal to) fSep
1703
- }
1704
- }
1705
-
1706
- /**
1707
- *
1708
- * @param {STriInfo[]} pTriInfos
1709
- * @param {number[]|Int32Array} piTriList_out
1710
- * @param {SMikkTSpaceContext } pContext
1711
- * @param {number} iNrTrianglesIn
1712
- * @return {number}
1713
- */
1714
- function GenerateInitialVerticesIndexList(pTriInfos, piTriList_out, pContext, iNrTrianglesIn) {
1715
- let iTSpacesOffs = 0, f = 0, t = 0;
1716
- let iDstTriIndex = 0;
1717
- for (f = 0; f < m_getNumFaces(pContext); f++) {
1718
- const verts = m_getNumVerticesOfFace(pContext, f);
1719
- if (verts !== 3 && verts !== 4) continue;
1720
-
1721
- pTriInfos[iDstTriIndex].iOrgFaceNumber = f;
1722
- pTriInfos[iDstTriIndex].iTSpacesOffs = iTSpacesOffs;
1723
-
1724
- if (verts === 3) {
1725
- const pVerts = pTriInfos[iDstTriIndex].vert_num;
1726
- pVerts[0] = 0;
1727
- pVerts[1] = 1;
1728
- pVerts[2] = 2;
1729
- piTriList_out[iDstTriIndex * 3 + 0] = MakeIndex(f, 0);
1730
- piTriList_out[iDstTriIndex * 3 + 1] = MakeIndex(f, 1);
1731
- piTriList_out[iDstTriIndex * 3 + 2] = MakeIndex(f, 2);
1732
- ++iDstTriIndex; // next
1733
- } else {
1734
- {
1735
- pTriInfos[iDstTriIndex + 1].iOrgFaceNumber = f;
1736
- pTriInfos[iDstTriIndex + 1].iTSpacesOffs = iTSpacesOffs;
1737
- }
1738
-
1739
- {
1740
- // need an order independent way to evaluate
1741
- // tspace on quads. This is done by splitting
1742
- // along the shortest diagonal.
1743
- const i0 = MakeIndex(f, 0);
1744
- const i1 = MakeIndex(f, 1);
1745
- const i2 = MakeIndex(f, 2);
1746
- const i3 = MakeIndex(f, 3);
1747
-
1748
- const T0 = new SVec3();
1749
- GetTexCoord(T0, pContext, i0);
1750
- const T1 = new SVec3();
1751
- GetTexCoord(T1, pContext, i1);
1752
- const T2 = new SVec3();
1753
- GetTexCoord(T2, pContext, i2);
1754
- const T3 = new SVec3();
1755
- GetTexCoord(T3, pContext, i3);
1756
-
1757
-
1758
- const distSQ_02 = DistanceSquared(T2, T0);
1759
- const distSQ_13 = DistanceSquared(T3, T1);
1760
- let bQuadDiagIs_02 = false;
1761
- if (distSQ_02 < distSQ_13)
1762
- bQuadDiagIs_02 = true;
1763
- else if (distSQ_13 < distSQ_02)
1764
- bQuadDiagIs_02 = false;
1765
- else {
1766
- const P0 = GetPosition(pContext, i0);
1767
- const P1 = GetPosition(pContext, i1);
1768
- const P2 = GetPosition(pContext, i2);
1769
- const P3 = GetPosition(pContext, i3);
1770
- const distSQ_02 = DistanceSquared(P2, P0);
1771
- const distSQ_13 = DistanceSquared(P3, P1);
1772
-
1773
- bQuadDiagIs_02 = distSQ_13 >= distSQ_02;
1774
- }
1775
-
1776
- if (bQuadDiagIs_02) {
1777
- {
1778
- const pVerts_A = pTriInfos[iDstTriIndex].vert_num;
1779
- pVerts_A[0] = 0;
1780
- pVerts_A[1] = 1;
1781
- pVerts_A[2] = 2;
1782
- }
1783
- piTriList_out[iDstTriIndex * 3 + 0] = i0;
1784
- piTriList_out[iDstTriIndex * 3 + 1] = i1;
1785
- piTriList_out[iDstTriIndex * 3 + 2] = i2;
1786
- ++iDstTriIndex; // next
1787
- {
1788
- const pVerts_B = pTriInfos[iDstTriIndex].vert_num;
1789
- pVerts_B[0] = 0;
1790
- pVerts_B[1] = 2;
1791
- pVerts_B[2] = 3;
1792
- }
1793
- piTriList_out[iDstTriIndex * 3 + 0] = i0;
1794
- piTriList_out[iDstTriIndex * 3 + 1] = i2;
1795
- piTriList_out[iDstTriIndex * 3 + 2] = i3;
1796
- ++iDstTriIndex; // next
1797
- } else {
1798
- {
1799
- const pVerts_A = pTriInfos[iDstTriIndex].vert_num;
1800
- pVerts_A[0] = 0;
1801
- pVerts_A[1] = 1;
1802
- pVerts_A[2] = 3;
1803
- }
1804
- piTriList_out[iDstTriIndex * 3 + 0] = i0;
1805
- piTriList_out[iDstTriIndex * 3 + 1] = i1;
1806
- piTriList_out[iDstTriIndex * 3 + 2] = i3;
1807
- ++iDstTriIndex; // next
1808
- {
1809
- const pVerts_B = pTriInfos[iDstTriIndex].vert_num;
1810
- pVerts_B[0] = 1;
1811
- pVerts_B[1] = 2;
1812
- pVerts_B[2] = 3;
1813
- }
1814
- piTriList_out[iDstTriIndex * 3 + 0] = i1;
1815
- piTriList_out[iDstTriIndex * 3 + 1] = i2;
1816
- piTriList_out[iDstTriIndex * 3 + 2] = i3;
1817
- ++iDstTriIndex; // next
1818
- }
1819
- }
1820
- }
1821
-
1822
- iTSpacesOffs += verts;
1823
- assert(iDstTriIndex <= iNrTrianglesIn);
1824
- }
1825
-
1826
- for (t = 0; t < iNrTrianglesIn; t++)
1827
- pTriInfos[t].iFlag = 0;
1828
-
1829
- // return total amount of tspaces
1830
- return iTSpacesOffs;
1831
- }
1832
-
1833
- /**
1834
- *
1835
- * @param {STriInfo[]} pTriInfos
1836
- * @param {SGroup[]} pGroups
1837
- * @param {number[]|Int32Array} piGroupTrianglesBuffer
1838
- * @param {number[]|Int32Array} piTriListIn
1839
- * @param {number} iNrTrianglesIn
1840
- * @return {number}
1841
- */
1842
- function Build4RuleGroups(pTriInfos, pGroups, piGroupTrianglesBuffer, piTriListIn, iNrTrianglesIn) {
1843
- const iNrMaxGroups = iNrTrianglesIn * 3;
1844
- let iNrActiveGroups = 0;
1845
- let iOffset = 0, f = 0, i = 0;
1846
-
1847
- for (; f < iNrTrianglesIn; f++) {
1848
- const tri_info_f = pTriInfos[f];
1849
-
1850
- for (i = 0; i < 3; i++) {
1851
- // if not assigned to a group
1852
- if ((tri_info_f.iFlag & GROUP_WITH_ANY) === 0 && tri_info_f.AssignedGroup[i] == null) {
1853
- let neigh_indexL, neigh_indexR;
1854
- const vert_index = piTriListIn[f * 3 + i];
1855
-
1856
- assert(iNrActiveGroups < iNrMaxGroups);
1857
-
1858
- const assigned_group = pGroups[iNrActiveGroups];
1859
- tri_info_f.AssignedGroup[i] = assigned_group;
1860
-
1861
- assigned_group.iVertexRepresentitive = vert_index;
1862
-
1863
- const bOrPre = (tri_info_f.iFlag & ORIENT_PRESERVING) !== 0;
1864
-
1865
- assigned_group.bOrientPreservering = bOrPre;
1866
- assigned_group.iNrFaces = 0;
1867
- assigned_group.pFaceIndices = new Int32Array(piGroupTrianglesBuffer.buffer, iOffset * 4);
1868
- ++iNrActiveGroups;
1869
-
1870
- AddTriToGroup(assigned_group, f);
1871
-
1872
- neigh_indexL = tri_info_f.FaceNeighbors[i];
1873
- neigh_indexR = tri_info_f.FaceNeighbors[i > 0 ? (i - 1) : 2];
1874
-
1875
- if (neigh_indexL >= 0) // neighbor
1876
- {
1877
- const bAnswer = AssignRecur(
1878
- piTriListIn,
1879
- pTriInfos,
1880
- neigh_indexL,
1881
- assigned_group
1882
- );
1883
-
1884
- const bOrPre2 = (pTriInfos[neigh_indexL].iFlag & ORIENT_PRESERVING) !== 0;
1885
- const bDiff = bOrPre !== bOrPre2;
1886
- assert(bAnswer || bDiff);
1887
- }
1888
- if (neigh_indexR >= 0) // neighbor
1889
- {
1890
- const bAnswer = AssignRecur(
1891
- piTriListIn,
1892
- pTriInfos,
1893
- neigh_indexR,
1894
- assigned_group
1895
- );
1896
-
1897
- const bOrPre2 = (pTriInfos[neigh_indexR].iFlag & ORIENT_PRESERVING) !== 0;
1898
- const bDiff = bOrPre !== bOrPre2;
1899
- assert(bAnswer || bDiff);
1900
- }
1901
-
1902
- // update offset
1903
- iOffset += assigned_group.iNrFaces;
1904
- // since the groups are disjoint a triangle can never
1905
- // belong to more than 3 groups. Subsequently something
1906
- // is completely screwed if this assertion ever hits.
1907
- assert(iOffset <= iNrMaxGroups);
1908
- }
1909
- }
1910
- }
1911
-
1912
- return iNrActiveGroups;
1913
- }
1914
-
1915
- /**
1916
- *
1917
- * @param {SGroup} pGroup
1918
- * @param {number} iTriIndex
1919
- * @returns {void}
1920
- */
1921
- function AddTriToGroup(pGroup, iTriIndex) {
1922
- pGroup.pFaceIndices[pGroup.iNrFaces] = iTriIndex;
1923
- pGroup.iNrFaces++;
1924
- }
1925
-
1926
- /**
1927
- *
1928
- * @param {number[]} piTriListIn
1929
- * @param {STriInfo[]} psTriInfos
1930
- * @param {number} iMyTriIndex
1931
- * @param {SGroup} pGroup
1932
- * @return {boolean}
1933
- */
1934
- function AssignRecur(piTriListIn, psTriInfos, iMyTriIndex, pGroup) {
1935
-
1936
- // track down vertex
1937
- const iVertRep = pGroup.iVertexRepresentitive;
1938
- const pVerts = piTriListIn[iMyTriIndex * 3];
1939
-
1940
- let i = -1;
1941
- if (pVerts[0] === iVertRep) i = 0;
1942
- else if (pVerts[1] === iVertRep) i = 1;
1943
- else if (pVerts[2] === iVertRep) i = 2;
1944
-
1945
- assert(i >= 0 && i < 3);
1946
-
1947
- const pMyTriInfo = psTriInfos[iMyTriIndex];
1948
-
1949
- // early out
1950
- if (pMyTriInfo.AssignedGroup[i] === pGroup) {
1951
- return true;
1952
- } else if (pMyTriInfo.AssignedGroup[i] !== null) {
1953
- return false;
1954
- }
1955
-
1956
- if ((pMyTriInfo.iFlag & GROUP_WITH_ANY) !== 0) {
1957
- // first to group with a group-with-anything triangle
1958
- // determines it's orientation.
1959
- // This is the only existing order dependency in the code!!
1960
- if (
1961
- pMyTriInfo.AssignedGroup[0] === null &&
1962
- pMyTriInfo.AssignedGroup[1] === null &&
1963
- pMyTriInfo.AssignedGroup[2] === null
1964
- ) {
1965
- pMyTriInfo.iFlag &= (~ORIENT_PRESERVING);
1966
- pMyTriInfo.iFlag |= (pGroup.bOrientPreservering ? ORIENT_PRESERVING : 0);
1967
- }
1968
- }
1969
- {
1970
- const bOrient = (pMyTriInfo.iFlag & ORIENT_PRESERVING) !== 0;
1971
- if (bOrient !== pGroup.bOrientPreservering) return false;
1972
- }
1973
-
1974
- AddTriToGroup(pGroup, iMyTriIndex);
1975
- pMyTriInfo.AssignedGroup[i] = pGroup;
1976
-
1977
- {
1978
- const neigh_indexL = pMyTriInfo.FaceNeighbors[i];
1979
- const neigh_indexR = pMyTriInfo.FaceNeighbors[i > 0 ? (i - 1) : 2];
1980
- if (neigh_indexL >= 0)
1981
- AssignRecur(piTriListIn, psTriInfos, neigh_indexL, pGroup);
1982
- if (neigh_indexR >= 0)
1983
- AssignRecur(piTriListIn, psTriInfos, neigh_indexR, pGroup);
1984
- }
1985
-
1986
-
1987
- return true;
1988
- }
1989
-
1990
- /**
1991
- *
1992
- * @param {STSpace[]} psTspace
1993
- * @param {STriInfo[]} pTriInfos
1994
- * @param {SGroup[]} pGroups
1995
- * @param {number} iNrActiveGroups
1996
- * @param {number[]|Int32Array} piTriListIn
1997
- * @param {number} fThresCos
1998
- * @param {SMikkTSpaceContext} pContext
1999
- * @returns {boolean}
2000
- */
2001
- function GenerateTSpaces(psTspace, pTriInfos, pGroups, iNrActiveGroups, piTriListIn, fThresCos, pContext) {
2002
-
2003
- let iMaxNrFaces = 0, iUniqueTspaces = 0, g = 0, i = 0;
2004
-
2005
- for (g = 0; g < iNrActiveGroups; g++) {
2006
- if (iMaxNrFaces < pGroups[g].iNrFaces) {
2007
- iMaxNrFaces = pGroups[g].iNrFaces;
2008
- }
2009
- }
2010
-
2011
- if (iMaxNrFaces === 0) {
2012
- return true;
2013
- }
2014
-
2015
-
2016
- // make initial allocations
2017
- /**
2018
- * @type {STSpace[]}
2019
- */
2020
- let pSubGroupTspace = malloc(STSpace, iMaxNrFaces);
2021
- /**
2022
- *
2023
- * @type {SSubGroup[]}
2024
- */
2025
- let pUniSubGroups = malloc(SSubGroup, iMaxNrFaces);
2026
- /**
2027
- *
2028
- * @type {Int32Array|null}
2029
- */
2030
- let pTmpMembers = new Int32Array(iMaxNrFaces);
2031
-
2032
- iUniqueTspaces = 0;
2033
-
2034
- const vOs = new SVec3();
2035
- const vOt = new SVec3();
2036
-
2037
- for (g = 0; g < iNrActiveGroups; g++) {
2038
- const pGroup = pGroups[g];
2039
- let iUniqueSubGroups = 0, s = 0;
2040
-
2041
- for (i = 0; i < pGroup.iNrFaces; i++) // triangles
2042
- {
2043
- const f = pGroup.pFaceIndices[i]; // triangle number
2044
- let index = -1, iVertIndex = -1, iOF_1 = -1, iMembers = 0, j = 0, l = 0;
2045
- /**
2046
- * @type {SSubGroup}
2047
- */
2048
- const tmp_group = new SSubGroup();
2049
- let bFound = false;
2050
- /**
2051
- * @type {SVec3}
2052
- */
2053
- let n;
2054
-
2055
- if (pTriInfos[f].AssignedGroup[0] === pGroup) index = 0;
2056
- else if (pTriInfos[f].AssignedGroup[1] === pGroup) index = 1;
2057
- else if (pTriInfos[f].AssignedGroup[2] === pGroup) index = 2;
2058
- assert(index >= 0 && index < 3);
2059
-
2060
- iVertIndex = piTriListIn[f * 3 + index];
2061
- assert(iVertIndex === pGroup.iVertexRepresentitive);
2062
-
2063
- // is normalized already
2064
- n = GetNormal(pContext, iVertIndex);
2065
-
2066
- // project
2067
-
2068
- vsub(vOs, pTriInfos[f].vOs, vscale(vdot(n, pTriInfos[f].vOs), n));
2069
- vsub(vOt, pTriInfos[f].vOt, vscale(vdot(n, pTriInfos[f].vOt), n));
2070
-
2071
- NormalizeSafe(vOs, vOs);
2072
- NormalizeSafe(vOt, vOt);
2073
-
2074
- // original face number
2075
- iOF_1 = pTriInfos[f].iOrgFaceNumber;
2076
-
2077
- iMembers = 0;
2078
- for (j = 0; j < pGroup.iNrFaces; j++) {
2079
- const t = pGroup.pFaceIndices[j]; // triangle number
2080
- const iOF_2 = pTriInfos[t].iOrgFaceNumber;
2081
-
2082
- // project
2083
- let vOs2 = new SVec3();
2084
- vsub(vOs2, pTriInfos[t].vOs, vscale(vdot(n, pTriInfos[t].vOs), n));
2085
-
2086
- let vOt2 = new SVec3();
2087
- vsub(vOt2, pTriInfos[t].vOt, vscale(vdot(n, pTriInfos[t].vOt), n));
2088
-
2089
- NormalizeSafe(vOs2, vOs2);
2090
- NormalizeSafe(vOt2, vOt2);
2091
-
2092
- {
2093
- const bAny = ((pTriInfos[f].iFlag | pTriInfos[t].iFlag) & GROUP_WITH_ANY) !== 0;
2094
- // make sure triangles which belong to the same quad are joined.
2095
- const bSameOrgFace = iOF_1 === iOF_2;
2096
-
2097
- const fCosS = vdot(vOs, vOs2);
2098
- const fCosT = vdot(vOt, vOt2);
2099
-
2100
- assert(f !== t || bSameOrgFace); // sanity check
2101
- if (bAny || bSameOrgFace || (fCosS > fThresCos && fCosT > fThresCos))
2102
- pTmpMembers[iMembers++] = t;
2103
- }
2104
- }
2105
-
2106
- // sort pTmpMembers
2107
- tmp_group.iNrFaces = iMembers;
2108
- tmp_group.pTriMembers = pTmpMembers;
2109
-
2110
- if (iMembers > 1) {
2111
- const uSeed = INTERNAL_RND_SORT_SEED; // could replace with a random seed?
2112
- QuickSort(pTmpMembers, 0, iMembers - 1, uSeed);
2113
- }
2114
-
2115
- // look for an existing match
2116
- bFound = false;
2117
- l = 0;
2118
- while (l < iUniqueSubGroups && !bFound) {
2119
- bFound = CompareSubGroups(tmp_group, pUniSubGroups[l]);
2120
- if (!bFound) ++l;
2121
- }
2122
-
2123
- // assign tangent space index
2124
- assert(bFound || l === iUniqueSubGroups);
2125
- //piTempTangIndices[f*3+index] = iUniqueTspaces+l;
2126
-
2127
- // if no match was found we allocate a new subgroup
2128
- if (!bFound) {
2129
- // insert new subgroup
2130
- const pIndices = new Int32Array(iMembers);
2131
-
2132
- pUniSubGroups[iUniqueSubGroups].iNrFaces = iMembers;
2133
- pUniSubGroups[iUniqueSubGroups].pTriMembers = pIndices;
2134
-
2135
- array_copy(tmp_group.pTriMembers, 0, pIndices, 0, iMembers);
2136
-
2137
- pSubGroupTspace[iUniqueSubGroups] = EvalTspace(
2138
- tmp_group.pTriMembers,
2139
- iMembers,
2140
- piTriListIn,
2141
- pTriInfos,
2142
- pContext,
2143
- pGroup.iVertexRepresentitive
2144
- );
2145
-
2146
- ++iUniqueSubGroups;
2147
- }
2148
-
2149
- // output tspace
2150
- {
2151
- const iOffs = pTriInfos[f].iTSpacesOffs;
2152
- const iVert = pTriInfos[f].vert_num[index];
2153
- /**
2154
- * @type {STSpace}
2155
- */
2156
- const pTS_out = psTspace[iOffs + iVert];
2157
- assert(pTS_out.iCounter < 2);
2158
- assert(((pTriInfos[f].iFlag & ORIENT_PRESERVING) !== 0) === pGroup.bOrientPreservering);
2159
- if (pTS_out.iCounter === 1) {
2160
- AvgTSpace(pTS_out, pTS_out, pSubGroupTspace[l]);
2161
- pTS_out.iCounter = 2; // update counter
2162
- pTS_out.bOrient = pGroup.bOrientPreservering;
2163
- } else {
2164
- assert(pTS_out.iCounter === 0);
2165
- pTS_out.copy(pSubGroupTspace[l]);
2166
- pTS_out.iCounter = 1; // update counter
2167
- pTS_out.bOrient = pGroup.bOrientPreservering;
2168
- }
2169
- }
2170
- }
2171
-
2172
- // clean up and offset iUniqueTspaces
2173
-
2174
- iUniqueTspaces += iUniqueSubGroups;
2175
- }
2176
-
2177
- return true;
2178
- }
2179
-
2180
-
2181
- /**
2182
- *
2183
- * @param {STriInfo[]} pTriInfos
2184
- * @param {number[]|Int32Array} piTriList_out
2185
- * @param {number} iNrTrianglesIn
2186
- * @param {number} iTotTris
2187
- * @returns {void}
2188
- */
2189
- function DegenPrologue(pTriInfos, piTriList_out, iNrTrianglesIn, iTotTris) {
2190
- let iNextGoodTriangleSearchIndex = -1;
2191
- let bStillFindingGoodOnes = false;
2192
-
2193
- // locate quads with only one good triangle
2194
- let t = 0;
2195
- while (t < (iTotTris - 1)) {
2196
- const iFO_a = pTriInfos[t].iOrgFaceNumber;
2197
- const iFO_b = pTriInfos[t + 1].iOrgFaceNumber;
2198
- if (iFO_a === iFO_b) // this is a quad
2199
- {
2200
- const bIsDeg_a = (pTriInfos[t].iFlag & MARK_DEGENERATE) !== 0;
2201
- const bIsDeg_b = (pTriInfos[t + 1].iFlag & MARK_DEGENERATE) !== 0;
2202
- if ((bIsDeg_a ^ bIsDeg_b) !== 0) {
2203
- pTriInfos[t].iFlag |= QUAD_ONE_DEGEN_TRI;
2204
- pTriInfos[t + 1].iFlag |= QUAD_ONE_DEGEN_TRI;
2205
- }
2206
- t += 2;
2207
- } else
2208
- ++t;
2209
- }
2210
-
2211
- // reorder list so all degen triangles are moved to the back
2212
- // without reordering the good triangles
2213
- iNextGoodTriangleSearchIndex = 1;
2214
- t = 0;
2215
- bStillFindingGoodOnes = true;
2216
- while (t < iNrTrianglesIn && bStillFindingGoodOnes) {
2217
- const bIsGood = (pTriInfos[t].iFlag & MARK_DEGENERATE) === 0;
2218
- if (bIsGood) {
2219
- if (iNextGoodTriangleSearchIndex < (t + 2))
2220
- iNextGoodTriangleSearchIndex = t + 2;
2221
- } else {
2222
- let t0, t1;
2223
- // search for the first good triangle.
2224
- let bJustADegenerate = true;
2225
- while (bJustADegenerate && iNextGoodTriangleSearchIndex < iTotTris) {
2226
- const bIsGood = (pTriInfos[iNextGoodTriangleSearchIndex].iFlag & MARK_DEGENERATE) === 0;
2227
- if (bIsGood) bJustADegenerate = false;
2228
- else ++iNextGoodTriangleSearchIndex;
2229
- }
2230
-
2231
- t0 = t;
2232
- t1 = iNextGoodTriangleSearchIndex;
2233
- ++iNextGoodTriangleSearchIndex;
2234
- assert(iNextGoodTriangleSearchIndex > (t + 1));
2235
-
2236
- // swap triangle t0 and t1
2237
- if (!bJustADegenerate) {
2238
- let i = 0;
2239
- for (; i < 3; i++) {
2240
- const index = piTriList_out[t0 * 3 + i];
2241
- piTriList_out[t0 * 3 + i] = piTriList_out[t1 * 3 + i];
2242
- piTriList_out[t1 * 3 + i] = index;
2243
- }
2244
- {
2245
- const tri_info = pTriInfos[t0];
2246
- pTriInfos[t0] = pTriInfos[t1];
2247
- pTriInfos[t1] = tri_info;
2248
- }
2249
- } else
2250
- bStillFindingGoodOnes = false; // this is not supposed to happen
2251
- }
2252
-
2253
- if (bStillFindingGoodOnes) ++t;
2254
- }
2255
-
2256
- assert(bStillFindingGoodOnes); // code will still work.
2257
- assert(iNrTrianglesIn === t);
2258
- }
2259
-
2260
- /**
2261
- *
2262
- * @param {STSpace[]} psTspace
2263
- * @param {STriInfo[]} pTriInfos
2264
- * @param {number[]|Int32Array} piTriListIn
2265
- * @param {SMikkTSpaceContext} pContext
2266
- * @param {number} iNrTrianglesIn
2267
- * @param {number} iTotTris
2268
- * @returns {void}
2269
- */
2270
- function DegenEpilogue(psTspace, pTriInfos, piTriListIn, pContext, iNrTrianglesIn, iTotTris) {
2271
- let t = 0, i = 0;
2272
-
2273
- // deal with degenerate triangles
2274
- // punishment for degenerate triangles is O(N^2)
2275
-
2276
- for (t = iNrTrianglesIn; t < iTotTris; t++) {
2277
- const tri0 = pTriInfos[t];
2278
-
2279
- // degenerate triangles on a quad with one good triangle are skipped
2280
- // here but processed in the next loop
2281
- const bSkip = (tri0.iFlag & QUAD_ONE_DEGEN_TRI) !== 0;
2282
-
2283
- if (!bSkip) {
2284
- for (i = 0; i < 3; i++) {
2285
-
2286
- const index1 = piTriListIn[t * 3 + i];
2287
- // search through the good triangles
2288
- let bNotFound = true;
2289
- let j = 0;
2290
-
2291
- while (bNotFound && j < (3 * iNrTrianglesIn)) {
2292
- const index2 = piTriListIn[j];
2293
-
2294
- if (index1 === index2) {
2295
- bNotFound = false;
2296
- } else {
2297
- ++j;
2298
- }
2299
- }
2300
-
2301
- if (!bNotFound) {
2302
- const iTri = j / 3;
2303
- const iVert = j % 3;
2304
-
2305
- const tri1 = pTriInfos[iTri];
2306
-
2307
- const iSrcVert = tri1.vert_num[iVert];
2308
- const iSrcOffs = tri1.iTSpacesOffs;
2309
-
2310
- const iDstVert = tri0.vert_num[i];
2311
- const iDstOffs = tri0.iTSpacesOffs;
2312
-
2313
- // copy tspace
2314
- psTspace[iDstOffs + iDstVert] = psTspace[iSrcOffs + iSrcVert];
2315
- }
2316
- }
2317
- }
2318
- }
2319
-
2320
- // deal with degenerate quads with one good triangle
2321
- for (t = 0; t < iNrTrianglesIn; t++) {
2322
- // this triangle belongs to a quad where the
2323
- // other triangle is degenerate
2324
- const tri = pTriInfos[t];
2325
-
2326
- if ((tri.iFlag & QUAD_ONE_DEGEN_TRI) !== 0) {
2327
-
2328
- /**
2329
- * @type {SVec3}
2330
- */
2331
- let vDstP;
2332
- let iOrgF = -1, i = 0;
2333
- let bNotFound;
2334
-
2335
- const pV = tri.vert_num;
2336
- const iFlag = (1 << pV[0]) | (1 << pV[1]) | (1 << pV[2]);
2337
-
2338
- let iMissingIndex = 0;
2339
-
2340
- if ((iFlag & 2) === 0) {
2341
- iMissingIndex = 1;
2342
- } else if ((iFlag & 4) === 0) {
2343
- iMissingIndex = 2;
2344
- } else if ((iFlag & 8) === 0) {
2345
- iMissingIndex = 3;
2346
- }
2347
-
2348
- iOrgF = tri.iOrgFaceNumber;
2349
- vDstP = GetPosition(pContext, MakeIndex(iOrgF, iMissingIndex));
2350
- bNotFound = true;
2351
-
2352
- i = 0;
2353
-
2354
- while (bNotFound && i < 3) {
2355
-
2356
- const iVert = pV[i];
2357
- const vSrcP = GetPosition(pContext, MakeIndex(iOrgF, iVert));
2358
-
2359
- if (veq(vSrcP, vDstP)) {
2360
- const iOffs = tri.iTSpacesOffs;
2361
- psTspace[iOffs + iMissingIndex] = psTspace[iOffs + iVert];
2362
- bNotFound = false;
2363
- } else {
2364
- ++i;
2365
- }
2366
-
2367
- }
2368
- assert(!bNotFound);
2369
- }
2370
- }
2371
- }