@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,375 +1,408 @@
|
|
|
1
|
-
import { BlockMaskBuffer, SOLID_HI, SOLID_LO } from './common.js';
|
|
2
|
-
import { logger } from '../Logger.js';
|
|
3
|
-
const FACE_X0 = 0x11111111;
|
|
4
|
-
const FACE_X3 = 0x88888888;
|
|
5
|
-
const FACE_Y0 =
|
|
6
|
-
const FACE_Y3 =
|
|
7
|
-
const FACE_Z0_LO =
|
|
8
|
-
const FACE_Z3_HI =
|
|
9
|
-
/** Count set bits in a 32-bit unsigned integer. */
|
|
10
|
-
|
|
11
|
-
n >>>= 0;
|
|
12
|
-
n -= (n >>> 1) & 0x55555555;
|
|
13
|
-
n = (n & 0x33333333) + ((n >>> 2) & 0x33333333);
|
|
14
|
-
return (((n + (n >>> 4)) &
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
let lo = 0;
|
|
18
|
-
let hi = sorted.length - 1;
|
|
19
|
-
while (lo <= hi) {
|
|
20
|
-
const mid = (lo + hi) >> 1;
|
|
21
|
-
const v = sorted[mid];
|
|
22
|
-
if (v < value) {
|
|
23
|
-
lo = mid + 1;
|
|
24
|
-
}
|
|
25
|
-
else if (v > value) {
|
|
26
|
-
hi = mid - 1;
|
|
27
|
-
}
|
|
28
|
-
else {
|
|
29
|
-
return true;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
return false;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
let lo = 0;
|
|
36
|
-
let hi = sortedBlockIdx.length - 1;
|
|
37
|
-
while (lo <= hi) {
|
|
38
|
-
const mid = (lo + hi) >> 1;
|
|
39
|
-
const v = sortedBlockIdx[mid];
|
|
40
|
-
if (v < target) {
|
|
41
|
-
lo = mid + 1;
|
|
42
|
-
}
|
|
43
|
-
else if (v > target) {
|
|
44
|
-
hi = mid - 1;
|
|
45
|
-
}
|
|
46
|
-
else {
|
|
47
|
-
return mid;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
return undefined;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const n = blockIdx.length;
|
|
54
|
-
if (n <= 1) {
|
|
55
|
-
return;
|
|
56
|
-
}
|
|
57
|
-
const stackLo = [0];
|
|
58
|
-
const stackHi = [n - 1];
|
|
59
|
-
|
|
60
|
-
const k = blockIdx[a];
|
|
61
|
-
blockIdx[a] = blockIdx[b];
|
|
62
|
-
blockIdx[b] = k;
|
|
63
|
-
const alo = masks[a * 2];
|
|
64
|
-
const ahi = masks[a * 2 + 1];
|
|
65
|
-
masks[a * 2] = masks[b * 2];
|
|
66
|
-
masks[a * 2 + 1] = masks[b * 2 + 1];
|
|
67
|
-
masks[b * 2] = alo;
|
|
68
|
-
masks[b * 2 + 1] = ahi;
|
|
69
|
-
}
|
|
70
|
-
while (stackLo.length > 0) {
|
|
71
|
-
const lo = stackLo.pop();
|
|
72
|
-
const hi = stackHi.pop();
|
|
73
|
-
if (hi - lo < 16) {
|
|
74
|
-
for (let i = lo + 1; i <= hi; i++) {
|
|
75
|
-
const k = blockIdx[i];
|
|
76
|
-
const m0 = masks[i * 2];
|
|
77
|
-
const m1 = masks[i * 2 + 1];
|
|
78
|
-
let j = i - 1;
|
|
79
|
-
while (j >= lo && blockIdx[j] > k) {
|
|
80
|
-
blockIdx[j + 1] = blockIdx[j];
|
|
81
|
-
masks[(j + 1) * 2] = masks[j * 2];
|
|
82
|
-
masks[(j + 1) * 2 + 1] = masks[j * 2 + 1];
|
|
83
|
-
j--;
|
|
84
|
-
}
|
|
85
|
-
blockIdx[j + 1] = k;
|
|
86
|
-
masks[(j + 1) * 2] = m0;
|
|
87
|
-
masks[(j + 1) * 2 + 1] = m1;
|
|
88
|
-
}
|
|
89
|
-
continue;
|
|
90
|
-
}
|
|
91
|
-
const mid = (lo + hi) >>> 1;
|
|
92
|
-
if (blockIdx[mid] < blockIdx[lo]) {
|
|
93
|
-
swap(mid, lo);
|
|
94
|
-
}
|
|
95
|
-
if (blockIdx[hi] < blockIdx[lo]) {
|
|
96
|
-
swap(hi, lo);
|
|
97
|
-
}
|
|
98
|
-
if (blockIdx[hi] < blockIdx[mid]) {
|
|
99
|
-
swap(hi, mid);
|
|
100
|
-
}
|
|
101
|
-
const pivot = blockIdx[mid];
|
|
102
|
-
let i = lo;
|
|
103
|
-
let j = hi;
|
|
104
|
-
while (i <= j) {
|
|
105
|
-
while (blockIdx[i] < pivot) {
|
|
106
|
-
i++;
|
|
107
|
-
}
|
|
108
|
-
while (blockIdx[j] > pivot) {
|
|
109
|
-
j--;
|
|
110
|
-
}
|
|
111
|
-
if (i <= j) {
|
|
112
|
-
if (i !== j) {
|
|
113
|
-
swap(i, j);
|
|
114
|
-
}
|
|
115
|
-
i++;
|
|
116
|
-
j--;
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
if (j - lo > hi - i) {
|
|
120
|
-
if (lo < j) {
|
|
121
|
-
stackLo.push(lo);
|
|
122
|
-
stackHi.push(j);
|
|
123
|
-
}
|
|
124
|
-
if (i < hi) {
|
|
125
|
-
stackLo.push(i);
|
|
126
|
-
stackHi.push(hi);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
else {
|
|
130
|
-
if (i < hi) {
|
|
131
|
-
stackLo.push(i);
|
|
132
|
-
stackHi.push(hi);
|
|
133
|
-
}
|
|
134
|
-
if (lo < j) {
|
|
135
|
-
stackLo.push(lo);
|
|
136
|
-
stackHi.push(j);
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
if (nx < 0 || ny < 0 || nz < 0 || nx >= nbx || ny >= nby || nz >= nbz) {
|
|
143
|
-
write(curLo, curHi);
|
|
144
|
-
return;
|
|
145
|
-
}
|
|
146
|
-
const adjBlockIdx = nx + ny * nbx + nz * nbx * nby;
|
|
147
|
-
if (hasSolid(adjBlockIdx)) {
|
|
148
|
-
write(curLo | ourFaceMask, curHi | ourFaceMask);
|
|
149
|
-
return;
|
|
150
|
-
}
|
|
151
|
-
const adjIdx = getMixedIndex(adjBlockIdx);
|
|
152
|
-
if (adjIdx === undefined) {
|
|
153
|
-
write(curLo, curHi);
|
|
154
|
-
return;
|
|
155
|
-
}
|
|
156
|
-
const adjLo = masks[adjIdx * 2];
|
|
157
|
-
const adjHi = masks[adjIdx * 2 + 1];
|
|
158
|
-
const faceLo = adjLo & adjFaceMask;
|
|
159
|
-
const faceHi = adjHi & adjFaceMask;
|
|
160
|
-
if (shiftLeft) {
|
|
161
|
-
write(curLo | (faceLo << shiftAmount), curHi | (faceHi << shiftAmount));
|
|
162
|
-
}
|
|
163
|
-
else {
|
|
164
|
-
write(curLo | (faceLo >>> shiftAmount), curHi | (faceHi >>> shiftAmount));
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
if (nx < 0 || ny < 0 || nz < 0 || nx >= nbx || ny >= nby || nz >= nbz) {
|
|
169
|
-
write(curLo, curHi);
|
|
170
|
-
return;
|
|
171
|
-
}
|
|
172
|
-
const adjBlockIdx = nx + ny * nbx + nz * nbx * nby;
|
|
173
|
-
if (hasSolid(adjBlockIdx)) {
|
|
174
|
-
if (plusZ) {
|
|
175
|
-
write(curLo, curHi | FACE_Z3_HI);
|
|
176
|
-
}
|
|
177
|
-
else {
|
|
178
|
-
write(curLo | FACE_Z0_LO, curHi);
|
|
179
|
-
}
|
|
180
|
-
return;
|
|
181
|
-
}
|
|
182
|
-
const adjIdx = getMixedIndex(adjBlockIdx);
|
|
183
|
-
if (adjIdx === undefined) {
|
|
184
|
-
write(curLo, curHi);
|
|
185
|
-
return;
|
|
186
|
-
}
|
|
187
|
-
const adjLo = masks[adjIdx * 2];
|
|
188
|
-
const adjHi = masks[adjIdx * 2 + 1];
|
|
189
|
-
if (plusZ) {
|
|
190
|
-
write(curLo, curHi | ((adjLo & FACE_Z0_LO) << 16));
|
|
191
|
-
}
|
|
192
|
-
else {
|
|
193
|
-
write(curLo | ((adjHi & FACE_Z3_HI) >>> 16), curHi);
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
/**
|
|
197
|
-
* Block cleanup pass:
|
|
198
|
-
* - remove voxels that have no supporting 6-neighborhood occupancy
|
|
199
|
-
* - fill single-voxel holes fully enclosed by 6 neighbors
|
|
200
|
-
* Includes cross-block neighbor propagation for face-adjacent blocks.
|
|
201
|
-
*/
|
|
202
|
-
export
|
|
203
|
-
const mixed = blocks.getMixedBlocks();
|
|
204
|
-
const solids = blocks.getSolidBlocks();
|
|
205
|
-
const mixedCount = mixed.blockIdx.length;
|
|
206
|
-
const masks = mixed.masks;
|
|
207
|
-
if (mixedCount === 0) {
|
|
208
|
-
return blocks;
|
|
209
|
-
}
|
|
210
|
-
const mixedBlockIdx = new Float64Array(mixedCount);
|
|
211
|
-
for (let i = 0; i < mixedCount; i++) {
|
|
212
|
-
mixedBlockIdx[i] = mixed.blockIdx[i];
|
|
213
|
-
}
|
|
214
|
-
sortMixedByBlockIdx(mixedBlockIdx, masks);
|
|
215
|
-
const sortedSolid = new Float64Array(solids.length);
|
|
216
|
-
for (let i = 0; i < solids.length; i++) {
|
|
217
|
-
sortedSolid[i] = solids[i];
|
|
218
|
-
}
|
|
219
|
-
sortedSolid.sort();
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
const
|
|
231
|
-
const
|
|
232
|
-
const
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
let
|
|
238
|
-
let
|
|
239
|
-
let
|
|
240
|
-
let
|
|
241
|
-
let
|
|
242
|
-
let
|
|
243
|
-
let
|
|
244
|
-
let
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
lo
|
|
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
|
-
const
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
for (let i = 0; i <
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
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
|
-
|
|
360
|
-
|
|
361
|
-
const
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
const
|
|
366
|
-
const
|
|
367
|
-
const
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
1
|
+
import { BlockMaskBuffer, SOLID_HI, SOLID_LO } from './common.js';
|
|
2
|
+
import { logger } from '../Logger.js';
|
|
3
|
+
const FACE_X0 = 0x11111111;
|
|
4
|
+
const FACE_X3 = 0x88888888;
|
|
5
|
+
const FACE_Y0 = 0x000f000f;
|
|
6
|
+
const FACE_Y3 = 0xf000f000;
|
|
7
|
+
const FACE_Z0_LO = 0x0000ffff;
|
|
8
|
+
const FACE_Z3_HI = 0xffff0000 >>> 0;
|
|
9
|
+
/** Count set bits in a 32-bit unsigned integer. */
|
|
10
|
+
function popcount(n) {
|
|
11
|
+
n >>>= 0;
|
|
12
|
+
n -= (n >>> 1) & 0x55555555;
|
|
13
|
+
n = (n & 0x33333333) + ((n >>> 2) & 0x33333333);
|
|
14
|
+
return (((n + (n >>> 4)) & 0x0f0f0f0f) * 0x01010101) >>> 24;
|
|
15
|
+
}
|
|
16
|
+
function sortedUint32Has(sorted, value) {
|
|
17
|
+
let lo = 0;
|
|
18
|
+
let hi = sorted.length - 1;
|
|
19
|
+
while (lo <= hi) {
|
|
20
|
+
const mid = (lo + hi) >> 1;
|
|
21
|
+
const v = sorted[mid];
|
|
22
|
+
if (v < value) {
|
|
23
|
+
lo = mid + 1;
|
|
24
|
+
}
|
|
25
|
+
else if (v > value) {
|
|
26
|
+
hi = mid - 1;
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
function findMixedBlockIndex(sortedBlockIdx, target) {
|
|
35
|
+
let lo = 0;
|
|
36
|
+
let hi = sortedBlockIdx.length - 1;
|
|
37
|
+
while (lo <= hi) {
|
|
38
|
+
const mid = (lo + hi) >> 1;
|
|
39
|
+
const v = sortedBlockIdx[mid];
|
|
40
|
+
if (v < target) {
|
|
41
|
+
lo = mid + 1;
|
|
42
|
+
}
|
|
43
|
+
else if (v > target) {
|
|
44
|
+
hi = mid - 1;
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
return mid;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
function sortMixedByBlockIdx(blockIdx, masks) {
|
|
53
|
+
const n = blockIdx.length;
|
|
54
|
+
if (n <= 1) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
const stackLo = [0];
|
|
58
|
+
const stackHi = [n - 1];
|
|
59
|
+
function swap(a, b) {
|
|
60
|
+
const k = blockIdx[a];
|
|
61
|
+
blockIdx[a] = blockIdx[b];
|
|
62
|
+
blockIdx[b] = k;
|
|
63
|
+
const alo = masks[a * 2];
|
|
64
|
+
const ahi = masks[a * 2 + 1];
|
|
65
|
+
masks[a * 2] = masks[b * 2];
|
|
66
|
+
masks[a * 2 + 1] = masks[b * 2 + 1];
|
|
67
|
+
masks[b * 2] = alo;
|
|
68
|
+
masks[b * 2 + 1] = ahi;
|
|
69
|
+
}
|
|
70
|
+
while (stackLo.length > 0) {
|
|
71
|
+
const lo = stackLo.pop();
|
|
72
|
+
const hi = stackHi.pop();
|
|
73
|
+
if (hi - lo < 16) {
|
|
74
|
+
for (let i = lo + 1; i <= hi; i++) {
|
|
75
|
+
const k = blockIdx[i];
|
|
76
|
+
const m0 = masks[i * 2];
|
|
77
|
+
const m1 = masks[i * 2 + 1];
|
|
78
|
+
let j = i - 1;
|
|
79
|
+
while (j >= lo && blockIdx[j] > k) {
|
|
80
|
+
blockIdx[j + 1] = blockIdx[j];
|
|
81
|
+
masks[(j + 1) * 2] = masks[j * 2];
|
|
82
|
+
masks[(j + 1) * 2 + 1] = masks[j * 2 + 1];
|
|
83
|
+
j--;
|
|
84
|
+
}
|
|
85
|
+
blockIdx[j + 1] = k;
|
|
86
|
+
masks[(j + 1) * 2] = m0;
|
|
87
|
+
masks[(j + 1) * 2 + 1] = m1;
|
|
88
|
+
}
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
const mid = (lo + hi) >>> 1;
|
|
92
|
+
if (blockIdx[mid] < blockIdx[lo]) {
|
|
93
|
+
swap(mid, lo);
|
|
94
|
+
}
|
|
95
|
+
if (blockIdx[hi] < blockIdx[lo]) {
|
|
96
|
+
swap(hi, lo);
|
|
97
|
+
}
|
|
98
|
+
if (blockIdx[hi] < blockIdx[mid]) {
|
|
99
|
+
swap(hi, mid);
|
|
100
|
+
}
|
|
101
|
+
const pivot = blockIdx[mid];
|
|
102
|
+
let i = lo;
|
|
103
|
+
let j = hi;
|
|
104
|
+
while (i <= j) {
|
|
105
|
+
while (blockIdx[i] < pivot) {
|
|
106
|
+
i++;
|
|
107
|
+
}
|
|
108
|
+
while (blockIdx[j] > pivot) {
|
|
109
|
+
j--;
|
|
110
|
+
}
|
|
111
|
+
if (i <= j) {
|
|
112
|
+
if (i !== j) {
|
|
113
|
+
swap(i, j);
|
|
114
|
+
}
|
|
115
|
+
i++;
|
|
116
|
+
j--;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
if (j - lo > hi - i) {
|
|
120
|
+
if (lo < j) {
|
|
121
|
+
stackLo.push(lo);
|
|
122
|
+
stackHi.push(j);
|
|
123
|
+
}
|
|
124
|
+
if (i < hi) {
|
|
125
|
+
stackLo.push(i);
|
|
126
|
+
stackHi.push(hi);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
if (i < hi) {
|
|
131
|
+
stackLo.push(i);
|
|
132
|
+
stackHi.push(hi);
|
|
133
|
+
}
|
|
134
|
+
if (lo < j) {
|
|
135
|
+
stackLo.push(lo);
|
|
136
|
+
stackHi.push(j);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
function addCrossFace(nx, ny, nz, nbx, nby, nbz, hasSolid, getMixedIndex, masks, ourFaceMask, adjFaceMask, shiftAmount, shiftLeft, curLo, curHi, write) {
|
|
142
|
+
if (nx < 0 || ny < 0 || nz < 0 || nx >= nbx || ny >= nby || nz >= nbz) {
|
|
143
|
+
write(curLo, curHi);
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
const adjBlockIdx = nx + ny * nbx + nz * nbx * nby;
|
|
147
|
+
if (hasSolid(adjBlockIdx)) {
|
|
148
|
+
write(curLo | ourFaceMask, curHi | ourFaceMask);
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
const adjIdx = getMixedIndex(adjBlockIdx);
|
|
152
|
+
if (adjIdx === undefined) {
|
|
153
|
+
write(curLo, curHi);
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
const adjLo = masks[adjIdx * 2];
|
|
157
|
+
const adjHi = masks[adjIdx * 2 + 1];
|
|
158
|
+
const faceLo = adjLo & adjFaceMask;
|
|
159
|
+
const faceHi = adjHi & adjFaceMask;
|
|
160
|
+
if (shiftLeft) {
|
|
161
|
+
write(curLo | (faceLo << shiftAmount), curHi | (faceHi << shiftAmount));
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
write(curLo | (faceLo >>> shiftAmount), curHi | (faceHi >>> shiftAmount));
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
function addCrossFaceZ(nx, ny, nz, nbx, nby, nbz, hasSolid, getMixedIndex, masks, plusZ, curLo, curHi, write) {
|
|
168
|
+
if (nx < 0 || ny < 0 || nz < 0 || nx >= nbx || ny >= nby || nz >= nbz) {
|
|
169
|
+
write(curLo, curHi);
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
const adjBlockIdx = nx + ny * nbx + nz * nbx * nby;
|
|
173
|
+
if (hasSolid(adjBlockIdx)) {
|
|
174
|
+
if (plusZ) {
|
|
175
|
+
write(curLo, curHi | FACE_Z3_HI);
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
write(curLo | FACE_Z0_LO, curHi);
|
|
179
|
+
}
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
const adjIdx = getMixedIndex(adjBlockIdx);
|
|
183
|
+
if (adjIdx === undefined) {
|
|
184
|
+
write(curLo, curHi);
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
const adjLo = masks[adjIdx * 2];
|
|
188
|
+
const adjHi = masks[adjIdx * 2 + 1];
|
|
189
|
+
if (plusZ) {
|
|
190
|
+
write(curLo, curHi | ((adjLo & FACE_Z0_LO) << 16));
|
|
191
|
+
}
|
|
192
|
+
else {
|
|
193
|
+
write(curLo | ((adjHi & FACE_Z3_HI) >>> 16), curHi);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Block cleanup pass:
|
|
198
|
+
* - remove voxels that have no supporting 6-neighborhood occupancy
|
|
199
|
+
* - fill single-voxel holes fully enclosed by 6 neighbors
|
|
200
|
+
* Includes cross-block neighbor propagation for face-adjacent blocks.
|
|
201
|
+
*/
|
|
202
|
+
export function filterAndFillBlocks(blocks, nbx = Infinity, nby = Infinity, nbz = Infinity) {
|
|
203
|
+
const mixed = blocks.getMixedBlocks();
|
|
204
|
+
const solids = blocks.getSolidBlocks();
|
|
205
|
+
const mixedCount = mixed.blockIdx.length;
|
|
206
|
+
const masks = mixed.masks;
|
|
207
|
+
if (mixedCount === 0) {
|
|
208
|
+
return blocks;
|
|
209
|
+
}
|
|
210
|
+
const mixedBlockIdx = new Float64Array(mixedCount);
|
|
211
|
+
for (let i = 0; i < mixedCount; i++) {
|
|
212
|
+
mixedBlockIdx[i] = mixed.blockIdx[i];
|
|
213
|
+
}
|
|
214
|
+
sortMixedByBlockIdx(mixedBlockIdx, masks);
|
|
215
|
+
const sortedSolid = new Float64Array(solids.length);
|
|
216
|
+
for (let i = 0; i < solids.length; i++) {
|
|
217
|
+
sortedSolid[i] = solids[i];
|
|
218
|
+
}
|
|
219
|
+
sortedSolid.sort();
|
|
220
|
+
function hasSolid(blockIdx) {
|
|
221
|
+
return sortedUint32Has(sortedSolid, blockIdx);
|
|
222
|
+
}
|
|
223
|
+
function getMixedIndex(blockIdx) {
|
|
224
|
+
return findMixedBlockIndex(mixedBlockIdx, blockIdx);
|
|
225
|
+
}
|
|
226
|
+
const newMasks = new Uint32Array(masks.length);
|
|
227
|
+
let voxelsRemoved = 0;
|
|
228
|
+
let voxelsFilled = 0;
|
|
229
|
+
for (let i = 0; i < mixedCount; i++) {
|
|
230
|
+
const blockIdx = mixedBlockIdx[i];
|
|
231
|
+
const origLo = masks[i * 2];
|
|
232
|
+
const origHi = masks[i * 2 + 1];
|
|
233
|
+
const bx = blockIdx % nbx;
|
|
234
|
+
const byBz = (blockIdx / nbx) | 0;
|
|
235
|
+
const by = byBz % nby;
|
|
236
|
+
const bz = (blockIdx / (nbx * nby)) | 0;
|
|
237
|
+
let pxLo = (origLo >>> 1) & ~FACE_X3;
|
|
238
|
+
let pxHi = (origHi >>> 1) & ~FACE_X3;
|
|
239
|
+
let mxLo = (origLo << 1) & ~FACE_X0;
|
|
240
|
+
let mxHi = (origHi << 1) & ~FACE_X0;
|
|
241
|
+
let pyLo = (origLo >>> 4) & ~FACE_Y3;
|
|
242
|
+
let pyHi = (origHi >>> 4) & ~FACE_Y3;
|
|
243
|
+
let myLo = (origLo << 4) & ~FACE_Y0;
|
|
244
|
+
let myHi = (origHi << 4) & ~FACE_Y0;
|
|
245
|
+
let pzLo = (origLo >>> 16) | (origHi << 16);
|
|
246
|
+
let pzHi = origHi >>> 16;
|
|
247
|
+
let mzLo = origLo << 16;
|
|
248
|
+
let mzHi = (origHi << 16) | (origLo >>> 16);
|
|
249
|
+
function writePx(lo, hi) {
|
|
250
|
+
pxLo = lo;
|
|
251
|
+
pxHi = hi;
|
|
252
|
+
}
|
|
253
|
+
function writeMx(lo, hi) {
|
|
254
|
+
mxLo = lo;
|
|
255
|
+
mxHi = hi;
|
|
256
|
+
}
|
|
257
|
+
function writePy(lo, hi) {
|
|
258
|
+
pyLo = lo;
|
|
259
|
+
pyHi = hi;
|
|
260
|
+
}
|
|
261
|
+
function writeMy(lo, hi) {
|
|
262
|
+
myLo = lo;
|
|
263
|
+
myHi = hi;
|
|
264
|
+
}
|
|
265
|
+
function writePz(lo, hi) {
|
|
266
|
+
pzLo = lo;
|
|
267
|
+
pzHi = hi;
|
|
268
|
+
}
|
|
269
|
+
function writeMz(lo, hi) {
|
|
270
|
+
mzLo = lo;
|
|
271
|
+
mzHi = hi;
|
|
272
|
+
}
|
|
273
|
+
addCrossFace(bx + 1, by, bz, nbx, nby, nbz, hasSolid, getMixedIndex, masks, FACE_X3, FACE_X0, 3, true, pxLo, pxHi, writePx);
|
|
274
|
+
addCrossFace(bx - 1, by, bz, nbx, nby, nbz, hasSolid, getMixedIndex, masks, FACE_X0, FACE_X3, 3, false, mxLo, mxHi, writeMx);
|
|
275
|
+
addCrossFace(bx, by + 1, bz, nbx, nby, nbz, hasSolid, getMixedIndex, masks, FACE_Y3, FACE_Y0, 12, true, pyLo, pyHi, writePy);
|
|
276
|
+
addCrossFace(bx, by - 1, bz, nbx, nby, nbz, hasSolid, getMixedIndex, masks, FACE_Y0, FACE_Y3, 12, false, myLo, myHi, writeMy);
|
|
277
|
+
addCrossFaceZ(bx, by, bz + 1, nbx, nby, nbz, hasSolid, getMixedIndex, masks, true, pzLo, pzHi, writePz);
|
|
278
|
+
addCrossFaceZ(bx, by, bz - 1, nbx, nby, nbz, hasSolid, getMixedIndex, masks, false, mzLo, mzHi, writeMz);
|
|
279
|
+
const neighborLo = pxLo | mxLo | pyLo | myLo | pzLo | mzLo;
|
|
280
|
+
const neighborHi = pxHi | mxHi | pyHi | myHi | pzHi | mzHi;
|
|
281
|
+
let lo = origLo & neighborLo;
|
|
282
|
+
let hi = origHi & neighborHi;
|
|
283
|
+
const fillLo = ~lo & pxLo & mxLo & pyLo & myLo & pzLo & mzLo;
|
|
284
|
+
const fillHi = ~hi & pxHi & mxHi & pyHi & myHi & pzHi & mzHi;
|
|
285
|
+
lo |= fillLo;
|
|
286
|
+
hi |= fillHi;
|
|
287
|
+
voxelsRemoved += popcount(origLo & ~lo) + popcount(origHi & ~hi);
|
|
288
|
+
voxelsFilled += popcount(lo & ~origLo) + popcount(hi & ~origHi);
|
|
289
|
+
newMasks[i * 2] = lo >>> 0;
|
|
290
|
+
newMasks[i * 2 + 1] = hi >>> 0;
|
|
291
|
+
}
|
|
292
|
+
const result = new BlockMaskBuffer();
|
|
293
|
+
for (let i = 0; i < mixedCount; i++) {
|
|
294
|
+
const lo = newMasks[i * 2];
|
|
295
|
+
const hi = newMasks[i * 2 + 1];
|
|
296
|
+
result.addBlock(mixedBlockIdx[i], lo, hi);
|
|
297
|
+
}
|
|
298
|
+
for (let i = 0; i < solids.length; i++) {
|
|
299
|
+
result.addBlock(solids[i], SOLID_LO, SOLID_HI);
|
|
300
|
+
}
|
|
301
|
+
logger.info(`voxel filter: ${voxelsRemoved} voxels removed, ${voxelsFilled} voxels filled`);
|
|
302
|
+
return result;
|
|
303
|
+
}
|
|
304
|
+
/** Crop blocks into [min, max) block range and rebase linear block coordinates. */
|
|
305
|
+
export function cropBlocksToRange(blocks, sourceNbx, sourceNby, cropMinBx, cropMinBy, cropMinBz, cropMaxBx, cropMaxBy, cropMaxBz) {
|
|
306
|
+
const cropped = new BlockMaskBuffer();
|
|
307
|
+
const outNbx = cropMaxBx - cropMinBx;
|
|
308
|
+
const outNby = cropMaxBy - cropMinBy;
|
|
309
|
+
const sourceBStride = sourceNbx * sourceNby;
|
|
310
|
+
const solids = blocks.getSolidBlocks();
|
|
311
|
+
for (let i = 0; i < solids.length; i++) {
|
|
312
|
+
const blockIdx = solids[i];
|
|
313
|
+
const bx = blockIdx % sourceNbx;
|
|
314
|
+
const byBz = (blockIdx / sourceNbx) | 0;
|
|
315
|
+
const by = byBz % sourceNby;
|
|
316
|
+
const bz = (blockIdx / sourceBStride) | 0;
|
|
317
|
+
if (bx < cropMinBx || by < cropMinBy || bz < cropMinBz) {
|
|
318
|
+
continue;
|
|
319
|
+
}
|
|
320
|
+
if (bx >= cropMaxBx || by >= cropMaxBy || bz >= cropMaxBz) {
|
|
321
|
+
continue;
|
|
322
|
+
}
|
|
323
|
+
cropped.addBlock(bx - cropMinBx + (by - cropMinBy) * outNbx + (bz - cropMinBz) * outNbx * outNby, SOLID_LO, SOLID_HI);
|
|
324
|
+
}
|
|
325
|
+
const mixed = blocks.getMixedBlocks();
|
|
326
|
+
for (let i = 0; i < mixed.blockIdx.length; i++) {
|
|
327
|
+
const blockIdx = mixed.blockIdx[i];
|
|
328
|
+
const bx = blockIdx % sourceNbx;
|
|
329
|
+
const byBz = (blockIdx / sourceNbx) | 0;
|
|
330
|
+
const by = byBz % sourceNby;
|
|
331
|
+
const bz = (blockIdx / sourceBStride) | 0;
|
|
332
|
+
if (bx < cropMinBx || by < cropMinBy || bz < cropMinBz) {
|
|
333
|
+
continue;
|
|
334
|
+
}
|
|
335
|
+
if (bx >= cropMaxBx || by >= cropMaxBy || bz >= cropMaxBz) {
|
|
336
|
+
continue;
|
|
337
|
+
}
|
|
338
|
+
cropped.addBlock(bx - cropMinBx + (by - cropMinBy) * outNbx + (bz - cropMinBz) * outNbx * outNby, mixed.masks[i * 2], mixed.masks[i * 2 + 1]);
|
|
339
|
+
}
|
|
340
|
+
return cropped;
|
|
341
|
+
}
|
|
342
|
+
/** Compute world-space bounds corresponding to a cropped block range. */
|
|
343
|
+
export function cropBounds(gridBounds, voxelResolution, cropMinBx, cropMinBy, cropMinBz, cropMaxBx, cropMaxBy, cropMaxBz) {
|
|
344
|
+
const blockSize = 4 * voxelResolution;
|
|
345
|
+
const croppedMin = {
|
|
346
|
+
x: gridBounds.min.x + cropMinBx * blockSize,
|
|
347
|
+
y: gridBounds.min.y + cropMinBy * blockSize,
|
|
348
|
+
z: gridBounds.min.z + cropMinBz * blockSize,
|
|
349
|
+
};
|
|
350
|
+
return {
|
|
351
|
+
min: croppedMin,
|
|
352
|
+
max: {
|
|
353
|
+
x: croppedMin.x + (cropMaxBx - cropMinBx) * blockSize,
|
|
354
|
+
y: croppedMin.y + (cropMaxBy - cropMinBy) * blockSize,
|
|
355
|
+
z: croppedMin.z + (cropMaxBz - cropMinBz) * blockSize,
|
|
356
|
+
},
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
/** Tight crop to occupied block bounds. */
|
|
360
|
+
export function cropToOccupied(grid, gridBounds, voxelResolution) {
|
|
361
|
+
const occupied = grid.getOccupiedBlockBounds();
|
|
362
|
+
if (!occupied) {
|
|
363
|
+
return { grid, gridBounds };
|
|
364
|
+
}
|
|
365
|
+
const { minBx, minBy, minBz, maxBx, maxBy, maxBz } = occupied;
|
|
366
|
+
const cropMaxBx = maxBx + 1;
|
|
367
|
+
const cropMaxBy = maxBy + 1;
|
|
368
|
+
const cropMaxBz = maxBz + 1;
|
|
369
|
+
const { nbx, nby, nbz } = grid;
|
|
370
|
+
if (minBx === 0 && minBy === 0 && minBz === 0 && cropMaxBx === nbx && cropMaxBy === nby && cropMaxBz === nbz) {
|
|
371
|
+
return { grid, gridBounds };
|
|
372
|
+
}
|
|
373
|
+
return {
|
|
374
|
+
grid: grid.cropTo(minBx, minBy, minBz, cropMaxBx, cropMaxBy, cropMaxBz),
|
|
375
|
+
gridBounds: cropBounds(gridBounds, voxelResolution, minBx, minBy, minBz, cropMaxBx, cropMaxBy, cropMaxBz),
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
/** Tight crop to navigable (non-fully-solid) block bounds. */
|
|
379
|
+
export function cropToNavigable(grid, gridBounds, voxelResolution) {
|
|
380
|
+
const navBounds = grid.getNavigableBlockBounds();
|
|
381
|
+
if (!navBounds) {
|
|
382
|
+
return { grid, gridBounds };
|
|
383
|
+
}
|
|
384
|
+
const { minBx, minBy, minBz, maxBx, maxBy, maxBz } = navBounds;
|
|
385
|
+
const { nbx, nby, nbz } = grid;
|
|
386
|
+
// Keep one solid wall block around the navigable cavity. The runtime
|
|
387
|
+
// treats out-of-grid as solid, but collision extraction sees out-of-grid
|
|
388
|
+
// as empty; this padding keeps collision meshes sealed at crop edges.
|
|
389
|
+
const MARGIN = 1;
|
|
390
|
+
const cropMinBx = Math.max(0, minBx - MARGIN);
|
|
391
|
+
const cropMinBy = Math.max(0, minBy - MARGIN);
|
|
392
|
+
const cropMinBz = Math.max(0, minBz - MARGIN);
|
|
393
|
+
const cropMaxBx = Math.min(nbx, maxBx + 1 + MARGIN);
|
|
394
|
+
const cropMaxBy = Math.min(nby, maxBy + 1 + MARGIN);
|
|
395
|
+
const cropMaxBz = Math.min(nbz, maxBz + 1 + MARGIN);
|
|
396
|
+
if (cropMinBx === 0 &&
|
|
397
|
+
cropMinBy === 0 &&
|
|
398
|
+
cropMinBz === 0 &&
|
|
399
|
+
cropMaxBx === nbx &&
|
|
400
|
+
cropMaxBy === nby &&
|
|
401
|
+
cropMaxBz === nbz) {
|
|
402
|
+
return { grid, gridBounds };
|
|
403
|
+
}
|
|
404
|
+
return {
|
|
405
|
+
grid: grid.cropTo(cropMinBx, cropMinBy, cropMinBz, cropMaxBx, cropMaxBy, cropMaxBz),
|
|
406
|
+
gridBounds: cropBounds(gridBounds, voxelResolution, cropMinBx, cropMinBy, cropMinBz, cropMaxBx, cropMaxBy, cropMaxBz),
|
|
407
|
+
};
|
|
408
|
+
}
|