@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.
- package/core/assert.js +2 -2
- package/core/collection/RingBuffer.js +15 -0
- package/core/collection/array/array_swap.js +3 -3
- package/core/collection/map/AsyncLoadingCache.js +47 -0
- package/core/geom/3d/aabb/aabb3_compute_distance_above_plane_max.js +1 -1
- package/core/math/statistics/computeSampleSize_Cochran.js +3 -3
- package/editor/ecs/component/editors/geom/QuaternionEditor.js +12 -5
- package/engine/Engine.js +6 -1
- package/engine/EngineBootstrapper.js +2 -1
- package/engine/EngineHarness.js +5 -1
- package/engine/asset/AssetManager.js +97 -7
- package/engine/development/performance/AbstractMetric.js +3 -1
- package/engine/development/performance/RingBufferMetric.js +39 -4
- package/engine/ecs/EntityBuilder.js +29 -4
- package/engine/graphics/ecs/camera/topdown/TopDownCameraControllerSystem.js +17 -1
- package/engine/graphics/ecs/decal/v2/FPDecalSystem.js +18 -30
- package/engine/graphics/ecs/decal/v2/prototypeDecalSystem.js +57 -16
- package/engine/graphics/ecs/mesh-v2/allocate_v3.js +37 -0
- package/engine/graphics/geometry/MikkT/AddTriToGroup.js +10 -0
- package/engine/graphics/geometry/MikkT/AssignRecur.js +84 -0
- package/engine/graphics/geometry/MikkT/AvgTSpace.js +38 -0
- package/engine/graphics/geometry/MikkT/Build4RuleGroups.js +96 -0
- package/engine/graphics/geometry/MikkT/BuildNeighborsFast.js +137 -0
- package/engine/graphics/geometry/MikkT/CalcTexArea.js +31 -0
- package/engine/graphics/geometry/MikkT/CompareSubGroups.js +26 -0
- package/engine/graphics/geometry/MikkT/DegenEpilogue.js +220 -0
- package/engine/graphics/geometry/MikkT/DegenPrologue.js +115 -0
- package/engine/graphics/geometry/MikkT/EvalTspace.js +128 -0
- package/engine/graphics/geometry/MikkT/GenerateInitialVerticesIndexList.js +48 -0
- package/engine/graphics/geometry/MikkT/GenerateSharedVerticesIndexList.js +184 -0
- package/engine/graphics/geometry/MikkT/GenerateTSpaces.js +226 -0
- package/engine/graphics/geometry/MikkT/GetEdge.js +45 -0
- package/engine/graphics/geometry/MikkT/GetNormal.js +16 -0
- package/engine/graphics/geometry/MikkT/GetPosition.js +25 -0
- package/engine/graphics/geometry/MikkT/GetTexCoord.js +18 -0
- package/engine/graphics/geometry/MikkT/InitTriInfo.js +180 -0
- package/engine/graphics/geometry/MikkT/Length.js +10 -0
- package/engine/graphics/geometry/MikkT/MakeIndex.js +18 -0
- package/engine/graphics/geometry/MikkT/MikkTSpace.js +165 -2197
- package/engine/graphics/geometry/MikkT/NormalizeSafe.js +21 -0
- package/engine/graphics/geometry/MikkT/NotZero.js +10 -0
- package/engine/graphics/geometry/MikkT/QuickSort.js +54 -0
- package/engine/graphics/geometry/MikkT/QuickSortEdges.js +71 -0
- package/engine/graphics/geometry/MikkT/SSubGroup.js +15 -0
- package/engine/graphics/geometry/MikkT/STSpace.js +36 -0
- package/engine/graphics/geometry/MikkT/constants/GROUP_WITH_ANY.js +1 -0
- package/engine/graphics/geometry/MikkT/constants/INTERNAL_RND_SORT_SEED.js +1 -0
- package/engine/graphics/geometry/MikkT/constants/MARK_DEGENERATE.js +1 -0
- package/engine/graphics/geometry/MikkT/constants/ORIENT_PRESERVING.js +1 -0
- package/engine/graphics/geometry/MikkT/constants/QUAD_ONE_DEGEN_TRI.js +1 -0
- package/engine/graphics/geometry/MikkT/m_getNormal.js +16 -0
- package/engine/graphics/geometry/MikkT/m_getNumFaces.js +8 -0
- package/engine/graphics/geometry/MikkT/m_getNumVerticesOfFace.js +11 -0
- package/engine/graphics/geometry/MikkT/m_getPosition.js +20 -0
- package/engine/graphics/geometry/MikkT/m_getTexCoord.js +16 -0
- package/engine/graphics/geometry/MikkT/m_setTSpace.js +35 -0
- package/engine/graphics/geometry/MikkT/m_setTSpaceBasic.js +22 -0
- package/engine/graphics/geometry/MikkT/malloc.js +16 -0
- package/engine/graphics/geometry/MikkT/v3_scale_dot_sub_normalize.js +52 -0
- package/engine/graphics/impostors/octahedral/ImpostorBaker.js +3 -2
- package/engine/graphics/impostors/octahedral/prototypeBaker.js +5 -5
- package/engine/graphics/render/forward_plus/LightManager.js +16 -6
- package/engine/graphics/render/forward_plus/data/TextureBackedMemoryRegion.js +7 -1
- package/engine/graphics/render/forward_plus/materials/FP_SHADER_CHUNK_ACCUMULATION.js +13 -3
- package/engine/graphics/render/forward_plus/materials/FP_SHADER_CHUNK_PREAMBLE.js +2 -1
- package/engine/graphics/render/forward_plus/plugin/MaterialTransformer.js +14 -2
- package/engine/graphics/render/forward_plus/query/query_bvh_frustum_from_texture.js +2 -2
- package/engine/intelligence/behavior/util/RotationBehavior.js +69 -0
- package/generation/example/filters/SampleGroundMoistureFilter.js +5 -5
- package/package.json +1 -1
|
@@ -1,8 +1,19 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
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
|
-
|
|
193
|
-
|
|
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
|
|
304
|
-
* @
|
|
305
|
-
* @param {number} iVert
|
|
306
|
-
* @returns {void}
|
|
161
|
+
* @param {number} fAngularThreshold
|
|
162
|
+
* @return {boolean}
|
|
307
163
|
*/
|
|
308
|
-
function
|
|
309
|
-
//
|
|
310
|
-
|
|
164
|
+
export function genTangSpace(pContext, fAngularThreshold = 180) {
|
|
165
|
+
// count nr_triangles
|
|
166
|
+
let iNrTSPaces = 0, iNrMaxGroups = 0;
|
|
167
|
+
let iNrActiveGroups = 0;
|
|
311
168
|
|
|
312
|
-
|
|
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
|
-
|
|
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
|
-
|
|
332
|
-
|
|
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
|
-
|
|
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
|
-
|
|
370
|
-
|
|
185
|
+
// make an initial triangle --> face index list
|
|
186
|
+
iNrTSPaces = GenerateInitialVerticesIndexList(pTriInfos, piTriListIn, pContext, iNrTrianglesIn);
|
|
371
187
|
|
|
372
|
-
//
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
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
|
-
|
|
380
|
-
const
|
|
381
|
-
|
|
382
|
-
const ORIENT_PRESERVING = 8;
|
|
193
|
+
// Mark all degenerate triangles
|
|
194
|
+
const iTotTris = iNrTrianglesIn;
|
|
195
|
+
let iDegenTriangles = 0;
|
|
383
196
|
|
|
384
|
-
const
|
|
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
|
-
|
|
405
|
-
|
|
406
|
-
|
|
205
|
+
const i0 = piTriListIn[t3 ];
|
|
206
|
+
const i1 = piTriListIn[t3 + 1];
|
|
207
|
+
const i2 = piTriListIn[t3 + 2];
|
|
407
208
|
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
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
|
-
|
|
423
|
-
|
|
424
|
-
|
|
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
|
-
|
|
442
|
-
|
|
443
|
-
|
|
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
|
-
|
|
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
|
-
|
|
460
|
-
|
|
461
|
-
|
|
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
|
-
|
|
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
|
-
|
|
273
|
+
stSpace.fMagS = 1.0;
|
|
485
274
|
|
|
486
|
-
|
|
487
|
-
|
|
275
|
+
stSpace.vOt[0] = 0.0;
|
|
276
|
+
stSpace.vOt[1] = 1.0;
|
|
277
|
+
stSpace.vOt[2] = 0.0;
|
|
488
278
|
|
|
489
|
-
|
|
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
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
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
|
-
//
|
|
545
|
-
//
|
|
546
|
-
//
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
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
|
-
|
|
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
|
-
|
|
562
|
-
|
|
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
|
-
|
|
565
|
-
|
|
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
|
-
|
|
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
|
-
}
|