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