@manycore/aholo-splat-transform 1.2.8 → 1.2.9
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/CHANGELOG.md +120 -113
- package/README.md +39 -39
- package/THIRD_PARTY_LICENSES.txt +1373 -1373
- package/bin/cli.js +125 -118
- package/dist/SplatData.d.ts +67 -67
- package/dist/SplatData.js +167 -150
- package/dist/constant.d.ts +3 -3
- package/dist/constant.js +13 -13
- package/dist/file/IFile.d.ts +5 -5
- package/dist/file/IFile.js +1 -1
- package/dist/file/esz.d.ts +11 -11
- package/dist/file/esz.js +337 -322
- package/dist/file/index.d.ts +8 -8
- package/dist/file/index.js +7 -7
- package/dist/file/ksplat.d.ts +12 -12
- package/dist/file/ksplat.js +293 -231
- package/dist/file/lcc.d.ts +11 -11
- package/dist/file/lcc.js +161 -158
- package/dist/file/ply.d.ts +13 -13
- package/dist/file/ply.js +439 -390
- package/dist/file/sog.d.ts +80 -80
- package/dist/file/sog.js +525 -494
- package/dist/file/splat.d.ts +6 -6
- package/dist/file/splat.js +119 -99
- package/dist/file/spz.d.ts +11 -11
- package/dist/file/spz.js +597 -583
- package/dist/file/voxel.d.ts +43 -37
- package/dist/file/voxel.js +411 -280
- package/dist/index.d.ts +33 -33
- package/dist/index.js +54 -54
- package/dist/native/index.d.ts +54 -54
- package/dist/native/index.js +122 -129
- package/dist/native/utils.d.ts +1 -0
- package/dist/native/utils.js +54 -0
- package/dist/tasks/AutoChunkLodTask.d.ts +13 -13
- package/dist/tasks/AutoChunkLodTask.js +117 -117
- package/dist/tasks/AutoLodTask.d.ts +10 -10
- package/dist/tasks/AutoLodTask.js +20 -20
- package/dist/tasks/BaseTask.d.ts +15 -15
- package/dist/tasks/BaseTask.js +5 -5
- package/dist/tasks/FlexLodTask.d.ts +12 -12
- package/dist/tasks/FlexLodTask.js +54 -44
- package/dist/tasks/ModifyTask.d.ts +9 -9
- package/dist/tasks/ModifyTask.js +166 -156
- package/dist/tasks/ReadTask.d.ts +9 -9
- package/dist/tasks/ReadTask.js +29 -29
- package/dist/tasks/SkeletonLodTask.d.ts +10 -10
- package/dist/tasks/SkeletonLodTask.js +176 -156
- package/dist/tasks/VoxelTask.d.ts +35 -30
- package/dist/tasks/VoxelTask.js +40 -37
- package/dist/tasks/WriteTask.d.ts +12 -12
- package/dist/tasks/WriteTask.js +70 -70
- package/dist/utils/BufferReader.d.ts +12 -12
- package/dist/utils/BufferReader.js +45 -45
- package/dist/utils/Logger.d.ts +11 -11
- package/dist/utils/Logger.js +40 -40
- package/dist/utils/StreamChunkDecoder.d.ts +16 -16
- package/dist/utils/StreamChunkDecoder.js +31 -31
- package/dist/utils/index.d.ts +27 -27
- package/dist/utils/index.js +101 -101
- package/dist/utils/k-means.d.ts +4 -4
- package/dist/utils/k-means.js +340 -341
- package/dist/utils/math.d.ts +46 -46
- package/dist/utils/math.js +350 -346
- package/dist/utils/quantize-1d.d.ts +4 -4
- package/dist/utils/quantize-1d.js +164 -164
- package/dist/utils/sh-rotate.d.ts +2 -2
- package/dist/utils/sh-rotate.js +236 -175
- package/dist/utils/splat.d.ts +21 -21
- package/dist/utils/splat.js +397 -387
- package/dist/utils/voxel/binary.d.ts +8 -0
- package/dist/utils/voxel/binary.js +176 -0
- package/dist/utils/voxel/common.d.ts +178 -162
- package/dist/utils/voxel/common.js +1752 -1682
- package/dist/utils/voxel/coplanar-merge.d.ts +63 -63
- package/dist/utils/voxel/coplanar-merge.js +818 -819
- package/dist/utils/voxel/filter-cluster.d.ts +20 -0
- package/dist/utils/voxel/filter-cluster.js +628 -0
- package/dist/utils/voxel/gpu-dilation.d.ts +2 -2
- package/dist/utils/voxel/gpu-dilation.js +677 -656
- package/dist/utils/voxel/marching-cubes.d.ts +42 -42
- package/dist/utils/voxel/marching-cubes.js +1645 -1657
- package/dist/utils/voxel/mesh.d.ts +3 -3
- package/dist/utils/voxel/mesh.js +130 -130
- package/dist/utils/voxel/nav.d.ts +29 -29
- package/dist/utils/voxel/nav.js +1068 -1043
- package/dist/utils/voxel/postprocess.d.ts +23 -23
- package/dist/utils/voxel/postprocess.js +408 -375
- package/dist/utils/voxel/voxel-faces.d.ts +18 -18
- package/dist/utils/voxel/voxel-faces.js +662 -663
- package/dist/utils/voxel/voxelize.d.ts +34 -33
- package/dist/utils/voxel/voxelize.js +1208 -1193
- package/dist/utils/webgpu.d.ts +8 -8
- package/dist/utils/webgpu.js +122 -122
- package/package.json +37 -39
- package/dist/native/cpp/bin/linux/binding.node +0 -0
- package/dist/native/cpp/bin/windows/binding.node +0 -0
|
@@ -1,1657 +1,1645 @@
|
|
|
1
|
-
import { BLOCKS_PER_WORD, EVEN_BITS, BLOCK_EMPTY, BLOCK_SOLID, readBlockType } from './common.js';
|
|
2
|
-
// ============================================================================
|
|
3
|
-
// Voxel bit helpers
|
|
4
|
-
// ============================================================================
|
|
5
|
-
// Bit layout within a 4x4x4 block: bitIdx = lx + ly*4 + lz*16
|
|
6
|
-
// lo = bits 0-31 (lz 0-1), hi = bits 32-63 (lz 2-3)
|
|
7
|
-
/**
|
|
8
|
-
* Test whether a voxel is occupied within a block's bitmask.
|
|
9
|
-
*
|
|
10
|
-
* @param lo - Lower 32 bits of the block mask
|
|
11
|
-
* @param hi - Upper 32 bits of the block mask
|
|
12
|
-
* @param lx - Local x coordinate (0-3)
|
|
13
|
-
* @param ly - Local y coordinate (0-3)
|
|
14
|
-
* @param lz - Local z coordinate (0-3)
|
|
15
|
-
* @returns True if the voxel is occupied
|
|
16
|
-
*/
|
|
17
|
-
function isVoxelSet(lo, hi, lx, ly, lz) {
|
|
18
|
-
const bitIdx = lx + ly * 4 + lz * 16;
|
|
19
|
-
if (bitIdx < 32) {
|
|
20
|
-
return (lo & (1 << bitIdx)) !== 0;
|
|
21
|
-
}
|
|
22
|
-
return (hi & (1 << (bitIdx - 32))) !== 0;
|
|
23
|
-
}
|
|
24
|
-
// ============================================================================
|
|
25
|
-
// Marching Cubes
|
|
26
|
-
// ============================================================================
|
|
27
|
-
// Sentinel values for the per-block 3x3x3 neighbor table.
|
|
28
|
-
const NEIGHBOR_EMPTY = -2;
|
|
29
|
-
const NEIGHBOR_SOLID = -1;
|
|
30
|
-
/**
|
|
31
|
-
* Extract a triangle mesh from a SparseVoxelGrid using marching cubes.
|
|
32
|
-
*
|
|
33
|
-
* Each voxel is treated as a cell in the marching cubes grid. Corner values
|
|
34
|
-
* are binary (0 = empty, 1 = occupied) with a 0.5 threshold. Vertices are
|
|
35
|
-
* placed at edge midpoints, producing the binary-field isosurface between
|
|
36
|
-
* occupied and empty samples.
|
|
37
|
-
*
|
|
38
|
-
* @param grid - Voxel grid (after filtering / nav phases)
|
|
39
|
-
* @param gridBounds - Grid bounds aligned to block boundaries
|
|
40
|
-
* @param voxelResolution - Size of each voxel in world units
|
|
41
|
-
* @param options - Optional extraction settings
|
|
42
|
-
* @returns Mesh with positions and indices
|
|
43
|
-
*/
|
|
44
|
-
function marchingCubes(grid, gridBounds, voxelResolution, options = {}) {
|
|
45
|
-
const { nbx, nby, nbz, bStride, types, masks } = grid;
|
|
46
|
-
const totalBlocks = nbx * nby * nbz;
|
|
47
|
-
const mergeFlatFaces = options.mergeFlatFaces === true;
|
|
48
|
-
// Vertex deduplication: edge ID -> vertex index. Open-addressed typed-
|
|
49
|
-
// array hash table rather than `Map<number, number>` because (1) a single
|
|
50
|
-
// V8 Map is capped at ~2^24 entries (large carved scenes blow past this),
|
|
51
|
-
// and (2) per-entry overhead in Map is ~50 bytes vs 12 bytes (Float64 key
|
|
52
|
-
// + Uint32 value) here, which matters when the table holds tens of
|
|
53
|
-
// millions of vertices.
|
|
54
|
-
//
|
|
55
|
-
// Empty slots are marked with `key === -1` (real keys are non-negative).
|
|
56
|
-
// Hash uses Fibonacci constant on the lower 32 bits of the key. The same
|
|
57
|
-
// structure is used for orphan-cell deduplication, where `vVals` is unused.
|
|
58
|
-
let vCap = 1 << 14;
|
|
59
|
-
let vMask = vCap - 1;
|
|
60
|
-
let vSize = 0;
|
|
61
|
-
let vKeys = new Float64Array(vCap).fill(-1);
|
|
62
|
-
let vVals = new Uint32Array(vCap);
|
|
63
|
-
|
|
64
|
-
const oldKeys = vKeys;
|
|
65
|
-
const oldVals = vVals;
|
|
66
|
-
const oldCap = vCap;
|
|
67
|
-
vCap *= 2;
|
|
68
|
-
vMask = vCap - 1;
|
|
69
|
-
vKeys = new Float64Array(vCap).fill(-1);
|
|
70
|
-
vVals = new Uint32Array(vCap);
|
|
71
|
-
for (let j = 0; j < oldCap; j++) {
|
|
72
|
-
const k = oldKeys[j];
|
|
73
|
-
if (k === -1) {
|
|
74
|
-
continue;
|
|
75
|
-
}
|
|
76
|
-
let i = (Math.imul(k | 0,
|
|
77
|
-
while (vKeys[i] !== -1) {
|
|
78
|
-
i = (i + 1) & vMask;
|
|
79
|
-
}
|
|
80
|
-
vKeys[i] = k;
|
|
81
|
-
vVals[i] = oldVals[j];
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
let oCap = 1 << 14;
|
|
85
|
-
let oMask = oCap - 1;
|
|
86
|
-
let oSize = 0;
|
|
87
|
-
let oKeys = new Float64Array(oCap).fill(-1);
|
|
88
|
-
|
|
89
|
-
const oldKeys = oKeys;
|
|
90
|
-
const oldCap = oCap;
|
|
91
|
-
oCap *= 2;
|
|
92
|
-
oMask = oCap - 1;
|
|
93
|
-
oKeys = new Float64Array(oCap).fill(-1);
|
|
94
|
-
for (let j = 0; j < oldCap; j++) {
|
|
95
|
-
const k = oldKeys[j];
|
|
96
|
-
if (k === -1) {
|
|
97
|
-
continue;
|
|
98
|
-
}
|
|
99
|
-
let i = (Math.imul(k | 0,
|
|
100
|
-
while (oKeys[i] !== -1) {
|
|
101
|
-
i = (i + 1) & oMask;
|
|
102
|
-
}
|
|
103
|
-
oKeys[i] = k;
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
// Growable typed-array buffers. Capacity doubles on demand to avoid
|
|
107
|
-
// the GC churn of pushing into JS number[] for huge meshes.
|
|
108
|
-
let posCap = 1024;
|
|
109
|
-
let posLen = 0;
|
|
110
|
-
let positions = new Float32Array(posCap);
|
|
111
|
-
let idxCap = 1024;
|
|
112
|
-
let idxLen = 0;
|
|
113
|
-
let indices = new Uint32Array(idxCap);
|
|
114
|
-
const originX = gridBounds.min.x;
|
|
115
|
-
const originY = gridBounds.min.y;
|
|
116
|
-
const originZ = gridBounds.min.z;
|
|
117
|
-
const gridNx = Math.round((gridBounds.max.x - gridBounds.min.x) / voxelResolution);
|
|
118
|
-
const gridNy = Math.round((gridBounds.max.y - gridBounds.min.y) / voxelResolution);
|
|
119
|
-
const gridNz = Math.round((gridBounds.max.z - gridBounds.min.z) / voxelResolution);
|
|
120
|
-
// Compute strides from actual grid dimensions (+3 for the -1 boundary
|
|
121
|
-
// extension, the far edge +1, and one extra for safety).
|
|
122
|
-
const strideX = gridNx + 3;
|
|
123
|
-
const strideXY = strideX * (gridNy + 3);
|
|
124
|
-
const scaledCoordStride = (Math.max(gridNx, gridNy, gridNz) + 3) * 2 + 5;
|
|
125
|
-
const scaledCoordOffset = 3;
|
|
126
|
-
// Per-block 3x3x3 neighbor lookup table populated once per processed block.
|
|
127
|
-
// Index = (dx+1) + (dy+1)*3 + (dz+1)*9, dx/dy/dz in {-1, 0, 1}.
|
|
128
|
-
// neighborEntry: NEIGHBOR_EMPTY, NEIGHBOR_SOLID, or NEIGHBOR_MIXED (mask in neighborMasks).
|
|
129
|
-
const NEIGHBOR_MIXED = 0;
|
|
130
|
-
const neighborEntry = new Int32Array(27);
|
|
131
|
-
const neighborMasks = new Uint32Array(54);
|
|
132
|
-
// Reused scratch for emitted edge vertex indices.
|
|
133
|
-
const edgeVerts = new Int32Array(12);
|
|
134
|
-
// Block coordinate of the block currently being processed. Captured by
|
|
135
|
-
// isOccupiedLocal so it can fold the per-corner block lookup into a
|
|
136
|
-
// direct typed-array index instead of a hash lookup.
|
|
137
|
-
let bx = 0, by = 0, bz = 0;
|
|
138
|
-
|
|
139
|
-
if (cx < 0 || cy < 0 || cz < 0) {
|
|
140
|
-
return false;
|
|
141
|
-
}
|
|
142
|
-
const idx = (
|
|
143
|
-
const entry = neighborEntry[idx];
|
|
144
|
-
if (entry === NEIGHBOR_EMPTY) {
|
|
145
|
-
return false;
|
|
146
|
-
}
|
|
147
|
-
if (entry === NEIGHBOR_SOLID) {
|
|
148
|
-
return true;
|
|
149
|
-
}
|
|
150
|
-
const lo = neighborMasks[idx * 2];
|
|
151
|
-
const hi = neighborMasks[idx * 2 + 1];
|
|
152
|
-
return isVoxelSet(lo, hi, cx & 3, cy & 3, cz & 3);
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
if (posLen + 3 > posCap) {
|
|
156
|
-
posCap *= 2;
|
|
157
|
-
const grown = new Float32Array(posCap);
|
|
158
|
-
grown.set(positions);
|
|
159
|
-
positions = grown;
|
|
160
|
-
}
|
|
161
|
-
const idx = posLen / 3;
|
|
162
|
-
positions[posLen++] = px;
|
|
163
|
-
positions[posLen++] = py;
|
|
164
|
-
positions[posLen++] = pz;
|
|
165
|
-
return idx;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
if (idxLen + additional <= idxCap) {
|
|
169
|
-
return;
|
|
170
|
-
}
|
|
171
|
-
while (idxLen + additional > idxCap) {
|
|
172
|
-
idxCap *= 2;
|
|
173
|
-
}
|
|
174
|
-
const grown = new Uint32Array(idxCap);
|
|
175
|
-
grown.set(indices);
|
|
176
|
-
indices = grown;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
ensureIndexCapacity(3);
|
|
180
|
-
indices[idxLen++] = a;
|
|
181
|
-
indices[idxLen++] = b;
|
|
182
|
-
indices[idxLen++] = c;
|
|
183
|
-
}
|
|
184
|
-
// When flat MC face cells are merged into large rectangles, rectangle
|
|
185
|
-
// boundaries must still be split at any neighbouring raw-MC vertex that
|
|
186
|
-
// lies along the same edge. Otherwise the pre-merged mesh can contain
|
|
187
|
-
// T-junctions before the final coplanarMerge pass. Coordinates here use
|
|
188
|
-
// the exact binary-MC half-grid: voxel corners are even, MC edge
|
|
189
|
-
// midpoints are odd on the crossed edge axis.
|
|
190
|
-
const splitLinePoints = mergeFlatFaces ? new Map() : undefined;
|
|
191
|
-
let collectSplitPoints = mergeFlatFaces;
|
|
192
|
-
|
|
193
|
-
const x = x2 + scaledCoordOffset;
|
|
194
|
-
const y = y2 + scaledCoordOffset;
|
|
195
|
-
const z = z2 + scaledCoordOffset;
|
|
196
|
-
if (varAxis === 0) {
|
|
197
|
-
return (y * scaledCoordStride + z) * 3;
|
|
198
|
-
}
|
|
199
|
-
if (varAxis === 1) {
|
|
200
|
-
return (x * scaledCoordStride + z) * 3 + 1;
|
|
201
|
-
}
|
|
202
|
-
return (x * scaledCoordStride + y) * 3 + 2;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
if (!splitLinePoints) {
|
|
206
|
-
return;
|
|
207
|
-
}
|
|
208
|
-
let points = splitLinePoints.get(key);
|
|
209
|
-
if (!points) {
|
|
210
|
-
points = [];
|
|
211
|
-
splitLinePoints.set(key, points);
|
|
212
|
-
}
|
|
213
|
-
points.push(value);
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
if (!splitLinePoints || !collectSplitPoints) {
|
|
217
|
-
return;
|
|
218
|
-
}
|
|
219
|
-
const x2 = vx * 2 + (axis === 0 ? 1 : 0);
|
|
220
|
-
const y2 = vy * 2 + (axis === 1 ? 1 : 0);
|
|
221
|
-
const z2 = vz * 2 + (axis === 2 ? 1 : 0);
|
|
222
|
-
addSplitLinePoint(splitLineKey(0, x2, y2, z2), x2);
|
|
223
|
-
addSplitLinePoint(splitLineKey(1, x2, y2, z2), y2);
|
|
224
|
-
addSplitLinePoint(splitLineKey(2, x2, y2, z2), z2);
|
|
225
|
-
}
|
|
226
|
-
// Get or create a vertex at the midpoint of an edge.
|
|
227
|
-
// Edge is identified by the lower corner voxel coordinate and axis (0=x, 1=y, 2=z).
|
|
228
|
-
|
|
229
|
-
// Pack (vx, vy, vz, axis) into a single key. Offset by 1 so that
|
|
230
|
-
// vx = -1 (from the boundary extension) maps to 0, keeping keys non-negative.
|
|
231
|
-
const key = (
|
|
232
|
-
// Probe for either the matching slot or the next empty one.
|
|
233
|
-
let i = (Math.imul(key | 0,
|
|
234
|
-
while (true) {
|
|
235
|
-
const k = vKeys[i];
|
|
236
|
-
if (k === key) {
|
|
237
|
-
return vVals[i];
|
|
238
|
-
}
|
|
239
|
-
if (k === -1) {
|
|
240
|
-
break;
|
|
241
|
-
}
|
|
242
|
-
i = (i + 1) & vMask;
|
|
243
|
-
}
|
|
244
|
-
let px = originX + vx * voxelResolution;
|
|
245
|
-
let py = originY + vy * voxelResolution;
|
|
246
|
-
let pz = originZ + vz * voxelResolution;
|
|
247
|
-
// Place vertex at edge midpoint (binary field -> always at 0.5)
|
|
248
|
-
if (axis === 0) {
|
|
249
|
-
px += voxelResolution * 0.5;
|
|
250
|
-
}
|
|
251
|
-
else if (axis === 1) {
|
|
252
|
-
py += voxelResolution * 0.5;
|
|
253
|
-
}
|
|
254
|
-
else {
|
|
255
|
-
pz += voxelResolution * 0.5;
|
|
256
|
-
}
|
|
257
|
-
const idx = addPosition(px, py, pz);
|
|
258
|
-
addSplitPointForVertex(vx, vy, vz, axis);
|
|
259
|
-
vKeys[i] = key;
|
|
260
|
-
vVals[i] = idx;
|
|
261
|
-
vSize++;
|
|
262
|
-
if (vSize > ((vCap * 0.7) | 0)) {
|
|
263
|
-
vGrow();
|
|
264
|
-
}
|
|
265
|
-
return idx;
|
|
266
|
-
}
|
|
267
|
-
// Full-face MC cases can be merged before vertex creation. Encode each
|
|
268
|
-
// unit face cell as a sortable integer in Float64: bucket / plane / u / v,
|
|
269
|
-
// where bucket = axis*2 + positiveNormalBit and coordinates are offset by
|
|
270
|
-
// +1 to cover the -1 boundary extension.
|
|
271
|
-
const faceCoordStride = Math.max(gridNx, gridNy, gridNz) + 3;
|
|
272
|
-
let faceCellCap = 0;
|
|
273
|
-
let faceCellLen = 0;
|
|
274
|
-
let faceCellKeys = new Float64Array(0);
|
|
275
|
-
const diagCoordStride = (Math.max(gridNx, gridNy, gridNz) + 3) * 8 + 9;
|
|
276
|
-
const diagCoordOffset = Math.floor(diagCoordStride / 2);
|
|
277
|
-
let diagCellCap = 0;
|
|
278
|
-
let diagCellLen = 0;
|
|
279
|
-
let diagCellKeys = new Float64Array(0);
|
|
280
|
-
|
|
281
|
-
if (faceCellLen === faceCellCap) {
|
|
282
|
-
faceCellCap = faceCellCap === 0 ? 1024 : faceCellCap * 2;
|
|
283
|
-
const grown = new Float64Array(faceCellCap);
|
|
284
|
-
grown.set(faceCellKeys);
|
|
285
|
-
faceCellKeys = grown;
|
|
286
|
-
}
|
|
287
|
-
faceCellKeys[faceCellLen++] =
|
|
288
|
-
((
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
grown
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
(
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
if (!mergeFlatFaces) {
|
|
304
|
-
return false;
|
|
305
|
-
}
|
|
306
|
-
switch (cubeIndex) {
|
|
307
|
-
case 153: // low-X corners occupied, high-X corners empty => +X normal
|
|
308
|
-
addFaceCell(1, vx, vy, vz);
|
|
309
|
-
return true;
|
|
310
|
-
case 102: // high-X occupied => -X normal
|
|
311
|
-
addFaceCell(0, vx, vy, vz);
|
|
312
|
-
return true;
|
|
313
|
-
case 51: // low-Y occupied => +Y normal
|
|
314
|
-
addFaceCell(3, vy, vx, vz);
|
|
315
|
-
return true;
|
|
316
|
-
case 204: // high-Y occupied => -Y normal
|
|
317
|
-
addFaceCell(2, vy, vx, vz);
|
|
318
|
-
return true;
|
|
319
|
-
case 15: // low-Z occupied => +Z normal
|
|
320
|
-
addFaceCell(5, vz, vx, vy);
|
|
321
|
-
return true;
|
|
322
|
-
case 240: // high-Z occupied => -Z normal
|
|
323
|
-
addFaceCell(4, vz, vx, vy);
|
|
324
|
-
return true;
|
|
325
|
-
default:
|
|
326
|
-
return false;
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
if (axis === 0) {
|
|
331
|
-
return [p * 2 + 1, u * 2, v * 2];
|
|
332
|
-
}
|
|
333
|
-
if (axis === 1) {
|
|
334
|
-
return [u * 2, p * 2 + 1, v * 2];
|
|
335
|
-
}
|
|
336
|
-
return [u * 2, v * 2, p * 2 + 1];
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
const pair = axisA === 0 ? (axisB === 1 ? 0 : 1) : 2;
|
|
340
|
-
const signBits = (signA > 0 ? 1 : 0) | (signB > 0 ? 2 : 0);
|
|
341
|
-
return pair * 4 + signBits;
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
const pair = (bucket / 4) | 0;
|
|
345
|
-
const signBits = bucket & 3;
|
|
346
|
-
const signA = (signBits & 1) !== 0 ? 1 : -1;
|
|
347
|
-
const signB = (signBits & 2) !== 0 ? 1 : -1;
|
|
348
|
-
if (pair === 0) {
|
|
349
|
-
return { axisA: 0, axisB: 1, axisE: 2, signA, signB };
|
|
350
|
-
}
|
|
351
|
-
if (pair === 1) {
|
|
352
|
-
return { axisA: 0, axisB: 2, axisE: 1, signA, signB };
|
|
353
|
-
}
|
|
354
|
-
return { axisA: 1, axisB: 2, axisE: 0, signA, signB };
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
if (axis === 0) {
|
|
358
|
-
return x;
|
|
359
|
-
}
|
|
360
|
-
if (axis === 1) {
|
|
361
|
-
return y;
|
|
362
|
-
}
|
|
363
|
-
return z;
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
const { axisA, axisB, axisE, signA, signB } = decodeDiagBucket(bucket);
|
|
367
|
-
const out = [0, 0, 0];
|
|
368
|
-
out[axisA] = signA * ((plane + u) / 2);
|
|
369
|
-
out[axisB] = signB * ((plane - u) / 2);
|
|
370
|
-
out[axisE] = e;
|
|
371
|
-
return [out[0], out[1], out[2]];
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
const x = vx * 2;
|
|
375
|
-
const y = vy * 2;
|
|
376
|
-
const z = vz * 2;
|
|
377
|
-
switch (edge) {
|
|
378
|
-
case 0:
|
|
379
|
-
out[offset] = x + 1;
|
|
380
|
-
out[offset + 1] = y;
|
|
381
|
-
out[offset + 2] = z;
|
|
382
|
-
break;
|
|
383
|
-
case 1:
|
|
384
|
-
out[offset] = x + 2;
|
|
385
|
-
out[offset + 1] = y + 1;
|
|
386
|
-
out[offset + 2] = z;
|
|
387
|
-
break;
|
|
388
|
-
case 2:
|
|
389
|
-
out[offset] = x + 1;
|
|
390
|
-
out[offset + 1] = y + 2;
|
|
391
|
-
out[offset + 2] = z;
|
|
392
|
-
break;
|
|
393
|
-
case 3:
|
|
394
|
-
out[offset] = x;
|
|
395
|
-
out[offset + 1] = y + 1;
|
|
396
|
-
out[offset + 2] = z;
|
|
397
|
-
break;
|
|
398
|
-
case 4:
|
|
399
|
-
out[offset] = x + 1;
|
|
400
|
-
out[offset + 1] = y;
|
|
401
|
-
out[offset + 2] = z + 2;
|
|
402
|
-
break;
|
|
403
|
-
case 5:
|
|
404
|
-
out[offset] = x + 2;
|
|
405
|
-
out[offset + 1] = y + 1;
|
|
406
|
-
out[offset + 2] = z + 2;
|
|
407
|
-
break;
|
|
408
|
-
case 6:
|
|
409
|
-
out[offset] = x + 1;
|
|
410
|
-
out[offset + 1] = y + 2;
|
|
411
|
-
out[offset + 2] = z + 2;
|
|
412
|
-
break;
|
|
413
|
-
case 7:
|
|
414
|
-
out[offset] = x;
|
|
415
|
-
out[offset + 1] = y + 1;
|
|
416
|
-
out[offset + 2] = z + 2;
|
|
417
|
-
break;
|
|
418
|
-
case 8:
|
|
419
|
-
out[offset] = x;
|
|
420
|
-
out[offset + 1] = y;
|
|
421
|
-
out[offset + 2] = z + 1;
|
|
422
|
-
break;
|
|
423
|
-
case 9:
|
|
424
|
-
out[offset] = x + 2;
|
|
425
|
-
out[offset + 1] = y;
|
|
426
|
-
out[offset + 2] = z + 1;
|
|
427
|
-
break;
|
|
428
|
-
case 10:
|
|
429
|
-
out[offset] = x + 2;
|
|
430
|
-
out[offset + 1] = y + 2;
|
|
431
|
-
out[offset + 2] = z + 1;
|
|
432
|
-
break;
|
|
433
|
-
default:
|
|
434
|
-
out[offset] = x;
|
|
435
|
-
out[offset + 1] = y + 2;
|
|
436
|
-
out[offset + 2] = z + 1;
|
|
437
|
-
break;
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
const pairVerts = new Int32Array(18);
|
|
441
|
-
const uniqueVerts = new Int32Array(12);
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
edgeScaledPoint(triRow[edgesA
|
|
458
|
-
edgeScaledPoint(triRow[
|
|
459
|
-
edgeScaledPoint(triRow[
|
|
460
|
-
edgeScaledPoint(triRow[edgesB
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
uniqueVerts[dst
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
const
|
|
491
|
-
const
|
|
492
|
-
const
|
|
493
|
-
const
|
|
494
|
-
const
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
let
|
|
501
|
-
let
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
const
|
|
521
|
-
const
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
let
|
|
526
|
-
let
|
|
527
|
-
let
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
const
|
|
531
|
-
const
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
const
|
|
555
|
-
const
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
!pointInUnique(
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
let
|
|
565
|
-
let
|
|
566
|
-
let
|
|
567
|
-
let
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
const
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
let
|
|
628
|
-
let
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
const
|
|
666
|
-
const
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
let varAxis;
|
|
696
|
-
let start;
|
|
697
|
-
let end;
|
|
698
|
-
if (x0 !== x1) {
|
|
699
|
-
varAxis = 0;
|
|
700
|
-
start = x0;
|
|
701
|
-
end = x1;
|
|
702
|
-
}
|
|
703
|
-
else if (y0 !== y1) {
|
|
704
|
-
varAxis = 1;
|
|
705
|
-
start = y0;
|
|
706
|
-
end = y1;
|
|
707
|
-
}
|
|
708
|
-
else {
|
|
709
|
-
varAxis = 2;
|
|
710
|
-
start = z0;
|
|
711
|
-
end = z1;
|
|
712
|
-
}
|
|
713
|
-
const points = splitLinePoints?.get(splitLineKey(varAxis, x0, y0, z0));
|
|
714
|
-
if (!points) {
|
|
715
|
-
return;
|
|
716
|
-
}
|
|
717
|
-
const lo = Math.min(start, end);
|
|
718
|
-
const hi = Math.max(start, end);
|
|
719
|
-
const forward = start <= end;
|
|
720
|
-
|
|
721
|
-
if (varAxis === 0) {
|
|
722
|
-
|
|
723
|
-
}
|
|
724
|
-
else if (varAxis === 1) {
|
|
725
|
-
|
|
726
|
-
}
|
|
727
|
-
else {
|
|
728
|
-
|
|
729
|
-
}
|
|
730
|
-
}
|
|
731
|
-
if (forward) {
|
|
732
|
-
for (let i = 0; i < points.length; i++) {
|
|
733
|
-
const t = points[i];
|
|
734
|
-
if (t < lo) {
|
|
735
|
-
continue;
|
|
736
|
-
}
|
|
737
|
-
if (t > hi) {
|
|
738
|
-
break;
|
|
739
|
-
}
|
|
740
|
-
emitPoint(t);
|
|
741
|
-
}
|
|
742
|
-
}
|
|
743
|
-
else {
|
|
744
|
-
for (let i = points.length - 1; i >= 0; i--) {
|
|
745
|
-
const t = points[i];
|
|
746
|
-
if (t > hi) {
|
|
747
|
-
continue;
|
|
748
|
-
}
|
|
749
|
-
if (t < lo) {
|
|
750
|
-
break;
|
|
751
|
-
}
|
|
752
|
-
emitPoint(t);
|
|
753
|
-
}
|
|
754
|
-
}
|
|
755
|
-
}
|
|
756
|
-
|
|
757
|
-
if (useLocalCcw) {
|
|
758
|
-
appendTri(a, b, c);
|
|
759
|
-
}
|
|
760
|
-
else {
|
|
761
|
-
appendTri(a, c, b);
|
|
762
|
-
}
|
|
763
|
-
}
|
|
764
|
-
|
|
765
|
-
const abx = perimeterU[b] - perimeterU[a];
|
|
766
|
-
const aby = perimeterV[b] - perimeterV[a];
|
|
767
|
-
const acx = perimeterU[c] - perimeterU[a];
|
|
768
|
-
const acy = perimeterV[c] - perimeterV[a];
|
|
769
|
-
if (abx * acy - aby * acx <= 0) {
|
|
770
|
-
return;
|
|
771
|
-
}
|
|
772
|
-
appendOrientedTri(perimeterScratch[a], perimeterScratch[b], perimeterScratch[c], useLocalCcw);
|
|
773
|
-
}
|
|
774
|
-
|
|
775
|
-
const chainALen = chainAEnd - chainAStart;
|
|
776
|
-
const chainBLen = chainBEnd - chainBStart;
|
|
777
|
-
if (chainALen < 2 || chainBLen < 2) {
|
|
778
|
-
return;
|
|
779
|
-
}
|
|
780
|
-
const v2 = chainBEnd - 1;
|
|
781
|
-
for (let i = chainAStart; i < chainAEnd - 2; i++) {
|
|
782
|
-
appendPerimeterTri(v2, i, i + 1, useLocalCcw);
|
|
783
|
-
}
|
|
784
|
-
const pivot = chainALen > 1 ? chainAEnd - 2 : chainAStart;
|
|
785
|
-
for (let i = chainBStart; i < chainBEnd - 1; i++) {
|
|
786
|
-
appendPerimeterTri(pivot, i, i + 1, useLocalCcw);
|
|
787
|
-
}
|
|
788
|
-
}
|
|
789
|
-
|
|
790
|
-
const axis = bucket >> 1;
|
|
791
|
-
const positive = (bucket & 1) === 1;
|
|
792
|
-
const a = scaledFacePoint(axis, p, u0, v0);
|
|
793
|
-
const b = scaledFacePoint(axis, p, u1, v0);
|
|
794
|
-
const c = scaledFacePoint(axis, p, u1, v1);
|
|
795
|
-
const d = scaledFacePoint(axis, p, u0, v1);
|
|
796
|
-
perimeterLen = 0;
|
|
797
|
-
const side0Start = 0;
|
|
798
|
-
addSplitEdgeVertices(axis, a[0], a[1], a[2], b[0], b[1], b[2]);
|
|
799
|
-
const side0End = perimeterLen;
|
|
800
|
-
const side1Start = side0End - 1;
|
|
801
|
-
addSplitEdgeVertices(axis, b[0], b[1], b[2], c[0], c[1], c[2]);
|
|
802
|
-
const side1End = perimeterLen;
|
|
803
|
-
const side2Start = side1End - 1;
|
|
804
|
-
addSplitEdgeVertices(axis, c[0], c[1], c[2], d[0], d[1], d[2]);
|
|
805
|
-
const side2End = perimeterLen;
|
|
806
|
-
const side3Start = side2End - 1;
|
|
807
|
-
addSplitEdgeVertices(axis, d[0], d[1], d[2], a[0], a[1], a[2]);
|
|
808
|
-
const side3End = perimeterLen;
|
|
809
|
-
const localCcwIsPositive = axis !== 1;
|
|
810
|
-
const useLocalCcw = positive === localCcwIsPositive;
|
|
811
|
-
triangulateTwoSideChain(side0Start, side0End, side1Start, side1End, useLocalCcw);
|
|
812
|
-
triangulateTwoSideChain(side2Start, side2End, side3Start, side3End, useLocalCcw);
|
|
813
|
-
}
|
|
814
|
-
|
|
815
|
-
const a = diagPoint(bucket, plane, u0, e0);
|
|
816
|
-
const b = diagPoint(bucket, plane, u1, e0);
|
|
817
|
-
const c = diagPoint(bucket, plane, u1, e1);
|
|
818
|
-
const d = diagPoint(bucket, plane, u0, e1);
|
|
819
|
-
|
|
820
|
-
addDiagPerimeterPoint(bucket, x2, y2, z2);
|
|
821
|
-
}
|
|
822
|
-
perimeterLen = 0;
|
|
823
|
-
const side0Start = 0;
|
|
824
|
-
addPoint(a[0], a[1], a[2]);
|
|
825
|
-
addPoint(b[0], b[1], b[2]);
|
|
826
|
-
const side0End = perimeterLen;
|
|
827
|
-
const side1Start = side0End - 1;
|
|
828
|
-
addSplitEdgeVertices(0, b[0], b[1], b[2], c[0], c[1], c[2], addPoint);
|
|
829
|
-
const side1End = perimeterLen;
|
|
830
|
-
const side2Start = side1End - 1;
|
|
831
|
-
addPoint(d[0], d[1], d[2]);
|
|
832
|
-
const side2End = perimeterLen;
|
|
833
|
-
const side3Start = side2End - 1;
|
|
834
|
-
addSplitEdgeVertices(0, d[0], d[1], d[2], a[0], a[1], a[2], addPoint);
|
|
835
|
-
const side3End = perimeterLen;
|
|
836
|
-
const { axisA, axisB, signA, signB } = decodeDiagBucket(bucket);
|
|
837
|
-
const abx = b[0] - a[0];
|
|
838
|
-
const aby = b[1] - a[1];
|
|
839
|
-
const abz = b[2] - a[2];
|
|
840
|
-
const bcx = c[0] - b[0];
|
|
841
|
-
const bcy = c[1] - b[1];
|
|
842
|
-
const bcz = c[2] - b[2];
|
|
843
|
-
const nx = aby * bcz - abz * bcy;
|
|
844
|
-
const ny = abz * bcx - abx * bcz;
|
|
845
|
-
const nz = abx * bcy - aby * bcx;
|
|
846
|
-
const normal = [0, 0, 0];
|
|
847
|
-
normal[axisA] = signA;
|
|
848
|
-
normal[axisB] = signB;
|
|
849
|
-
const useLocalCcw = nx * normal[0] + ny * normal[1] + nz * normal[2] > 0;
|
|
850
|
-
triangulateTwoSideChain(side0Start, side0End, side1Start, side1End, useLocalCcw);
|
|
851
|
-
triangulateTwoSideChain(side2Start, side2End, side3Start, side3End, useLocalCcw);
|
|
852
|
-
}
|
|
853
|
-
|
|
854
|
-
if (faceCellLen === 0 && diagCellLen === 0) {
|
|
855
|
-
return;
|
|
856
|
-
}
|
|
857
|
-
const keys = faceCellKeys.subarray(0, faceCellLen);
|
|
858
|
-
faceCellKeys = new Float64Array(0);
|
|
859
|
-
keys.sort();
|
|
860
|
-
let rectCap = 1024;
|
|
861
|
-
let rectLen = 0;
|
|
862
|
-
let rectBucket = new Int32Array(rectCap);
|
|
863
|
-
let rectP = new Int32Array(rectCap);
|
|
864
|
-
let rectU0 = new Int32Array(rectCap);
|
|
865
|
-
let rectV0 = new Int32Array(rectCap);
|
|
866
|
-
let rectU1 = new Int32Array(rectCap);
|
|
867
|
-
let rectV1 = new Int32Array(rectCap);
|
|
868
|
-
let diagRectCap = 1024;
|
|
869
|
-
let diagRectLen = 0;
|
|
870
|
-
let diagRectBucket = new Int32Array(diagRectCap);
|
|
871
|
-
let diagRectPlane = new Int32Array(diagRectCap);
|
|
872
|
-
let diagRectU0 = new Int32Array(diagRectCap);
|
|
873
|
-
let diagRectE0 = new Int32Array(diagRectCap);
|
|
874
|
-
let diagRectU1 = new Int32Array(diagRectCap);
|
|
875
|
-
let diagRectE1 = new Int32Array(diagRectCap);
|
|
876
|
-
|
|
877
|
-
if (rectLen === rectCap) {
|
|
878
|
-
rectCap *= 2;
|
|
879
|
-
|
|
880
|
-
const out = new Int32Array(rectCap);
|
|
881
|
-
out.set(src);
|
|
882
|
-
return out;
|
|
883
|
-
}
|
|
884
|
-
rectBucket = grow(rectBucket);
|
|
885
|
-
rectP = grow(rectP);
|
|
886
|
-
rectU0 = grow(rectU0);
|
|
887
|
-
rectV0 = grow(rectV0);
|
|
888
|
-
rectU1 = grow(rectU1);
|
|
889
|
-
rectV1 = grow(rectV1);
|
|
890
|
-
}
|
|
891
|
-
rectBucket[rectLen] = bucket;
|
|
892
|
-
rectP[rectLen] = p;
|
|
893
|
-
rectU0[rectLen] = u0;
|
|
894
|
-
rectV0[rectLen] = v0;
|
|
895
|
-
rectU1[rectLen] = u1;
|
|
896
|
-
rectV1[rectLen] = v1;
|
|
897
|
-
rectLen++;
|
|
898
|
-
}
|
|
899
|
-
|
|
900
|
-
if (diagRectLen === diagRectCap) {
|
|
901
|
-
diagRectCap *= 2;
|
|
902
|
-
|
|
903
|
-
const out = new Int32Array(diagRectCap);
|
|
904
|
-
out.set(src);
|
|
905
|
-
return out;
|
|
906
|
-
}
|
|
907
|
-
diagRectBucket = grow(diagRectBucket);
|
|
908
|
-
diagRectPlane = grow(diagRectPlane);
|
|
909
|
-
diagRectU0 = grow(diagRectU0);
|
|
910
|
-
diagRectE0 = grow(diagRectE0);
|
|
911
|
-
diagRectU1 = grow(diagRectU1);
|
|
912
|
-
diagRectE1 = grow(diagRectE1);
|
|
913
|
-
}
|
|
914
|
-
diagRectBucket[diagRectLen] = bucket;
|
|
915
|
-
diagRectPlane[diagRectLen] = plane;
|
|
916
|
-
diagRectU0[diagRectLen] = u0;
|
|
917
|
-
diagRectE0[diagRectLen] = e0;
|
|
918
|
-
diagRectU1[diagRectLen] = u1;
|
|
919
|
-
diagRectE1[diagRectLen] = e1;
|
|
920
|
-
diagRectLen++;
|
|
921
|
-
}
|
|
922
|
-
|
|
923
|
-
let q = Math.floor(key / faceCoordStride);
|
|
924
|
-
q = Math.floor(q / faceCoordStride);
|
|
925
|
-
const pOff = q % faceCoordStride;
|
|
926
|
-
const bucket = Math.floor(q / faceCoordStride);
|
|
927
|
-
return { bucket, pOff };
|
|
928
|
-
}
|
|
929
|
-
|
|
930
|
-
const vOff = key % faceCoordStride;
|
|
931
|
-
const q = Math.floor(key / faceCoordStride);
|
|
932
|
-
const uOff = q % faceCoordStride;
|
|
933
|
-
return uOff * faceCoordStride + vOff;
|
|
934
|
-
}
|
|
935
|
-
let start = 0;
|
|
936
|
-
while (start < keys.length) {
|
|
937
|
-
const { bucket, pOff } = decodeGroup(keys[start]);
|
|
938
|
-
let end = start + 1;
|
|
939
|
-
while (end < keys.length) {
|
|
940
|
-
const g = decodeGroup(keys[end]);
|
|
941
|
-
if (g.bucket !== bucket || g.pOff !== pOff) {
|
|
942
|
-
break;
|
|
943
|
-
}
|
|
944
|
-
end++;
|
|
945
|
-
}
|
|
946
|
-
const count = end - start;
|
|
947
|
-
let hCap = 1;
|
|
948
|
-
while (hCap < count / 0.7) {
|
|
949
|
-
hCap *= 2;
|
|
950
|
-
}
|
|
951
|
-
const hMask = hCap - 1;
|
|
952
|
-
const hKeys = new Float64Array(hCap).fill(-1);
|
|
953
|
-
const hVals = new Int32Array(hCap);
|
|
954
|
-
|
|
955
|
-
const hi = (key / 0x100000000) | 0;
|
|
956
|
-
return (Math.imul((key | 0) ^ hi,
|
|
957
|
-
}
|
|
958
|
-
for (let i = 0; i < count; i++) {
|
|
959
|
-
const uvKey = decodeUvKey(keys[start + i]);
|
|
960
|
-
let h = hash(uvKey);
|
|
961
|
-
while (hKeys[h] !== -1) {
|
|
962
|
-
h = (h + 1) & hMask;
|
|
963
|
-
}
|
|
964
|
-
hKeys[h] = uvKey;
|
|
965
|
-
hVals[h] = i;
|
|
966
|
-
}
|
|
967
|
-
|
|
968
|
-
let h = hash(uvKey);
|
|
969
|
-
while (true) {
|
|
970
|
-
const k = hKeys[h];
|
|
971
|
-
if (k === uvKey) {
|
|
972
|
-
return hVals[h];
|
|
973
|
-
}
|
|
974
|
-
if (k === -1) {
|
|
975
|
-
return -1;
|
|
976
|
-
}
|
|
977
|
-
h = (h + 1) & hMask;
|
|
978
|
-
}
|
|
979
|
-
}
|
|
980
|
-
const visited = new Uint8Array(count);
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
const
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
diagKeys.
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
const
|
|
1031
|
-
q = Math.floor(
|
|
1032
|
-
const
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
i
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
const
|
|
1074
|
-
const
|
|
1075
|
-
const
|
|
1076
|
-
const
|
|
1077
|
-
const
|
|
1078
|
-
const
|
|
1079
|
-
const
|
|
1080
|
-
const
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
addSplitSegment(
|
|
1084
|
-
addSplitSegment(
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
const
|
|
1090
|
-
const
|
|
1091
|
-
const
|
|
1092
|
-
const
|
|
1093
|
-
const
|
|
1094
|
-
const
|
|
1095
|
-
const
|
|
1096
|
-
const
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
addSplitSegment(
|
|
1100
|
-
addSplitSegment(
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
//
|
|
1126
|
-
//
|
|
1127
|
-
//
|
|
1128
|
-
//
|
|
1129
|
-
//
|
|
1130
|
-
//
|
|
1131
|
-
//
|
|
1132
|
-
//
|
|
1133
|
-
//
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
const
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
//
|
|
1157
|
-
//
|
|
1158
|
-
//
|
|
1159
|
-
//
|
|
1160
|
-
//
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
const
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
neighborMasks[slot * 2
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
//
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
//
|
|
1205
|
-
//
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
//
|
|
1213
|
-
//
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
const
|
|
1219
|
-
const
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
const
|
|
1223
|
-
const
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
//
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
if (
|
|
1257
|
-
|
|
1258
|
-
}
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
const
|
|
1269
|
-
const
|
|
1270
|
-
const
|
|
1271
|
-
const
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
if (
|
|
1281
|
-
continue;
|
|
1282
|
-
}
|
|
1283
|
-
const
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
//
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
//
|
|
1364
|
-
//
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
0xbac, 0xaa5, 0x9af, 0x8a6, 0xfaa, 0xea3, 0xda9, 0xca0,
|
|
1374
|
-
0x460, 0x569, 0x663, 0x76a, 0x066, 0x16f, 0x265, 0x36c,
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
]
|
|
1399
|
-
|
|
1400
|
-
[],
|
|
1401
|
-
[0, 8,
|
|
1402
|
-
[0,
|
|
1403
|
-
[
|
|
1404
|
-
[
|
|
1405
|
-
[
|
|
1406
|
-
[
|
|
1407
|
-
[
|
|
1408
|
-
[
|
|
1409
|
-
[
|
|
1410
|
-
[
|
|
1411
|
-
[
|
|
1412
|
-
[
|
|
1413
|
-
[
|
|
1414
|
-
[
|
|
1415
|
-
[9,
|
|
1416
|
-
[
|
|
1417
|
-
[4,
|
|
1418
|
-
[0,
|
|
1419
|
-
[4,
|
|
1420
|
-
[
|
|
1421
|
-
[
|
|
1422
|
-
[
|
|
1423
|
-
[
|
|
1424
|
-
[
|
|
1425
|
-
[
|
|
1426
|
-
[
|
|
1427
|
-
[
|
|
1428
|
-
[
|
|
1429
|
-
[
|
|
1430
|
-
[
|
|
1431
|
-
[
|
|
1432
|
-
[9, 5, 4],
|
|
1433
|
-
[9, 5,
|
|
1434
|
-
[0, 5,
|
|
1435
|
-
[
|
|
1436
|
-
[
|
|
1437
|
-
[3, 0,
|
|
1438
|
-
[
|
|
1439
|
-
[
|
|
1440
|
-
[9, 5,
|
|
1441
|
-
[
|
|
1442
|
-
[0,
|
|
1443
|
-
[2,
|
|
1444
|
-
[
|
|
1445
|
-
[
|
|
1446
|
-
[
|
|
1447
|
-
[
|
|
1448
|
-
[9,
|
|
1449
|
-
[
|
|
1450
|
-
[0,
|
|
1451
|
-
[
|
|
1452
|
-
[
|
|
1453
|
-
[
|
|
1454
|
-
[
|
|
1455
|
-
[
|
|
1456
|
-
[
|
|
1457
|
-
[
|
|
1458
|
-
[
|
|
1459
|
-
[
|
|
1460
|
-
[
|
|
1461
|
-
[
|
|
1462
|
-
[
|
|
1463
|
-
[
|
|
1464
|
-
[
|
|
1465
|
-
[0, 8,
|
|
1466
|
-
[
|
|
1467
|
-
[
|
|
1468
|
-
[
|
|
1469
|
-
[
|
|
1470
|
-
[9,
|
|
1471
|
-
[
|
|
1472
|
-
[2,
|
|
1473
|
-
[
|
|
1474
|
-
[
|
|
1475
|
-
[
|
|
1476
|
-
[
|
|
1477
|
-
[
|
|
1478
|
-
[
|
|
1479
|
-
[
|
|
1480
|
-
[5,
|
|
1481
|
-
[
|
|
1482
|
-
[
|
|
1483
|
-
[
|
|
1484
|
-
[
|
|
1485
|
-
[
|
|
1486
|
-
[
|
|
1487
|
-
[
|
|
1488
|
-
[
|
|
1489
|
-
[
|
|
1490
|
-
[0,
|
|
1491
|
-
[
|
|
1492
|
-
[
|
|
1493
|
-
[
|
|
1494
|
-
[
|
|
1495
|
-
[6,
|
|
1496
|
-
[
|
|
1497
|
-
[
|
|
1498
|
-
[
|
|
1499
|
-
[
|
|
1500
|
-
[
|
|
1501
|
-
[
|
|
1502
|
-
[
|
|
1503
|
-
[
|
|
1504
|
-
[
|
|
1505
|
-
[
|
|
1506
|
-
[
|
|
1507
|
-
[
|
|
1508
|
-
[
|
|
1509
|
-
[
|
|
1510
|
-
[
|
|
1511
|
-
[
|
|
1512
|
-
[
|
|
1513
|
-
[0,
|
|
1514
|
-
[
|
|
1515
|
-
[
|
|
1516
|
-
[
|
|
1517
|
-
[
|
|
1518
|
-
[
|
|
1519
|
-
[
|
|
1520
|
-
[
|
|
1521
|
-
[
|
|
1522
|
-
[
|
|
1523
|
-
[
|
|
1524
|
-
[
|
|
1525
|
-
[0,
|
|
1526
|
-
[
|
|
1527
|
-
[
|
|
1528
|
-
[7, 6,
|
|
1529
|
-
[
|
|
1530
|
-
[0,
|
|
1531
|
-
[
|
|
1532
|
-
[
|
|
1533
|
-
[
|
|
1534
|
-
[
|
|
1535
|
-
[
|
|
1536
|
-
[
|
|
1537
|
-
[
|
|
1538
|
-
[
|
|
1539
|
-
[
|
|
1540
|
-
[
|
|
1541
|
-
[
|
|
1542
|
-
[
|
|
1543
|
-
[
|
|
1544
|
-
[6, 8, 4,
|
|
1545
|
-
[
|
|
1546
|
-
[
|
|
1547
|
-
[9, 4, 6,
|
|
1548
|
-
[
|
|
1549
|
-
[
|
|
1550
|
-
[
|
|
1551
|
-
[
|
|
1552
|
-
[
|
|
1553
|
-
[
|
|
1554
|
-
[
|
|
1555
|
-
[
|
|
1556
|
-
[
|
|
1557
|
-
[
|
|
1558
|
-
[
|
|
1559
|
-
[
|
|
1560
|
-
[4,
|
|
1561
|
-
[
|
|
1562
|
-
[
|
|
1563
|
-
[
|
|
1564
|
-
[9, 5,
|
|
1565
|
-
[6, 11,
|
|
1566
|
-
[
|
|
1567
|
-
[
|
|
1568
|
-
[
|
|
1569
|
-
[
|
|
1570
|
-
[
|
|
1571
|
-
[6,
|
|
1572
|
-
[
|
|
1573
|
-
[
|
|
1574
|
-
[
|
|
1575
|
-
[
|
|
1576
|
-
[6,
|
|
1577
|
-
[
|
|
1578
|
-
[0,
|
|
1579
|
-
[
|
|
1580
|
-
[
|
|
1581
|
-
[
|
|
1582
|
-
[
|
|
1583
|
-
[
|
|
1584
|
-
[
|
|
1585
|
-
[
|
|
1586
|
-
[
|
|
1587
|
-
[
|
|
1588
|
-
[
|
|
1589
|
-
[
|
|
1590
|
-
[0,
|
|
1591
|
-
[10, 5,
|
|
1592
|
-
[
|
|
1593
|
-
[
|
|
1594
|
-
[
|
|
1595
|
-
[
|
|
1596
|
-
[
|
|
1597
|
-
[
|
|
1598
|
-
[
|
|
1599
|
-
[
|
|
1600
|
-
[2, 5,
|
|
1601
|
-
[
|
|
1602
|
-
[
|
|
1603
|
-
[9,
|
|
1604
|
-
[
|
|
1605
|
-
[
|
|
1606
|
-
[
|
|
1607
|
-
[
|
|
1608
|
-
[
|
|
1609
|
-
[
|
|
1610
|
-
[
|
|
1611
|
-
[
|
|
1612
|
-
[
|
|
1613
|
-
[0,
|
|
1614
|
-
[
|
|
1615
|
-
[
|
|
1616
|
-
[
|
|
1617
|
-
[
|
|
1618
|
-
[
|
|
1619
|
-
[
|
|
1620
|
-
[
|
|
1621
|
-
[
|
|
1622
|
-
[
|
|
1623
|
-
[
|
|
1624
|
-
[4,
|
|
1625
|
-
[
|
|
1626
|
-
[
|
|
1627
|
-
[
|
|
1628
|
-
[
|
|
1629
|
-
[
|
|
1630
|
-
[
|
|
1631
|
-
[
|
|
1632
|
-
[
|
|
1633
|
-
[
|
|
1634
|
-
[
|
|
1635
|
-
[
|
|
1636
|
-
[
|
|
1637
|
-
[
|
|
1638
|
-
[
|
|
1639
|
-
[
|
|
1640
|
-
[
|
|
1641
|
-
[
|
|
1642
|
-
[0,
|
|
1643
|
-
[
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
[0, 2, 11, 8, 0, 11],
|
|
1647
|
-
[3, 2, 11],
|
|
1648
|
-
[2, 3, 8, 2, 8, 10, 10, 8, 9],
|
|
1649
|
-
[9, 10, 2, 0, 9, 2],
|
|
1650
|
-
[2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8],
|
|
1651
|
-
[1, 10, 2],
|
|
1652
|
-
[1, 3, 8, 9, 1, 8],
|
|
1653
|
-
[0, 9, 1],
|
|
1654
|
-
[0, 3, 8],
|
|
1655
|
-
[]
|
|
1656
|
-
];
|
|
1657
|
-
export { marchingCubes };
|
|
1
|
+
import { BLOCKS_PER_WORD, EVEN_BITS, BLOCK_EMPTY, BLOCK_SOLID, readBlockType, } from './common.js';
|
|
2
|
+
// ============================================================================
|
|
3
|
+
// Voxel bit helpers
|
|
4
|
+
// ============================================================================
|
|
5
|
+
// Bit layout within a 4x4x4 block: bitIdx = lx + ly*4 + lz*16
|
|
6
|
+
// lo = bits 0-31 (lz 0-1), hi = bits 32-63 (lz 2-3)
|
|
7
|
+
/**
|
|
8
|
+
* Test whether a voxel is occupied within a block's bitmask.
|
|
9
|
+
*
|
|
10
|
+
* @param lo - Lower 32 bits of the block mask
|
|
11
|
+
* @param hi - Upper 32 bits of the block mask
|
|
12
|
+
* @param lx - Local x coordinate (0-3)
|
|
13
|
+
* @param ly - Local y coordinate (0-3)
|
|
14
|
+
* @param lz - Local z coordinate (0-3)
|
|
15
|
+
* @returns True if the voxel is occupied
|
|
16
|
+
*/
|
|
17
|
+
function isVoxelSet(lo, hi, lx, ly, lz) {
|
|
18
|
+
const bitIdx = lx + ly * 4 + lz * 16;
|
|
19
|
+
if (bitIdx < 32) {
|
|
20
|
+
return (lo & (1 << bitIdx)) !== 0;
|
|
21
|
+
}
|
|
22
|
+
return (hi & (1 << (bitIdx - 32))) !== 0;
|
|
23
|
+
}
|
|
24
|
+
// ============================================================================
|
|
25
|
+
// Marching Cubes
|
|
26
|
+
// ============================================================================
|
|
27
|
+
// Sentinel values for the per-block 3x3x3 neighbor table.
|
|
28
|
+
const NEIGHBOR_EMPTY = -2;
|
|
29
|
+
const NEIGHBOR_SOLID = -1;
|
|
30
|
+
/**
|
|
31
|
+
* Extract a triangle mesh from a SparseVoxelGrid using marching cubes.
|
|
32
|
+
*
|
|
33
|
+
* Each voxel is treated as a cell in the marching cubes grid. Corner values
|
|
34
|
+
* are binary (0 = empty, 1 = occupied) with a 0.5 threshold. Vertices are
|
|
35
|
+
* placed at edge midpoints, producing the binary-field isosurface between
|
|
36
|
+
* occupied and empty samples.
|
|
37
|
+
*
|
|
38
|
+
* @param grid - Voxel grid (after filtering / nav phases)
|
|
39
|
+
* @param gridBounds - Grid bounds aligned to block boundaries
|
|
40
|
+
* @param voxelResolution - Size of each voxel in world units
|
|
41
|
+
* @param options - Optional extraction settings
|
|
42
|
+
* @returns Mesh with positions and indices
|
|
43
|
+
*/
|
|
44
|
+
function marchingCubes(grid, gridBounds, voxelResolution, options = {}) {
|
|
45
|
+
const { nbx, nby, nbz, bStride, types, masks } = grid;
|
|
46
|
+
const totalBlocks = nbx * nby * nbz;
|
|
47
|
+
const mergeFlatFaces = options.mergeFlatFaces === true;
|
|
48
|
+
// Vertex deduplication: edge ID -> vertex index. Open-addressed typed-
|
|
49
|
+
// array hash table rather than `Map<number, number>` because (1) a single
|
|
50
|
+
// V8 Map is capped at ~2^24 entries (large carved scenes blow past this),
|
|
51
|
+
// and (2) per-entry overhead in Map is ~50 bytes vs 12 bytes (Float64 key
|
|
52
|
+
// + Uint32 value) here, which matters when the table holds tens of
|
|
53
|
+
// millions of vertices.
|
|
54
|
+
//
|
|
55
|
+
// Empty slots are marked with `key === -1` (real keys are non-negative).
|
|
56
|
+
// Hash uses Fibonacci constant on the lower 32 bits of the key. The same
|
|
57
|
+
// structure is used for orphan-cell deduplication, where `vVals` is unused.
|
|
58
|
+
let vCap = 1 << 14;
|
|
59
|
+
let vMask = vCap - 1;
|
|
60
|
+
let vSize = 0;
|
|
61
|
+
let vKeys = new Float64Array(vCap).fill(-1);
|
|
62
|
+
let vVals = new Uint32Array(vCap);
|
|
63
|
+
function vGrow() {
|
|
64
|
+
const oldKeys = vKeys;
|
|
65
|
+
const oldVals = vVals;
|
|
66
|
+
const oldCap = vCap;
|
|
67
|
+
vCap *= 2;
|
|
68
|
+
vMask = vCap - 1;
|
|
69
|
+
vKeys = new Float64Array(vCap).fill(-1);
|
|
70
|
+
vVals = new Uint32Array(vCap);
|
|
71
|
+
for (let j = 0; j < oldCap; j++) {
|
|
72
|
+
const k = oldKeys[j];
|
|
73
|
+
if (k === -1) {
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
let i = (Math.imul(k | 0, 0x9e3779b9) >>> 0) & vMask;
|
|
77
|
+
while (vKeys[i] !== -1) {
|
|
78
|
+
i = (i + 1) & vMask;
|
|
79
|
+
}
|
|
80
|
+
vKeys[i] = k;
|
|
81
|
+
vVals[i] = oldVals[j];
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
let oCap = 1 << 14;
|
|
85
|
+
let oMask = oCap - 1;
|
|
86
|
+
let oSize = 0;
|
|
87
|
+
let oKeys = new Float64Array(oCap).fill(-1);
|
|
88
|
+
function oGrow() {
|
|
89
|
+
const oldKeys = oKeys;
|
|
90
|
+
const oldCap = oCap;
|
|
91
|
+
oCap *= 2;
|
|
92
|
+
oMask = oCap - 1;
|
|
93
|
+
oKeys = new Float64Array(oCap).fill(-1);
|
|
94
|
+
for (let j = 0; j < oldCap; j++) {
|
|
95
|
+
const k = oldKeys[j];
|
|
96
|
+
if (k === -1) {
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
let i = (Math.imul(k | 0, 0x9e3779b9) >>> 0) & oMask;
|
|
100
|
+
while (oKeys[i] !== -1) {
|
|
101
|
+
i = (i + 1) & oMask;
|
|
102
|
+
}
|
|
103
|
+
oKeys[i] = k;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
// Growable typed-array buffers. Capacity doubles on demand to avoid
|
|
107
|
+
// the GC churn of pushing into JS number[] for huge meshes.
|
|
108
|
+
let posCap = 1024;
|
|
109
|
+
let posLen = 0;
|
|
110
|
+
let positions = new Float32Array(posCap);
|
|
111
|
+
let idxCap = 1024;
|
|
112
|
+
let idxLen = 0;
|
|
113
|
+
let indices = new Uint32Array(idxCap);
|
|
114
|
+
const originX = gridBounds.min.x;
|
|
115
|
+
const originY = gridBounds.min.y;
|
|
116
|
+
const originZ = gridBounds.min.z;
|
|
117
|
+
const gridNx = Math.round((gridBounds.max.x - gridBounds.min.x) / voxelResolution);
|
|
118
|
+
const gridNy = Math.round((gridBounds.max.y - gridBounds.min.y) / voxelResolution);
|
|
119
|
+
const gridNz = Math.round((gridBounds.max.z - gridBounds.min.z) / voxelResolution);
|
|
120
|
+
// Compute strides from actual grid dimensions (+3 for the -1 boundary
|
|
121
|
+
// extension, the far edge +1, and one extra for safety).
|
|
122
|
+
const strideX = gridNx + 3;
|
|
123
|
+
const strideXY = strideX * (gridNy + 3);
|
|
124
|
+
const scaledCoordStride = (Math.max(gridNx, gridNy, gridNz) + 3) * 2 + 5;
|
|
125
|
+
const scaledCoordOffset = 3;
|
|
126
|
+
// Per-block 3x3x3 neighbor lookup table populated once per processed block.
|
|
127
|
+
// Index = (dx+1) + (dy+1)*3 + (dz+1)*9, dx/dy/dz in {-1, 0, 1}.
|
|
128
|
+
// neighborEntry: NEIGHBOR_EMPTY, NEIGHBOR_SOLID, or NEIGHBOR_MIXED (mask in neighborMasks).
|
|
129
|
+
const NEIGHBOR_MIXED = 0;
|
|
130
|
+
const neighborEntry = new Int32Array(27);
|
|
131
|
+
const neighborMasks = new Uint32Array(54);
|
|
132
|
+
// Reused scratch for emitted edge vertex indices.
|
|
133
|
+
const edgeVerts = new Int32Array(12);
|
|
134
|
+
// Block coordinate of the block currently being processed. Captured by
|
|
135
|
+
// isOccupiedLocal so it can fold the per-corner block lookup into a
|
|
136
|
+
// direct typed-array index instead of a hash lookup.
|
|
137
|
+
let bx = 0, by = 0, bz = 0;
|
|
138
|
+
function isOccupiedLocal(cx, cy, cz) {
|
|
139
|
+
if (cx < 0 || cy < 0 || cz < 0) {
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
const idx = (cx >> 2) - bx + 1 + ((cy >> 2) - by + 1) * 3 + ((cz >> 2) - bz + 1) * 9;
|
|
143
|
+
const entry = neighborEntry[idx];
|
|
144
|
+
if (entry === NEIGHBOR_EMPTY) {
|
|
145
|
+
return false;
|
|
146
|
+
}
|
|
147
|
+
if (entry === NEIGHBOR_SOLID) {
|
|
148
|
+
return true;
|
|
149
|
+
}
|
|
150
|
+
const lo = neighborMasks[idx * 2];
|
|
151
|
+
const hi = neighborMasks[idx * 2 + 1];
|
|
152
|
+
return isVoxelSet(lo, hi, cx & 3, cy & 3, cz & 3);
|
|
153
|
+
}
|
|
154
|
+
function addPosition(px, py, pz) {
|
|
155
|
+
if (posLen + 3 > posCap) {
|
|
156
|
+
posCap *= 2;
|
|
157
|
+
const grown = new Float32Array(posCap);
|
|
158
|
+
grown.set(positions);
|
|
159
|
+
positions = grown;
|
|
160
|
+
}
|
|
161
|
+
const idx = posLen / 3;
|
|
162
|
+
positions[posLen++] = px;
|
|
163
|
+
positions[posLen++] = py;
|
|
164
|
+
positions[posLen++] = pz;
|
|
165
|
+
return idx;
|
|
166
|
+
}
|
|
167
|
+
function ensureIndexCapacity(additional) {
|
|
168
|
+
if (idxLen + additional <= idxCap) {
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
while (idxLen + additional > idxCap) {
|
|
172
|
+
idxCap *= 2;
|
|
173
|
+
}
|
|
174
|
+
const grown = new Uint32Array(idxCap);
|
|
175
|
+
grown.set(indices);
|
|
176
|
+
indices = grown;
|
|
177
|
+
}
|
|
178
|
+
function appendTri(a, b, c) {
|
|
179
|
+
ensureIndexCapacity(3);
|
|
180
|
+
indices[idxLen++] = a;
|
|
181
|
+
indices[idxLen++] = b;
|
|
182
|
+
indices[idxLen++] = c;
|
|
183
|
+
}
|
|
184
|
+
// When flat MC face cells are merged into large rectangles, rectangle
|
|
185
|
+
// boundaries must still be split at any neighbouring raw-MC vertex that
|
|
186
|
+
// lies along the same edge. Otherwise the pre-merged mesh can contain
|
|
187
|
+
// T-junctions before the final coplanarMerge pass. Coordinates here use
|
|
188
|
+
// the exact binary-MC half-grid: voxel corners are even, MC edge
|
|
189
|
+
// midpoints are odd on the crossed edge axis.
|
|
190
|
+
const splitLinePoints = mergeFlatFaces ? new Map() : undefined;
|
|
191
|
+
let collectSplitPoints = mergeFlatFaces;
|
|
192
|
+
function splitLineKey(varAxis, x2, y2, z2) {
|
|
193
|
+
const x = x2 + scaledCoordOffset;
|
|
194
|
+
const y = y2 + scaledCoordOffset;
|
|
195
|
+
const z = z2 + scaledCoordOffset;
|
|
196
|
+
if (varAxis === 0) {
|
|
197
|
+
return (y * scaledCoordStride + z) * 3;
|
|
198
|
+
}
|
|
199
|
+
if (varAxis === 1) {
|
|
200
|
+
return (x * scaledCoordStride + z) * 3 + 1;
|
|
201
|
+
}
|
|
202
|
+
return (x * scaledCoordStride + y) * 3 + 2;
|
|
203
|
+
}
|
|
204
|
+
function addSplitLinePoint(key, value) {
|
|
205
|
+
if (!splitLinePoints) {
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
let points = splitLinePoints.get(key);
|
|
209
|
+
if (!points) {
|
|
210
|
+
points = [];
|
|
211
|
+
splitLinePoints.set(key, points);
|
|
212
|
+
}
|
|
213
|
+
points.push(value);
|
|
214
|
+
}
|
|
215
|
+
function addSplitPointForVertex(vx, vy, vz, axis) {
|
|
216
|
+
if (!splitLinePoints || !collectSplitPoints) {
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
const x2 = vx * 2 + (axis === 0 ? 1 : 0);
|
|
220
|
+
const y2 = vy * 2 + (axis === 1 ? 1 : 0);
|
|
221
|
+
const z2 = vz * 2 + (axis === 2 ? 1 : 0);
|
|
222
|
+
addSplitLinePoint(splitLineKey(0, x2, y2, z2), x2);
|
|
223
|
+
addSplitLinePoint(splitLineKey(1, x2, y2, z2), y2);
|
|
224
|
+
addSplitLinePoint(splitLineKey(2, x2, y2, z2), z2);
|
|
225
|
+
}
|
|
226
|
+
// Get or create a vertex at the midpoint of an edge.
|
|
227
|
+
// Edge is identified by the lower corner voxel coordinate and axis (0=x, 1=y, 2=z).
|
|
228
|
+
function getVertex(vx, vy, vz, axis) {
|
|
229
|
+
// Pack (vx, vy, vz, axis) into a single key. Offset by 1 so that
|
|
230
|
+
// vx = -1 (from the boundary extension) maps to 0, keeping keys non-negative.
|
|
231
|
+
const key = (vx + 1 + (vy + 1) * strideX + (vz + 1) * strideXY) * 3 + axis;
|
|
232
|
+
// Probe for either the matching slot or the next empty one.
|
|
233
|
+
let i = (Math.imul(key | 0, 0x9e3779b9) >>> 0) & vMask;
|
|
234
|
+
while (true) {
|
|
235
|
+
const k = vKeys[i];
|
|
236
|
+
if (k === key) {
|
|
237
|
+
return vVals[i];
|
|
238
|
+
}
|
|
239
|
+
if (k === -1) {
|
|
240
|
+
break;
|
|
241
|
+
}
|
|
242
|
+
i = (i + 1) & vMask;
|
|
243
|
+
}
|
|
244
|
+
let px = originX + vx * voxelResolution;
|
|
245
|
+
let py = originY + vy * voxelResolution;
|
|
246
|
+
let pz = originZ + vz * voxelResolution;
|
|
247
|
+
// Place vertex at edge midpoint (binary field -> always at 0.5)
|
|
248
|
+
if (axis === 0) {
|
|
249
|
+
px += voxelResolution * 0.5;
|
|
250
|
+
}
|
|
251
|
+
else if (axis === 1) {
|
|
252
|
+
py += voxelResolution * 0.5;
|
|
253
|
+
}
|
|
254
|
+
else {
|
|
255
|
+
pz += voxelResolution * 0.5;
|
|
256
|
+
}
|
|
257
|
+
const idx = addPosition(px, py, pz);
|
|
258
|
+
addSplitPointForVertex(vx, vy, vz, axis);
|
|
259
|
+
vKeys[i] = key;
|
|
260
|
+
vVals[i] = idx;
|
|
261
|
+
vSize++;
|
|
262
|
+
if (vSize > ((vCap * 0.7) | 0)) {
|
|
263
|
+
vGrow();
|
|
264
|
+
}
|
|
265
|
+
return idx;
|
|
266
|
+
}
|
|
267
|
+
// Full-face MC cases can be merged before vertex creation. Encode each
|
|
268
|
+
// unit face cell as a sortable integer in Float64: bucket / plane / u / v,
|
|
269
|
+
// where bucket = axis*2 + positiveNormalBit and coordinates are offset by
|
|
270
|
+
// +1 to cover the -1 boundary extension.
|
|
271
|
+
const faceCoordStride = Math.max(gridNx, gridNy, gridNz) + 3;
|
|
272
|
+
let faceCellCap = 0;
|
|
273
|
+
let faceCellLen = 0;
|
|
274
|
+
let faceCellKeys = new Float64Array(0);
|
|
275
|
+
const diagCoordStride = (Math.max(gridNx, gridNy, gridNz) + 3) * 8 + 9;
|
|
276
|
+
const diagCoordOffset = Math.floor(diagCoordStride / 2);
|
|
277
|
+
let diagCellCap = 0;
|
|
278
|
+
let diagCellLen = 0;
|
|
279
|
+
let diagCellKeys = new Float64Array(0);
|
|
280
|
+
function addFaceCell(bucket, p, u, v) {
|
|
281
|
+
if (faceCellLen === faceCellCap) {
|
|
282
|
+
faceCellCap = faceCellCap === 0 ? 1024 : faceCellCap * 2;
|
|
283
|
+
const grown = new Float64Array(faceCellCap);
|
|
284
|
+
grown.set(faceCellKeys);
|
|
285
|
+
faceCellKeys = grown;
|
|
286
|
+
}
|
|
287
|
+
faceCellKeys[faceCellLen++] =
|
|
288
|
+
((bucket * faceCoordStride + (p + 1)) * faceCoordStride + (u + 1)) * faceCoordStride + (v + 1);
|
|
289
|
+
}
|
|
290
|
+
function addDiagCell(bucket, plane, u, e) {
|
|
291
|
+
if (diagCellLen === diagCellCap) {
|
|
292
|
+
diagCellCap = diagCellCap === 0 ? 1024 : diagCellCap * 2;
|
|
293
|
+
const grown = new Float64Array(diagCellCap);
|
|
294
|
+
grown.set(diagCellKeys);
|
|
295
|
+
diagCellKeys = grown;
|
|
296
|
+
}
|
|
297
|
+
diagCellKeys[diagCellLen++] =
|
|
298
|
+
((bucket * diagCoordStride + (plane + diagCoordOffset)) * diagCoordStride + (u + diagCoordOffset)) *
|
|
299
|
+
diagCoordStride +
|
|
300
|
+
(e + diagCoordOffset);
|
|
301
|
+
}
|
|
302
|
+
function collectFlatFace(cubeIndex, vx, vy, vz) {
|
|
303
|
+
if (!mergeFlatFaces) {
|
|
304
|
+
return false;
|
|
305
|
+
}
|
|
306
|
+
switch (cubeIndex) {
|
|
307
|
+
case 153: // low-X corners occupied, high-X corners empty => +X normal
|
|
308
|
+
addFaceCell(1, vx, vy, vz);
|
|
309
|
+
return true;
|
|
310
|
+
case 102: // high-X occupied => -X normal
|
|
311
|
+
addFaceCell(0, vx, vy, vz);
|
|
312
|
+
return true;
|
|
313
|
+
case 51: // low-Y occupied => +Y normal
|
|
314
|
+
addFaceCell(3, vy, vx, vz);
|
|
315
|
+
return true;
|
|
316
|
+
case 204: // high-Y occupied => -Y normal
|
|
317
|
+
addFaceCell(2, vy, vx, vz);
|
|
318
|
+
return true;
|
|
319
|
+
case 15: // low-Z occupied => +Z normal
|
|
320
|
+
addFaceCell(5, vz, vx, vy);
|
|
321
|
+
return true;
|
|
322
|
+
case 240: // high-Z occupied => -Z normal
|
|
323
|
+
addFaceCell(4, vz, vx, vy);
|
|
324
|
+
return true;
|
|
325
|
+
default:
|
|
326
|
+
return false;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
function scaledFacePoint(axis, p, u, v) {
|
|
330
|
+
if (axis === 0) {
|
|
331
|
+
return [p * 2 + 1, u * 2, v * 2];
|
|
332
|
+
}
|
|
333
|
+
if (axis === 1) {
|
|
334
|
+
return [u * 2, p * 2 + 1, v * 2];
|
|
335
|
+
}
|
|
336
|
+
return [u * 2, v * 2, p * 2 + 1];
|
|
337
|
+
}
|
|
338
|
+
function diagBucket(axisA, axisB, signA, signB) {
|
|
339
|
+
const pair = axisA === 0 ? (axisB === 1 ? 0 : 1) : 2;
|
|
340
|
+
const signBits = (signA > 0 ? 1 : 0) | (signB > 0 ? 2 : 0);
|
|
341
|
+
return pair * 4 + signBits;
|
|
342
|
+
}
|
|
343
|
+
function decodeDiagBucket(bucket) {
|
|
344
|
+
const pair = (bucket / 4) | 0;
|
|
345
|
+
const signBits = bucket & 3;
|
|
346
|
+
const signA = (signBits & 1) !== 0 ? 1 : -1;
|
|
347
|
+
const signB = (signBits & 2) !== 0 ? 1 : -1;
|
|
348
|
+
if (pair === 0) {
|
|
349
|
+
return { axisA: 0, axisB: 1, axisE: 2, signA, signB };
|
|
350
|
+
}
|
|
351
|
+
if (pair === 1) {
|
|
352
|
+
return { axisA: 0, axisB: 2, axisE: 1, signA, signB };
|
|
353
|
+
}
|
|
354
|
+
return { axisA: 1, axisB: 2, axisE: 0, signA, signB };
|
|
355
|
+
}
|
|
356
|
+
function coordByAxis(x, y, z, axis) {
|
|
357
|
+
if (axis === 0) {
|
|
358
|
+
return x;
|
|
359
|
+
}
|
|
360
|
+
if (axis === 1) {
|
|
361
|
+
return y;
|
|
362
|
+
}
|
|
363
|
+
return z;
|
|
364
|
+
}
|
|
365
|
+
function diagPoint(bucket, plane, u, e) {
|
|
366
|
+
const { axisA, axisB, axisE, signA, signB } = decodeDiagBucket(bucket);
|
|
367
|
+
const out = [0, 0, 0];
|
|
368
|
+
out[axisA] = signA * ((plane + u) / 2);
|
|
369
|
+
out[axisB] = signB * ((plane - u) / 2);
|
|
370
|
+
out[axisE] = e;
|
|
371
|
+
return [out[0], out[1], out[2]];
|
|
372
|
+
}
|
|
373
|
+
function edgeScaledPoint(edge, vx, vy, vz, out, offset) {
|
|
374
|
+
const x = vx * 2;
|
|
375
|
+
const y = vy * 2;
|
|
376
|
+
const z = vz * 2;
|
|
377
|
+
switch (edge) {
|
|
378
|
+
case 0:
|
|
379
|
+
out[offset] = x + 1;
|
|
380
|
+
out[offset + 1] = y;
|
|
381
|
+
out[offset + 2] = z;
|
|
382
|
+
break;
|
|
383
|
+
case 1:
|
|
384
|
+
out[offset] = x + 2;
|
|
385
|
+
out[offset + 1] = y + 1;
|
|
386
|
+
out[offset + 2] = z;
|
|
387
|
+
break;
|
|
388
|
+
case 2:
|
|
389
|
+
out[offset] = x + 1;
|
|
390
|
+
out[offset + 1] = y + 2;
|
|
391
|
+
out[offset + 2] = z;
|
|
392
|
+
break;
|
|
393
|
+
case 3:
|
|
394
|
+
out[offset] = x;
|
|
395
|
+
out[offset + 1] = y + 1;
|
|
396
|
+
out[offset + 2] = z;
|
|
397
|
+
break;
|
|
398
|
+
case 4:
|
|
399
|
+
out[offset] = x + 1;
|
|
400
|
+
out[offset + 1] = y;
|
|
401
|
+
out[offset + 2] = z + 2;
|
|
402
|
+
break;
|
|
403
|
+
case 5:
|
|
404
|
+
out[offset] = x + 2;
|
|
405
|
+
out[offset + 1] = y + 1;
|
|
406
|
+
out[offset + 2] = z + 2;
|
|
407
|
+
break;
|
|
408
|
+
case 6:
|
|
409
|
+
out[offset] = x + 1;
|
|
410
|
+
out[offset + 1] = y + 2;
|
|
411
|
+
out[offset + 2] = z + 2;
|
|
412
|
+
break;
|
|
413
|
+
case 7:
|
|
414
|
+
out[offset] = x;
|
|
415
|
+
out[offset + 1] = y + 1;
|
|
416
|
+
out[offset + 2] = z + 2;
|
|
417
|
+
break;
|
|
418
|
+
case 8:
|
|
419
|
+
out[offset] = x;
|
|
420
|
+
out[offset + 1] = y;
|
|
421
|
+
out[offset + 2] = z + 1;
|
|
422
|
+
break;
|
|
423
|
+
case 9:
|
|
424
|
+
out[offset] = x + 2;
|
|
425
|
+
out[offset + 1] = y;
|
|
426
|
+
out[offset + 2] = z + 1;
|
|
427
|
+
break;
|
|
428
|
+
case 10:
|
|
429
|
+
out[offset] = x + 2;
|
|
430
|
+
out[offset + 1] = y + 2;
|
|
431
|
+
out[offset + 2] = z + 1;
|
|
432
|
+
break;
|
|
433
|
+
default:
|
|
434
|
+
out[offset] = x;
|
|
435
|
+
out[offset + 1] = y + 2;
|
|
436
|
+
out[offset + 2] = z + 1;
|
|
437
|
+
break;
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
const pairVerts = new Int32Array(18);
|
|
441
|
+
const uniqueVerts = new Int32Array(12);
|
|
442
|
+
function samePoint(src, a, b) {
|
|
443
|
+
return src[a] === src[b] && src[a + 1] === src[b + 1] && src[a + 2] === src[b + 2];
|
|
444
|
+
}
|
|
445
|
+
function pointInUnique(x, y, z, uniqueCount) {
|
|
446
|
+
for (let i = 0; i < uniqueCount; i++) {
|
|
447
|
+
const o = i * 3;
|
|
448
|
+
if (uniqueVerts[o] === x && uniqueVerts[o + 1] === y && uniqueVerts[o + 2] === z) {
|
|
449
|
+
return true;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
return false;
|
|
453
|
+
}
|
|
454
|
+
function collectDiagPair(triRow, triA, triB, vx, vy, vz) {
|
|
455
|
+
const edgesA = triA * 3;
|
|
456
|
+
const edgesB = triB * 3;
|
|
457
|
+
edgeScaledPoint(triRow[edgesA], vx, vy, vz, pairVerts, 0);
|
|
458
|
+
edgeScaledPoint(triRow[edgesA + 2], vx, vy, vz, pairVerts, 3);
|
|
459
|
+
edgeScaledPoint(triRow[edgesA + 1], vx, vy, vz, pairVerts, 6);
|
|
460
|
+
edgeScaledPoint(triRow[edgesB], vx, vy, vz, pairVerts, 9);
|
|
461
|
+
edgeScaledPoint(triRow[edgesB + 2], vx, vy, vz, pairVerts, 12);
|
|
462
|
+
edgeScaledPoint(triRow[edgesB + 1], vx, vy, vz, pairVerts, 15);
|
|
463
|
+
let uniqueCount = 0;
|
|
464
|
+
for (let i = 0; i < 6; i++) {
|
|
465
|
+
const src = i * 3;
|
|
466
|
+
let found = false;
|
|
467
|
+
for (let j = 0; j < uniqueCount; j++) {
|
|
468
|
+
const dst = j * 3;
|
|
469
|
+
if (pairVerts[src] === uniqueVerts[dst] &&
|
|
470
|
+
pairVerts[src + 1] === uniqueVerts[dst + 1] &&
|
|
471
|
+
pairVerts[src + 2] === uniqueVerts[dst + 2]) {
|
|
472
|
+
found = true;
|
|
473
|
+
break;
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
if (!found) {
|
|
477
|
+
if (uniqueCount === 4) {
|
|
478
|
+
return false;
|
|
479
|
+
}
|
|
480
|
+
const dst = uniqueCount * 3;
|
|
481
|
+
uniqueVerts[dst] = pairVerts[src];
|
|
482
|
+
uniqueVerts[dst + 1] = pairVerts[src + 1];
|
|
483
|
+
uniqueVerts[dst + 2] = pairVerts[src + 2];
|
|
484
|
+
uniqueCount++;
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
if (uniqueCount !== 4) {
|
|
488
|
+
return false;
|
|
489
|
+
}
|
|
490
|
+
const ax = pairVerts[3] - pairVerts[0];
|
|
491
|
+
const ay = pairVerts[4] - pairVerts[1];
|
|
492
|
+
const az = pairVerts[5] - pairVerts[2];
|
|
493
|
+
const bx = pairVerts[6] - pairVerts[0];
|
|
494
|
+
const by = pairVerts[7] - pairVerts[1];
|
|
495
|
+
const bz = pairVerts[8] - pairVerts[2];
|
|
496
|
+
const normal = [ay * bz - az * by, az * bx - ax * bz, ax * by - ay * bx];
|
|
497
|
+
const absNormal = [Math.abs(normal[0]), Math.abs(normal[1]), Math.abs(normal[2])];
|
|
498
|
+
let axisE = -1;
|
|
499
|
+
let axisA = -1;
|
|
500
|
+
let axisB = -1;
|
|
501
|
+
for (let i = 0; i < 3; i++) {
|
|
502
|
+
if (absNormal[i] === 0) {
|
|
503
|
+
axisE = i;
|
|
504
|
+
}
|
|
505
|
+
else if (axisA === -1) {
|
|
506
|
+
axisA = i;
|
|
507
|
+
}
|
|
508
|
+
else {
|
|
509
|
+
axisB = i;
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
if (axisE === -1 || axisA === -1 || axisB === -1) {
|
|
513
|
+
return false;
|
|
514
|
+
}
|
|
515
|
+
if (absNormal[axisA] !== absNormal[axisB]) {
|
|
516
|
+
return false;
|
|
517
|
+
}
|
|
518
|
+
const signA = normal[axisA] > 0 ? 1 : -1;
|
|
519
|
+
const signB = normal[axisB] > 0 ? 1 : -1;
|
|
520
|
+
const bucket = diagBucket(axisA, axisB, signA, signB);
|
|
521
|
+
const plane = signA * coordByAxis(uniqueVerts[0], uniqueVerts[1], uniqueVerts[2], axisA) +
|
|
522
|
+
signB * coordByAxis(uniqueVerts[0], uniqueVerts[1], uniqueVerts[2], axisB);
|
|
523
|
+
let minU = Infinity;
|
|
524
|
+
let maxU = -Infinity;
|
|
525
|
+
let minE = Infinity;
|
|
526
|
+
let maxE = -Infinity;
|
|
527
|
+
for (let i = 0; i < uniqueCount; i++) {
|
|
528
|
+
const o = i * 3;
|
|
529
|
+
const a = coordByAxis(uniqueVerts[o], uniqueVerts[o + 1], uniqueVerts[o + 2], axisA);
|
|
530
|
+
const b = coordByAxis(uniqueVerts[o], uniqueVerts[o + 1], uniqueVerts[o + 2], axisB);
|
|
531
|
+
const e = coordByAxis(uniqueVerts[o], uniqueVerts[o + 1], uniqueVerts[o + 2], axisE);
|
|
532
|
+
if (signA * a + signB * b !== plane) {
|
|
533
|
+
return false;
|
|
534
|
+
}
|
|
535
|
+
const u = signA * a - signB * b;
|
|
536
|
+
if (u < minU) {
|
|
537
|
+
minU = u;
|
|
538
|
+
}
|
|
539
|
+
if (u > maxU) {
|
|
540
|
+
maxU = u;
|
|
541
|
+
}
|
|
542
|
+
if (e < minE) {
|
|
543
|
+
minE = e;
|
|
544
|
+
}
|
|
545
|
+
if (e > maxE) {
|
|
546
|
+
maxE = e;
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
if (maxU - minU !== 2 || maxE - minE !== 2) {
|
|
550
|
+
return false;
|
|
551
|
+
}
|
|
552
|
+
const p00 = diagPoint(bucket, plane, minU, minE);
|
|
553
|
+
const p10 = diagPoint(bucket, plane, maxU, minE);
|
|
554
|
+
const p11 = diagPoint(bucket, plane, maxU, maxE);
|
|
555
|
+
const p01 = diagPoint(bucket, plane, minU, maxE);
|
|
556
|
+
if (!pointInUnique(p00[0], p00[1], p00[2], uniqueCount) ||
|
|
557
|
+
!pointInUnique(p10[0], p10[1], p10[2], uniqueCount) ||
|
|
558
|
+
!pointInUnique(p11[0], p11[1], p11[2], uniqueCount) ||
|
|
559
|
+
!pointInUnique(p01[0], p01[1], p01[2], uniqueCount)) {
|
|
560
|
+
return false;
|
|
561
|
+
}
|
|
562
|
+
let sharedCount = 0;
|
|
563
|
+
let sharedU0 = 0;
|
|
564
|
+
let sharedE0 = 0;
|
|
565
|
+
let sharedU1 = 0;
|
|
566
|
+
let sharedE1 = 0;
|
|
567
|
+
for (let i = 0; i < 3; i++) {
|
|
568
|
+
const oi = i * 3;
|
|
569
|
+
for (let j = 3; j < 6; j++) {
|
|
570
|
+
const oj = j * 3;
|
|
571
|
+
if (!samePoint(pairVerts, oi, oj)) {
|
|
572
|
+
continue;
|
|
573
|
+
}
|
|
574
|
+
const a = coordByAxis(pairVerts[oi], pairVerts[oi + 1], pairVerts[oi + 2], axisA);
|
|
575
|
+
const b = coordByAxis(pairVerts[oi], pairVerts[oi + 1], pairVerts[oi + 2], axisB);
|
|
576
|
+
const e = coordByAxis(pairVerts[oi], pairVerts[oi + 1], pairVerts[oi + 2], axisE);
|
|
577
|
+
if (sharedCount === 0) {
|
|
578
|
+
sharedU0 = signA * a - signB * b;
|
|
579
|
+
sharedE0 = e;
|
|
580
|
+
}
|
|
581
|
+
else {
|
|
582
|
+
sharedU1 = signA * a - signB * b;
|
|
583
|
+
sharedE1 = e;
|
|
584
|
+
}
|
|
585
|
+
sharedCount++;
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
if (sharedCount !== 2 || sharedU0 === sharedU1 || sharedE0 === sharedE1) {
|
|
589
|
+
return false;
|
|
590
|
+
}
|
|
591
|
+
addDiagCell(bucket, plane, minU, minE);
|
|
592
|
+
return true;
|
|
593
|
+
}
|
|
594
|
+
function collectDiagFaces(triRow, vx, vy, vz) {
|
|
595
|
+
if (!mergeFlatFaces) {
|
|
596
|
+
return 0;
|
|
597
|
+
}
|
|
598
|
+
const triCount = (triRow.length / 3) | 0;
|
|
599
|
+
let usedMask = 0;
|
|
600
|
+
for (let i = 0; i < triCount; i++) {
|
|
601
|
+
if ((usedMask & (1 << i)) !== 0) {
|
|
602
|
+
continue;
|
|
603
|
+
}
|
|
604
|
+
for (let j = i + 1; j < triCount; j++) {
|
|
605
|
+
if ((usedMask & (1 << j)) !== 0) {
|
|
606
|
+
continue;
|
|
607
|
+
}
|
|
608
|
+
if (collectDiagPair(triRow, i, j, vx, vy, vz)) {
|
|
609
|
+
usedMask |= (1 << i) | (1 << j);
|
|
610
|
+
break;
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
return usedMask;
|
|
615
|
+
}
|
|
616
|
+
function getScaledVertex(x2, y2, z2) {
|
|
617
|
+
if ((x2 & 1) !== 0) {
|
|
618
|
+
return getVertex((x2 - 1) / 2, y2 / 2, z2 / 2, 0);
|
|
619
|
+
}
|
|
620
|
+
if ((y2 & 1) !== 0) {
|
|
621
|
+
return getVertex(x2 / 2, (y2 - 1) / 2, z2 / 2, 1);
|
|
622
|
+
}
|
|
623
|
+
return getVertex(x2 / 2, y2 / 2, (z2 - 1) / 2, 2);
|
|
624
|
+
}
|
|
625
|
+
let perimeterScratch = new Uint32Array(16);
|
|
626
|
+
let perimeterU = new Int32Array(16);
|
|
627
|
+
let perimeterV = new Int32Array(16);
|
|
628
|
+
let perimeterLen = 0;
|
|
629
|
+
function localFaceUv(axis, x2, y2, z2) {
|
|
630
|
+
if (axis === 0) {
|
|
631
|
+
return [y2, z2];
|
|
632
|
+
}
|
|
633
|
+
if (axis === 1) {
|
|
634
|
+
return [x2, z2];
|
|
635
|
+
}
|
|
636
|
+
return [x2, y2];
|
|
637
|
+
}
|
|
638
|
+
function addPerimeterVertex(vertex, u, v) {
|
|
639
|
+
if (perimeterLen > 0 && perimeterScratch[perimeterLen - 1] === vertex) {
|
|
640
|
+
return;
|
|
641
|
+
}
|
|
642
|
+
if (perimeterLen === perimeterScratch.length) {
|
|
643
|
+
const grown = new Uint32Array(perimeterScratch.length * 2);
|
|
644
|
+
grown.set(perimeterScratch);
|
|
645
|
+
perimeterScratch = grown;
|
|
646
|
+
const grownU = new Int32Array(perimeterU.length * 2);
|
|
647
|
+
grownU.set(perimeterU);
|
|
648
|
+
perimeterU = grownU;
|
|
649
|
+
const grownV = new Int32Array(perimeterV.length * 2);
|
|
650
|
+
grownV.set(perimeterV);
|
|
651
|
+
perimeterV = grownV;
|
|
652
|
+
}
|
|
653
|
+
perimeterScratch[perimeterLen] = vertex;
|
|
654
|
+
perimeterU[perimeterLen] = u;
|
|
655
|
+
perimeterV[perimeterLen] = v;
|
|
656
|
+
perimeterLen++;
|
|
657
|
+
}
|
|
658
|
+
function addPerimeterPoint(axis, x2, y2, z2) {
|
|
659
|
+
const [u, v] = localFaceUv(axis, x2, y2, z2);
|
|
660
|
+
addPerimeterVertex(getScaledVertex(x2, y2, z2), u, v);
|
|
661
|
+
}
|
|
662
|
+
function addDiagPerimeterPoint(bucket, x2, y2, z2) {
|
|
663
|
+
const { axisA, axisB, axisE, signA, signB } = decodeDiagBucket(bucket);
|
|
664
|
+
const a = coordByAxis(x2, y2, z2, axisA);
|
|
665
|
+
const b = coordByAxis(x2, y2, z2, axisB);
|
|
666
|
+
const e = coordByAxis(x2, y2, z2, axisE);
|
|
667
|
+
addPerimeterVertex(getScaledVertex(x2, y2, z2), signA * a - signB * b, e);
|
|
668
|
+
}
|
|
669
|
+
function addSplitSegment(x0, y0, z0, x1, y1, z1) {
|
|
670
|
+
const changes = (x0 !== x1 ? 1 : 0) + (y0 !== y1 ? 1 : 0) + (z0 !== z1 ? 1 : 0);
|
|
671
|
+
if (changes !== 1) {
|
|
672
|
+
return;
|
|
673
|
+
}
|
|
674
|
+
if (x0 !== x1) {
|
|
675
|
+
const key = splitLineKey(0, x0, y0, z0);
|
|
676
|
+
addSplitLinePoint(key, x0);
|
|
677
|
+
addSplitLinePoint(key, x1);
|
|
678
|
+
}
|
|
679
|
+
else if (y0 !== y1) {
|
|
680
|
+
const key = splitLineKey(1, x0, y0, z0);
|
|
681
|
+
addSplitLinePoint(key, y0);
|
|
682
|
+
addSplitLinePoint(key, y1);
|
|
683
|
+
}
|
|
684
|
+
else {
|
|
685
|
+
const key = splitLineKey(2, x0, y0, z0);
|
|
686
|
+
addSplitLinePoint(key, z0);
|
|
687
|
+
addSplitLinePoint(key, z1);
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
function addSplitEdgeVertices(axis, x0, y0, z0, x1, y1, z1, addPoint) {
|
|
691
|
+
function addDefaultPerimeterPoint(px, py, pz) {
|
|
692
|
+
addPerimeterPoint(axis, px, py, pz);
|
|
693
|
+
}
|
|
694
|
+
const emit = addPoint ?? addDefaultPerimeterPoint;
|
|
695
|
+
let varAxis;
|
|
696
|
+
let start;
|
|
697
|
+
let end;
|
|
698
|
+
if (x0 !== x1) {
|
|
699
|
+
varAxis = 0;
|
|
700
|
+
start = x0;
|
|
701
|
+
end = x1;
|
|
702
|
+
}
|
|
703
|
+
else if (y0 !== y1) {
|
|
704
|
+
varAxis = 1;
|
|
705
|
+
start = y0;
|
|
706
|
+
end = y1;
|
|
707
|
+
}
|
|
708
|
+
else {
|
|
709
|
+
varAxis = 2;
|
|
710
|
+
start = z0;
|
|
711
|
+
end = z1;
|
|
712
|
+
}
|
|
713
|
+
const points = splitLinePoints?.get(splitLineKey(varAxis, x0, y0, z0));
|
|
714
|
+
if (!points) {
|
|
715
|
+
return;
|
|
716
|
+
}
|
|
717
|
+
const lo = Math.min(start, end);
|
|
718
|
+
const hi = Math.max(start, end);
|
|
719
|
+
const forward = start <= end;
|
|
720
|
+
function emitPoint(t) {
|
|
721
|
+
if (varAxis === 0) {
|
|
722
|
+
emit(t, y0, z0);
|
|
723
|
+
}
|
|
724
|
+
else if (varAxis === 1) {
|
|
725
|
+
emit(x0, t, z0);
|
|
726
|
+
}
|
|
727
|
+
else {
|
|
728
|
+
emit(x0, y0, t);
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
if (forward) {
|
|
732
|
+
for (let i = 0; i < points.length; i++) {
|
|
733
|
+
const t = points[i];
|
|
734
|
+
if (t < lo) {
|
|
735
|
+
continue;
|
|
736
|
+
}
|
|
737
|
+
if (t > hi) {
|
|
738
|
+
break;
|
|
739
|
+
}
|
|
740
|
+
emitPoint(t);
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
else {
|
|
744
|
+
for (let i = points.length - 1; i >= 0; i--) {
|
|
745
|
+
const t = points[i];
|
|
746
|
+
if (t > hi) {
|
|
747
|
+
continue;
|
|
748
|
+
}
|
|
749
|
+
if (t < lo) {
|
|
750
|
+
break;
|
|
751
|
+
}
|
|
752
|
+
emitPoint(t);
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
function appendOrientedTri(a, b, c, useLocalCcw) {
|
|
757
|
+
if (useLocalCcw) {
|
|
758
|
+
appendTri(a, b, c);
|
|
759
|
+
}
|
|
760
|
+
else {
|
|
761
|
+
appendTri(a, c, b);
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
function appendPerimeterTri(a, b, c, useLocalCcw) {
|
|
765
|
+
const abx = perimeterU[b] - perimeterU[a];
|
|
766
|
+
const aby = perimeterV[b] - perimeterV[a];
|
|
767
|
+
const acx = perimeterU[c] - perimeterU[a];
|
|
768
|
+
const acy = perimeterV[c] - perimeterV[a];
|
|
769
|
+
if (abx * acy - aby * acx <= 0) {
|
|
770
|
+
return;
|
|
771
|
+
}
|
|
772
|
+
appendOrientedTri(perimeterScratch[a], perimeterScratch[b], perimeterScratch[c], useLocalCcw);
|
|
773
|
+
}
|
|
774
|
+
function triangulateTwoSideChain(chainAStart, chainAEnd, chainBStart, chainBEnd, useLocalCcw) {
|
|
775
|
+
const chainALen = chainAEnd - chainAStart;
|
|
776
|
+
const chainBLen = chainBEnd - chainBStart;
|
|
777
|
+
if (chainALen < 2 || chainBLen < 2) {
|
|
778
|
+
return;
|
|
779
|
+
}
|
|
780
|
+
const v2 = chainBEnd - 1;
|
|
781
|
+
for (let i = chainAStart; i < chainAEnd - 2; i++) {
|
|
782
|
+
appendPerimeterTri(v2, i, i + 1, useLocalCcw);
|
|
783
|
+
}
|
|
784
|
+
const pivot = chainALen > 1 ? chainAEnd - 2 : chainAStart;
|
|
785
|
+
for (let i = chainBStart; i < chainBEnd - 1; i++) {
|
|
786
|
+
appendPerimeterTri(pivot, i, i + 1, useLocalCcw);
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
function emitFaceRectangle(bucket, p, u0, v0, u1, v1) {
|
|
790
|
+
const axis = bucket >> 1;
|
|
791
|
+
const positive = (bucket & 1) === 1;
|
|
792
|
+
const a = scaledFacePoint(axis, p, u0, v0);
|
|
793
|
+
const b = scaledFacePoint(axis, p, u1, v0);
|
|
794
|
+
const c = scaledFacePoint(axis, p, u1, v1);
|
|
795
|
+
const d = scaledFacePoint(axis, p, u0, v1);
|
|
796
|
+
perimeterLen = 0;
|
|
797
|
+
const side0Start = 0;
|
|
798
|
+
addSplitEdgeVertices(axis, a[0], a[1], a[2], b[0], b[1], b[2]);
|
|
799
|
+
const side0End = perimeterLen;
|
|
800
|
+
const side1Start = side0End - 1;
|
|
801
|
+
addSplitEdgeVertices(axis, b[0], b[1], b[2], c[0], c[1], c[2]);
|
|
802
|
+
const side1End = perimeterLen;
|
|
803
|
+
const side2Start = side1End - 1;
|
|
804
|
+
addSplitEdgeVertices(axis, c[0], c[1], c[2], d[0], d[1], d[2]);
|
|
805
|
+
const side2End = perimeterLen;
|
|
806
|
+
const side3Start = side2End - 1;
|
|
807
|
+
addSplitEdgeVertices(axis, d[0], d[1], d[2], a[0], a[1], a[2]);
|
|
808
|
+
const side3End = perimeterLen;
|
|
809
|
+
const localCcwIsPositive = axis !== 1;
|
|
810
|
+
const useLocalCcw = positive === localCcwIsPositive;
|
|
811
|
+
triangulateTwoSideChain(side0Start, side0End, side1Start, side1End, useLocalCcw);
|
|
812
|
+
triangulateTwoSideChain(side2Start, side2End, side3Start, side3End, useLocalCcw);
|
|
813
|
+
}
|
|
814
|
+
function emitDiagRectangle(bucket, plane, u0, e0, u1, e1) {
|
|
815
|
+
const a = diagPoint(bucket, plane, u0, e0);
|
|
816
|
+
const b = diagPoint(bucket, plane, u1, e0);
|
|
817
|
+
const c = diagPoint(bucket, plane, u1, e1);
|
|
818
|
+
const d = diagPoint(bucket, plane, u0, e1);
|
|
819
|
+
function addPoint(x2, y2, z2) {
|
|
820
|
+
addDiagPerimeterPoint(bucket, x2, y2, z2);
|
|
821
|
+
}
|
|
822
|
+
perimeterLen = 0;
|
|
823
|
+
const side0Start = 0;
|
|
824
|
+
addPoint(a[0], a[1], a[2]);
|
|
825
|
+
addPoint(b[0], b[1], b[2]);
|
|
826
|
+
const side0End = perimeterLen;
|
|
827
|
+
const side1Start = side0End - 1;
|
|
828
|
+
addSplitEdgeVertices(0, b[0], b[1], b[2], c[0], c[1], c[2], addPoint);
|
|
829
|
+
const side1End = perimeterLen;
|
|
830
|
+
const side2Start = side1End - 1;
|
|
831
|
+
addPoint(d[0], d[1], d[2]);
|
|
832
|
+
const side2End = perimeterLen;
|
|
833
|
+
const side3Start = side2End - 1;
|
|
834
|
+
addSplitEdgeVertices(0, d[0], d[1], d[2], a[0], a[1], a[2], addPoint);
|
|
835
|
+
const side3End = perimeterLen;
|
|
836
|
+
const { axisA, axisB, signA, signB } = decodeDiagBucket(bucket);
|
|
837
|
+
const abx = b[0] - a[0];
|
|
838
|
+
const aby = b[1] - a[1];
|
|
839
|
+
const abz = b[2] - a[2];
|
|
840
|
+
const bcx = c[0] - b[0];
|
|
841
|
+
const bcy = c[1] - b[1];
|
|
842
|
+
const bcz = c[2] - b[2];
|
|
843
|
+
const nx = aby * bcz - abz * bcy;
|
|
844
|
+
const ny = abz * bcx - abx * bcz;
|
|
845
|
+
const nz = abx * bcy - aby * bcx;
|
|
846
|
+
const normal = [0, 0, 0];
|
|
847
|
+
normal[axisA] = signA;
|
|
848
|
+
normal[axisB] = signB;
|
|
849
|
+
const useLocalCcw = nx * normal[0] + ny * normal[1] + nz * normal[2] > 0;
|
|
850
|
+
triangulateTwoSideChain(side0Start, side0End, side1Start, side1End, useLocalCcw);
|
|
851
|
+
triangulateTwoSideChain(side2Start, side2End, side3Start, side3End, useLocalCcw);
|
|
852
|
+
}
|
|
853
|
+
function flushFaceCells() {
|
|
854
|
+
if (faceCellLen === 0 && diagCellLen === 0) {
|
|
855
|
+
return;
|
|
856
|
+
}
|
|
857
|
+
const keys = faceCellKeys.subarray(0, faceCellLen);
|
|
858
|
+
faceCellKeys = new Float64Array(0);
|
|
859
|
+
keys.sort();
|
|
860
|
+
let rectCap = 1024;
|
|
861
|
+
let rectLen = 0;
|
|
862
|
+
let rectBucket = new Int32Array(rectCap);
|
|
863
|
+
let rectP = new Int32Array(rectCap);
|
|
864
|
+
let rectU0 = new Int32Array(rectCap);
|
|
865
|
+
let rectV0 = new Int32Array(rectCap);
|
|
866
|
+
let rectU1 = new Int32Array(rectCap);
|
|
867
|
+
let rectV1 = new Int32Array(rectCap);
|
|
868
|
+
let diagRectCap = 1024;
|
|
869
|
+
let diagRectLen = 0;
|
|
870
|
+
let diagRectBucket = new Int32Array(diagRectCap);
|
|
871
|
+
let diagRectPlane = new Int32Array(diagRectCap);
|
|
872
|
+
let diagRectU0 = new Int32Array(diagRectCap);
|
|
873
|
+
let diagRectE0 = new Int32Array(diagRectCap);
|
|
874
|
+
let diagRectU1 = new Int32Array(diagRectCap);
|
|
875
|
+
let diagRectE1 = new Int32Array(diagRectCap);
|
|
876
|
+
function addRect(bucket, p, u0, v0, u1, v1) {
|
|
877
|
+
if (rectLen === rectCap) {
|
|
878
|
+
rectCap *= 2;
|
|
879
|
+
function grow(src) {
|
|
880
|
+
const out = new Int32Array(rectCap);
|
|
881
|
+
out.set(src);
|
|
882
|
+
return out;
|
|
883
|
+
}
|
|
884
|
+
rectBucket = grow(rectBucket);
|
|
885
|
+
rectP = grow(rectP);
|
|
886
|
+
rectU0 = grow(rectU0);
|
|
887
|
+
rectV0 = grow(rectV0);
|
|
888
|
+
rectU1 = grow(rectU1);
|
|
889
|
+
rectV1 = grow(rectV1);
|
|
890
|
+
}
|
|
891
|
+
rectBucket[rectLen] = bucket;
|
|
892
|
+
rectP[rectLen] = p;
|
|
893
|
+
rectU0[rectLen] = u0;
|
|
894
|
+
rectV0[rectLen] = v0;
|
|
895
|
+
rectU1[rectLen] = u1;
|
|
896
|
+
rectV1[rectLen] = v1;
|
|
897
|
+
rectLen++;
|
|
898
|
+
}
|
|
899
|
+
function addDiagRect(bucket, plane, u0, e0, u1, e1) {
|
|
900
|
+
if (diagRectLen === diagRectCap) {
|
|
901
|
+
diagRectCap *= 2;
|
|
902
|
+
function grow(src) {
|
|
903
|
+
const out = new Int32Array(diagRectCap);
|
|
904
|
+
out.set(src);
|
|
905
|
+
return out;
|
|
906
|
+
}
|
|
907
|
+
diagRectBucket = grow(diagRectBucket);
|
|
908
|
+
diagRectPlane = grow(diagRectPlane);
|
|
909
|
+
diagRectU0 = grow(diagRectU0);
|
|
910
|
+
diagRectE0 = grow(diagRectE0);
|
|
911
|
+
diagRectU1 = grow(diagRectU1);
|
|
912
|
+
diagRectE1 = grow(diagRectE1);
|
|
913
|
+
}
|
|
914
|
+
diagRectBucket[diagRectLen] = bucket;
|
|
915
|
+
diagRectPlane[diagRectLen] = plane;
|
|
916
|
+
diagRectU0[diagRectLen] = u0;
|
|
917
|
+
diagRectE0[diagRectLen] = e0;
|
|
918
|
+
diagRectU1[diagRectLen] = u1;
|
|
919
|
+
diagRectE1[diagRectLen] = e1;
|
|
920
|
+
diagRectLen++;
|
|
921
|
+
}
|
|
922
|
+
function decodeGroup(key) {
|
|
923
|
+
let q = Math.floor(key / faceCoordStride);
|
|
924
|
+
q = Math.floor(q / faceCoordStride);
|
|
925
|
+
const pOff = q % faceCoordStride;
|
|
926
|
+
const bucket = Math.floor(q / faceCoordStride);
|
|
927
|
+
return { bucket, pOff };
|
|
928
|
+
}
|
|
929
|
+
function decodeUvKey(key) {
|
|
930
|
+
const vOff = key % faceCoordStride;
|
|
931
|
+
const q = Math.floor(key / faceCoordStride);
|
|
932
|
+
const uOff = q % faceCoordStride;
|
|
933
|
+
return uOff * faceCoordStride + vOff;
|
|
934
|
+
}
|
|
935
|
+
let start = 0;
|
|
936
|
+
while (start < keys.length) {
|
|
937
|
+
const { bucket, pOff } = decodeGroup(keys[start]);
|
|
938
|
+
let end = start + 1;
|
|
939
|
+
while (end < keys.length) {
|
|
940
|
+
const g = decodeGroup(keys[end]);
|
|
941
|
+
if (g.bucket !== bucket || g.pOff !== pOff) {
|
|
942
|
+
break;
|
|
943
|
+
}
|
|
944
|
+
end++;
|
|
945
|
+
}
|
|
946
|
+
const count = end - start;
|
|
947
|
+
let hCap = 1;
|
|
948
|
+
while (hCap < count / 0.7) {
|
|
949
|
+
hCap *= 2;
|
|
950
|
+
}
|
|
951
|
+
const hMask = hCap - 1;
|
|
952
|
+
const hKeys = new Float64Array(hCap).fill(-1);
|
|
953
|
+
const hVals = new Int32Array(hCap);
|
|
954
|
+
function hash(key) {
|
|
955
|
+
const hi = (key / 0x100000000) | 0;
|
|
956
|
+
return (Math.imul((key | 0) ^ hi, 0x9e3779b9) >>> 0) & hMask;
|
|
957
|
+
}
|
|
958
|
+
for (let i = 0; i < count; i++) {
|
|
959
|
+
const uvKey = decodeUvKey(keys[start + i]);
|
|
960
|
+
let h = hash(uvKey);
|
|
961
|
+
while (hKeys[h] !== -1) {
|
|
962
|
+
h = (h + 1) & hMask;
|
|
963
|
+
}
|
|
964
|
+
hKeys[h] = uvKey;
|
|
965
|
+
hVals[h] = i;
|
|
966
|
+
}
|
|
967
|
+
function lookup(uvKey) {
|
|
968
|
+
let h = hash(uvKey);
|
|
969
|
+
while (true) {
|
|
970
|
+
const k = hKeys[h];
|
|
971
|
+
if (k === uvKey) {
|
|
972
|
+
return hVals[h];
|
|
973
|
+
}
|
|
974
|
+
if (k === -1) {
|
|
975
|
+
return -1;
|
|
976
|
+
}
|
|
977
|
+
h = (h + 1) & hMask;
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
const visited = new Uint8Array(count);
|
|
981
|
+
function uvKeyOf(uOff, vOff) {
|
|
982
|
+
return uOff * faceCoordStride + vOff;
|
|
983
|
+
}
|
|
984
|
+
const p = pOff - 1;
|
|
985
|
+
for (let i = 0; i < count; i++) {
|
|
986
|
+
if (visited[i]) {
|
|
987
|
+
continue;
|
|
988
|
+
}
|
|
989
|
+
const uvKey = decodeUvKey(keys[start + i]);
|
|
990
|
+
const uOff = Math.floor(uvKey / faceCoordStride);
|
|
991
|
+
const vOff = uvKey % faceCoordStride;
|
|
992
|
+
let width = 1;
|
|
993
|
+
while (true) {
|
|
994
|
+
const idx = lookup(uvKeyOf(uOff + width, vOff));
|
|
995
|
+
if (idx === -1 || visited[idx]) {
|
|
996
|
+
break;
|
|
997
|
+
}
|
|
998
|
+
width++;
|
|
999
|
+
}
|
|
1000
|
+
let height = 1;
|
|
1001
|
+
while (true) {
|
|
1002
|
+
let canGrow = true;
|
|
1003
|
+
for (let du = 0; du < width; du++) {
|
|
1004
|
+
const idx = lookup(uvKeyOf(uOff + du, vOff + height));
|
|
1005
|
+
if (idx === -1 || visited[idx]) {
|
|
1006
|
+
canGrow = false;
|
|
1007
|
+
break;
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
if (!canGrow) {
|
|
1011
|
+
break;
|
|
1012
|
+
}
|
|
1013
|
+
height++;
|
|
1014
|
+
}
|
|
1015
|
+
for (let dv = 0; dv < height; dv++) {
|
|
1016
|
+
for (let du = 0; du < width; du++) {
|
|
1017
|
+
const idx = lookup(uvKeyOf(uOff + du, vOff + dv));
|
|
1018
|
+
visited[idx] = 1;
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
addRect(bucket, p, uOff - 1, vOff - 1, uOff - 1 + width, vOff - 1 + height);
|
|
1022
|
+
}
|
|
1023
|
+
start = end;
|
|
1024
|
+
}
|
|
1025
|
+
if (diagCellLen > 0) {
|
|
1026
|
+
const diagKeys = diagCellKeys.slice(0, diagCellLen);
|
|
1027
|
+
diagCellKeys = new Float64Array(0);
|
|
1028
|
+
diagKeys.sort();
|
|
1029
|
+
function decodeDiagKey(key) {
|
|
1030
|
+
const eOff = key % diagCoordStride;
|
|
1031
|
+
let q = Math.floor(key / diagCoordStride);
|
|
1032
|
+
const uOff = q % diagCoordStride;
|
|
1033
|
+
q = Math.floor(q / diagCoordStride);
|
|
1034
|
+
const planeOff = q % diagCoordStride;
|
|
1035
|
+
const bucket = Math.floor(q / diagCoordStride);
|
|
1036
|
+
return {
|
|
1037
|
+
bucket,
|
|
1038
|
+
plane: planeOff - diagCoordOffset,
|
|
1039
|
+
u: uOff - diagCoordOffset,
|
|
1040
|
+
e: eOff - diagCoordOffset,
|
|
1041
|
+
};
|
|
1042
|
+
}
|
|
1043
|
+
let diagStart = 0;
|
|
1044
|
+
while (diagStart < diagKeys.length) {
|
|
1045
|
+
const first = decodeDiagKey(diagKeys[diagStart]);
|
|
1046
|
+
let diagEnd = diagStart + 1;
|
|
1047
|
+
while (diagEnd < diagKeys.length) {
|
|
1048
|
+
const next = decodeDiagKey(diagKeys[diagEnd]);
|
|
1049
|
+
if (next.bucket !== first.bucket || next.plane !== first.plane || next.u !== first.u) {
|
|
1050
|
+
break;
|
|
1051
|
+
}
|
|
1052
|
+
diagEnd++;
|
|
1053
|
+
}
|
|
1054
|
+
let i = diagStart;
|
|
1055
|
+
while (i < diagEnd) {
|
|
1056
|
+
const run = decodeDiagKey(diagKeys[i]);
|
|
1057
|
+
let e1 = run.e + 2;
|
|
1058
|
+
i++;
|
|
1059
|
+
while (i < diagEnd) {
|
|
1060
|
+
const next = decodeDiagKey(diagKeys[i]);
|
|
1061
|
+
if (next.e !== e1) {
|
|
1062
|
+
break;
|
|
1063
|
+
}
|
|
1064
|
+
e1 += 2;
|
|
1065
|
+
i++;
|
|
1066
|
+
}
|
|
1067
|
+
addDiagRect(run.bucket, run.plane, run.u, run.e, run.u + 2, e1);
|
|
1068
|
+
}
|
|
1069
|
+
diagStart = diagEnd;
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
for (let r = 0; r < rectLen; r++) {
|
|
1073
|
+
const axis = rectBucket[r] >> 1;
|
|
1074
|
+
const p = rectP[r];
|
|
1075
|
+
const u0 = rectU0[r];
|
|
1076
|
+
const v0 = rectV0[r];
|
|
1077
|
+
const u1 = rectU1[r];
|
|
1078
|
+
const v1 = rectV1[r];
|
|
1079
|
+
const a = scaledFacePoint(axis, p, u0, v0);
|
|
1080
|
+
const b = scaledFacePoint(axis, p, u1, v0);
|
|
1081
|
+
const c = scaledFacePoint(axis, p, u1, v1);
|
|
1082
|
+
const d = scaledFacePoint(axis, p, u0, v1);
|
|
1083
|
+
addSplitSegment(a[0], a[1], a[2], b[0], b[1], b[2]);
|
|
1084
|
+
addSplitSegment(b[0], b[1], b[2], c[0], c[1], c[2]);
|
|
1085
|
+
addSplitSegment(c[0], c[1], c[2], d[0], d[1], d[2]);
|
|
1086
|
+
addSplitSegment(d[0], d[1], d[2], a[0], a[1], a[2]);
|
|
1087
|
+
}
|
|
1088
|
+
for (let r = 0; r < diagRectLen; r++) {
|
|
1089
|
+
const bucket = diagRectBucket[r];
|
|
1090
|
+
const plane = diagRectPlane[r];
|
|
1091
|
+
const u0 = diagRectU0[r];
|
|
1092
|
+
const e0 = diagRectE0[r];
|
|
1093
|
+
const u1 = diagRectU1[r];
|
|
1094
|
+
const e1 = diagRectE1[r];
|
|
1095
|
+
const a = diagPoint(bucket, plane, u0, e0);
|
|
1096
|
+
const b = diagPoint(bucket, plane, u1, e0);
|
|
1097
|
+
const c = diagPoint(bucket, plane, u1, e1);
|
|
1098
|
+
const d = diagPoint(bucket, plane, u0, e1);
|
|
1099
|
+
addSplitSegment(a[0], a[1], a[2], b[0], b[1], b[2]);
|
|
1100
|
+
addSplitSegment(b[0], b[1], b[2], c[0], c[1], c[2]);
|
|
1101
|
+
addSplitSegment(c[0], c[1], c[2], d[0], d[1], d[2]);
|
|
1102
|
+
addSplitSegment(d[0], d[1], d[2], a[0], a[1], a[2]);
|
|
1103
|
+
}
|
|
1104
|
+
if (splitLinePoints) {
|
|
1105
|
+
for (const points of splitLinePoints.values()) {
|
|
1106
|
+
points.sort((a, b) => a - b);
|
|
1107
|
+
let write = 0;
|
|
1108
|
+
for (let i = 0; i < points.length; i++) {
|
|
1109
|
+
if (i === 0 || points[i] !== points[i - 1]) {
|
|
1110
|
+
points[write++] = points[i];
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
points.length = write;
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
collectSplitPoints = false;
|
|
1117
|
+
for (let r = 0; r < rectLen; r++) {
|
|
1118
|
+
emitFaceRectangle(rectBucket[r], rectP[r], rectU0[r], rectV0[r], rectU1[r], rectV1[r]);
|
|
1119
|
+
}
|
|
1120
|
+
for (let r = 0; r < diagRectLen; r++) {
|
|
1121
|
+
emitDiagRectangle(diagRectBucket[r], diagRectPlane[r], diagRectU0[r], diagRectE0[r], diagRectU1[r], diagRectE1[r]);
|
|
1122
|
+
}
|
|
1123
|
+
splitLinePoints?.clear();
|
|
1124
|
+
}
|
|
1125
|
+
// Track processed orphan cells to avoid duplicate triangles. When a cell's
|
|
1126
|
+
// owner block doesn't exist, multiple neighboring blocks can reach it via
|
|
1127
|
+
// the -1 boundary extension. The hash table ensures each orphan cell is
|
|
1128
|
+
// only processed once. Same typed-array structure as the vertex hash —
|
|
1129
|
+
// see `oKeys` / `oGrow` above. Stored separately because keys collide with
|
|
1130
|
+
// vertex keys (same encoding, different namespace).
|
|
1131
|
+
// Iterate non-empty blocks of the grid. Local SparseVoxelGrid stores one
|
|
1132
|
+
// byte per block type instead of upstream's packed 2-bit type words.
|
|
1133
|
+
// Each block is processed once; the inner loop also handles boundary cells
|
|
1134
|
+
// that straddle into neighboring blocks (so we don't need a separate pass
|
|
1135
|
+
// for orphan boundary cells along the negative grid edges).
|
|
1136
|
+
for (let w = 0; w < types.length; w++) {
|
|
1137
|
+
const word = types[w];
|
|
1138
|
+
if (word === 0) {
|
|
1139
|
+
continue;
|
|
1140
|
+
}
|
|
1141
|
+
let nonEmpty = ((word & EVEN_BITS) | ((word >>> 1) & EVEN_BITS)) >>> 0;
|
|
1142
|
+
const baseBlockIdx = w * BLOCKS_PER_WORD;
|
|
1143
|
+
while (nonEmpty) {
|
|
1144
|
+
const bp = 31 - Math.clz32(nonEmpty & -nonEmpty);
|
|
1145
|
+
const lane = bp >>> 1;
|
|
1146
|
+
const blockIdx = baseBlockIdx + lane;
|
|
1147
|
+
nonEmpty &= nonEmpty - 1;
|
|
1148
|
+
if (blockIdx >= totalBlocks) {
|
|
1149
|
+
break;
|
|
1150
|
+
}
|
|
1151
|
+
// Decode block coordinates.
|
|
1152
|
+
bx = blockIdx % nbx;
|
|
1153
|
+
const byBz = (blockIdx / nbx) | 0;
|
|
1154
|
+
by = byBz % nby;
|
|
1155
|
+
bz = (byBz / nby) | 0;
|
|
1156
|
+
// Populate the 3x3x3 neighbor table for this block. After this loop,
|
|
1157
|
+
// every per-cell occupancy query is a direct typed-array index.
|
|
1158
|
+
//
|
|
1159
|
+
// Track `allNeighborsSolid` so we can skip the entire cell loop
|
|
1160
|
+
// for blocks deep inside an obstruction, where every cubeIndex is
|
|
1161
|
+
// 255 and no triangles are emitted. On large carved scenes this
|
|
1162
|
+
// is the bulk of SOLID blocks and dominates marching-cubes runtime.
|
|
1163
|
+
let currentBlockIsSolid = false;
|
|
1164
|
+
let allNeighborsSolid = true;
|
|
1165
|
+
for (let dz = -1; dz <= 1; dz++) {
|
|
1166
|
+
const nbZ = bz + dz;
|
|
1167
|
+
for (let dy = -1; dy <= 1; dy++) {
|
|
1168
|
+
const nbY = by + dy;
|
|
1169
|
+
for (let dx = -1; dx <= 1; dx++) {
|
|
1170
|
+
const nbX = bx + dx;
|
|
1171
|
+
const slot = dx + 1 + (dy + 1) * 3 + (dz + 1) * 9;
|
|
1172
|
+
if (nbX < 0 || nbY < 0 || nbZ < 0 || nbX >= nbx || nbY >= nby || nbZ >= nbz) {
|
|
1173
|
+
neighborEntry[slot] = NEIGHBOR_EMPTY;
|
|
1174
|
+
allNeighborsSolid = false;
|
|
1175
|
+
continue;
|
|
1176
|
+
}
|
|
1177
|
+
const nbIdx = nbX + nbY * nbx + nbZ * bStride;
|
|
1178
|
+
const bt = readBlockType(types, nbIdx);
|
|
1179
|
+
if (bt === BLOCK_EMPTY) {
|
|
1180
|
+
neighborEntry[slot] = NEIGHBOR_EMPTY;
|
|
1181
|
+
allNeighborsSolid = false;
|
|
1182
|
+
}
|
|
1183
|
+
else if (bt === BLOCK_SOLID) {
|
|
1184
|
+
neighborEntry[slot] = NEIGHBOR_SOLID;
|
|
1185
|
+
if (dx === 0 && dy === 0 && dz === 0) {
|
|
1186
|
+
currentBlockIsSolid = true;
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
else {
|
|
1190
|
+
neighborEntry[slot] = NEIGHBOR_MIXED;
|
|
1191
|
+
allNeighborsSolid = false;
|
|
1192
|
+
const ms = masks.slot(nbIdx);
|
|
1193
|
+
neighborMasks[slot * 2] = masks.lo[ms];
|
|
1194
|
+
neighborMasks[slot * 2 + 1] = masks.hi[ms];
|
|
1195
|
+
}
|
|
1196
|
+
}
|
|
1197
|
+
}
|
|
1198
|
+
}
|
|
1199
|
+
// Block is fully interior to a solid region — every cubeIndex is
|
|
1200
|
+
// 255, every cell would emit 0 triangles. Skip the cell loop.
|
|
1201
|
+
if (currentBlockIsSolid && allNeighborsSolid) {
|
|
1202
|
+
continue;
|
|
1203
|
+
}
|
|
1204
|
+
// Iterate cell origins from -1 through 3 on each axis. The -1 and
|
|
1205
|
+
// 3 layers straddle block edges and close surfaces where no
|
|
1206
|
+
// neighboring block exists.
|
|
1207
|
+
for (let lz = -1; lz < 4; lz++) {
|
|
1208
|
+
const lzInside = lz >= 0 && lz <= 2;
|
|
1209
|
+
for (let ly = -1; ly < 4; ly++) {
|
|
1210
|
+
const lyInside = ly >= 0 && ly <= 2;
|
|
1211
|
+
for (let lx = -1; lx < 4; lx++) {
|
|
1212
|
+
// For solid blocks, the 27 cells with all axes in 0..2
|
|
1213
|
+
// are fully inside the block. All 8 corners are 1 so
|
|
1214
|
+
// cubeIndex == 255 and no triangles are emitted -- skip.
|
|
1215
|
+
if (currentBlockIsSolid && lzInside && lyInside && lx >= 0 && lx <= 2) {
|
|
1216
|
+
continue;
|
|
1217
|
+
}
|
|
1218
|
+
const vx = bx * 4 + lx;
|
|
1219
|
+
const vy = by * 4 + ly;
|
|
1220
|
+
const vz = bz * 4 + lz;
|
|
1221
|
+
// Determine which block owns this cell
|
|
1222
|
+
const ownerBx = vx >> 2;
|
|
1223
|
+
const ownerBy = vy >> 2;
|
|
1224
|
+
const ownerBz = vz >> 2;
|
|
1225
|
+
if (ownerBx !== bx || ownerBy !== by || ownerBz !== bz) {
|
|
1226
|
+
// Cell belongs to a different block — skip if that
|
|
1227
|
+
// block is non-empty (it will process the cell itself).
|
|
1228
|
+
if (ownerBx >= 0 &&
|
|
1229
|
+
ownerBy >= 0 &&
|
|
1230
|
+
ownerBz >= 0 &&
|
|
1231
|
+
ownerBx < nbx &&
|
|
1232
|
+
ownerBy < nby &&
|
|
1233
|
+
ownerBz < nbz) {
|
|
1234
|
+
const ownerIdx = ownerBx + ownerBy * nbx + ownerBz * bStride;
|
|
1235
|
+
if (readBlockType(types, ownerIdx) !== BLOCK_EMPTY) {
|
|
1236
|
+
continue;
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
1239
|
+
// Owner block doesn't exist or is out-of-bounds —
|
|
1240
|
+
// deduplicate so only the first neighboring block to
|
|
1241
|
+
// reach this cell emits triangles.
|
|
1242
|
+
const cellKey = vx + 1 + (vy + 1) * strideX + (vz + 1) * strideXY;
|
|
1243
|
+
let oi = (Math.imul(cellKey | 0, 0x9e3779b9) >>> 0) & oMask;
|
|
1244
|
+
let oFound = false;
|
|
1245
|
+
while (true) {
|
|
1246
|
+
const ok = oKeys[oi];
|
|
1247
|
+
if (ok === cellKey) {
|
|
1248
|
+
oFound = true;
|
|
1249
|
+
break;
|
|
1250
|
+
}
|
|
1251
|
+
if (ok === -1) {
|
|
1252
|
+
break;
|
|
1253
|
+
}
|
|
1254
|
+
oi = (oi + 1) & oMask;
|
|
1255
|
+
}
|
|
1256
|
+
if (oFound) {
|
|
1257
|
+
continue;
|
|
1258
|
+
}
|
|
1259
|
+
oKeys[oi] = cellKey;
|
|
1260
|
+
oSize++;
|
|
1261
|
+
if (oSize > ((oCap * 0.7) | 0)) {
|
|
1262
|
+
oGrow();
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1265
|
+
// Get corner values for this cell (8 corners)
|
|
1266
|
+
// Corners: (vx,vy,vz), (vx+1,vy,vz), (vx+1,vy+1,vz), (vx,vy+1,vz),
|
|
1267
|
+
// (vx,vy,vz+1), (vx+1,vy,vz+1), (vx+1,vy+1,vz+1), (vx,vy+1,vz+1)
|
|
1268
|
+
const c0 = isOccupiedLocal(vx, vy, vz) ? 1 : 0;
|
|
1269
|
+
const c1 = isOccupiedLocal(vx + 1, vy, vz) ? 1 : 0;
|
|
1270
|
+
const c2 = isOccupiedLocal(vx + 1, vy + 1, vz) ? 1 : 0;
|
|
1271
|
+
const c3 = isOccupiedLocal(vx, vy + 1, vz) ? 1 : 0;
|
|
1272
|
+
const c4 = isOccupiedLocal(vx, vy, vz + 1) ? 1 : 0;
|
|
1273
|
+
const c5 = isOccupiedLocal(vx + 1, vy, vz + 1) ? 1 : 0;
|
|
1274
|
+
const c6 = isOccupiedLocal(vx + 1, vy + 1, vz + 1) ? 1 : 0;
|
|
1275
|
+
const c7 = isOccupiedLocal(vx, vy + 1, vz + 1) ? 1 : 0;
|
|
1276
|
+
const cubeIndex = c0 | (c1 << 1) | (c2 << 2) | (c3 << 3) | (c4 << 4) | (c5 << 5) | (c6 << 6) | (c7 << 7);
|
|
1277
|
+
if (cubeIndex === 0 || cubeIndex === 255) {
|
|
1278
|
+
continue;
|
|
1279
|
+
}
|
|
1280
|
+
if (collectFlatFace(cubeIndex, vx, vy, vz)) {
|
|
1281
|
+
continue;
|
|
1282
|
+
}
|
|
1283
|
+
const edges = EDGE_TABLE[cubeIndex]; // eslint-disable-line no-use-before-define
|
|
1284
|
+
if (edges === 0) {
|
|
1285
|
+
continue;
|
|
1286
|
+
}
|
|
1287
|
+
const triRow = TRI_TABLE[cubeIndex]; // eslint-disable-line no-use-before-define
|
|
1288
|
+
const triLen = triRow.length;
|
|
1289
|
+
const usedMask = collectDiagFaces(triRow, vx, vy, vz);
|
|
1290
|
+
let neededEdges = 0;
|
|
1291
|
+
let emitTriLen = 0;
|
|
1292
|
+
for (let t = 0; t < triLen; t += 3) {
|
|
1293
|
+
const triIdx = (t / 3) | 0;
|
|
1294
|
+
if ((usedMask & (1 << triIdx)) !== 0) {
|
|
1295
|
+
continue;
|
|
1296
|
+
}
|
|
1297
|
+
neededEdges |= (1 << triRow[t]) | (1 << triRow[t + 1]) | (1 << triRow[t + 2]);
|
|
1298
|
+
emitTriLen += 3;
|
|
1299
|
+
}
|
|
1300
|
+
if (neededEdges === 0) {
|
|
1301
|
+
continue;
|
|
1302
|
+
}
|
|
1303
|
+
// Compute vertices on edges used by triangles that
|
|
1304
|
+
// were not absorbed into a merged binary-MC rectangle.
|
|
1305
|
+
if (neededEdges & 1) {
|
|
1306
|
+
edgeVerts[0] = getVertex(vx, vy, vz, 0);
|
|
1307
|
+
} // edge 0: x-axis at (vx, vy, vz)
|
|
1308
|
+
if (neededEdges & 2) {
|
|
1309
|
+
edgeVerts[1] = getVertex(vx + 1, vy, vz, 1);
|
|
1310
|
+
} // edge 1: y-axis at (vx+1, vy, vz)
|
|
1311
|
+
if (neededEdges & 4) {
|
|
1312
|
+
edgeVerts[2] = getVertex(vx, vy + 1, vz, 0);
|
|
1313
|
+
} // edge 2: x-axis at (vx, vy+1, vz)
|
|
1314
|
+
if (neededEdges & 8) {
|
|
1315
|
+
edgeVerts[3] = getVertex(vx, vy, vz, 1);
|
|
1316
|
+
} // edge 3: y-axis at (vx, vy, vz)
|
|
1317
|
+
if (neededEdges & 16) {
|
|
1318
|
+
edgeVerts[4] = getVertex(vx, vy, vz + 1, 0);
|
|
1319
|
+
} // edge 4: x-axis at (vx, vy, vz+1)
|
|
1320
|
+
if (neededEdges & 32) {
|
|
1321
|
+
edgeVerts[5] = getVertex(vx + 1, vy, vz + 1, 1);
|
|
1322
|
+
} // edge 5: y-axis at (vx+1, vy, vz+1)
|
|
1323
|
+
if (neededEdges & 64) {
|
|
1324
|
+
edgeVerts[6] = getVertex(vx, vy + 1, vz + 1, 0);
|
|
1325
|
+
} // edge 6: x-axis at (vx, vy+1, vz+1)
|
|
1326
|
+
if (neededEdges & 128) {
|
|
1327
|
+
edgeVerts[7] = getVertex(vx, vy, vz + 1, 1);
|
|
1328
|
+
} // edge 7: y-axis at (vx, vy, vz+1)
|
|
1329
|
+
if (neededEdges & 256) {
|
|
1330
|
+
edgeVerts[8] = getVertex(vx, vy, vz, 2);
|
|
1331
|
+
} // edge 8: z-axis at (vx, vy, vz)
|
|
1332
|
+
if (neededEdges & 512) {
|
|
1333
|
+
edgeVerts[9] = getVertex(vx + 1, vy, vz, 2);
|
|
1334
|
+
} // edge 9: z-axis at (vx+1, vy, vz)
|
|
1335
|
+
if (neededEdges & 1024) {
|
|
1336
|
+
edgeVerts[10] = getVertex(vx + 1, vy + 1, vz, 2);
|
|
1337
|
+
} // edge 10: z-axis at (vx+1, vy+1, vz)
|
|
1338
|
+
if (neededEdges & 2048) {
|
|
1339
|
+
edgeVerts[11] = getVertex(vx, vy + 1, vz, 2);
|
|
1340
|
+
} // edge 11: z-axis at (vx, vy+1, vz)
|
|
1341
|
+
// Emit triangles (reversed winding to face outward)
|
|
1342
|
+
ensureIndexCapacity(emitTriLen);
|
|
1343
|
+
for (let t = 0; t < triLen; t += 3) {
|
|
1344
|
+
const triIdx = (t / 3) | 0;
|
|
1345
|
+
if ((usedMask & (1 << triIdx)) !== 0) {
|
|
1346
|
+
continue;
|
|
1347
|
+
}
|
|
1348
|
+
indices[idxLen++] = edgeVerts[triRow[t]];
|
|
1349
|
+
indices[idxLen++] = edgeVerts[triRow[t + 2]];
|
|
1350
|
+
indices[idxLen++] = edgeVerts[triRow[t + 1]];
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1353
|
+
}
|
|
1354
|
+
}
|
|
1355
|
+
}
|
|
1356
|
+
}
|
|
1357
|
+
flushFaceCells();
|
|
1358
|
+
return {
|
|
1359
|
+
positions: positions.slice(0, posLen),
|
|
1360
|
+
indices: indices.slice(0, idxLen),
|
|
1361
|
+
};
|
|
1362
|
+
}
|
|
1363
|
+
// ============================================================================
|
|
1364
|
+
// Marching Cubes Lookup Tables
|
|
1365
|
+
// ============================================================================
|
|
1366
|
+
// Standard tables from Paul Bourke's polygonising a scalar field.
|
|
1367
|
+
// EDGE_TABLE: 256 entries, each a 12-bit mask of which edges are intersected.
|
|
1368
|
+
// TRI_TABLE: 256 entries, each an array of edge indices forming triangles.
|
|
1369
|
+
const EDGE_TABLE = [
|
|
1370
|
+
0x000, 0x109, 0x203, 0x30a, 0x406, 0x50f, 0x605, 0x70c, 0x80c, 0x905, 0xa0f, 0xb06, 0xc0a, 0xd03, 0xe09, 0xf00,
|
|
1371
|
+
0x190, 0x099, 0x393, 0x29a, 0x596, 0x49f, 0x795, 0x69c, 0x99c, 0x895, 0xb9f, 0xa96, 0xd9a, 0xc93, 0xf99, 0xe90,
|
|
1372
|
+
0x230, 0x339, 0x033, 0x13a, 0x636, 0x73f, 0x435, 0x53c, 0xa3c, 0xb35, 0x83f, 0x936, 0xe3a, 0xf33, 0xc39, 0xd30,
|
|
1373
|
+
0x3a0, 0x2a9, 0x1a3, 0x0aa, 0x7a6, 0x6af, 0x5a5, 0x4ac, 0xbac, 0xaa5, 0x9af, 0x8a6, 0xfaa, 0xea3, 0xda9, 0xca0,
|
|
1374
|
+
0x460, 0x569, 0x663, 0x76a, 0x066, 0x16f, 0x265, 0x36c, 0xc6c, 0xd65, 0xe6f, 0xf66, 0x86a, 0x963, 0xa69, 0xb60,
|
|
1375
|
+
0x5f0, 0x4f9, 0x7f3, 0x6fa, 0x1f6, 0x0ff, 0x3f5, 0x2fc, 0xdfc, 0xcf5, 0xfff, 0xef6, 0x9fa, 0x8f3, 0xbf9, 0xaf0,
|
|
1376
|
+
0x650, 0x759, 0x453, 0x55a, 0x256, 0x35f, 0x055, 0x15c, 0xe5c, 0xf55, 0xc5f, 0xd56, 0xa5a, 0xb53, 0x859, 0x950,
|
|
1377
|
+
0x7c0, 0x6c9, 0x5c3, 0x4ca, 0x3c6, 0x2cf, 0x1c5, 0x0cc, 0xfcc, 0xec5, 0xdcf, 0xcc6, 0xbca, 0xac3, 0x9c9, 0x8c0,
|
|
1378
|
+
0x8c0, 0x9c9, 0xac3, 0xbca, 0xcc6, 0xdcf, 0xec5, 0xfcc, 0x0cc, 0x1c5, 0x2cf, 0x3c6, 0x4ca, 0x5c3, 0x6c9, 0x7c0,
|
|
1379
|
+
0x950, 0x859, 0xb53, 0xa5a, 0xd56, 0xc5f, 0xf55, 0xe5c, 0x15c, 0x055, 0x35f, 0x256, 0x55a, 0x453, 0x759, 0x650,
|
|
1380
|
+
0xaf0, 0xbf9, 0x8f3, 0x9fa, 0xef6, 0xfff, 0xcf5, 0xdfc, 0x2fc, 0x3f5, 0x0ff, 0x1f6, 0x6fa, 0x7f3, 0x4f9, 0x5f0,
|
|
1381
|
+
0xb60, 0xa69, 0x963, 0x86a, 0xf66, 0xe6f, 0xd65, 0xc6c, 0x36c, 0x265, 0x16f, 0x066, 0x76a, 0x663, 0x569, 0x460,
|
|
1382
|
+
0xca0, 0xda9, 0xea3, 0xfaa, 0x8a6, 0x9af, 0xaa5, 0xbac, 0x4ac, 0x5a5, 0x6af, 0x7a6, 0x0aa, 0x1a3, 0x2a9, 0x3a0,
|
|
1383
|
+
0xd30, 0xc39, 0xf33, 0xe3a, 0x936, 0x83f, 0xb35, 0xa3c, 0x53c, 0x435, 0x73f, 0x636, 0x13a, 0x033, 0x339, 0x230,
|
|
1384
|
+
0xe90, 0xf99, 0xc93, 0xd9a, 0xa96, 0xb9f, 0x895, 0x99c, 0x69c, 0x795, 0x49f, 0x596, 0x29a, 0x393, 0x099, 0x190,
|
|
1385
|
+
0xf00, 0xe09, 0xd03, 0xc0a, 0xb06, 0xa0f, 0x905, 0x80c, 0x70c, 0x605, 0x50f, 0x406, 0x30a, 0x203, 0x109, 0x000,
|
|
1386
|
+
];
|
|
1387
|
+
const TRI_TABLE = [
|
|
1388
|
+
[],
|
|
1389
|
+
[0, 8, 3],
|
|
1390
|
+
[0, 1, 9],
|
|
1391
|
+
[1, 8, 3, 9, 8, 1],
|
|
1392
|
+
[1, 2, 10],
|
|
1393
|
+
[0, 8, 3, 1, 2, 10],
|
|
1394
|
+
[9, 2, 10, 0, 2, 9],
|
|
1395
|
+
[2, 8, 3, 2, 10, 8, 10, 9, 8],
|
|
1396
|
+
[3, 11, 2],
|
|
1397
|
+
[0, 11, 2, 8, 11, 0],
|
|
1398
|
+
[1, 9, 0, 2, 3, 11],
|
|
1399
|
+
[1, 11, 2, 1, 9, 11, 9, 8, 11],
|
|
1400
|
+
[3, 10, 1, 11, 10, 3],
|
|
1401
|
+
[0, 10, 1, 0, 8, 10, 8, 11, 10],
|
|
1402
|
+
[3, 9, 0, 3, 11, 9, 11, 10, 9],
|
|
1403
|
+
[9, 8, 10, 10, 8, 11],
|
|
1404
|
+
[4, 7, 8],
|
|
1405
|
+
[4, 3, 0, 7, 3, 4],
|
|
1406
|
+
[0, 1, 9, 8, 4, 7],
|
|
1407
|
+
[4, 1, 9, 4, 7, 1, 7, 3, 1],
|
|
1408
|
+
[1, 2, 10, 8, 4, 7],
|
|
1409
|
+
[3, 4, 7, 3, 0, 4, 1, 2, 10],
|
|
1410
|
+
[9, 2, 10, 9, 0, 2, 8, 4, 7],
|
|
1411
|
+
[2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4],
|
|
1412
|
+
[8, 4, 7, 3, 11, 2],
|
|
1413
|
+
[11, 4, 7, 11, 2, 4, 2, 0, 4],
|
|
1414
|
+
[9, 0, 1, 8, 4, 7, 2, 3, 11],
|
|
1415
|
+
[4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1],
|
|
1416
|
+
[3, 10, 1, 3, 11, 10, 7, 8, 4],
|
|
1417
|
+
[1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4],
|
|
1418
|
+
[4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3],
|
|
1419
|
+
[4, 7, 11, 4, 11, 9, 9, 11, 10],
|
|
1420
|
+
[9, 5, 4],
|
|
1421
|
+
[9, 5, 4, 0, 8, 3],
|
|
1422
|
+
[0, 5, 4, 1, 5, 0],
|
|
1423
|
+
[8, 5, 4, 8, 3, 5, 3, 1, 5],
|
|
1424
|
+
[1, 2, 10, 9, 5, 4],
|
|
1425
|
+
[3, 0, 8, 1, 2, 10, 4, 9, 5],
|
|
1426
|
+
[5, 2, 10, 5, 4, 2, 4, 0, 2],
|
|
1427
|
+
[2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8],
|
|
1428
|
+
[9, 5, 4, 2, 3, 11],
|
|
1429
|
+
[0, 11, 2, 0, 8, 11, 4, 9, 5],
|
|
1430
|
+
[0, 5, 4, 0, 1, 5, 2, 3, 11],
|
|
1431
|
+
[2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5],
|
|
1432
|
+
[10, 3, 11, 10, 1, 3, 9, 5, 4],
|
|
1433
|
+
[4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10],
|
|
1434
|
+
[5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3],
|
|
1435
|
+
[5, 4, 8, 5, 8, 10, 10, 8, 11],
|
|
1436
|
+
[9, 7, 8, 5, 7, 9],
|
|
1437
|
+
[9, 3, 0, 9, 5, 3, 5, 7, 3],
|
|
1438
|
+
[0, 7, 8, 0, 1, 7, 1, 5, 7],
|
|
1439
|
+
[1, 5, 3, 3, 5, 7],
|
|
1440
|
+
[9, 7, 8, 9, 5, 7, 10, 1, 2],
|
|
1441
|
+
[10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3],
|
|
1442
|
+
[8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2],
|
|
1443
|
+
[2, 10, 5, 2, 5, 3, 3, 5, 7],
|
|
1444
|
+
[7, 9, 5, 7, 8, 9, 3, 11, 2],
|
|
1445
|
+
[9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11],
|
|
1446
|
+
[2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7],
|
|
1447
|
+
[11, 2, 1, 11, 1, 7, 7, 1, 5],
|
|
1448
|
+
[9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11],
|
|
1449
|
+
[5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0],
|
|
1450
|
+
[11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0],
|
|
1451
|
+
[11, 10, 5, 7, 11, 5],
|
|
1452
|
+
[10, 6, 5],
|
|
1453
|
+
[0, 8, 3, 5, 10, 6],
|
|
1454
|
+
[9, 0, 1, 5, 10, 6],
|
|
1455
|
+
[1, 8, 3, 1, 9, 8, 5, 10, 6],
|
|
1456
|
+
[1, 6, 5, 2, 6, 1],
|
|
1457
|
+
[1, 6, 5, 1, 2, 6, 3, 0, 8],
|
|
1458
|
+
[9, 6, 5, 9, 0, 6, 0, 2, 6],
|
|
1459
|
+
[5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8],
|
|
1460
|
+
[2, 3, 11, 10, 6, 5],
|
|
1461
|
+
[11, 0, 8, 11, 2, 0, 10, 6, 5],
|
|
1462
|
+
[0, 1, 9, 2, 3, 11, 5, 10, 6],
|
|
1463
|
+
[5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11],
|
|
1464
|
+
[6, 3, 11, 6, 5, 3, 5, 1, 3],
|
|
1465
|
+
[0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6],
|
|
1466
|
+
[3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9],
|
|
1467
|
+
[6, 5, 9, 6, 9, 11, 11, 9, 8],
|
|
1468
|
+
[5, 10, 6, 4, 7, 8],
|
|
1469
|
+
[4, 3, 0, 4, 7, 3, 6, 5, 10],
|
|
1470
|
+
[1, 9, 0, 5, 10, 6, 8, 4, 7],
|
|
1471
|
+
[10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4],
|
|
1472
|
+
[6, 1, 2, 6, 5, 1, 4, 7, 8],
|
|
1473
|
+
[1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7],
|
|
1474
|
+
[8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6],
|
|
1475
|
+
[7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9],
|
|
1476
|
+
[3, 11, 2, 7, 8, 4, 10, 6, 5],
|
|
1477
|
+
[5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11],
|
|
1478
|
+
[0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6],
|
|
1479
|
+
[9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6],
|
|
1480
|
+
[8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6],
|
|
1481
|
+
[5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11],
|
|
1482
|
+
[0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7],
|
|
1483
|
+
[6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9],
|
|
1484
|
+
[10, 4, 9, 6, 4, 10],
|
|
1485
|
+
[4, 10, 6, 4, 9, 10, 0, 8, 3],
|
|
1486
|
+
[10, 0, 1, 10, 6, 0, 6, 4, 0],
|
|
1487
|
+
[8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10],
|
|
1488
|
+
[1, 4, 9, 1, 2, 4, 2, 6, 4],
|
|
1489
|
+
[3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4],
|
|
1490
|
+
[0, 2, 4, 4, 2, 6],
|
|
1491
|
+
[8, 3, 2, 8, 2, 4, 4, 2, 6],
|
|
1492
|
+
[10, 4, 9, 10, 6, 4, 11, 2, 3],
|
|
1493
|
+
[0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6],
|
|
1494
|
+
[3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10],
|
|
1495
|
+
[6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1],
|
|
1496
|
+
[9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3],
|
|
1497
|
+
[8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1],
|
|
1498
|
+
[3, 11, 6, 3, 6, 0, 0, 6, 4],
|
|
1499
|
+
[6, 4, 8, 11, 6, 8],
|
|
1500
|
+
[7, 10, 6, 7, 8, 10, 8, 9, 10],
|
|
1501
|
+
[0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10],
|
|
1502
|
+
[10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0],
|
|
1503
|
+
[10, 6, 7, 10, 7, 1, 1, 7, 3],
|
|
1504
|
+
[1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7],
|
|
1505
|
+
[2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9],
|
|
1506
|
+
[7, 8, 0, 7, 0, 6, 6, 0, 2],
|
|
1507
|
+
[7, 3, 2, 6, 7, 2],
|
|
1508
|
+
[2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7],
|
|
1509
|
+
[2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7],
|
|
1510
|
+
[1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11],
|
|
1511
|
+
[11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1],
|
|
1512
|
+
[8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6],
|
|
1513
|
+
[0, 9, 1, 11, 6, 7],
|
|
1514
|
+
[7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0],
|
|
1515
|
+
[7, 11, 6],
|
|
1516
|
+
[7, 6, 11],
|
|
1517
|
+
[3, 0, 8, 11, 7, 6],
|
|
1518
|
+
[0, 1, 9, 11, 7, 6],
|
|
1519
|
+
[8, 1, 9, 8, 3, 1, 11, 7, 6],
|
|
1520
|
+
[10, 1, 2, 6, 11, 7],
|
|
1521
|
+
[1, 2, 10, 3, 0, 8, 6, 11, 7],
|
|
1522
|
+
[2, 9, 0, 2, 10, 9, 6, 11, 7],
|
|
1523
|
+
[6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8],
|
|
1524
|
+
[7, 2, 3, 6, 2, 7],
|
|
1525
|
+
[7, 0, 8, 7, 6, 0, 6, 2, 0],
|
|
1526
|
+
[2, 7, 6, 2, 3, 7, 0, 1, 9],
|
|
1527
|
+
[1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6],
|
|
1528
|
+
[10, 7, 6, 10, 1, 7, 1, 3, 7],
|
|
1529
|
+
[10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8],
|
|
1530
|
+
[0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7],
|
|
1531
|
+
[7, 6, 10, 7, 10, 8, 8, 10, 9],
|
|
1532
|
+
[6, 8, 4, 11, 8, 6],
|
|
1533
|
+
[3, 6, 11, 3, 0, 6, 0, 4, 6],
|
|
1534
|
+
[8, 6, 11, 8, 4, 6, 9, 0, 1],
|
|
1535
|
+
[9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6],
|
|
1536
|
+
[6, 8, 4, 6, 11, 8, 2, 10, 1],
|
|
1537
|
+
[1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6],
|
|
1538
|
+
[4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9],
|
|
1539
|
+
[10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3],
|
|
1540
|
+
[8, 2, 3, 8, 4, 2, 4, 6, 2],
|
|
1541
|
+
[0, 4, 2, 4, 6, 2],
|
|
1542
|
+
[1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8],
|
|
1543
|
+
[1, 9, 4, 1, 4, 2, 2, 4, 6],
|
|
1544
|
+
[8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1],
|
|
1545
|
+
[10, 1, 0, 10, 0, 6, 6, 0, 4],
|
|
1546
|
+
[4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3],
|
|
1547
|
+
[10, 9, 4, 6, 10, 4],
|
|
1548
|
+
[4, 9, 5, 7, 6, 11],
|
|
1549
|
+
[0, 8, 3, 4, 9, 5, 11, 7, 6],
|
|
1550
|
+
[5, 0, 1, 5, 4, 0, 7, 6, 11],
|
|
1551
|
+
[11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5],
|
|
1552
|
+
[9, 5, 4, 10, 1, 2, 7, 6, 11],
|
|
1553
|
+
[6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5],
|
|
1554
|
+
[7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2],
|
|
1555
|
+
[3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6],
|
|
1556
|
+
[7, 2, 3, 7, 6, 2, 5, 4, 9],
|
|
1557
|
+
[9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7],
|
|
1558
|
+
[3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0],
|
|
1559
|
+
[6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8],
|
|
1560
|
+
[9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7],
|
|
1561
|
+
[1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4],
|
|
1562
|
+
[4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10],
|
|
1563
|
+
[7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10],
|
|
1564
|
+
[6, 9, 5, 6, 11, 9, 11, 8, 9],
|
|
1565
|
+
[3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5],
|
|
1566
|
+
[0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11],
|
|
1567
|
+
[6, 11, 3, 6, 3, 5, 5, 3, 1],
|
|
1568
|
+
[1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6],
|
|
1569
|
+
[0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10],
|
|
1570
|
+
[11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5],
|
|
1571
|
+
[6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3],
|
|
1572
|
+
[5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2],
|
|
1573
|
+
[9, 5, 6, 9, 6, 0, 0, 6, 2],
|
|
1574
|
+
[1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8],
|
|
1575
|
+
[1, 5, 6, 2, 1, 6],
|
|
1576
|
+
[1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6],
|
|
1577
|
+
[10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0],
|
|
1578
|
+
[0, 3, 8, 5, 6, 10],
|
|
1579
|
+
[10, 5, 6],
|
|
1580
|
+
[11, 5, 10, 7, 5, 11],
|
|
1581
|
+
[11, 5, 10, 11, 7, 5, 8, 3, 0],
|
|
1582
|
+
[5, 11, 7, 5, 10, 11, 1, 9, 0],
|
|
1583
|
+
[10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1],
|
|
1584
|
+
[11, 1, 2, 11, 7, 1, 7, 5, 1],
|
|
1585
|
+
[0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11],
|
|
1586
|
+
[9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7],
|
|
1587
|
+
[7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2],
|
|
1588
|
+
[2, 5, 10, 2, 3, 5, 3, 7, 5],
|
|
1589
|
+
[8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5],
|
|
1590
|
+
[9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2],
|
|
1591
|
+
[9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2],
|
|
1592
|
+
[1, 3, 5, 3, 7, 5],
|
|
1593
|
+
[0, 8, 7, 0, 7, 1, 1, 7, 5],
|
|
1594
|
+
[9, 0, 3, 9, 3, 5, 5, 3, 7],
|
|
1595
|
+
[9, 8, 7, 5, 9, 7],
|
|
1596
|
+
[5, 8, 4, 5, 10, 8, 10, 11, 8],
|
|
1597
|
+
[5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0],
|
|
1598
|
+
[0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5],
|
|
1599
|
+
[10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4],
|
|
1600
|
+
[2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8],
|
|
1601
|
+
[0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11],
|
|
1602
|
+
[0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5],
|
|
1603
|
+
[9, 4, 5, 2, 11, 3],
|
|
1604
|
+
[2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4],
|
|
1605
|
+
[5, 10, 2, 5, 2, 4, 4, 2, 0],
|
|
1606
|
+
[3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9],
|
|
1607
|
+
[5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2],
|
|
1608
|
+
[8, 4, 5, 8, 5, 3, 3, 5, 1],
|
|
1609
|
+
[0, 4, 5, 1, 0, 5],
|
|
1610
|
+
[8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5],
|
|
1611
|
+
[9, 4, 5],
|
|
1612
|
+
[4, 11, 7, 4, 9, 11, 9, 10, 11],
|
|
1613
|
+
[0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11],
|
|
1614
|
+
[1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11],
|
|
1615
|
+
[3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4],
|
|
1616
|
+
[4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2],
|
|
1617
|
+
[9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3],
|
|
1618
|
+
[11, 7, 4, 11, 4, 2, 2, 4, 0],
|
|
1619
|
+
[11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4],
|
|
1620
|
+
[2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9],
|
|
1621
|
+
[9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7],
|
|
1622
|
+
[3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10],
|
|
1623
|
+
[1, 10, 2, 8, 7, 4],
|
|
1624
|
+
[4, 9, 1, 4, 1, 7, 7, 1, 3],
|
|
1625
|
+
[4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1],
|
|
1626
|
+
[4, 0, 3, 7, 4, 3],
|
|
1627
|
+
[4, 8, 7],
|
|
1628
|
+
[9, 10, 8, 10, 11, 8],
|
|
1629
|
+
[3, 0, 9, 3, 9, 11, 11, 9, 10],
|
|
1630
|
+
[0, 1, 10, 0, 10, 8, 8, 10, 11],
|
|
1631
|
+
[3, 1, 10, 11, 3, 10],
|
|
1632
|
+
[1, 2, 11, 1, 11, 9, 9, 11, 8],
|
|
1633
|
+
[3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9],
|
|
1634
|
+
[0, 2, 11, 8, 0, 11],
|
|
1635
|
+
[3, 2, 11],
|
|
1636
|
+
[2, 3, 8, 2, 8, 10, 10, 8, 9],
|
|
1637
|
+
[9, 10, 2, 0, 9, 2],
|
|
1638
|
+
[2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8],
|
|
1639
|
+
[1, 10, 2],
|
|
1640
|
+
[1, 3, 8, 9, 1, 8],
|
|
1641
|
+
[0, 9, 1],
|
|
1642
|
+
[0, 3, 8],
|
|
1643
|
+
[],
|
|
1644
|
+
];
|
|
1645
|
+
export { marchingCubes };
|