@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,663 +1,662 @@
|
|
|
1
|
-
import { BLOCK_EMPTY, BLOCK_SOLID, BLOCKS_PER_WORD, EVEN_BITS, readBlockType } from './common.js';
|
|
2
|
-
const HASH_MUL =
|
|
3
|
-
/**
|
|
4
|
-
* Extract a watertight voxel-boundary mesh from a SparseVoxelGrid.
|
|
5
|
-
*
|
|
6
|
-
* Exposed voxel faces are first greedily merged into axis-aligned rectangles.
|
|
7
|
-
* Rectangle boundaries are then split at every collinear rectangle corner
|
|
8
|
-
* before triangulation, so adjacent rectangles share matching edges instead
|
|
9
|
-
* of producing T-junctions.
|
|
10
|
-
*
|
|
11
|
-
* @param grid - Voxel grid after filtering / nav phases.
|
|
12
|
-
* @param gridBounds - Grid bounds aligned to block boundaries.
|
|
13
|
-
* @param voxelResolution - Size of each voxel in world units.
|
|
14
|
-
* @returns Mesh with positions and indices.
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
const { nbx, nby, nbz, bStride, types, masks, nx, ny, nz } = grid;
|
|
18
|
-
const totalBlocks = nbx * nby * nbz;
|
|
19
|
-
const coordStride = Math.max(nx, ny, nz) + 1;
|
|
20
|
-
let faceCap = 1024;
|
|
21
|
-
let faceLen = 0;
|
|
22
|
-
let faceKeys = new Float64Array(faceCap);
|
|
23
|
-
|
|
24
|
-
if (faceLen === faceCap) {
|
|
25
|
-
faceCap *= 2;
|
|
26
|
-
const grown = new Float64Array(faceCap);
|
|
27
|
-
grown.set(faceKeys);
|
|
28
|
-
faceKeys = grown;
|
|
29
|
-
}
|
|
30
|
-
faceKeys[faceLen++] =
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
if (bt ===
|
|
52
|
-
return
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
case
|
|
63
|
-
addFace(
|
|
64
|
-
break; //
|
|
65
|
-
case
|
|
66
|
-
addFace(
|
|
67
|
-
break; //
|
|
68
|
-
case
|
|
69
|
-
addFace(
|
|
70
|
-
break; //
|
|
71
|
-
case
|
|
72
|
-
addFace(
|
|
73
|
-
break; //
|
|
74
|
-
|
|
75
|
-
addFace(
|
|
76
|
-
break; //
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
const
|
|
137
|
-
const
|
|
138
|
-
const
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
if (!isVoxelSetGlobal(ix
|
|
152
|
-
addVoxelFace(ix, iy, iz,
|
|
153
|
-
}
|
|
154
|
-
if (!isVoxelSetGlobal(ix
|
|
155
|
-
addVoxelFace(ix, iy, iz,
|
|
156
|
-
}
|
|
157
|
-
if (!isVoxelSetGlobal(ix, iy
|
|
158
|
-
addVoxelFace(ix, iy, iz,
|
|
159
|
-
}
|
|
160
|
-
if (!isVoxelSetGlobal(ix, iy
|
|
161
|
-
addVoxelFace(ix, iy, iz,
|
|
162
|
-
}
|
|
163
|
-
if (!isVoxelSetGlobal(ix, iy, iz
|
|
164
|
-
addVoxelFace(ix, iy, iz,
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
const
|
|
189
|
-
const
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
let
|
|
205
|
-
let
|
|
206
|
-
let
|
|
207
|
-
let
|
|
208
|
-
let
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
if (k ===
|
|
288
|
-
return
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
const
|
|
303
|
-
const
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
let
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
points
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
addLinePoint(key,
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
addLinePoint(key,
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
addLinePoint(key,
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
const
|
|
383
|
-
const
|
|
384
|
-
const
|
|
385
|
-
const
|
|
386
|
-
const
|
|
387
|
-
|
|
388
|
-
addLineSegment(
|
|
389
|
-
addLineSegment(
|
|
390
|
-
addLineSegment(
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
let
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
let
|
|
404
|
-
let
|
|
405
|
-
let
|
|
406
|
-
let
|
|
407
|
-
let
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
let
|
|
411
|
-
let
|
|
412
|
-
let
|
|
413
|
-
let
|
|
414
|
-
let
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
grown
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
positions[posLen++] = gridBounds.min.
|
|
425
|
-
positions[posLen++] = gridBounds.min.
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
const
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
grown
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
indices[idxLen++] =
|
|
456
|
-
indices[idxLen++] =
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
grown
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
grownU
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
grownV
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
let
|
|
492
|
-
let
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
const
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
const
|
|
515
|
-
const
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
const
|
|
564
|
-
const
|
|
565
|
-
const
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
const
|
|
571
|
-
const
|
|
572
|
-
const
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
let
|
|
601
|
-
let
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
const
|
|
605
|
-
const
|
|
606
|
-
const
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
const
|
|
623
|
-
const
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
const
|
|
632
|
-
const
|
|
633
|
-
const
|
|
634
|
-
const
|
|
635
|
-
const
|
|
636
|
-
const
|
|
637
|
-
const
|
|
638
|
-
const
|
|
639
|
-
const
|
|
640
|
-
const
|
|
641
|
-
const
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
addEdgeVertices(axis,
|
|
645
|
-
addEdgeVertices(axis,
|
|
646
|
-
addEdgeVertices(axis,
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
const
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
};
|
|
663
|
-
export { voxelFaces };
|
|
1
|
+
import { BLOCK_EMPTY, BLOCK_SOLID, BLOCKS_PER_WORD, EVEN_BITS, readBlockType } from './common.js';
|
|
2
|
+
const HASH_MUL = 0x9e3779b9;
|
|
3
|
+
/**
|
|
4
|
+
* Extract a watertight voxel-boundary mesh from a SparseVoxelGrid.
|
|
5
|
+
*
|
|
6
|
+
* Exposed voxel faces are first greedily merged into axis-aligned rectangles.
|
|
7
|
+
* Rectangle boundaries are then split at every collinear rectangle corner
|
|
8
|
+
* before triangulation, so adjacent rectangles share matching edges instead
|
|
9
|
+
* of producing T-junctions.
|
|
10
|
+
*
|
|
11
|
+
* @param grid - Voxel grid after filtering / nav phases.
|
|
12
|
+
* @param gridBounds - Grid bounds aligned to block boundaries.
|
|
13
|
+
* @param voxelResolution - Size of each voxel in world units.
|
|
14
|
+
* @returns Mesh with positions and indices.
|
|
15
|
+
*/
|
|
16
|
+
function voxelFaces(grid, gridBounds, voxelResolution) {
|
|
17
|
+
const { nbx, nby, nbz, bStride, types, masks, nx, ny, nz } = grid;
|
|
18
|
+
const totalBlocks = nbx * nby * nbz;
|
|
19
|
+
const coordStride = Math.max(nx, ny, nz) + 1;
|
|
20
|
+
let faceCap = 1024;
|
|
21
|
+
let faceLen = 0;
|
|
22
|
+
let faceKeys = new Float64Array(faceCap);
|
|
23
|
+
function addFace(bucket, p, u, v) {
|
|
24
|
+
if (faceLen === faceCap) {
|
|
25
|
+
faceCap *= 2;
|
|
26
|
+
const grown = new Float64Array(faceCap);
|
|
27
|
+
grown.set(faceKeys);
|
|
28
|
+
faceKeys = grown;
|
|
29
|
+
}
|
|
30
|
+
faceKeys[faceLen++] = ((bucket * coordStride + p) * coordStride + u) * coordStride + v;
|
|
31
|
+
}
|
|
32
|
+
function blockTypeAt(bx, by, bz) {
|
|
33
|
+
if (bx < 0 || by < 0 || bz < 0 || bx >= nbx || by >= nby || bz >= nbz) {
|
|
34
|
+
return BLOCK_EMPTY;
|
|
35
|
+
}
|
|
36
|
+
return readBlockType(types, bx + by * nbx + bz * bStride);
|
|
37
|
+
}
|
|
38
|
+
function isVoxelSetLocal(lo, hi, lx, ly, lz) {
|
|
39
|
+
const bitIdx = lx + (ly << 2) + (lz << 4);
|
|
40
|
+
return bitIdx < 32 ? ((lo >>> bitIdx) & 1) !== 0 : ((hi >>> (bitIdx - 32)) & 1) !== 0;
|
|
41
|
+
}
|
|
42
|
+
function isVoxelSetGlobal(ix, iy, iz) {
|
|
43
|
+
if (ix < 0 || iy < 0 || iz < 0 || ix >= nx || iy >= ny || iz >= nz) {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
const blockIdx = (ix >> 2) + (iy >> 2) * nbx + (iz >> 2) * bStride;
|
|
47
|
+
const bt = readBlockType(types, blockIdx);
|
|
48
|
+
if (bt === BLOCK_EMPTY) {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
if (bt === BLOCK_SOLID) {
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
const s = masks.slot(blockIdx);
|
|
55
|
+
return isVoxelSetLocal(masks.lo[s], masks.hi[s], ix & 3, iy & 3, iz & 3);
|
|
56
|
+
}
|
|
57
|
+
function addVoxelFace(ix, iy, iz, bucket) {
|
|
58
|
+
switch (bucket) {
|
|
59
|
+
case 0:
|
|
60
|
+
addFace(0, ix, iy, iz);
|
|
61
|
+
break; // -X
|
|
62
|
+
case 1:
|
|
63
|
+
addFace(1, ix + 1, iy, iz);
|
|
64
|
+
break; // +X
|
|
65
|
+
case 2:
|
|
66
|
+
addFace(2, iy, ix, iz);
|
|
67
|
+
break; // -Y
|
|
68
|
+
case 3:
|
|
69
|
+
addFace(3, iy + 1, ix, iz);
|
|
70
|
+
break; // +Y
|
|
71
|
+
case 4:
|
|
72
|
+
addFace(4, iz, ix, iy);
|
|
73
|
+
break; // -Z
|
|
74
|
+
default:
|
|
75
|
+
addFace(5, iz + 1, ix, iy);
|
|
76
|
+
break; // +Z
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
function processSolidBlock(bx, by, bz) {
|
|
80
|
+
const x0 = bx << 2;
|
|
81
|
+
const y0 = by << 2;
|
|
82
|
+
const z0 = bz << 2;
|
|
83
|
+
function emitX(bucket, neighborBlockType, ix, nx2) {
|
|
84
|
+
if (neighborBlockType === BLOCK_SOLID) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
for (let lz = 0; lz < 4; lz++) {
|
|
88
|
+
const iz = z0 + lz;
|
|
89
|
+
for (let ly = 0; ly < 4; ly++) {
|
|
90
|
+
const iy = y0 + ly;
|
|
91
|
+
if (neighborBlockType === BLOCK_EMPTY || !isVoxelSetGlobal(nx2, iy, iz)) {
|
|
92
|
+
addVoxelFace(ix, iy, iz, bucket);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
function emitY(bucket, neighborBlockType, iy, ny2) {
|
|
98
|
+
if (neighborBlockType === BLOCK_SOLID) {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
for (let lz = 0; lz < 4; lz++) {
|
|
102
|
+
const iz = z0 + lz;
|
|
103
|
+
for (let lx = 0; lx < 4; lx++) {
|
|
104
|
+
const ix = x0 + lx;
|
|
105
|
+
if (neighborBlockType === BLOCK_EMPTY || !isVoxelSetGlobal(ix, ny2, iz)) {
|
|
106
|
+
addVoxelFace(ix, iy, iz, bucket);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
function emitZ(bucket, neighborBlockType, iz, nz2) {
|
|
112
|
+
if (neighborBlockType === BLOCK_SOLID) {
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
for (let ly = 0; ly < 4; ly++) {
|
|
116
|
+
const iy = y0 + ly;
|
|
117
|
+
for (let lx = 0; lx < 4; lx++) {
|
|
118
|
+
const ix = x0 + lx;
|
|
119
|
+
if (neighborBlockType === BLOCK_EMPTY || !isVoxelSetGlobal(ix, iy, nz2)) {
|
|
120
|
+
addVoxelFace(ix, iy, iz, bucket);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
emitX(0, blockTypeAt(bx - 1, by, bz), x0, x0 - 1);
|
|
126
|
+
emitX(1, blockTypeAt(bx + 1, by, bz), x0 + 3, x0 + 4);
|
|
127
|
+
emitY(2, blockTypeAt(bx, by - 1, bz), y0, y0 - 1);
|
|
128
|
+
emitY(3, blockTypeAt(bx, by + 1, bz), y0 + 3, y0 + 4);
|
|
129
|
+
emitZ(4, blockTypeAt(bx, by, bz - 1), z0, z0 - 1);
|
|
130
|
+
emitZ(5, blockTypeAt(bx, by, bz + 1), z0 + 3, z0 + 4);
|
|
131
|
+
}
|
|
132
|
+
function processMixedBlock(blockIdx, bx, by, bz) {
|
|
133
|
+
const s = masks.slot(blockIdx);
|
|
134
|
+
const lo = masks.lo[s];
|
|
135
|
+
const hi = masks.hi[s];
|
|
136
|
+
const x0 = bx << 2;
|
|
137
|
+
const y0 = by << 2;
|
|
138
|
+
const z0 = bz << 2;
|
|
139
|
+
for (let lz = 0; lz < 4; lz++) {
|
|
140
|
+
const iz = z0 + lz;
|
|
141
|
+
for (let ly = 0; ly < 4; ly++) {
|
|
142
|
+
const iy = y0 + ly;
|
|
143
|
+
for (let lx = 0; lx < 4; lx++) {
|
|
144
|
+
if (!isVoxelSetLocal(lo, hi, lx, ly, lz)) {
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
const ix = x0 + lx;
|
|
148
|
+
if (!isVoxelSetGlobal(ix - 1, iy, iz)) {
|
|
149
|
+
addVoxelFace(ix, iy, iz, 0);
|
|
150
|
+
}
|
|
151
|
+
if (!isVoxelSetGlobal(ix + 1, iy, iz)) {
|
|
152
|
+
addVoxelFace(ix, iy, iz, 1);
|
|
153
|
+
}
|
|
154
|
+
if (!isVoxelSetGlobal(ix, iy - 1, iz)) {
|
|
155
|
+
addVoxelFace(ix, iy, iz, 2);
|
|
156
|
+
}
|
|
157
|
+
if (!isVoxelSetGlobal(ix, iy + 1, iz)) {
|
|
158
|
+
addVoxelFace(ix, iy, iz, 3);
|
|
159
|
+
}
|
|
160
|
+
if (!isVoxelSetGlobal(ix, iy, iz - 1)) {
|
|
161
|
+
addVoxelFace(ix, iy, iz, 4);
|
|
162
|
+
}
|
|
163
|
+
if (!isVoxelSetGlobal(ix, iy, iz + 1)) {
|
|
164
|
+
addVoxelFace(ix, iy, iz, 5);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
for (let w = 0; w < types.length; w++) {
|
|
171
|
+
const word = types[w];
|
|
172
|
+
if (word === 0) {
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
let nonEmpty = ((word & EVEN_BITS) | ((word >>> 1) & EVEN_BITS)) >>> 0;
|
|
176
|
+
const baseBlockIdx = w * BLOCKS_PER_WORD;
|
|
177
|
+
while (nonEmpty) {
|
|
178
|
+
const bp = 31 - Math.clz32(nonEmpty & -nonEmpty);
|
|
179
|
+
const lane = bp >>> 1;
|
|
180
|
+
const blockIdx = baseBlockIdx + lane;
|
|
181
|
+
nonEmpty &= nonEmpty - 1;
|
|
182
|
+
if (blockIdx >= totalBlocks) {
|
|
183
|
+
break;
|
|
184
|
+
}
|
|
185
|
+
const bx = blockIdx % nbx;
|
|
186
|
+
const byBz = (blockIdx / nbx) | 0;
|
|
187
|
+
const by = byBz % nby;
|
|
188
|
+
const bz = (byBz / nby) | 0;
|
|
189
|
+
const bt = (word >>> (lane << 1)) & 3;
|
|
190
|
+
if (bt === BLOCK_SOLID) {
|
|
191
|
+
processSolidBlock(bx, by, bz);
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
processMixedBlock(blockIdx, bx, by, bz);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
if (faceLen === 0) {
|
|
199
|
+
return { positions: new Float32Array(0), indices: new Uint32Array(0) };
|
|
200
|
+
}
|
|
201
|
+
let rectCap = 1024;
|
|
202
|
+
let rectLen = 0;
|
|
203
|
+
let rectBucket = new Int32Array(rectCap);
|
|
204
|
+
let rectP = new Int32Array(rectCap);
|
|
205
|
+
let rectU0 = new Int32Array(rectCap);
|
|
206
|
+
let rectV0 = new Int32Array(rectCap);
|
|
207
|
+
let rectU1 = new Int32Array(rectCap);
|
|
208
|
+
let rectV1 = new Int32Array(rectCap);
|
|
209
|
+
function addRect(bucket, p, u0, v0, u1, v1) {
|
|
210
|
+
if (rectLen === rectCap) {
|
|
211
|
+
rectCap *= 2;
|
|
212
|
+
function grow(src) {
|
|
213
|
+
const out = new Int32Array(rectCap);
|
|
214
|
+
out.set(src);
|
|
215
|
+
return out;
|
|
216
|
+
}
|
|
217
|
+
rectBucket = grow(rectBucket);
|
|
218
|
+
rectP = grow(rectP);
|
|
219
|
+
rectU0 = grow(rectU0);
|
|
220
|
+
rectV0 = grow(rectV0);
|
|
221
|
+
rectU1 = grow(rectU1);
|
|
222
|
+
rectV1 = grow(rectV1);
|
|
223
|
+
}
|
|
224
|
+
rectBucket[rectLen] = bucket;
|
|
225
|
+
rectP[rectLen] = p;
|
|
226
|
+
rectU0[rectLen] = u0;
|
|
227
|
+
rectV0[rectLen] = v0;
|
|
228
|
+
rectU1[rectLen] = u1;
|
|
229
|
+
rectV1[rectLen] = v1;
|
|
230
|
+
rectLen++;
|
|
231
|
+
}
|
|
232
|
+
const keys = faceKeys.subarray(0, faceLen);
|
|
233
|
+
faceKeys = new Float64Array(0);
|
|
234
|
+
keys.sort();
|
|
235
|
+
function decodeGroup(key) {
|
|
236
|
+
let q = Math.floor(key / coordStride);
|
|
237
|
+
q = Math.floor(q / coordStride);
|
|
238
|
+
const p = q % coordStride;
|
|
239
|
+
const bucket = Math.floor(q / coordStride);
|
|
240
|
+
return { bucket, p };
|
|
241
|
+
}
|
|
242
|
+
function decodeUvKey(key) {
|
|
243
|
+
const v = key % coordStride;
|
|
244
|
+
const q = Math.floor(key / coordStride);
|
|
245
|
+
const u = q % coordStride;
|
|
246
|
+
return u * coordStride + v;
|
|
247
|
+
}
|
|
248
|
+
let groupStart = 0;
|
|
249
|
+
while (groupStart < keys.length) {
|
|
250
|
+
const { bucket, p } = decodeGroup(keys[groupStart]);
|
|
251
|
+
let groupEnd = groupStart + 1;
|
|
252
|
+
while (groupEnd < keys.length) {
|
|
253
|
+
const g = decodeGroup(keys[groupEnd]);
|
|
254
|
+
if (g.bucket !== bucket || g.p !== p) {
|
|
255
|
+
break;
|
|
256
|
+
}
|
|
257
|
+
groupEnd++;
|
|
258
|
+
}
|
|
259
|
+
const count = groupEnd - groupStart;
|
|
260
|
+
let hCap = 1;
|
|
261
|
+
while (hCap < count / 0.7) {
|
|
262
|
+
hCap *= 2;
|
|
263
|
+
}
|
|
264
|
+
const hMask = hCap - 1;
|
|
265
|
+
const hKeys = new Float64Array(hCap).fill(-1);
|
|
266
|
+
const hVals = new Int32Array(hCap);
|
|
267
|
+
function hash(key) {
|
|
268
|
+
const hi = (key / 0x100000000) | 0;
|
|
269
|
+
return (Math.imul((key | 0) ^ hi, HASH_MUL) >>> 0) & hMask;
|
|
270
|
+
}
|
|
271
|
+
for (let i = 0; i < count; i++) {
|
|
272
|
+
const uvKey = decodeUvKey(keys[groupStart + i]);
|
|
273
|
+
let h = hash(uvKey);
|
|
274
|
+
while (hKeys[h] !== -1) {
|
|
275
|
+
h = (h + 1) & hMask;
|
|
276
|
+
}
|
|
277
|
+
hKeys[h] = uvKey;
|
|
278
|
+
hVals[h] = i;
|
|
279
|
+
}
|
|
280
|
+
function lookup(uvKey) {
|
|
281
|
+
let h = hash(uvKey);
|
|
282
|
+
while (true) {
|
|
283
|
+
const k = hKeys[h];
|
|
284
|
+
if (k === uvKey) {
|
|
285
|
+
return hVals[h];
|
|
286
|
+
}
|
|
287
|
+
if (k === -1) {
|
|
288
|
+
return -1;
|
|
289
|
+
}
|
|
290
|
+
h = (h + 1) & hMask;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
const visited = new Uint8Array(count);
|
|
294
|
+
function uvKeyOf(u, v) {
|
|
295
|
+
return u * coordStride + v;
|
|
296
|
+
}
|
|
297
|
+
for (let i = 0; i < count; i++) {
|
|
298
|
+
if (visited[i]) {
|
|
299
|
+
continue;
|
|
300
|
+
}
|
|
301
|
+
const uvKey = decodeUvKey(keys[groupStart + i]);
|
|
302
|
+
const u0 = Math.floor(uvKey / coordStride);
|
|
303
|
+
const v0 = uvKey % coordStride;
|
|
304
|
+
let width = 1;
|
|
305
|
+
while (true) {
|
|
306
|
+
const idx = lookup(uvKeyOf(u0 + width, v0));
|
|
307
|
+
if (idx === -1 || visited[idx]) {
|
|
308
|
+
break;
|
|
309
|
+
}
|
|
310
|
+
width++;
|
|
311
|
+
}
|
|
312
|
+
let height = 1;
|
|
313
|
+
while (true) {
|
|
314
|
+
let canGrow = true;
|
|
315
|
+
for (let du = 0; du < width; du++) {
|
|
316
|
+
const idx = lookup(uvKeyOf(u0 + du, v0 + height));
|
|
317
|
+
if (idx === -1 || visited[idx]) {
|
|
318
|
+
canGrow = false;
|
|
319
|
+
break;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
if (!canGrow) {
|
|
323
|
+
break;
|
|
324
|
+
}
|
|
325
|
+
height++;
|
|
326
|
+
}
|
|
327
|
+
for (let dv = 0; dv < height; dv++) {
|
|
328
|
+
for (let du = 0; du < width; du++) {
|
|
329
|
+
visited[lookup(uvKeyOf(u0 + du, v0 + dv))] = 1;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
addRect(bucket, p, u0, v0, u0 + width, v0 + height);
|
|
333
|
+
}
|
|
334
|
+
groupStart = groupEnd;
|
|
335
|
+
}
|
|
336
|
+
function globalPoint(axis, p, u, v) {
|
|
337
|
+
if (axis === 0) {
|
|
338
|
+
return [p, u, v];
|
|
339
|
+
}
|
|
340
|
+
if (axis === 1) {
|
|
341
|
+
return [u, p, v];
|
|
342
|
+
}
|
|
343
|
+
return [u, v, p];
|
|
344
|
+
}
|
|
345
|
+
function lineKey(varAxis, x, y, z) {
|
|
346
|
+
if (varAxis === 0) {
|
|
347
|
+
return (y + z * coordStride) * 3;
|
|
348
|
+
}
|
|
349
|
+
if (varAxis === 1) {
|
|
350
|
+
return (x + z * coordStride) * 3 + 1;
|
|
351
|
+
}
|
|
352
|
+
return (x + y * coordStride) * 3 + 2;
|
|
353
|
+
}
|
|
354
|
+
const linePoints = new Map();
|
|
355
|
+
function addLinePoint(key, value) {
|
|
356
|
+
let points = linePoints.get(key);
|
|
357
|
+
if (!points) {
|
|
358
|
+
points = [];
|
|
359
|
+
linePoints.set(key, points);
|
|
360
|
+
}
|
|
361
|
+
points.push(value);
|
|
362
|
+
}
|
|
363
|
+
function addLineSegment(x0, y0, z0, x1, y1, z1) {
|
|
364
|
+
if (x0 !== x1) {
|
|
365
|
+
const key = lineKey(0, x0, y0, z0);
|
|
366
|
+
addLinePoint(key, x0);
|
|
367
|
+
addLinePoint(key, x1);
|
|
368
|
+
}
|
|
369
|
+
else if (y0 !== y1) {
|
|
370
|
+
const key = lineKey(1, x0, y0, z0);
|
|
371
|
+
addLinePoint(key, y0);
|
|
372
|
+
addLinePoint(key, y1);
|
|
373
|
+
}
|
|
374
|
+
else {
|
|
375
|
+
const key = lineKey(2, x0, y0, z0);
|
|
376
|
+
addLinePoint(key, z0);
|
|
377
|
+
addLinePoint(key, z1);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
for (let r = 0; r < rectLen; r++) {
|
|
381
|
+
const axis = rectBucket[r] >> 1;
|
|
382
|
+
const p = rectP[r];
|
|
383
|
+
const a = globalPoint(axis, p, rectU0[r], rectV0[r]);
|
|
384
|
+
const b = globalPoint(axis, p, rectU1[r], rectV0[r]);
|
|
385
|
+
const c = globalPoint(axis, p, rectU1[r], rectV1[r]);
|
|
386
|
+
const d = globalPoint(axis, p, rectU0[r], rectV1[r]);
|
|
387
|
+
addLineSegment(a[0], a[1], a[2], b[0], b[1], b[2]);
|
|
388
|
+
addLineSegment(b[0], b[1], b[2], c[0], c[1], c[2]);
|
|
389
|
+
addLineSegment(c[0], c[1], c[2], d[0], d[1], d[2]);
|
|
390
|
+
addLineSegment(d[0], d[1], d[2], a[0], a[1], a[2]);
|
|
391
|
+
}
|
|
392
|
+
for (const points of linePoints.values()) {
|
|
393
|
+
points.sort((a, b) => a - b);
|
|
394
|
+
let write = 0;
|
|
395
|
+
for (let i = 0; i < points.length; i++) {
|
|
396
|
+
if (i === 0 || points[i] !== points[i - 1]) {
|
|
397
|
+
points[write++] = points[i];
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
points.length = write;
|
|
401
|
+
}
|
|
402
|
+
let posCap = 1024;
|
|
403
|
+
let posLen = 0;
|
|
404
|
+
let positions = new Float32Array(posCap);
|
|
405
|
+
let idxCap = 1024;
|
|
406
|
+
let idxLen = 0;
|
|
407
|
+
let indices = new Uint32Array(idxCap);
|
|
408
|
+
const vertexMap = new Map();
|
|
409
|
+
let perimeterScratch = new Uint32Array(16);
|
|
410
|
+
let perimeterU = new Int32Array(16);
|
|
411
|
+
let perimeterV = new Int32Array(16);
|
|
412
|
+
let perimeterLen = 0;
|
|
413
|
+
let triPrev = new Int32Array(16);
|
|
414
|
+
let triNext = new Int32Array(16);
|
|
415
|
+
function addPosition(x, y, z) {
|
|
416
|
+
if (posLen + 3 > posCap) {
|
|
417
|
+
posCap *= 2;
|
|
418
|
+
const grown = new Float32Array(posCap);
|
|
419
|
+
grown.set(positions);
|
|
420
|
+
positions = grown;
|
|
421
|
+
}
|
|
422
|
+
const idx = posLen / 3;
|
|
423
|
+
positions[posLen++] = gridBounds.min.x + x * voxelResolution;
|
|
424
|
+
positions[posLen++] = gridBounds.min.y + y * voxelResolution;
|
|
425
|
+
positions[posLen++] = gridBounds.min.z + z * voxelResolution;
|
|
426
|
+
return idx;
|
|
427
|
+
}
|
|
428
|
+
function vertexKey(x, y, z) {
|
|
429
|
+
return x + y * coordStride + z * coordStride * coordStride;
|
|
430
|
+
}
|
|
431
|
+
function getVertex(x, y, z) {
|
|
432
|
+
const key = vertexKey(x, y, z);
|
|
433
|
+
const existing = vertexMap.get(key);
|
|
434
|
+
if (existing !== undefined) {
|
|
435
|
+
return existing;
|
|
436
|
+
}
|
|
437
|
+
const idx = addPosition(x, y, z);
|
|
438
|
+
vertexMap.set(key, idx);
|
|
439
|
+
return idx;
|
|
440
|
+
}
|
|
441
|
+
function ensureIndexCapacity(additional) {
|
|
442
|
+
if (idxLen + additional <= idxCap) {
|
|
443
|
+
return;
|
|
444
|
+
}
|
|
445
|
+
while (idxLen + additional > idxCap) {
|
|
446
|
+
idxCap *= 2;
|
|
447
|
+
}
|
|
448
|
+
const grown = new Uint32Array(idxCap);
|
|
449
|
+
grown.set(indices);
|
|
450
|
+
indices = grown;
|
|
451
|
+
}
|
|
452
|
+
function appendTri(a, b, c) {
|
|
453
|
+
ensureIndexCapacity(3);
|
|
454
|
+
indices[idxLen++] = a;
|
|
455
|
+
indices[idxLen++] = b;
|
|
456
|
+
indices[idxLen++] = c;
|
|
457
|
+
}
|
|
458
|
+
function resetPerimeter() {
|
|
459
|
+
perimeterLen = 0;
|
|
460
|
+
}
|
|
461
|
+
function localUv(axis, x, y, z) {
|
|
462
|
+
if (axis === 0) {
|
|
463
|
+
return [y, z];
|
|
464
|
+
}
|
|
465
|
+
if (axis === 1) {
|
|
466
|
+
return [x, z];
|
|
467
|
+
}
|
|
468
|
+
return [x, y];
|
|
469
|
+
}
|
|
470
|
+
function addPerimeterVertex(v, u, pv) {
|
|
471
|
+
if (perimeterLen > 0 && perimeterScratch[perimeterLen - 1] === v) {
|
|
472
|
+
return;
|
|
473
|
+
}
|
|
474
|
+
if (perimeterLen === perimeterScratch.length) {
|
|
475
|
+
const grown = new Uint32Array(perimeterScratch.length * 2);
|
|
476
|
+
grown.set(perimeterScratch);
|
|
477
|
+
perimeterScratch = grown;
|
|
478
|
+
const grownU = new Int32Array(perimeterU.length * 2);
|
|
479
|
+
grownU.set(perimeterU);
|
|
480
|
+
perimeterU = grownU;
|
|
481
|
+
const grownV = new Int32Array(perimeterV.length * 2);
|
|
482
|
+
grownV.set(perimeterV);
|
|
483
|
+
perimeterV = grownV;
|
|
484
|
+
}
|
|
485
|
+
perimeterScratch[perimeterLen++] = v;
|
|
486
|
+
perimeterU[perimeterLen - 1] = u;
|
|
487
|
+
perimeterV[perimeterLen - 1] = pv;
|
|
488
|
+
}
|
|
489
|
+
function addEdgeVertices(axis, x0, y0, z0, x1, y1, z1) {
|
|
490
|
+
let varAxis;
|
|
491
|
+
let start;
|
|
492
|
+
let end;
|
|
493
|
+
if (x0 !== x1) {
|
|
494
|
+
varAxis = 0;
|
|
495
|
+
start = x0;
|
|
496
|
+
end = x1;
|
|
497
|
+
}
|
|
498
|
+
else if (y0 !== y1) {
|
|
499
|
+
varAxis = 1;
|
|
500
|
+
start = y0;
|
|
501
|
+
end = y1;
|
|
502
|
+
}
|
|
503
|
+
else {
|
|
504
|
+
varAxis = 2;
|
|
505
|
+
start = z0;
|
|
506
|
+
end = z1;
|
|
507
|
+
}
|
|
508
|
+
const key = lineKey(varAxis, x0, y0, z0);
|
|
509
|
+
const points = linePoints.get(key);
|
|
510
|
+
if (!points) {
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
const lo = Math.min(start, end);
|
|
514
|
+
const hi = Math.max(start, end);
|
|
515
|
+
const forward = start <= end;
|
|
516
|
+
function emitPoint(x, y, z) {
|
|
517
|
+
const [u, v] = localUv(axis, x, y, z);
|
|
518
|
+
addPerimeterVertex(getVertex(x, y, z), u, v);
|
|
519
|
+
}
|
|
520
|
+
if (forward) {
|
|
521
|
+
for (let i = 0; i < points.length; i++) {
|
|
522
|
+
const t = points[i];
|
|
523
|
+
if (t < lo) {
|
|
524
|
+
continue;
|
|
525
|
+
}
|
|
526
|
+
if (t > hi) {
|
|
527
|
+
break;
|
|
528
|
+
}
|
|
529
|
+
if (varAxis === 0) {
|
|
530
|
+
emitPoint(t, y0, z0);
|
|
531
|
+
}
|
|
532
|
+
else if (varAxis === 1) {
|
|
533
|
+
emitPoint(x0, t, z0);
|
|
534
|
+
}
|
|
535
|
+
else {
|
|
536
|
+
emitPoint(x0, y0, t);
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
else {
|
|
541
|
+
for (let i = points.length - 1; i >= 0; i--) {
|
|
542
|
+
const t = points[i];
|
|
543
|
+
if (t > hi) {
|
|
544
|
+
continue;
|
|
545
|
+
}
|
|
546
|
+
if (t < lo) {
|
|
547
|
+
break;
|
|
548
|
+
}
|
|
549
|
+
if (varAxis === 0) {
|
|
550
|
+
emitPoint(t, y0, z0);
|
|
551
|
+
}
|
|
552
|
+
else if (varAxis === 1) {
|
|
553
|
+
emitPoint(x0, t, z0);
|
|
554
|
+
}
|
|
555
|
+
else {
|
|
556
|
+
emitPoint(x0, y0, t);
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
function isConvexEar(prev, curr, next) {
|
|
562
|
+
const ax = perimeterU[curr] - perimeterU[prev];
|
|
563
|
+
const ay = perimeterV[curr] - perimeterV[prev];
|
|
564
|
+
const bx = perimeterU[next] - perimeterU[prev];
|
|
565
|
+
const by = perimeterV[next] - perimeterV[prev];
|
|
566
|
+
return ax * by - ay * bx > 0;
|
|
567
|
+
}
|
|
568
|
+
function isNonDegenerateTri(a, b, c) {
|
|
569
|
+
const ax = perimeterU[b] - perimeterU[a];
|
|
570
|
+
const ay = perimeterV[b] - perimeterV[a];
|
|
571
|
+
const bx = perimeterU[c] - perimeterU[a];
|
|
572
|
+
const by = perimeterV[c] - perimeterV[a];
|
|
573
|
+
return ax * by - ay * bx > 0;
|
|
574
|
+
}
|
|
575
|
+
function appendOrientedTri(a, b, c, useLocalCcw) {
|
|
576
|
+
if (useLocalCcw) {
|
|
577
|
+
appendTri(a, b, c);
|
|
578
|
+
}
|
|
579
|
+
else {
|
|
580
|
+
appendTri(a, c, b);
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
function triangulatePerimeter(useLocalCcw) {
|
|
584
|
+
if (perimeterLen < 3) {
|
|
585
|
+
return;
|
|
586
|
+
}
|
|
587
|
+
if (perimeterLen > triPrev.length) {
|
|
588
|
+
let cap = triPrev.length;
|
|
589
|
+
while (perimeterLen > cap) {
|
|
590
|
+
cap *= 2;
|
|
591
|
+
}
|
|
592
|
+
triPrev = new Int32Array(cap);
|
|
593
|
+
triNext = new Int32Array(cap);
|
|
594
|
+
}
|
|
595
|
+
for (let i = 0; i < perimeterLen; i++) {
|
|
596
|
+
triPrev[i] = i === 0 ? perimeterLen - 1 : i - 1;
|
|
597
|
+
triNext[i] = i === perimeterLen - 1 ? 0 : i + 1;
|
|
598
|
+
}
|
|
599
|
+
let remaining = perimeterLen;
|
|
600
|
+
let current = 0;
|
|
601
|
+
let attempts = 0;
|
|
602
|
+
while (remaining > 3 && attempts < remaining) {
|
|
603
|
+
const prev = triPrev[current];
|
|
604
|
+
const next = triNext[current];
|
|
605
|
+
const next2 = triNext[next];
|
|
606
|
+
const keepsArea = remaining !== 4 || isNonDegenerateTri(prev, next, next2);
|
|
607
|
+
if (keepsArea && isConvexEar(prev, current, next)) {
|
|
608
|
+
appendOrientedTri(perimeterScratch[prev], perimeterScratch[current], perimeterScratch[next], useLocalCcw);
|
|
609
|
+
triNext[prev] = next;
|
|
610
|
+
triPrev[next] = prev;
|
|
611
|
+
current = next;
|
|
612
|
+
remaining--;
|
|
613
|
+
attempts = 0;
|
|
614
|
+
}
|
|
615
|
+
else {
|
|
616
|
+
current = next;
|
|
617
|
+
attempts++;
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
if (remaining === 3) {
|
|
621
|
+
const a = current;
|
|
622
|
+
const b = triNext[a];
|
|
623
|
+
const c = triNext[b];
|
|
624
|
+
if (isConvexEar(a, b, c)) {
|
|
625
|
+
appendOrientedTri(perimeterScratch[a], perimeterScratch[b], perimeterScratch[c], useLocalCcw);
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
for (let r = 0; r < rectLen; r++) {
|
|
630
|
+
const bucket = rectBucket[r];
|
|
631
|
+
const axis = bucket >> 1;
|
|
632
|
+
const positive = (bucket & 1) === 1;
|
|
633
|
+
const p = rectP[r];
|
|
634
|
+
const u0 = rectU0[r];
|
|
635
|
+
const v0 = rectV0[r];
|
|
636
|
+
const u1 = rectU1[r];
|
|
637
|
+
const v1 = rectV1[r];
|
|
638
|
+
const a = globalPoint(axis, p, u0, v0);
|
|
639
|
+
const b = globalPoint(axis, p, u1, v0);
|
|
640
|
+
const c = globalPoint(axis, p, u1, v1);
|
|
641
|
+
const d = globalPoint(axis, p, u0, v1);
|
|
642
|
+
resetPerimeter();
|
|
643
|
+
addEdgeVertices(axis, a[0], a[1], a[2], b[0], b[1], b[2]);
|
|
644
|
+
addEdgeVertices(axis, b[0], b[1], b[2], c[0], c[1], c[2]);
|
|
645
|
+
addEdgeVertices(axis, c[0], c[1], c[2], d[0], d[1], d[2]);
|
|
646
|
+
addEdgeVertices(axis, d[0], d[1], d[2], a[0], a[1], a[2]);
|
|
647
|
+
if (perimeterLen > 1 && perimeterScratch[0] === perimeterScratch[perimeterLen - 1]) {
|
|
648
|
+
perimeterLen--;
|
|
649
|
+
}
|
|
650
|
+
if (perimeterLen < 3) {
|
|
651
|
+
continue;
|
|
652
|
+
}
|
|
653
|
+
const localCcwIsPositive = axis !== 1;
|
|
654
|
+
const useLocalCcw = positive === localCcwIsPositive;
|
|
655
|
+
triangulatePerimeter(useLocalCcw);
|
|
656
|
+
}
|
|
657
|
+
return {
|
|
658
|
+
positions: positions.slice(0, posLen),
|
|
659
|
+
indices: indices.slice(0, idxLen),
|
|
660
|
+
};
|
|
661
|
+
}
|
|
662
|
+
export { voxelFaces };
|