@manycore/aholo-splat-transform 1.2.6
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 +102 -0
- package/README.md +33 -0
- package/bin/cli.js +118 -0
- package/dist/SplatData.d.ts +67 -0
- package/dist/SplatData.js +156 -0
- package/dist/constant.d.ts +3 -0
- package/dist/constant.js +13 -0
- package/dist/file/IFile.d.ts +5 -0
- package/dist/file/IFile.js +1 -0
- package/dist/file/index.d.ts +7 -0
- package/dist/file/index.js +6 -0
- package/dist/file/ksplat.d.ts +12 -0
- package/dist/file/ksplat.js +232 -0
- package/dist/file/lcc.d.ts +11 -0
- package/dist/file/lcc.js +157 -0
- package/dist/file/ply.d.ts +13 -0
- package/dist/file/ply.js +388 -0
- package/dist/file/sog.d.ts +80 -0
- package/dist/file/sog.js +504 -0
- package/dist/file/splat.d.ts +6 -0
- package/dist/file/splat.js +99 -0
- package/dist/file/spz.d.ts +8 -0
- package/dist/file/spz.js +400 -0
- package/dist/file/voxel.d.ts +37 -0
- package/dist/file/voxel.js +280 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.js +54 -0
- package/dist/native/cpp/bin/linux/binding.node +0 -0
- package/dist/native/cpp/bin/windows/binding.node +0 -0
- package/dist/native/index.d.ts +54 -0
- package/dist/native/index.js +128 -0
- package/dist/tasks/AutoChunkLodTask.d.ts +13 -0
- package/dist/tasks/AutoChunkLodTask.js +117 -0
- package/dist/tasks/AutoLodTask.d.ts +10 -0
- package/dist/tasks/AutoLodTask.js +20 -0
- package/dist/tasks/BaseTask.d.ts +15 -0
- package/dist/tasks/BaseTask.js +5 -0
- package/dist/tasks/FlexLodTask.d.ts +12 -0
- package/dist/tasks/FlexLodTask.js +44 -0
- package/dist/tasks/ModifyTask.d.ts +9 -0
- package/dist/tasks/ModifyTask.js +156 -0
- package/dist/tasks/ReadTask.d.ts +8 -0
- package/dist/tasks/ReadTask.js +29 -0
- package/dist/tasks/SkeletonLodTask.d.ts +10 -0
- package/dist/tasks/SkeletonLodTask.js +156 -0
- package/dist/tasks/VoxelTask.d.ts +30 -0
- package/dist/tasks/VoxelTask.js +37 -0
- package/dist/tasks/WriteTask.d.ts +11 -0
- package/dist/tasks/WriteTask.js +70 -0
- package/dist/utils/BufferReader.d.ts +12 -0
- package/dist/utils/BufferReader.js +47 -0
- package/dist/utils/Logger.d.ts +11 -0
- package/dist/utils/Logger.js +38 -0
- package/dist/utils/StreamChunkDecoder.d.ts +16 -0
- package/dist/utils/StreamChunkDecoder.js +36 -0
- package/dist/utils/index.d.ts +27 -0
- package/dist/utils/index.js +101 -0
- package/dist/utils/k-means.d.ts +4 -0
- package/dist/utils/k-means.js +350 -0
- package/dist/utils/math.d.ts +46 -0
- package/dist/utils/math.js +351 -0
- package/dist/utils/quantize-1d.d.ts +4 -0
- package/dist/utils/quantize-1d.js +164 -0
- package/dist/utils/sh-rotate.d.ts +2 -0
- package/dist/utils/sh-rotate.js +175 -0
- package/dist/utils/splat.d.ts +20 -0
- package/dist/utils/splat.js +378 -0
- package/dist/utils/voxel/common.d.ts +162 -0
- package/dist/utils/voxel/common.js +1700 -0
- package/dist/utils/voxel/coplanar-merge.d.ts +63 -0
- package/dist/utils/voxel/coplanar-merge.js +819 -0
- package/dist/utils/voxel/gpu-dilation.d.ts +2 -0
- package/dist/utils/voxel/gpu-dilation.js +665 -0
- package/dist/utils/voxel/marching-cubes.d.ts +42 -0
- package/dist/utils/voxel/marching-cubes.js +1657 -0
- package/dist/utils/voxel/mesh.d.ts +3 -0
- package/dist/utils/voxel/mesh.js +130 -0
- package/dist/utils/voxel/nav.d.ts +29 -0
- package/dist/utils/voxel/nav.js +1043 -0
- package/dist/utils/voxel/postprocess.d.ts +23 -0
- package/dist/utils/voxel/postprocess.js +375 -0
- package/dist/utils/voxel/voxel-faces.d.ts +18 -0
- package/dist/utils/voxel/voxel-faces.js +663 -0
- package/dist/utils/voxel/voxelize.d.ts +33 -0
- package/dist/utils/voxel/voxelize.js +1193 -0
- package/dist/utils/webgpu.d.ts +8 -0
- package/dist/utils/webgpu.js +122 -0
- package/package.json +32 -0
|
@@ -0,0 +1,1657 @@
|
|
|
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
|
+
const 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
|
+
const 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
|
+
const 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
|
+
const 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
|
+
const 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
|
+
const 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
|
+
const 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
|
+
const 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
|
+
const 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
|
+
const 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
|
+
const 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)) *
|
|
289
|
+
faceCoordStride + (v + 1));
|
|
290
|
+
};
|
|
291
|
+
const addDiagCell = (bucket, plane, u, e) => {
|
|
292
|
+
if (diagCellLen === diagCellCap) {
|
|
293
|
+
diagCellCap = diagCellCap === 0 ? 1024 : diagCellCap * 2;
|
|
294
|
+
const grown = new Float64Array(diagCellCap);
|
|
295
|
+
grown.set(diagCellKeys);
|
|
296
|
+
diagCellKeys = grown;
|
|
297
|
+
}
|
|
298
|
+
diagCellKeys[diagCellLen++] =
|
|
299
|
+
(((bucket * diagCoordStride + (plane + diagCoordOffset)) * diagCoordStride +
|
|
300
|
+
(u + diagCoordOffset)) * diagCoordStride + (e + diagCoordOffset));
|
|
301
|
+
};
|
|
302
|
+
const 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
|
+
const 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
|
+
const 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
|
+
const 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
|
+
const 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
|
+
const 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
|
+
const 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
|
+
const samePoint = (src, a, b) => src[a] === src[b] && src[a + 1] === src[b + 1] && src[a + 2] === src[b + 2];
|
|
443
|
+
const pointInUnique = (x, y, z, uniqueCount) => {
|
|
444
|
+
for (let i = 0; i < uniqueCount; i++) {
|
|
445
|
+
const o = i * 3;
|
|
446
|
+
if (uniqueVerts[o] === x && uniqueVerts[o + 1] === y && uniqueVerts[o + 2] === z) {
|
|
447
|
+
return true;
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
return false;
|
|
451
|
+
};
|
|
452
|
+
const collectDiagPair = (triRow, triA, triB, vx, vy, vz) => {
|
|
453
|
+
const edgesA = triA * 3;
|
|
454
|
+
const edgesB = triB * 3;
|
|
455
|
+
edgeScaledPoint(triRow[edgesA], vx, vy, vz, pairVerts, 0);
|
|
456
|
+
edgeScaledPoint(triRow[edgesA + 2], vx, vy, vz, pairVerts, 3);
|
|
457
|
+
edgeScaledPoint(triRow[edgesA + 1], vx, vy, vz, pairVerts, 6);
|
|
458
|
+
edgeScaledPoint(triRow[edgesB], vx, vy, vz, pairVerts, 9);
|
|
459
|
+
edgeScaledPoint(triRow[edgesB + 2], vx, vy, vz, pairVerts, 12);
|
|
460
|
+
edgeScaledPoint(triRow[edgesB + 1], vx, vy, vz, pairVerts, 15);
|
|
461
|
+
let uniqueCount = 0;
|
|
462
|
+
for (let i = 0; i < 6; i++) {
|
|
463
|
+
const src = i * 3;
|
|
464
|
+
let found = false;
|
|
465
|
+
for (let j = 0; j < uniqueCount; j++) {
|
|
466
|
+
const dst = j * 3;
|
|
467
|
+
if (pairVerts[src] === uniqueVerts[dst] &&
|
|
468
|
+
pairVerts[src + 1] === uniqueVerts[dst + 1] &&
|
|
469
|
+
pairVerts[src + 2] === uniqueVerts[dst + 2]) {
|
|
470
|
+
found = true;
|
|
471
|
+
break;
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
if (!found) {
|
|
475
|
+
if (uniqueCount === 4) {
|
|
476
|
+
return false;
|
|
477
|
+
}
|
|
478
|
+
const dst = uniqueCount * 3;
|
|
479
|
+
uniqueVerts[dst] = pairVerts[src];
|
|
480
|
+
uniqueVerts[dst + 1] = pairVerts[src + 1];
|
|
481
|
+
uniqueVerts[dst + 2] = pairVerts[src + 2];
|
|
482
|
+
uniqueCount++;
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
if (uniqueCount !== 4) {
|
|
486
|
+
return false;
|
|
487
|
+
}
|
|
488
|
+
const ax = pairVerts[3] - pairVerts[0];
|
|
489
|
+
const ay = pairVerts[4] - pairVerts[1];
|
|
490
|
+
const az = pairVerts[5] - pairVerts[2];
|
|
491
|
+
const bx = pairVerts[6] - pairVerts[0];
|
|
492
|
+
const by = pairVerts[7] - pairVerts[1];
|
|
493
|
+
const bz = pairVerts[8] - pairVerts[2];
|
|
494
|
+
const normal = [
|
|
495
|
+
ay * bz - az * by,
|
|
496
|
+
az * bx - ax * bz,
|
|
497
|
+
ax * by - ay * bx
|
|
498
|
+
];
|
|
499
|
+
const absNormal = [Math.abs(normal[0]), Math.abs(normal[1]), Math.abs(normal[2])];
|
|
500
|
+
let axisE = -1;
|
|
501
|
+
let axisA = -1;
|
|
502
|
+
let axisB = -1;
|
|
503
|
+
for (let i = 0; i < 3; i++) {
|
|
504
|
+
if (absNormal[i] === 0) {
|
|
505
|
+
axisE = i;
|
|
506
|
+
}
|
|
507
|
+
else if (axisA === -1) {
|
|
508
|
+
axisA = i;
|
|
509
|
+
}
|
|
510
|
+
else {
|
|
511
|
+
axisB = i;
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
if (axisE === -1 || axisA === -1 || axisB === -1) {
|
|
515
|
+
return false;
|
|
516
|
+
}
|
|
517
|
+
if (absNormal[axisA] !== absNormal[axisB]) {
|
|
518
|
+
return false;
|
|
519
|
+
}
|
|
520
|
+
const signA = normal[axisA] > 0 ? 1 : -1;
|
|
521
|
+
const signB = normal[axisB] > 0 ? 1 : -1;
|
|
522
|
+
const bucket = diagBucket(axisA, axisB, signA, signB);
|
|
523
|
+
const plane = signA * coordByAxis(uniqueVerts[0], uniqueVerts[1], uniqueVerts[2], axisA) +
|
|
524
|
+
signB * coordByAxis(uniqueVerts[0], uniqueVerts[1], uniqueVerts[2], axisB);
|
|
525
|
+
let minU = Infinity;
|
|
526
|
+
let maxU = -Infinity;
|
|
527
|
+
let minE = Infinity;
|
|
528
|
+
let maxE = -Infinity;
|
|
529
|
+
for (let i = 0; i < uniqueCount; i++) {
|
|
530
|
+
const o = i * 3;
|
|
531
|
+
const a = coordByAxis(uniqueVerts[o], uniqueVerts[o + 1], uniqueVerts[o + 2], axisA);
|
|
532
|
+
const b = coordByAxis(uniqueVerts[o], uniqueVerts[o + 1], uniqueVerts[o + 2], axisB);
|
|
533
|
+
const e = coordByAxis(uniqueVerts[o], uniqueVerts[o + 1], uniqueVerts[o + 2], axisE);
|
|
534
|
+
if (signA * a + signB * b !== plane) {
|
|
535
|
+
return false;
|
|
536
|
+
}
|
|
537
|
+
const u = signA * a - signB * b;
|
|
538
|
+
if (u < minU) {
|
|
539
|
+
minU = u;
|
|
540
|
+
}
|
|
541
|
+
if (u > maxU) {
|
|
542
|
+
maxU = u;
|
|
543
|
+
}
|
|
544
|
+
if (e < minE) {
|
|
545
|
+
minE = e;
|
|
546
|
+
}
|
|
547
|
+
if (e > maxE) {
|
|
548
|
+
maxE = e;
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
if (maxU - minU !== 2 || maxE - minE !== 2) {
|
|
552
|
+
return false;
|
|
553
|
+
}
|
|
554
|
+
const p00 = diagPoint(bucket, plane, minU, minE);
|
|
555
|
+
const p10 = diagPoint(bucket, plane, maxU, minE);
|
|
556
|
+
const p11 = diagPoint(bucket, plane, maxU, maxE);
|
|
557
|
+
const p01 = diagPoint(bucket, plane, minU, maxE);
|
|
558
|
+
if (!pointInUnique(p00[0], p00[1], p00[2], uniqueCount) ||
|
|
559
|
+
!pointInUnique(p10[0], p10[1], p10[2], uniqueCount) ||
|
|
560
|
+
!pointInUnique(p11[0], p11[1], p11[2], uniqueCount) ||
|
|
561
|
+
!pointInUnique(p01[0], p01[1], p01[2], uniqueCount)) {
|
|
562
|
+
return false;
|
|
563
|
+
}
|
|
564
|
+
let sharedCount = 0;
|
|
565
|
+
let sharedU0 = 0;
|
|
566
|
+
let sharedE0 = 0;
|
|
567
|
+
let sharedU1 = 0;
|
|
568
|
+
let sharedE1 = 0;
|
|
569
|
+
for (let i = 0; i < 3; i++) {
|
|
570
|
+
const oi = i * 3;
|
|
571
|
+
for (let j = 3; j < 6; j++) {
|
|
572
|
+
const oj = j * 3;
|
|
573
|
+
if (!samePoint(pairVerts, oi, oj)) {
|
|
574
|
+
continue;
|
|
575
|
+
}
|
|
576
|
+
const a = coordByAxis(pairVerts[oi], pairVerts[oi + 1], pairVerts[oi + 2], axisA);
|
|
577
|
+
const b = coordByAxis(pairVerts[oi], pairVerts[oi + 1], pairVerts[oi + 2], axisB);
|
|
578
|
+
const e = coordByAxis(pairVerts[oi], pairVerts[oi + 1], pairVerts[oi + 2], axisE);
|
|
579
|
+
if (sharedCount === 0) {
|
|
580
|
+
sharedU0 = signA * a - signB * b;
|
|
581
|
+
sharedE0 = e;
|
|
582
|
+
}
|
|
583
|
+
else {
|
|
584
|
+
sharedU1 = signA * a - signB * b;
|
|
585
|
+
sharedE1 = e;
|
|
586
|
+
}
|
|
587
|
+
sharedCount++;
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
if (sharedCount !== 2 || sharedU0 === sharedU1 || sharedE0 === sharedE1) {
|
|
591
|
+
return false;
|
|
592
|
+
}
|
|
593
|
+
addDiagCell(bucket, plane, minU, minE);
|
|
594
|
+
return true;
|
|
595
|
+
};
|
|
596
|
+
const collectDiagFaces = (triRow, vx, vy, vz) => {
|
|
597
|
+
if (!mergeFlatFaces) {
|
|
598
|
+
return 0;
|
|
599
|
+
}
|
|
600
|
+
const triCount = (triRow.length / 3) | 0;
|
|
601
|
+
let usedMask = 0;
|
|
602
|
+
for (let i = 0; i < triCount; i++) {
|
|
603
|
+
if ((usedMask & (1 << i)) !== 0) {
|
|
604
|
+
continue;
|
|
605
|
+
}
|
|
606
|
+
for (let j = i + 1; j < triCount; j++) {
|
|
607
|
+
if ((usedMask & (1 << j)) !== 0) {
|
|
608
|
+
continue;
|
|
609
|
+
}
|
|
610
|
+
if (collectDiagPair(triRow, i, j, vx, vy, vz)) {
|
|
611
|
+
usedMask |= (1 << i) | (1 << j);
|
|
612
|
+
break;
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
return usedMask;
|
|
617
|
+
};
|
|
618
|
+
const getScaledVertex = (x2, y2, z2) => {
|
|
619
|
+
if ((x2 & 1) !== 0) {
|
|
620
|
+
return getVertex((x2 - 1) / 2, y2 / 2, z2 / 2, 0);
|
|
621
|
+
}
|
|
622
|
+
if ((y2 & 1) !== 0) {
|
|
623
|
+
return getVertex(x2 / 2, (y2 - 1) / 2, z2 / 2, 1);
|
|
624
|
+
}
|
|
625
|
+
return getVertex(x2 / 2, y2 / 2, (z2 - 1) / 2, 2);
|
|
626
|
+
};
|
|
627
|
+
let perimeterScratch = new Uint32Array(16);
|
|
628
|
+
let perimeterU = new Int32Array(16);
|
|
629
|
+
let perimeterV = new Int32Array(16);
|
|
630
|
+
let perimeterLen = 0;
|
|
631
|
+
const localFaceUv = (axis, x2, y2, z2) => {
|
|
632
|
+
if (axis === 0) {
|
|
633
|
+
return [y2, z2];
|
|
634
|
+
}
|
|
635
|
+
if (axis === 1) {
|
|
636
|
+
return [x2, z2];
|
|
637
|
+
}
|
|
638
|
+
return [x2, y2];
|
|
639
|
+
};
|
|
640
|
+
const addPerimeterVertex = (vertex, u, v) => {
|
|
641
|
+
if (perimeterLen > 0 && perimeterScratch[perimeterLen - 1] === vertex) {
|
|
642
|
+
return;
|
|
643
|
+
}
|
|
644
|
+
if (perimeterLen === perimeterScratch.length) {
|
|
645
|
+
const grown = new Uint32Array(perimeterScratch.length * 2);
|
|
646
|
+
grown.set(perimeterScratch);
|
|
647
|
+
perimeterScratch = grown;
|
|
648
|
+
const grownU = new Int32Array(perimeterU.length * 2);
|
|
649
|
+
grownU.set(perimeterU);
|
|
650
|
+
perimeterU = grownU;
|
|
651
|
+
const grownV = new Int32Array(perimeterV.length * 2);
|
|
652
|
+
grownV.set(perimeterV);
|
|
653
|
+
perimeterV = grownV;
|
|
654
|
+
}
|
|
655
|
+
perimeterScratch[perimeterLen] = vertex;
|
|
656
|
+
perimeterU[perimeterLen] = u;
|
|
657
|
+
perimeterV[perimeterLen] = v;
|
|
658
|
+
perimeterLen++;
|
|
659
|
+
};
|
|
660
|
+
const addPerimeterPoint = (axis, x2, y2, z2) => {
|
|
661
|
+
const [u, v] = localFaceUv(axis, x2, y2, z2);
|
|
662
|
+
addPerimeterVertex(getScaledVertex(x2, y2, z2), u, v);
|
|
663
|
+
};
|
|
664
|
+
const addDiagPerimeterPoint = (bucket, x2, y2, z2) => {
|
|
665
|
+
const { axisA, axisB, axisE, signA, signB } = decodeDiagBucket(bucket);
|
|
666
|
+
const a = coordByAxis(x2, y2, z2, axisA);
|
|
667
|
+
const b = coordByAxis(x2, y2, z2, axisB);
|
|
668
|
+
const e = coordByAxis(x2, y2, z2, axisE);
|
|
669
|
+
addPerimeterVertex(getScaledVertex(x2, y2, z2), signA * a - signB * b, e);
|
|
670
|
+
};
|
|
671
|
+
const addSplitSegment = (x0, y0, z0, x1, y1, z1) => {
|
|
672
|
+
const changes = (x0 !== x1 ? 1 : 0) + (y0 !== y1 ? 1 : 0) + (z0 !== z1 ? 1 : 0);
|
|
673
|
+
if (changes !== 1) {
|
|
674
|
+
return;
|
|
675
|
+
}
|
|
676
|
+
if (x0 !== x1) {
|
|
677
|
+
const key = splitLineKey(0, x0, y0, z0);
|
|
678
|
+
addSplitLinePoint(key, x0);
|
|
679
|
+
addSplitLinePoint(key, x1);
|
|
680
|
+
}
|
|
681
|
+
else if (y0 !== y1) {
|
|
682
|
+
const key = splitLineKey(1, x0, y0, z0);
|
|
683
|
+
addSplitLinePoint(key, y0);
|
|
684
|
+
addSplitLinePoint(key, y1);
|
|
685
|
+
}
|
|
686
|
+
else {
|
|
687
|
+
const key = splitLineKey(2, x0, y0, z0);
|
|
688
|
+
addSplitLinePoint(key, z0);
|
|
689
|
+
addSplitLinePoint(key, z1);
|
|
690
|
+
}
|
|
691
|
+
};
|
|
692
|
+
const addSplitEdgeVertices = (axis, x0, y0, z0, x1, y1, z1, addPoint = (px, py, pz) => {
|
|
693
|
+
addPerimeterPoint(axis, px, py, pz);
|
|
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
|
+
const emitPoint = (t) => {
|
|
721
|
+
if (varAxis === 0) {
|
|
722
|
+
addPoint(t, y0, z0);
|
|
723
|
+
}
|
|
724
|
+
else if (varAxis === 1) {
|
|
725
|
+
addPoint(x0, t, z0);
|
|
726
|
+
}
|
|
727
|
+
else {
|
|
728
|
+
addPoint(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
|
+
const appendOrientedTri = (a, b, c, useLocalCcw) => {
|
|
757
|
+
if (useLocalCcw) {
|
|
758
|
+
appendTri(a, b, c);
|
|
759
|
+
}
|
|
760
|
+
else {
|
|
761
|
+
appendTri(a, c, b);
|
|
762
|
+
}
|
|
763
|
+
};
|
|
764
|
+
const 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
|
+
const 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
|
+
const 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
|
+
const 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
|
+
const 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
|
+
const 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
|
+
const addRect = (bucket, p, u0, v0, u1, v1) => {
|
|
877
|
+
if (rectLen === rectCap) {
|
|
878
|
+
rectCap *= 2;
|
|
879
|
+
const 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
|
+
const addDiagRect = (bucket, plane, u0, e0, u1, e1) => {
|
|
900
|
+
if (diagRectLen === diagRectCap) {
|
|
901
|
+
diagRectCap *= 2;
|
|
902
|
+
const 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
|
+
const 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
|
+
const 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
|
+
const 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
|
+
const 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
|
+
const uvKeyOf = (uOff, vOff) => uOff * faceCoordStride + vOff;
|
|
982
|
+
const p = pOff - 1;
|
|
983
|
+
for (let i = 0; i < count; i++) {
|
|
984
|
+
if (visited[i]) {
|
|
985
|
+
continue;
|
|
986
|
+
}
|
|
987
|
+
const uvKey = decodeUvKey(keys[start + i]);
|
|
988
|
+
const uOff = Math.floor(uvKey / faceCoordStride);
|
|
989
|
+
const vOff = uvKey % faceCoordStride;
|
|
990
|
+
let width = 1;
|
|
991
|
+
while (true) {
|
|
992
|
+
const idx = lookup(uvKeyOf(uOff + width, vOff));
|
|
993
|
+
if (idx === -1 || visited[idx]) {
|
|
994
|
+
break;
|
|
995
|
+
}
|
|
996
|
+
width++;
|
|
997
|
+
}
|
|
998
|
+
let height = 1;
|
|
999
|
+
while (true) {
|
|
1000
|
+
let canGrow = true;
|
|
1001
|
+
for (let du = 0; du < width; du++) {
|
|
1002
|
+
const idx = lookup(uvKeyOf(uOff + du, vOff + height));
|
|
1003
|
+
if (idx === -1 || visited[idx]) {
|
|
1004
|
+
canGrow = false;
|
|
1005
|
+
break;
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
if (!canGrow) {
|
|
1009
|
+
break;
|
|
1010
|
+
}
|
|
1011
|
+
height++;
|
|
1012
|
+
}
|
|
1013
|
+
for (let dv = 0; dv < height; dv++) {
|
|
1014
|
+
for (let du = 0; du < width; du++) {
|
|
1015
|
+
const idx = lookup(uvKeyOf(uOff + du, vOff + dv));
|
|
1016
|
+
visited[idx] = 1;
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
addRect(bucket, p, uOff - 1, vOff - 1, uOff - 1 + width, vOff - 1 + height);
|
|
1020
|
+
}
|
|
1021
|
+
start = end;
|
|
1022
|
+
}
|
|
1023
|
+
if (diagCellLen > 0) {
|
|
1024
|
+
const diagKeys = diagCellKeys.slice(0, diagCellLen);
|
|
1025
|
+
diagCellKeys = new Float64Array(0);
|
|
1026
|
+
diagKeys.sort();
|
|
1027
|
+
const decodeDiagKey = (key) => {
|
|
1028
|
+
const eOff = key % diagCoordStride;
|
|
1029
|
+
let q = Math.floor(key / diagCoordStride);
|
|
1030
|
+
const uOff = q % diagCoordStride;
|
|
1031
|
+
q = Math.floor(q / diagCoordStride);
|
|
1032
|
+
const planeOff = q % diagCoordStride;
|
|
1033
|
+
const bucket = Math.floor(q / diagCoordStride);
|
|
1034
|
+
return {
|
|
1035
|
+
bucket,
|
|
1036
|
+
plane: planeOff - diagCoordOffset,
|
|
1037
|
+
u: uOff - diagCoordOffset,
|
|
1038
|
+
e: eOff - diagCoordOffset
|
|
1039
|
+
};
|
|
1040
|
+
};
|
|
1041
|
+
let diagStart = 0;
|
|
1042
|
+
while (diagStart < diagKeys.length) {
|
|
1043
|
+
const first = decodeDiagKey(diagKeys[diagStart]);
|
|
1044
|
+
let diagEnd = diagStart + 1;
|
|
1045
|
+
while (diagEnd < diagKeys.length) {
|
|
1046
|
+
const next = decodeDiagKey(diagKeys[diagEnd]);
|
|
1047
|
+
if (next.bucket !== first.bucket || next.plane !== first.plane || next.u !== first.u) {
|
|
1048
|
+
break;
|
|
1049
|
+
}
|
|
1050
|
+
diagEnd++;
|
|
1051
|
+
}
|
|
1052
|
+
let i = diagStart;
|
|
1053
|
+
while (i < diagEnd) {
|
|
1054
|
+
const run = decodeDiagKey(diagKeys[i]);
|
|
1055
|
+
let e1 = run.e + 2;
|
|
1056
|
+
i++;
|
|
1057
|
+
while (i < diagEnd) {
|
|
1058
|
+
const next = decodeDiagKey(diagKeys[i]);
|
|
1059
|
+
if (next.e !== e1) {
|
|
1060
|
+
break;
|
|
1061
|
+
}
|
|
1062
|
+
e1 += 2;
|
|
1063
|
+
i++;
|
|
1064
|
+
}
|
|
1065
|
+
addDiagRect(run.bucket, run.plane, run.u, run.e, run.u + 2, e1);
|
|
1066
|
+
}
|
|
1067
|
+
diagStart = diagEnd;
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1070
|
+
for (let r = 0; r < rectLen; r++) {
|
|
1071
|
+
const axis = rectBucket[r] >> 1;
|
|
1072
|
+
const p = rectP[r];
|
|
1073
|
+
const u0 = rectU0[r];
|
|
1074
|
+
const v0 = rectV0[r];
|
|
1075
|
+
const u1 = rectU1[r];
|
|
1076
|
+
const v1 = rectV1[r];
|
|
1077
|
+
const a = scaledFacePoint(axis, p, u0, v0);
|
|
1078
|
+
const b = scaledFacePoint(axis, p, u1, v0);
|
|
1079
|
+
const c = scaledFacePoint(axis, p, u1, v1);
|
|
1080
|
+
const d = scaledFacePoint(axis, p, u0, v1);
|
|
1081
|
+
addSplitSegment(a[0], a[1], a[2], b[0], b[1], b[2]);
|
|
1082
|
+
addSplitSegment(b[0], b[1], b[2], c[0], c[1], c[2]);
|
|
1083
|
+
addSplitSegment(c[0], c[1], c[2], d[0], d[1], d[2]);
|
|
1084
|
+
addSplitSegment(d[0], d[1], d[2], a[0], a[1], a[2]);
|
|
1085
|
+
}
|
|
1086
|
+
for (let r = 0; r < diagRectLen; r++) {
|
|
1087
|
+
const bucket = diagRectBucket[r];
|
|
1088
|
+
const plane = diagRectPlane[r];
|
|
1089
|
+
const u0 = diagRectU0[r];
|
|
1090
|
+
const e0 = diagRectE0[r];
|
|
1091
|
+
const u1 = diagRectU1[r];
|
|
1092
|
+
const e1 = diagRectE1[r];
|
|
1093
|
+
const a = diagPoint(bucket, plane, u0, e0);
|
|
1094
|
+
const b = diagPoint(bucket, plane, u1, e0);
|
|
1095
|
+
const c = diagPoint(bucket, plane, u1, e1);
|
|
1096
|
+
const d = diagPoint(bucket, plane, u0, e1);
|
|
1097
|
+
addSplitSegment(a[0], a[1], a[2], b[0], b[1], b[2]);
|
|
1098
|
+
addSplitSegment(b[0], b[1], b[2], c[0], c[1], c[2]);
|
|
1099
|
+
addSplitSegment(c[0], c[1], c[2], d[0], d[1], d[2]);
|
|
1100
|
+
addSplitSegment(d[0], d[1], d[2], a[0], a[1], a[2]);
|
|
1101
|
+
}
|
|
1102
|
+
if (splitLinePoints) {
|
|
1103
|
+
for (const points of splitLinePoints.values()) {
|
|
1104
|
+
points.sort((a, b) => a - b);
|
|
1105
|
+
let write = 0;
|
|
1106
|
+
for (let i = 0; i < points.length; i++) {
|
|
1107
|
+
if (i === 0 || points[i] !== points[i - 1]) {
|
|
1108
|
+
points[write++] = points[i];
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
points.length = write;
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
collectSplitPoints = false;
|
|
1115
|
+
for (let r = 0; r < rectLen; r++) {
|
|
1116
|
+
emitFaceRectangle(rectBucket[r], rectP[r], rectU0[r], rectV0[r], rectU1[r], rectV1[r]);
|
|
1117
|
+
}
|
|
1118
|
+
for (let r = 0; r < diagRectLen; r++) {
|
|
1119
|
+
emitDiagRectangle(diagRectBucket[r], diagRectPlane[r], diagRectU0[r], diagRectE0[r], diagRectU1[r], diagRectE1[r]);
|
|
1120
|
+
}
|
|
1121
|
+
splitLinePoints?.clear();
|
|
1122
|
+
};
|
|
1123
|
+
// Track processed orphan cells to avoid duplicate triangles. When a cell's
|
|
1124
|
+
// owner block doesn't exist, multiple neighboring blocks can reach it via
|
|
1125
|
+
// the -1 boundary extension. The hash table ensures each orphan cell is
|
|
1126
|
+
// only processed once. Same typed-array structure as the vertex hash —
|
|
1127
|
+
// see `oKeys` / `oGrow` above. Stored separately because keys collide with
|
|
1128
|
+
// vertex keys (same encoding, different namespace).
|
|
1129
|
+
// Iterate non-empty blocks of the grid. Local SparseVoxelGrid stores one
|
|
1130
|
+
// byte per block type instead of upstream's packed 2-bit type words.
|
|
1131
|
+
// Each block is processed once; the inner loop also handles boundary cells
|
|
1132
|
+
// that straddle into neighboring blocks (so we don't need a separate pass
|
|
1133
|
+
// for orphan boundary cells along the negative grid edges).
|
|
1134
|
+
for (let w = 0; w < types.length; w++) {
|
|
1135
|
+
const word = types[w];
|
|
1136
|
+
if (word === 0) {
|
|
1137
|
+
continue;
|
|
1138
|
+
}
|
|
1139
|
+
let nonEmpty = ((word & EVEN_BITS) | ((word >>> 1) & EVEN_BITS)) >>> 0;
|
|
1140
|
+
const baseBlockIdx = w * BLOCKS_PER_WORD;
|
|
1141
|
+
while (nonEmpty) {
|
|
1142
|
+
const bp = 31 - Math.clz32(nonEmpty & -nonEmpty);
|
|
1143
|
+
const lane = bp >>> 1;
|
|
1144
|
+
const blockIdx = baseBlockIdx + lane;
|
|
1145
|
+
nonEmpty &= nonEmpty - 1;
|
|
1146
|
+
if (blockIdx >= totalBlocks) {
|
|
1147
|
+
break;
|
|
1148
|
+
}
|
|
1149
|
+
// Decode block coordinates.
|
|
1150
|
+
bx = blockIdx % nbx;
|
|
1151
|
+
const byBz = (blockIdx / nbx) | 0;
|
|
1152
|
+
by = byBz % nby;
|
|
1153
|
+
bz = (byBz / nby) | 0;
|
|
1154
|
+
// Populate the 3x3x3 neighbor table for this block. After this loop,
|
|
1155
|
+
// every per-cell occupancy query is a direct typed-array index.
|
|
1156
|
+
//
|
|
1157
|
+
// Track `allNeighborsSolid` so we can skip the entire cell loop
|
|
1158
|
+
// for blocks deep inside an obstruction, where every cubeIndex is
|
|
1159
|
+
// 255 and no triangles are emitted. On large carved scenes this
|
|
1160
|
+
// is the bulk of SOLID blocks and dominates marching-cubes runtime.
|
|
1161
|
+
let currentBlockIsSolid = false;
|
|
1162
|
+
let allNeighborsSolid = true;
|
|
1163
|
+
for (let dz = -1; dz <= 1; dz++) {
|
|
1164
|
+
const nbZ = bz + dz;
|
|
1165
|
+
for (let dy = -1; dy <= 1; dy++) {
|
|
1166
|
+
const nbY = by + dy;
|
|
1167
|
+
for (let dx = -1; dx <= 1; dx++) {
|
|
1168
|
+
const nbX = bx + dx;
|
|
1169
|
+
const slot = (dx + 1) + (dy + 1) * 3 + (dz + 1) * 9;
|
|
1170
|
+
if (nbX < 0 || nbY < 0 || nbZ < 0 ||
|
|
1171
|
+
nbX >= nbx || nbY >= nby || nbZ >= nbz) {
|
|
1172
|
+
neighborEntry[slot] = NEIGHBOR_EMPTY;
|
|
1173
|
+
allNeighborsSolid = false;
|
|
1174
|
+
continue;
|
|
1175
|
+
}
|
|
1176
|
+
const nbIdx = nbX + nbY * nbx + nbZ * bStride;
|
|
1177
|
+
const bt = readBlockType(types, nbIdx);
|
|
1178
|
+
if (bt === BLOCK_EMPTY) {
|
|
1179
|
+
neighborEntry[slot] = NEIGHBOR_EMPTY;
|
|
1180
|
+
allNeighborsSolid = false;
|
|
1181
|
+
}
|
|
1182
|
+
else if (bt === BLOCK_SOLID) {
|
|
1183
|
+
neighborEntry[slot] = NEIGHBOR_SOLID;
|
|
1184
|
+
if (dx === 0 && dy === 0 && dz === 0) {
|
|
1185
|
+
currentBlockIsSolid = true;
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
else {
|
|
1189
|
+
neighborEntry[slot] = NEIGHBOR_MIXED;
|
|
1190
|
+
allNeighborsSolid = false;
|
|
1191
|
+
const ms = masks.slot(nbIdx);
|
|
1192
|
+
neighborMasks[slot * 2] = masks.lo[ms];
|
|
1193
|
+
neighborMasks[slot * 2 + 1] = masks.hi[ms];
|
|
1194
|
+
}
|
|
1195
|
+
}
|
|
1196
|
+
}
|
|
1197
|
+
}
|
|
1198
|
+
// Block is fully interior to a solid region — every cubeIndex is
|
|
1199
|
+
// 255, every cell would emit 0 triangles. Skip the cell loop.
|
|
1200
|
+
if (currentBlockIsSolid && allNeighborsSolid) {
|
|
1201
|
+
continue;
|
|
1202
|
+
}
|
|
1203
|
+
// Iterate cell origins from -1 through 3 on each axis. The -1 and
|
|
1204
|
+
// 3 layers straddle block edges and close surfaces where no
|
|
1205
|
+
// neighboring block exists.
|
|
1206
|
+
for (let lz = -1; lz < 4; lz++) {
|
|
1207
|
+
const lzInside = lz >= 0 && lz <= 2;
|
|
1208
|
+
for (let ly = -1; ly < 4; ly++) {
|
|
1209
|
+
const lyInside = ly >= 0 && ly <= 2;
|
|
1210
|
+
for (let lx = -1; lx < 4; lx++) {
|
|
1211
|
+
// For solid blocks, the 27 cells with all axes in 0..2
|
|
1212
|
+
// are fully inside the block. All 8 corners are 1 so
|
|
1213
|
+
// cubeIndex == 255 and no triangles are emitted -- skip.
|
|
1214
|
+
if (currentBlockIsSolid && lzInside && lyInside && lx >= 0 && lx <= 2) {
|
|
1215
|
+
continue;
|
|
1216
|
+
}
|
|
1217
|
+
const vx = bx * 4 + lx;
|
|
1218
|
+
const vy = by * 4 + ly;
|
|
1219
|
+
const vz = bz * 4 + lz;
|
|
1220
|
+
// Determine which block owns this cell
|
|
1221
|
+
const ownerBx = vx >> 2;
|
|
1222
|
+
const ownerBy = vy >> 2;
|
|
1223
|
+
const ownerBz = vz >> 2;
|
|
1224
|
+
if (ownerBx !== bx || ownerBy !== by || ownerBz !== bz) {
|
|
1225
|
+
// Cell belongs to a different block — skip if that
|
|
1226
|
+
// block is non-empty (it will process the cell itself).
|
|
1227
|
+
if (ownerBx >= 0 && ownerBy >= 0 && ownerBz >= 0 &&
|
|
1228
|
+
ownerBx < nbx && ownerBy < nby && ownerBz < nbz) {
|
|
1229
|
+
const ownerIdx = ownerBx + ownerBy * nbx + ownerBz * bStride;
|
|
1230
|
+
if (readBlockType(types, ownerIdx) !== BLOCK_EMPTY) {
|
|
1231
|
+
continue;
|
|
1232
|
+
}
|
|
1233
|
+
}
|
|
1234
|
+
// Owner block doesn't exist or is out-of-bounds —
|
|
1235
|
+
// deduplicate so only the first neighboring block to
|
|
1236
|
+
// reach this cell emits triangles.
|
|
1237
|
+
const cellKey = (vx + 1) + (vy + 1) * strideX + (vz + 1) * strideXY;
|
|
1238
|
+
let oi = (Math.imul(cellKey | 0, 0x9E3779B9) >>> 0) & oMask;
|
|
1239
|
+
let oFound = false;
|
|
1240
|
+
while (true) {
|
|
1241
|
+
const ok = oKeys[oi];
|
|
1242
|
+
if (ok === cellKey) {
|
|
1243
|
+
oFound = true;
|
|
1244
|
+
break;
|
|
1245
|
+
}
|
|
1246
|
+
if (ok === -1) {
|
|
1247
|
+
break;
|
|
1248
|
+
}
|
|
1249
|
+
oi = (oi + 1) & oMask;
|
|
1250
|
+
}
|
|
1251
|
+
if (oFound) {
|
|
1252
|
+
continue;
|
|
1253
|
+
}
|
|
1254
|
+
oKeys[oi] = cellKey;
|
|
1255
|
+
oSize++;
|
|
1256
|
+
if (oSize > ((oCap * 0.7) | 0)) {
|
|
1257
|
+
oGrow();
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
1260
|
+
// Get corner values for this cell (8 corners)
|
|
1261
|
+
// Corners: (vx,vy,vz), (vx+1,vy,vz), (vx+1,vy+1,vz), (vx,vy+1,vz),
|
|
1262
|
+
// (vx,vy,vz+1), (vx+1,vy,vz+1), (vx+1,vy+1,vz+1), (vx,vy+1,vz+1)
|
|
1263
|
+
const c0 = isOccupiedLocal(vx, vy, vz) ? 1 : 0;
|
|
1264
|
+
const c1 = isOccupiedLocal(vx + 1, vy, vz) ? 1 : 0;
|
|
1265
|
+
const c2 = isOccupiedLocal(vx + 1, vy + 1, vz) ? 1 : 0;
|
|
1266
|
+
const c3 = isOccupiedLocal(vx, vy + 1, vz) ? 1 : 0;
|
|
1267
|
+
const c4 = isOccupiedLocal(vx, vy, vz + 1) ? 1 : 0;
|
|
1268
|
+
const c5 = isOccupiedLocal(vx + 1, vy, vz + 1) ? 1 : 0;
|
|
1269
|
+
const c6 = isOccupiedLocal(vx + 1, vy + 1, vz + 1) ? 1 : 0;
|
|
1270
|
+
const c7 = isOccupiedLocal(vx, vy + 1, vz + 1) ? 1 : 0;
|
|
1271
|
+
const cubeIndex = c0 | (c1 << 1) | (c2 << 2) | (c3 << 3) |
|
|
1272
|
+
(c4 << 4) | (c5 << 5) | (c6 << 6) | (c7 << 7);
|
|
1273
|
+
if (cubeIndex === 0 || cubeIndex === 255) {
|
|
1274
|
+
continue;
|
|
1275
|
+
}
|
|
1276
|
+
if (collectFlatFace(cubeIndex, vx, vy, vz)) {
|
|
1277
|
+
continue;
|
|
1278
|
+
}
|
|
1279
|
+
const edges = EDGE_TABLE[cubeIndex]; // eslint-disable-line no-use-before-define
|
|
1280
|
+
if (edges === 0) {
|
|
1281
|
+
continue;
|
|
1282
|
+
}
|
|
1283
|
+
const triRow = TRI_TABLE[cubeIndex]; // eslint-disable-line no-use-before-define
|
|
1284
|
+
const triLen = triRow.length;
|
|
1285
|
+
const usedMask = collectDiagFaces(triRow, vx, vy, vz);
|
|
1286
|
+
let neededEdges = 0;
|
|
1287
|
+
let emitTriLen = 0;
|
|
1288
|
+
for (let t = 0; t < triLen; t += 3) {
|
|
1289
|
+
const triIdx = (t / 3) | 0;
|
|
1290
|
+
if ((usedMask & (1 << triIdx)) !== 0) {
|
|
1291
|
+
continue;
|
|
1292
|
+
}
|
|
1293
|
+
neededEdges |= (1 << triRow[t]) | (1 << triRow[t + 1]) | (1 << triRow[t + 2]);
|
|
1294
|
+
emitTriLen += 3;
|
|
1295
|
+
}
|
|
1296
|
+
if (neededEdges === 0) {
|
|
1297
|
+
continue;
|
|
1298
|
+
}
|
|
1299
|
+
// Compute vertices on edges used by triangles that
|
|
1300
|
+
// were not absorbed into a merged binary-MC rectangle.
|
|
1301
|
+
if (neededEdges & 1) {
|
|
1302
|
+
edgeVerts[0] = getVertex(vx, vy, vz, 0);
|
|
1303
|
+
} // edge 0: x-axis at (vx, vy, vz)
|
|
1304
|
+
if (neededEdges & 2) {
|
|
1305
|
+
edgeVerts[1] = getVertex(vx + 1, vy, vz, 1);
|
|
1306
|
+
} // edge 1: y-axis at (vx+1, vy, vz)
|
|
1307
|
+
if (neededEdges & 4) {
|
|
1308
|
+
edgeVerts[2] = getVertex(vx, vy + 1, vz, 0);
|
|
1309
|
+
} // edge 2: x-axis at (vx, vy+1, vz)
|
|
1310
|
+
if (neededEdges & 8) {
|
|
1311
|
+
edgeVerts[3] = getVertex(vx, vy, vz, 1);
|
|
1312
|
+
} // edge 3: y-axis at (vx, vy, vz)
|
|
1313
|
+
if (neededEdges & 16) {
|
|
1314
|
+
edgeVerts[4] = getVertex(vx, vy, vz + 1, 0);
|
|
1315
|
+
} // edge 4: x-axis at (vx, vy, vz+1)
|
|
1316
|
+
if (neededEdges & 32) {
|
|
1317
|
+
edgeVerts[5] = getVertex(vx + 1, vy, vz + 1, 1);
|
|
1318
|
+
} // edge 5: y-axis at (vx+1, vy, vz+1)
|
|
1319
|
+
if (neededEdges & 64) {
|
|
1320
|
+
edgeVerts[6] = getVertex(vx, vy + 1, vz + 1, 0);
|
|
1321
|
+
} // edge 6: x-axis at (vx, vy+1, vz+1)
|
|
1322
|
+
if (neededEdges & 128) {
|
|
1323
|
+
edgeVerts[7] = getVertex(vx, vy, vz + 1, 1);
|
|
1324
|
+
} // edge 7: y-axis at (vx, vy, vz+1)
|
|
1325
|
+
if (neededEdges & 256) {
|
|
1326
|
+
edgeVerts[8] = getVertex(vx, vy, vz, 2);
|
|
1327
|
+
} // edge 8: z-axis at (vx, vy, vz)
|
|
1328
|
+
if (neededEdges & 512) {
|
|
1329
|
+
edgeVerts[9] = getVertex(vx + 1, vy, vz, 2);
|
|
1330
|
+
} // edge 9: z-axis at (vx+1, vy, vz)
|
|
1331
|
+
if (neededEdges & 1024) {
|
|
1332
|
+
edgeVerts[10] = getVertex(vx + 1, vy + 1, vz, 2);
|
|
1333
|
+
} // edge 10: z-axis at (vx+1, vy+1, vz)
|
|
1334
|
+
if (neededEdges & 2048) {
|
|
1335
|
+
edgeVerts[11] = getVertex(vx, vy + 1, vz, 2);
|
|
1336
|
+
} // edge 11: z-axis at (vx, vy+1, vz)
|
|
1337
|
+
// Emit triangles (reversed winding to face outward)
|
|
1338
|
+
ensureIndexCapacity(emitTriLen);
|
|
1339
|
+
for (let t = 0; t < triLen; t += 3) {
|
|
1340
|
+
const triIdx = (t / 3) | 0;
|
|
1341
|
+
if ((usedMask & (1 << triIdx)) !== 0) {
|
|
1342
|
+
continue;
|
|
1343
|
+
}
|
|
1344
|
+
indices[idxLen++] = edgeVerts[triRow[t]];
|
|
1345
|
+
indices[idxLen++] = edgeVerts[triRow[t + 2]];
|
|
1346
|
+
indices[idxLen++] = edgeVerts[triRow[t + 1]];
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1349
|
+
}
|
|
1350
|
+
}
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1353
|
+
flushFaceCells();
|
|
1354
|
+
return {
|
|
1355
|
+
positions: positions.slice(0, posLen),
|
|
1356
|
+
indices: indices.slice(0, idxLen)
|
|
1357
|
+
};
|
|
1358
|
+
}
|
|
1359
|
+
// ============================================================================
|
|
1360
|
+
// Marching Cubes Lookup Tables
|
|
1361
|
+
// ============================================================================
|
|
1362
|
+
// Standard tables from Paul Bourke's polygonising a scalar field.
|
|
1363
|
+
// EDGE_TABLE: 256 entries, each a 12-bit mask of which edges are intersected.
|
|
1364
|
+
// TRI_TABLE: 256 entries, each an array of edge indices forming triangles.
|
|
1365
|
+
const EDGE_TABLE = [
|
|
1366
|
+
0x000, 0x109, 0x203, 0x30a, 0x406, 0x50f, 0x605, 0x70c,
|
|
1367
|
+
0x80c, 0x905, 0xa0f, 0xb06, 0xc0a, 0xd03, 0xe09, 0xf00,
|
|
1368
|
+
0x190, 0x099, 0x393, 0x29a, 0x596, 0x49f, 0x795, 0x69c,
|
|
1369
|
+
0x99c, 0x895, 0xb9f, 0xa96, 0xd9a, 0xc93, 0xf99, 0xe90,
|
|
1370
|
+
0x230, 0x339, 0x033, 0x13a, 0x636, 0x73f, 0x435, 0x53c,
|
|
1371
|
+
0xa3c, 0xb35, 0x83f, 0x936, 0xe3a, 0xf33, 0xc39, 0xd30,
|
|
1372
|
+
0x3a0, 0x2a9, 0x1a3, 0x0aa, 0x7a6, 0x6af, 0x5a5, 0x4ac,
|
|
1373
|
+
0xbac, 0xaa5, 0x9af, 0x8a6, 0xfaa, 0xea3, 0xda9, 0xca0,
|
|
1374
|
+
0x460, 0x569, 0x663, 0x76a, 0x066, 0x16f, 0x265, 0x36c,
|
|
1375
|
+
0xc6c, 0xd65, 0xe6f, 0xf66, 0x86a, 0x963, 0xa69, 0xb60,
|
|
1376
|
+
0x5f0, 0x4f9, 0x7f3, 0x6fa, 0x1f6, 0x0ff, 0x3f5, 0x2fc,
|
|
1377
|
+
0xdfc, 0xcf5, 0xfff, 0xef6, 0x9fa, 0x8f3, 0xbf9, 0xaf0,
|
|
1378
|
+
0x650, 0x759, 0x453, 0x55a, 0x256, 0x35f, 0x055, 0x15c,
|
|
1379
|
+
0xe5c, 0xf55, 0xc5f, 0xd56, 0xa5a, 0xb53, 0x859, 0x950,
|
|
1380
|
+
0x7c0, 0x6c9, 0x5c3, 0x4ca, 0x3c6, 0x2cf, 0x1c5, 0x0cc,
|
|
1381
|
+
0xfcc, 0xec5, 0xdcf, 0xcc6, 0xbca, 0xac3, 0x9c9, 0x8c0,
|
|
1382
|
+
0x8c0, 0x9c9, 0xac3, 0xbca, 0xcc6, 0xdcf, 0xec5, 0xfcc,
|
|
1383
|
+
0x0cc, 0x1c5, 0x2cf, 0x3c6, 0x4ca, 0x5c3, 0x6c9, 0x7c0,
|
|
1384
|
+
0x950, 0x859, 0xb53, 0xa5a, 0xd56, 0xc5f, 0xf55, 0xe5c,
|
|
1385
|
+
0x15c, 0x055, 0x35f, 0x256, 0x55a, 0x453, 0x759, 0x650,
|
|
1386
|
+
0xaf0, 0xbf9, 0x8f3, 0x9fa, 0xef6, 0xfff, 0xcf5, 0xdfc,
|
|
1387
|
+
0x2fc, 0x3f5, 0x0ff, 0x1f6, 0x6fa, 0x7f3, 0x4f9, 0x5f0,
|
|
1388
|
+
0xb60, 0xa69, 0x963, 0x86a, 0xf66, 0xe6f, 0xd65, 0xc6c,
|
|
1389
|
+
0x36c, 0x265, 0x16f, 0x066, 0x76a, 0x663, 0x569, 0x460,
|
|
1390
|
+
0xca0, 0xda9, 0xea3, 0xfaa, 0x8a6, 0x9af, 0xaa5, 0xbac,
|
|
1391
|
+
0x4ac, 0x5a5, 0x6af, 0x7a6, 0x0aa, 0x1a3, 0x2a9, 0x3a0,
|
|
1392
|
+
0xd30, 0xc39, 0xf33, 0xe3a, 0x936, 0x83f, 0xb35, 0xa3c,
|
|
1393
|
+
0x53c, 0x435, 0x73f, 0x636, 0x13a, 0x033, 0x339, 0x230,
|
|
1394
|
+
0xe90, 0xf99, 0xc93, 0xd9a, 0xa96, 0xb9f, 0x895, 0x99c,
|
|
1395
|
+
0x69c, 0x795, 0x49f, 0x596, 0x29a, 0x393, 0x099, 0x190,
|
|
1396
|
+
0xf00, 0xe09, 0xd03, 0xc0a, 0xb06, 0xa0f, 0x905, 0x80c,
|
|
1397
|
+
0x70c, 0x605, 0x50f, 0x406, 0x30a, 0x203, 0x109, 0x000
|
|
1398
|
+
];
|
|
1399
|
+
const TRI_TABLE = [
|
|
1400
|
+
[],
|
|
1401
|
+
[0, 8, 3],
|
|
1402
|
+
[0, 1, 9],
|
|
1403
|
+
[1, 8, 3, 9, 8, 1],
|
|
1404
|
+
[1, 2, 10],
|
|
1405
|
+
[0, 8, 3, 1, 2, 10],
|
|
1406
|
+
[9, 2, 10, 0, 2, 9],
|
|
1407
|
+
[2, 8, 3, 2, 10, 8, 10, 9, 8],
|
|
1408
|
+
[3, 11, 2],
|
|
1409
|
+
[0, 11, 2, 8, 11, 0],
|
|
1410
|
+
[1, 9, 0, 2, 3, 11],
|
|
1411
|
+
[1, 11, 2, 1, 9, 11, 9, 8, 11],
|
|
1412
|
+
[3, 10, 1, 11, 10, 3],
|
|
1413
|
+
[0, 10, 1, 0, 8, 10, 8, 11, 10],
|
|
1414
|
+
[3, 9, 0, 3, 11, 9, 11, 10, 9],
|
|
1415
|
+
[9, 8, 10, 10, 8, 11],
|
|
1416
|
+
[4, 7, 8],
|
|
1417
|
+
[4, 3, 0, 7, 3, 4],
|
|
1418
|
+
[0, 1, 9, 8, 4, 7],
|
|
1419
|
+
[4, 1, 9, 4, 7, 1, 7, 3, 1],
|
|
1420
|
+
[1, 2, 10, 8, 4, 7],
|
|
1421
|
+
[3, 4, 7, 3, 0, 4, 1, 2, 10],
|
|
1422
|
+
[9, 2, 10, 9, 0, 2, 8, 4, 7],
|
|
1423
|
+
[2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4],
|
|
1424
|
+
[8, 4, 7, 3, 11, 2],
|
|
1425
|
+
[11, 4, 7, 11, 2, 4, 2, 0, 4],
|
|
1426
|
+
[9, 0, 1, 8, 4, 7, 2, 3, 11],
|
|
1427
|
+
[4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1],
|
|
1428
|
+
[3, 10, 1, 3, 11, 10, 7, 8, 4],
|
|
1429
|
+
[1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4],
|
|
1430
|
+
[4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3],
|
|
1431
|
+
[4, 7, 11, 4, 11, 9, 9, 11, 10],
|
|
1432
|
+
[9, 5, 4],
|
|
1433
|
+
[9, 5, 4, 0, 8, 3],
|
|
1434
|
+
[0, 5, 4, 1, 5, 0],
|
|
1435
|
+
[8, 5, 4, 8, 3, 5, 3, 1, 5],
|
|
1436
|
+
[1, 2, 10, 9, 5, 4],
|
|
1437
|
+
[3, 0, 8, 1, 2, 10, 4, 9, 5],
|
|
1438
|
+
[5, 2, 10, 5, 4, 2, 4, 0, 2],
|
|
1439
|
+
[2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8],
|
|
1440
|
+
[9, 5, 4, 2, 3, 11],
|
|
1441
|
+
[0, 11, 2, 0, 8, 11, 4, 9, 5],
|
|
1442
|
+
[0, 5, 4, 0, 1, 5, 2, 3, 11],
|
|
1443
|
+
[2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5],
|
|
1444
|
+
[10, 3, 11, 10, 1, 3, 9, 5, 4],
|
|
1445
|
+
[4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10],
|
|
1446
|
+
[5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3],
|
|
1447
|
+
[5, 4, 8, 5, 8, 10, 10, 8, 11],
|
|
1448
|
+
[9, 7, 8, 5, 7, 9],
|
|
1449
|
+
[9, 3, 0, 9, 5, 3, 5, 7, 3],
|
|
1450
|
+
[0, 7, 8, 0, 1, 7, 1, 5, 7],
|
|
1451
|
+
[1, 5, 3, 3, 5, 7],
|
|
1452
|
+
[9, 7, 8, 9, 5, 7, 10, 1, 2],
|
|
1453
|
+
[10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3],
|
|
1454
|
+
[8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2],
|
|
1455
|
+
[2, 10, 5, 2, 5, 3, 3, 5, 7],
|
|
1456
|
+
[7, 9, 5, 7, 8, 9, 3, 11, 2],
|
|
1457
|
+
[9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11],
|
|
1458
|
+
[2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7],
|
|
1459
|
+
[11, 2, 1, 11, 1, 7, 7, 1, 5],
|
|
1460
|
+
[9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11],
|
|
1461
|
+
[5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0],
|
|
1462
|
+
[11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0],
|
|
1463
|
+
[11, 10, 5, 7, 11, 5],
|
|
1464
|
+
[10, 6, 5],
|
|
1465
|
+
[0, 8, 3, 5, 10, 6],
|
|
1466
|
+
[9, 0, 1, 5, 10, 6],
|
|
1467
|
+
[1, 8, 3, 1, 9, 8, 5, 10, 6],
|
|
1468
|
+
[1, 6, 5, 2, 6, 1],
|
|
1469
|
+
[1, 6, 5, 1, 2, 6, 3, 0, 8],
|
|
1470
|
+
[9, 6, 5, 9, 0, 6, 0, 2, 6],
|
|
1471
|
+
[5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8],
|
|
1472
|
+
[2, 3, 11, 10, 6, 5],
|
|
1473
|
+
[11, 0, 8, 11, 2, 0, 10, 6, 5],
|
|
1474
|
+
[0, 1, 9, 2, 3, 11, 5, 10, 6],
|
|
1475
|
+
[5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11],
|
|
1476
|
+
[6, 3, 11, 6, 5, 3, 5, 1, 3],
|
|
1477
|
+
[0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6],
|
|
1478
|
+
[3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9],
|
|
1479
|
+
[6, 5, 9, 6, 9, 11, 11, 9, 8],
|
|
1480
|
+
[5, 10, 6, 4, 7, 8],
|
|
1481
|
+
[4, 3, 0, 4, 7, 3, 6, 5, 10],
|
|
1482
|
+
[1, 9, 0, 5, 10, 6, 8, 4, 7],
|
|
1483
|
+
[10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4],
|
|
1484
|
+
[6, 1, 2, 6, 5, 1, 4, 7, 8],
|
|
1485
|
+
[1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7],
|
|
1486
|
+
[8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6],
|
|
1487
|
+
[7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9],
|
|
1488
|
+
[3, 11, 2, 7, 8, 4, 10, 6, 5],
|
|
1489
|
+
[5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11],
|
|
1490
|
+
[0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6],
|
|
1491
|
+
[9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6],
|
|
1492
|
+
[8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6],
|
|
1493
|
+
[5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11],
|
|
1494
|
+
[0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7],
|
|
1495
|
+
[6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9],
|
|
1496
|
+
[10, 4, 9, 6, 4, 10],
|
|
1497
|
+
[4, 10, 6, 4, 9, 10, 0, 8, 3],
|
|
1498
|
+
[10, 0, 1, 10, 6, 0, 6, 4, 0],
|
|
1499
|
+
[8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10],
|
|
1500
|
+
[1, 4, 9, 1, 2, 4, 2, 6, 4],
|
|
1501
|
+
[3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4],
|
|
1502
|
+
[0, 2, 4, 4, 2, 6],
|
|
1503
|
+
[8, 3, 2, 8, 2, 4, 4, 2, 6],
|
|
1504
|
+
[10, 4, 9, 10, 6, 4, 11, 2, 3],
|
|
1505
|
+
[0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6],
|
|
1506
|
+
[3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10],
|
|
1507
|
+
[6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1],
|
|
1508
|
+
[9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3],
|
|
1509
|
+
[8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1],
|
|
1510
|
+
[3, 11, 6, 3, 6, 0, 0, 6, 4],
|
|
1511
|
+
[6, 4, 8, 11, 6, 8],
|
|
1512
|
+
[7, 10, 6, 7, 8, 10, 8, 9, 10],
|
|
1513
|
+
[0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10],
|
|
1514
|
+
[10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0],
|
|
1515
|
+
[10, 6, 7, 10, 7, 1, 1, 7, 3],
|
|
1516
|
+
[1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7],
|
|
1517
|
+
[2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9],
|
|
1518
|
+
[7, 8, 0, 7, 0, 6, 6, 0, 2],
|
|
1519
|
+
[7, 3, 2, 6, 7, 2],
|
|
1520
|
+
[2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7],
|
|
1521
|
+
[2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7],
|
|
1522
|
+
[1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11],
|
|
1523
|
+
[11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1],
|
|
1524
|
+
[8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6],
|
|
1525
|
+
[0, 9, 1, 11, 6, 7],
|
|
1526
|
+
[7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0],
|
|
1527
|
+
[7, 11, 6],
|
|
1528
|
+
[7, 6, 11],
|
|
1529
|
+
[3, 0, 8, 11, 7, 6],
|
|
1530
|
+
[0, 1, 9, 11, 7, 6],
|
|
1531
|
+
[8, 1, 9, 8, 3, 1, 11, 7, 6],
|
|
1532
|
+
[10, 1, 2, 6, 11, 7],
|
|
1533
|
+
[1, 2, 10, 3, 0, 8, 6, 11, 7],
|
|
1534
|
+
[2, 9, 0, 2, 10, 9, 6, 11, 7],
|
|
1535
|
+
[6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8],
|
|
1536
|
+
[7, 2, 3, 6, 2, 7],
|
|
1537
|
+
[7, 0, 8, 7, 6, 0, 6, 2, 0],
|
|
1538
|
+
[2, 7, 6, 2, 3, 7, 0, 1, 9],
|
|
1539
|
+
[1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6],
|
|
1540
|
+
[10, 7, 6, 10, 1, 7, 1, 3, 7],
|
|
1541
|
+
[10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8],
|
|
1542
|
+
[0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7],
|
|
1543
|
+
[7, 6, 10, 7, 10, 8, 8, 10, 9],
|
|
1544
|
+
[6, 8, 4, 11, 8, 6],
|
|
1545
|
+
[3, 6, 11, 3, 0, 6, 0, 4, 6],
|
|
1546
|
+
[8, 6, 11, 8, 4, 6, 9, 0, 1],
|
|
1547
|
+
[9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6],
|
|
1548
|
+
[6, 8, 4, 6, 11, 8, 2, 10, 1],
|
|
1549
|
+
[1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6],
|
|
1550
|
+
[4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9],
|
|
1551
|
+
[10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3],
|
|
1552
|
+
[8, 2, 3, 8, 4, 2, 4, 6, 2],
|
|
1553
|
+
[0, 4, 2, 4, 6, 2],
|
|
1554
|
+
[1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8],
|
|
1555
|
+
[1, 9, 4, 1, 4, 2, 2, 4, 6],
|
|
1556
|
+
[8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1],
|
|
1557
|
+
[10, 1, 0, 10, 0, 6, 6, 0, 4],
|
|
1558
|
+
[4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3],
|
|
1559
|
+
[10, 9, 4, 6, 10, 4],
|
|
1560
|
+
[4, 9, 5, 7, 6, 11],
|
|
1561
|
+
[0, 8, 3, 4, 9, 5, 11, 7, 6],
|
|
1562
|
+
[5, 0, 1, 5, 4, 0, 7, 6, 11],
|
|
1563
|
+
[11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5],
|
|
1564
|
+
[9, 5, 4, 10, 1, 2, 7, 6, 11],
|
|
1565
|
+
[6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5],
|
|
1566
|
+
[7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2],
|
|
1567
|
+
[3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6],
|
|
1568
|
+
[7, 2, 3, 7, 6, 2, 5, 4, 9],
|
|
1569
|
+
[9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7],
|
|
1570
|
+
[3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0],
|
|
1571
|
+
[6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8],
|
|
1572
|
+
[9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7],
|
|
1573
|
+
[1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4],
|
|
1574
|
+
[4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10],
|
|
1575
|
+
[7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10],
|
|
1576
|
+
[6, 9, 5, 6, 11, 9, 11, 8, 9],
|
|
1577
|
+
[3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5],
|
|
1578
|
+
[0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11],
|
|
1579
|
+
[6, 11, 3, 6, 3, 5, 5, 3, 1],
|
|
1580
|
+
[1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6],
|
|
1581
|
+
[0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10],
|
|
1582
|
+
[11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5],
|
|
1583
|
+
[6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3],
|
|
1584
|
+
[5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2],
|
|
1585
|
+
[9, 5, 6, 9, 6, 0, 0, 6, 2],
|
|
1586
|
+
[1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8],
|
|
1587
|
+
[1, 5, 6, 2, 1, 6],
|
|
1588
|
+
[1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6],
|
|
1589
|
+
[10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0],
|
|
1590
|
+
[0, 3, 8, 5, 6, 10],
|
|
1591
|
+
[10, 5, 6],
|
|
1592
|
+
[11, 5, 10, 7, 5, 11],
|
|
1593
|
+
[11, 5, 10, 11, 7, 5, 8, 3, 0],
|
|
1594
|
+
[5, 11, 7, 5, 10, 11, 1, 9, 0],
|
|
1595
|
+
[10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1],
|
|
1596
|
+
[11, 1, 2, 11, 7, 1, 7, 5, 1],
|
|
1597
|
+
[0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11],
|
|
1598
|
+
[9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7],
|
|
1599
|
+
[7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2],
|
|
1600
|
+
[2, 5, 10, 2, 3, 5, 3, 7, 5],
|
|
1601
|
+
[8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5],
|
|
1602
|
+
[9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2],
|
|
1603
|
+
[9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2],
|
|
1604
|
+
[1, 3, 5, 3, 7, 5],
|
|
1605
|
+
[0, 8, 7, 0, 7, 1, 1, 7, 5],
|
|
1606
|
+
[9, 0, 3, 9, 3, 5, 5, 3, 7],
|
|
1607
|
+
[9, 8, 7, 5, 9, 7],
|
|
1608
|
+
[5, 8, 4, 5, 10, 8, 10, 11, 8],
|
|
1609
|
+
[5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0],
|
|
1610
|
+
[0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5],
|
|
1611
|
+
[10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4],
|
|
1612
|
+
[2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8],
|
|
1613
|
+
[0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11],
|
|
1614
|
+
[0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5],
|
|
1615
|
+
[9, 4, 5, 2, 11, 3],
|
|
1616
|
+
[2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4],
|
|
1617
|
+
[5, 10, 2, 5, 2, 4, 4, 2, 0],
|
|
1618
|
+
[3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9],
|
|
1619
|
+
[5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2],
|
|
1620
|
+
[8, 4, 5, 8, 5, 3, 3, 5, 1],
|
|
1621
|
+
[0, 4, 5, 1, 0, 5],
|
|
1622
|
+
[8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5],
|
|
1623
|
+
[9, 4, 5],
|
|
1624
|
+
[4, 11, 7, 4, 9, 11, 9, 10, 11],
|
|
1625
|
+
[0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11],
|
|
1626
|
+
[1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11],
|
|
1627
|
+
[3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4],
|
|
1628
|
+
[4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2],
|
|
1629
|
+
[9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3],
|
|
1630
|
+
[11, 7, 4, 11, 4, 2, 2, 4, 0],
|
|
1631
|
+
[11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4],
|
|
1632
|
+
[2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9],
|
|
1633
|
+
[9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7],
|
|
1634
|
+
[3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10],
|
|
1635
|
+
[1, 10, 2, 8, 7, 4],
|
|
1636
|
+
[4, 9, 1, 4, 1, 7, 7, 1, 3],
|
|
1637
|
+
[4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1],
|
|
1638
|
+
[4, 0, 3, 7, 4, 3],
|
|
1639
|
+
[4, 8, 7],
|
|
1640
|
+
[9, 10, 8, 10, 11, 8],
|
|
1641
|
+
[3, 0, 9, 3, 9, 11, 11, 9, 10],
|
|
1642
|
+
[0, 1, 10, 0, 10, 8, 8, 10, 11],
|
|
1643
|
+
[3, 1, 10, 11, 3, 10],
|
|
1644
|
+
[1, 2, 11, 1, 11, 9, 9, 11, 8],
|
|
1645
|
+
[3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9],
|
|
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 };
|