@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,1682 +1,1752 @@
|
|
|
1
|
-
/** 3D Morton (Z-order) for integer block coordinates. */
|
|
2
|
-
export
|
|
3
|
-
let result = 0;
|
|
4
|
-
let shift = 1;
|
|
5
|
-
for (let i = 0; i < 17; i++) {
|
|
6
|
-
if (x & 1) {
|
|
7
|
-
result += shift;
|
|
8
|
-
}
|
|
9
|
-
if (y & 1) {
|
|
10
|
-
result += shift * 2;
|
|
11
|
-
}
|
|
12
|
-
if (z & 1) {
|
|
13
|
-
result += shift * 4;
|
|
14
|
-
}
|
|
15
|
-
x >>>= 1;
|
|
16
|
-
y >>>= 1;
|
|
17
|
-
z >>>= 1;
|
|
18
|
-
shift *= 8;
|
|
19
|
-
}
|
|
20
|
-
return result;
|
|
21
|
-
}
|
|
22
|
-
export
|
|
23
|
-
let x = 0, y = 0, z = 0;
|
|
24
|
-
let bit = 1;
|
|
25
|
-
while (m > 0) {
|
|
26
|
-
const triplet = m % 8;
|
|
27
|
-
if (triplet & 1) {
|
|
28
|
-
x |= bit;
|
|
29
|
-
}
|
|
30
|
-
if (triplet & 2) {
|
|
31
|
-
y |= bit;
|
|
32
|
-
}
|
|
33
|
-
if (triplet & 4) {
|
|
34
|
-
z |= bit;
|
|
35
|
-
}
|
|
36
|
-
bit <<= 1;
|
|
37
|
-
m = Math.trunc(m / 8);
|
|
38
|
-
}
|
|
39
|
-
return [x, y, z];
|
|
40
|
-
}
|
|
41
|
-
/** Voxel leaf edge length in voxels (4³ block). */
|
|
42
|
-
export const LEAF_SIZE = 4;
|
|
43
|
-
export const ALPHA_THRESHOLD = 1
|
|
44
|
-
export
|
|
45
|
-
const blockSize = LEAF_SIZE * voxelResolution;
|
|
46
|
-
return {
|
|
47
|
-
min: {
|
|
48
|
-
x: Math.floor(bounds.min.x / blockSize) * blockSize,
|
|
49
|
-
y: Math.floor(bounds.min.y / blockSize) * blockSize,
|
|
50
|
-
z: Math.floor(bounds.min.z / blockSize) * blockSize
|
|
51
|
-
},
|
|
52
|
-
max: {
|
|
53
|
-
x: Math.ceil(bounds.max.x / blockSize) * blockSize,
|
|
54
|
-
y: Math.ceil(bounds.max.y / blockSize) * blockSize,
|
|
55
|
-
z: Math.ceil(bounds.max.z / blockSize) * blockSize
|
|
56
|
-
}
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
/**
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
const
|
|
91
|
-
const
|
|
92
|
-
const
|
|
93
|
-
const
|
|
94
|
-
const
|
|
95
|
-
const
|
|
96
|
-
const
|
|
97
|
-
const
|
|
98
|
-
const
|
|
99
|
-
const
|
|
100
|
-
const
|
|
101
|
-
const
|
|
102
|
-
const
|
|
103
|
-
const
|
|
104
|
-
const
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
const
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
const
|
|
112
|
-
const
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
const
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
swap(
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
if (
|
|
225
|
-
|
|
226
|
-
}
|
|
227
|
-
if (
|
|
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
|
-
if (
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
const
|
|
302
|
-
const
|
|
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
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
this.
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
this.mixedCountValue =
|
|
377
|
-
this.
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
this.
|
|
402
|
-
this.
|
|
403
|
-
this.
|
|
404
|
-
this.
|
|
405
|
-
this.
|
|
406
|
-
this.
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
this.
|
|
500
|
-
this.
|
|
501
|
-
this.
|
|
502
|
-
this.
|
|
503
|
-
this.
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
const
|
|
507
|
-
this.
|
|
508
|
-
this.
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
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
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
this.masks.
|
|
572
|
-
}
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
const
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
}
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
}
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
const
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
}
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
}
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
const
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
{
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1
|
+
/** 3D Morton (Z-order) for integer block coordinates. */
|
|
2
|
+
export function encodeMorton3(x, y, z) {
|
|
3
|
+
let result = 0;
|
|
4
|
+
let shift = 1;
|
|
5
|
+
for (let i = 0; i < 17; i++) {
|
|
6
|
+
if (x & 1) {
|
|
7
|
+
result += shift;
|
|
8
|
+
}
|
|
9
|
+
if (y & 1) {
|
|
10
|
+
result += shift * 2;
|
|
11
|
+
}
|
|
12
|
+
if (z & 1) {
|
|
13
|
+
result += shift * 4;
|
|
14
|
+
}
|
|
15
|
+
x >>>= 1;
|
|
16
|
+
y >>>= 1;
|
|
17
|
+
z >>>= 1;
|
|
18
|
+
shift *= 8;
|
|
19
|
+
}
|
|
20
|
+
return result;
|
|
21
|
+
}
|
|
22
|
+
export function decodeMorton3(m) {
|
|
23
|
+
let x = 0, y = 0, z = 0;
|
|
24
|
+
let bit = 1;
|
|
25
|
+
while (m > 0) {
|
|
26
|
+
const triplet = m % 8;
|
|
27
|
+
if (triplet & 1) {
|
|
28
|
+
x |= bit;
|
|
29
|
+
}
|
|
30
|
+
if (triplet & 2) {
|
|
31
|
+
y |= bit;
|
|
32
|
+
}
|
|
33
|
+
if (triplet & 4) {
|
|
34
|
+
z |= bit;
|
|
35
|
+
}
|
|
36
|
+
bit <<= 1;
|
|
37
|
+
m = Math.trunc(m / 8);
|
|
38
|
+
}
|
|
39
|
+
return [x, y, z];
|
|
40
|
+
}
|
|
41
|
+
/** Voxel leaf edge length in voxels (4³ block). */
|
|
42
|
+
export const LEAF_SIZE = 4;
|
|
43
|
+
export const ALPHA_THRESHOLD = 1 / 255;
|
|
44
|
+
export function alignGridBounds(bounds, voxelResolution) {
|
|
45
|
+
const blockSize = LEAF_SIZE * voxelResolution;
|
|
46
|
+
return {
|
|
47
|
+
min: {
|
|
48
|
+
x: Math.floor(bounds.min.x / blockSize) * blockSize,
|
|
49
|
+
y: Math.floor(bounds.min.y / blockSize) * blockSize,
|
|
50
|
+
z: Math.floor(bounds.min.z / blockSize) * blockSize,
|
|
51
|
+
},
|
|
52
|
+
max: {
|
|
53
|
+
x: Math.ceil(bounds.max.x / blockSize) * blockSize,
|
|
54
|
+
y: Math.ceil(bounds.max.y / blockSize) * blockSize,
|
|
55
|
+
z: Math.ceil(bounds.max.z / blockSize) * blockSize,
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Max linear block index (signed 32-bit): BlockMaskMap keys, worker `blockIdx >>> 0`,
|
|
61
|
+
* and types bitmap indexing. Implies types bitmap <= ceil(MAX / 16) * 4 ~= 512 MB.
|
|
62
|
+
*/
|
|
63
|
+
export const MAX_VOXEL_BLOCK_COUNT_INT32 = 0x7fff_ffff;
|
|
64
|
+
/** Preflight hard grid limits before voxelization allocates the types bitmap. */
|
|
65
|
+
export function checkVoxelGridCapacity(gridBounds, voxelResolution) {
|
|
66
|
+
const blockSize = LEAF_SIZE * voxelResolution;
|
|
67
|
+
const nbx = Math.round((gridBounds.max.x - gridBounds.min.x) / blockSize);
|
|
68
|
+
const nby = Math.round((gridBounds.max.y - gridBounds.min.y) / blockSize);
|
|
69
|
+
const nbz = Math.round((gridBounds.max.z - gridBounds.min.z) / blockSize);
|
|
70
|
+
const totalBlocks = nbx * nby * nbz;
|
|
71
|
+
if (totalBlocks > MAX_VOXEL_BLOCK_COUNT_INT32) {
|
|
72
|
+
throw new Error(`voxel grid block count ${totalBlocks} exceeds limit ${MAX_VOXEL_BLOCK_COUNT_INT32} ` +
|
|
73
|
+
`(linear block index must fit in 31 bits). ` +
|
|
74
|
+
`Tighten the box parameter or increase voxelResolution. grid blocks=${nbx}x${nby}x${nbz}`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/** Opacity-aware AABB half-extents from scale + unit quaternion. */
|
|
78
|
+
export function extentsFromQuatScale(sx, sy, sz, qx, qy, qz, qw, opacity, opacityThreshold = ALPHA_THRESHOLD) {
|
|
79
|
+
let extend = 3;
|
|
80
|
+
if (opacity !== undefined && opacity > opacityThreshold) {
|
|
81
|
+
// Tight bound from opacity threshold, clamped by default 3-sigma bound.
|
|
82
|
+
const opacityAware = Math.sqrt(2 * Math.log(opacity / opacityThreshold));
|
|
83
|
+
if (Number.isFinite(opacityAware)) {
|
|
84
|
+
extend = Math.min(extend, opacityAware);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
else if (opacity !== undefined && opacity <= opacityThreshold) {
|
|
88
|
+
return { ex: 0, ey: 0, ez: 0 };
|
|
89
|
+
}
|
|
90
|
+
const sX = extend * sx;
|
|
91
|
+
const sY = extend * sy;
|
|
92
|
+
const sZ = extend * sz;
|
|
93
|
+
const xx = qx * qx;
|
|
94
|
+
const yy = qy * qy;
|
|
95
|
+
const zz = qz * qz;
|
|
96
|
+
const xy = qx * qy;
|
|
97
|
+
const xz = qx * qz;
|
|
98
|
+
const yz = qy * qz;
|
|
99
|
+
const wx = qw * qx;
|
|
100
|
+
const wy = qw * qy;
|
|
101
|
+
const wz = qw * qz;
|
|
102
|
+
const m00 = 1 - 2 * (yy + zz);
|
|
103
|
+
const m01 = 2 * (xy - wz);
|
|
104
|
+
const m02 = 2 * (xz + wy);
|
|
105
|
+
const m10 = 2 * (xy + wz);
|
|
106
|
+
const m11 = 1 - 2 * (xx + zz);
|
|
107
|
+
const m12 = 2 * (yz - wx);
|
|
108
|
+
const m20 = 2 * (xz - wy);
|
|
109
|
+
const m21 = 2 * (yz + wx);
|
|
110
|
+
const m22 = 1 - 2 * (xx + yy);
|
|
111
|
+
const abs00 = Math.abs(m00);
|
|
112
|
+
const abs01 = Math.abs(m01);
|
|
113
|
+
const abs02 = Math.abs(m02);
|
|
114
|
+
const abs10 = Math.abs(m10);
|
|
115
|
+
const abs11 = Math.abs(m11);
|
|
116
|
+
const abs12 = Math.abs(m12);
|
|
117
|
+
const abs20 = Math.abs(m20);
|
|
118
|
+
const abs21 = Math.abs(m21);
|
|
119
|
+
const abs22 = Math.abs(m22);
|
|
120
|
+
const ex = abs00 * sX + abs01 * sY + abs02 * sZ;
|
|
121
|
+
const ey = abs10 * sX + abs11 * sY + abs12 * sZ;
|
|
122
|
+
const ez = abs20 * sX + abs21 * sY + abs22 * sZ;
|
|
123
|
+
return { ex, ey, ez };
|
|
124
|
+
}
|
|
125
|
+
function boundsOverlap(a, bMinX, bMinY, bMinZ, bMaxX, bMaxY, bMaxZ) {
|
|
126
|
+
return !(a.maxX < bMinX || a.minX > bMaxX || a.maxY < bMinY || a.minY > bMaxY || a.maxZ < bMinZ || a.minZ > bMaxZ);
|
|
127
|
+
}
|
|
128
|
+
function quickselect(axisData, idx, k) {
|
|
129
|
+
function valAt(p) {
|
|
130
|
+
return axisData[idx[p]];
|
|
131
|
+
}
|
|
132
|
+
function swap(i, j) {
|
|
133
|
+
const t = idx[i];
|
|
134
|
+
idx[i] = idx[j];
|
|
135
|
+
idx[j] = t;
|
|
136
|
+
}
|
|
137
|
+
const n = idx.length;
|
|
138
|
+
let l = 0;
|
|
139
|
+
let r = n - 1;
|
|
140
|
+
while (true) {
|
|
141
|
+
if (r <= l + 1) {
|
|
142
|
+
if (r === l + 1 && valAt(r) < valAt(l)) {
|
|
143
|
+
swap(l, r);
|
|
144
|
+
}
|
|
145
|
+
return idx[k];
|
|
146
|
+
}
|
|
147
|
+
const mid = (l + r) >>> 1;
|
|
148
|
+
swap(mid, l + 1);
|
|
149
|
+
if (valAt(l) > valAt(r)) {
|
|
150
|
+
swap(l, r);
|
|
151
|
+
}
|
|
152
|
+
if (valAt(l + 1) > valAt(r)) {
|
|
153
|
+
swap(l + 1, r);
|
|
154
|
+
}
|
|
155
|
+
if (valAt(l) > valAt(l + 1)) {
|
|
156
|
+
swap(l, l + 1);
|
|
157
|
+
}
|
|
158
|
+
let i = l + 1;
|
|
159
|
+
let j = r;
|
|
160
|
+
const pivotIdxVal = valAt(l + 1);
|
|
161
|
+
const pivotIdx = idx[l + 1];
|
|
162
|
+
while (true) {
|
|
163
|
+
do {
|
|
164
|
+
i++;
|
|
165
|
+
} while (i <= r && valAt(i) < pivotIdxVal);
|
|
166
|
+
do {
|
|
167
|
+
j--;
|
|
168
|
+
} while (j >= l && valAt(j) > pivotIdxVal);
|
|
169
|
+
if (j < i) {
|
|
170
|
+
break;
|
|
171
|
+
}
|
|
172
|
+
swap(i, j);
|
|
173
|
+
}
|
|
174
|
+
idx[l + 1] = idx[j];
|
|
175
|
+
idx[j] = pivotIdx;
|
|
176
|
+
if (j >= k) {
|
|
177
|
+
r = j - 1;
|
|
178
|
+
}
|
|
179
|
+
if (j <= k) {
|
|
180
|
+
l = i;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
export class GaussianBVH {
|
|
185
|
+
static { this.MAX_LEAF_SIZE = 64; }
|
|
186
|
+
constructor(x, y, z, extents) {
|
|
187
|
+
this.x = x;
|
|
188
|
+
this.y = y;
|
|
189
|
+
this.z = z;
|
|
190
|
+
this.extents = extents;
|
|
191
|
+
const indices = new Uint32Array(x.length);
|
|
192
|
+
for (let i = 0; i < x.length; i++) {
|
|
193
|
+
indices[i] = i;
|
|
194
|
+
}
|
|
195
|
+
this.root = this.buildNode(indices);
|
|
196
|
+
}
|
|
197
|
+
queryOverlappingRaw(minX, minY, minZ, maxX, maxY, maxZ) {
|
|
198
|
+
const result = [];
|
|
199
|
+
this.queryNode(this.root, minX, minY, minZ, maxX, maxY, maxZ, result);
|
|
200
|
+
return result;
|
|
201
|
+
}
|
|
202
|
+
queryOverlappingRawInto(minX, minY, minZ, maxX, maxY, maxZ, output, offset = 0) {
|
|
203
|
+
return this.queryNodeInto(this.root, minX, minY, minZ, maxX, maxY, maxZ, output, offset, 0);
|
|
204
|
+
}
|
|
205
|
+
computeBounds(indices) {
|
|
206
|
+
let minX = Infinity, minY = Infinity, minZ = Infinity;
|
|
207
|
+
let maxX = -Infinity, maxY = -Infinity, maxZ = -Infinity;
|
|
208
|
+
for (let i = 0; i < indices.length; i++) {
|
|
209
|
+
const idx = indices[i];
|
|
210
|
+
const ex = this.extents[idx * 3], ey = this.extents[idx * 3 + 1], ez = this.extents[idx * 3 + 2];
|
|
211
|
+
const gx = this.x[idx], gy = this.y[idx], gz = this.z[idx];
|
|
212
|
+
if (gx - ex < minX) {
|
|
213
|
+
minX = gx - ex;
|
|
214
|
+
}
|
|
215
|
+
if (gy - ey < minY) {
|
|
216
|
+
minY = gy - ey;
|
|
217
|
+
}
|
|
218
|
+
if (gz - ez < minZ) {
|
|
219
|
+
minZ = gz - ez;
|
|
220
|
+
}
|
|
221
|
+
if (gx + ex > maxX) {
|
|
222
|
+
maxX = gx + ex;
|
|
223
|
+
}
|
|
224
|
+
if (gy + ey > maxY) {
|
|
225
|
+
maxY = gy + ey;
|
|
226
|
+
}
|
|
227
|
+
if (gz + ez > maxZ) {
|
|
228
|
+
maxZ = gz + ez;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
return { minX, minY, minZ, maxX, maxY, maxZ };
|
|
232
|
+
}
|
|
233
|
+
buildNode(indices) {
|
|
234
|
+
const bounds = this.computeBounds(indices);
|
|
235
|
+
if (indices.length <= GaussianBVH.MAX_LEAF_SIZE) {
|
|
236
|
+
return { bounds, indices };
|
|
237
|
+
}
|
|
238
|
+
let minCx = Infinity, minCy = Infinity, minCz = Infinity;
|
|
239
|
+
let maxCx = -Infinity, maxCy = -Infinity, maxCz = -Infinity;
|
|
240
|
+
for (let i = 0; i < indices.length; i++) {
|
|
241
|
+
const idx = indices[i];
|
|
242
|
+
const cx = this.x[idx], cy = this.y[idx], cz = this.z[idx];
|
|
243
|
+
if (cx < minCx) {
|
|
244
|
+
minCx = cx;
|
|
245
|
+
}
|
|
246
|
+
if (cy < minCy) {
|
|
247
|
+
minCy = cy;
|
|
248
|
+
}
|
|
249
|
+
if (cz < minCz) {
|
|
250
|
+
minCz = cz;
|
|
251
|
+
}
|
|
252
|
+
if (cx > maxCx) {
|
|
253
|
+
maxCx = cx;
|
|
254
|
+
}
|
|
255
|
+
if (cy > maxCy) {
|
|
256
|
+
maxCy = cy;
|
|
257
|
+
}
|
|
258
|
+
if (cz > maxCz) {
|
|
259
|
+
maxCz = cz;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
const ex = maxCx - minCx, ey = maxCy - minCy, ez = maxCz - minCz;
|
|
263
|
+
const axis = ex >= ey && ex >= ez ? this.x : ey >= ez ? this.y : this.z;
|
|
264
|
+
const mid = indices.length >>> 1;
|
|
265
|
+
quickselect(axis, indices, mid);
|
|
266
|
+
return { bounds, left: this.buildNode(indices.subarray(0, mid)), right: this.buildNode(indices.subarray(mid)) };
|
|
267
|
+
}
|
|
268
|
+
queryNode(node, minX, minY, minZ, maxX, maxY, maxZ, result) {
|
|
269
|
+
if (!boundsOverlap(node.bounds, minX, minY, minZ, maxX, maxY, maxZ)) {
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
if (node.indices) {
|
|
273
|
+
for (let i = 0; i < node.indices.length; i++) {
|
|
274
|
+
const idx = node.indices[i];
|
|
275
|
+
const ex = this.extents[idx * 3], ey = this.extents[idx * 3 + 1], ez = this.extents[idx * 3 + 2];
|
|
276
|
+
const gx = this.x[idx], gy = this.y[idx], gz = this.z[idx];
|
|
277
|
+
if (!(gx + ex < minX ||
|
|
278
|
+
gx - ex > maxX ||
|
|
279
|
+
gy + ey < minY ||
|
|
280
|
+
gy - ey > maxY ||
|
|
281
|
+
gz + ez < minZ ||
|
|
282
|
+
gz - ez > maxZ)) {
|
|
283
|
+
result.push(idx);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
if (node.left) {
|
|
289
|
+
this.queryNode(node.left, minX, minY, minZ, maxX, maxY, maxZ, result);
|
|
290
|
+
}
|
|
291
|
+
if (node.right) {
|
|
292
|
+
this.queryNode(node.right, minX, minY, minZ, maxX, maxY, maxZ, result);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
queryNodeInto(node, minX, minY, minZ, maxX, maxY, maxZ, output, offset, count) {
|
|
296
|
+
if (!boundsOverlap(node.bounds, minX, minY, minZ, maxX, maxY, maxZ)) {
|
|
297
|
+
return count;
|
|
298
|
+
}
|
|
299
|
+
if (node.indices) {
|
|
300
|
+
for (let i = 0; i < node.indices.length; i++) {
|
|
301
|
+
const idx = node.indices[i];
|
|
302
|
+
const ex = this.extents[idx * 3], ey = this.extents[idx * 3 + 1], ez = this.extents[idx * 3 + 2];
|
|
303
|
+
const gx = this.x[idx], gy = this.y[idx], gz = this.z[idx];
|
|
304
|
+
if (!(gx + ex < minX ||
|
|
305
|
+
gx - ex > maxX ||
|
|
306
|
+
gy + ey < minY ||
|
|
307
|
+
gy - ey > maxY ||
|
|
308
|
+
gz + ez < minZ ||
|
|
309
|
+
gz - ez > maxZ)) {
|
|
310
|
+
if (offset + count < output.length) {
|
|
311
|
+
output[offset + count] = idx;
|
|
312
|
+
}
|
|
313
|
+
count++;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
return count;
|
|
317
|
+
}
|
|
318
|
+
if (node.left) {
|
|
319
|
+
count = this.queryNodeInto(node.left, minX, minY, minZ, maxX, maxY, maxZ, output, offset, count);
|
|
320
|
+
}
|
|
321
|
+
if (node.right) {
|
|
322
|
+
count = this.queryNodeInto(node.right, minX, minY, minZ, maxX, maxY, maxZ, output, offset, count);
|
|
323
|
+
}
|
|
324
|
+
return count;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
const SOLID_LO = 0xffffffff >>> 0;
|
|
328
|
+
const SOLID_HI = 0xffffffff >>> 0;
|
|
329
|
+
const SOLID_MASK = 0xffffffff >>> 0;
|
|
330
|
+
const INITIAL_BLOCK_BUFFER_CAPACITY = 1024;
|
|
331
|
+
function growFloat64(src, newCap) {
|
|
332
|
+
const grown = new Float64Array(newCap);
|
|
333
|
+
grown.set(src);
|
|
334
|
+
return grown;
|
|
335
|
+
}
|
|
336
|
+
function growUint32(src, newCap) {
|
|
337
|
+
const grown = new Uint32Array(newCap);
|
|
338
|
+
grown.set(src);
|
|
339
|
+
return grown;
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* Append-only buffer for streaming voxelization results.
|
|
343
|
+
* Stores (linear blockIdx, voxel mask) pairs for non-empty 4x4x4 blocks.
|
|
344
|
+
*
|
|
345
|
+
* Block keys are linear block indices `bx + by*nbx + bz*nbx*nby` in the
|
|
346
|
+
* producer's grid coordinate system. Producers and consumers must agree on
|
|
347
|
+
* the grid dimensions; the buffer itself is dimension-agnostic.
|
|
348
|
+
*/
|
|
349
|
+
export class BlockMaskBuffer {
|
|
350
|
+
constructor() {
|
|
351
|
+
this.solidIdx = new Float64Array(0);
|
|
352
|
+
this.solidCountValue = 0;
|
|
353
|
+
this.solidCap = 0;
|
|
354
|
+
this.mixedIdx = new Float64Array(0);
|
|
355
|
+
this.mixedCountValue = 0;
|
|
356
|
+
this.mixedCap = 0;
|
|
357
|
+
this.mixedMasks = new Uint32Array(0);
|
|
358
|
+
}
|
|
359
|
+
addBlock(blockIdx, lo, hi) {
|
|
360
|
+
if ((lo | hi) === 0) {
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
if (lo >>> 0 === SOLID_MASK && hi >>> 0 === SOLID_MASK) {
|
|
364
|
+
if (this.solidCountValue === this.solidCap) {
|
|
365
|
+
this.solidCap = this.solidCap === 0 ? INITIAL_BLOCK_BUFFER_CAPACITY : this.solidCap * 2;
|
|
366
|
+
this.solidIdx = growFloat64(this.solidIdx, this.solidCap);
|
|
367
|
+
}
|
|
368
|
+
this.solidIdx[this.solidCountValue++] = blockIdx;
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
if (this.mixedCountValue === this.mixedCap) {
|
|
372
|
+
this.mixedCap = this.mixedCap === 0 ? INITIAL_BLOCK_BUFFER_CAPACITY : this.mixedCap * 2;
|
|
373
|
+
this.mixedIdx = growFloat64(this.mixedIdx, this.mixedCap);
|
|
374
|
+
this.mixedMasks = growUint32(this.mixedMasks, this.mixedCap * 2);
|
|
375
|
+
}
|
|
376
|
+
this.mixedIdx[this.mixedCountValue] = blockIdx;
|
|
377
|
+
this.mixedMasks[this.mixedCountValue * 2] = lo >>> 0;
|
|
378
|
+
this.mixedMasks[this.mixedCountValue * 2 + 1] = hi >>> 0;
|
|
379
|
+
this.mixedCountValue++;
|
|
380
|
+
}
|
|
381
|
+
getMixedBlocks() {
|
|
382
|
+
return {
|
|
383
|
+
blockIdx: this.mixedIdx.subarray(0, this.mixedCountValue),
|
|
384
|
+
masks: this.mixedMasks.subarray(0, this.mixedCountValue * 2),
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
getSolidBlocks() {
|
|
388
|
+
return this.solidIdx.subarray(0, this.solidCountValue);
|
|
389
|
+
}
|
|
390
|
+
get count() {
|
|
391
|
+
return this.mixedCountValue + this.solidCountValue;
|
|
392
|
+
}
|
|
393
|
+
get mixedCount() {
|
|
394
|
+
return this.mixedCountValue;
|
|
395
|
+
}
|
|
396
|
+
get solidCount() {
|
|
397
|
+
return this.solidCountValue;
|
|
398
|
+
}
|
|
399
|
+
clear() {
|
|
400
|
+
this.solidIdx = new Float64Array(0);
|
|
401
|
+
this.solidCountValue = 0;
|
|
402
|
+
this.solidCap = 0;
|
|
403
|
+
this.mixedIdx = new Float64Array(0);
|
|
404
|
+
this.mixedMasks = new Uint32Array(0);
|
|
405
|
+
this.mixedCountValue = 0;
|
|
406
|
+
this.mixedCap = 0;
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
const BLOCK_EMPTY = 0;
|
|
410
|
+
const BLOCK_SOLID = 1;
|
|
411
|
+
const BLOCK_MIXED = 2;
|
|
412
|
+
const TYPE_MASK = 0x3;
|
|
413
|
+
const BLOCKS_PER_WORD = 16;
|
|
414
|
+
const EVEN_BITS = 0x55555555 >>> 0;
|
|
415
|
+
function getBlockTypeWordIndex(blockIdx) {
|
|
416
|
+
return Math.floor(blockIdx / BLOCKS_PER_WORD);
|
|
417
|
+
}
|
|
418
|
+
function getBlockTypeBitShift(blockIdx) {
|
|
419
|
+
return (blockIdx % BLOCKS_PER_WORD) << 1;
|
|
420
|
+
}
|
|
421
|
+
function getBlockTypeWordCount(totalBlocks) {
|
|
422
|
+
return Math.ceil(totalBlocks / BLOCKS_PER_WORD);
|
|
423
|
+
}
|
|
424
|
+
function readBlockType(types, blockIdx) {
|
|
425
|
+
const word = getBlockTypeWordIndex(blockIdx);
|
|
426
|
+
const shift = getBlockTypeBitShift(blockIdx);
|
|
427
|
+
return (types[word] >>> shift) & TYPE_MASK;
|
|
428
|
+
}
|
|
429
|
+
function writeBlockType(types, blockIdx, blockType) {
|
|
430
|
+
const word = getBlockTypeWordIndex(blockIdx);
|
|
431
|
+
const shift = getBlockTypeBitShift(blockIdx);
|
|
432
|
+
const mask = TYPE_MASK << shift;
|
|
433
|
+
types[word] = (types[word] & ~mask) | ((blockType & TYPE_MASK) << shift);
|
|
434
|
+
}
|
|
435
|
+
const EMPTY = -1;
|
|
436
|
+
class BlockMaskMap {
|
|
437
|
+
constructor(initialCapacity = 4096) {
|
|
438
|
+
const cap = 1 << (32 - Math.clz32(Math.max(15, initialCapacity - 1)));
|
|
439
|
+
this._capacity = cap;
|
|
440
|
+
this._mask = cap - 1;
|
|
441
|
+
this._size = 0;
|
|
442
|
+
this.keys = new Int32Array(cap).fill(EMPTY);
|
|
443
|
+
this.lo = new Uint32Array(cap);
|
|
444
|
+
this.hi = new Uint32Array(cap);
|
|
445
|
+
}
|
|
446
|
+
slot(key) {
|
|
447
|
+
const mask = this._mask;
|
|
448
|
+
let i = (Math.imul(key, 0x9e3779b9) >>> 0) & mask;
|
|
449
|
+
while (true) {
|
|
450
|
+
const k = this.keys[i];
|
|
451
|
+
if (k === key || k === EMPTY) {
|
|
452
|
+
return i;
|
|
453
|
+
}
|
|
454
|
+
i = (i + 1) & mask;
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
set(key, loVal, hiVal) {
|
|
458
|
+
let s = this.slot(key);
|
|
459
|
+
if (this.keys[s] === EMPTY) {
|
|
460
|
+
this.keys[s] = key;
|
|
461
|
+
this._size++;
|
|
462
|
+
if (this._size > ((this._capacity * 0.7) | 0)) {
|
|
463
|
+
this._grow();
|
|
464
|
+
s = this.slot(key);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
this.lo[s] = loVal;
|
|
468
|
+
this.hi[s] = hiVal;
|
|
469
|
+
}
|
|
470
|
+
removeAt(slot) {
|
|
471
|
+
this._size--;
|
|
472
|
+
const mask = this._mask;
|
|
473
|
+
let i = slot;
|
|
474
|
+
let j = slot;
|
|
475
|
+
while (true) {
|
|
476
|
+
j = (j + 1) & mask;
|
|
477
|
+
if (this.keys[j] === EMPTY) {
|
|
478
|
+
break;
|
|
479
|
+
}
|
|
480
|
+
const k = (Math.imul(this.keys[j], 0x9e3779b9) >>> 0) & mask;
|
|
481
|
+
if (i < j ? k <= i || k > j : k <= i && k > j) {
|
|
482
|
+
this.keys[i] = this.keys[j];
|
|
483
|
+
this.lo[i] = this.lo[j];
|
|
484
|
+
this.hi[i] = this.hi[j];
|
|
485
|
+
i = j;
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
this.keys[i] = EMPTY;
|
|
489
|
+
}
|
|
490
|
+
clear() {
|
|
491
|
+
this.keys.fill(EMPTY);
|
|
492
|
+
this._size = 0;
|
|
493
|
+
}
|
|
494
|
+
get size() {
|
|
495
|
+
return this._size;
|
|
496
|
+
}
|
|
497
|
+
releaseStorage() {
|
|
498
|
+
this.keys = new Int32Array(0);
|
|
499
|
+
this.lo = new Uint32Array(0);
|
|
500
|
+
this.hi = new Uint32Array(0);
|
|
501
|
+
this._size = 0;
|
|
502
|
+
this._capacity = 0;
|
|
503
|
+
this._mask = 0;
|
|
504
|
+
}
|
|
505
|
+
clone() {
|
|
506
|
+
const c = new BlockMaskMap(this._capacity);
|
|
507
|
+
c.keys.set(this.keys);
|
|
508
|
+
c.lo.set(this.lo);
|
|
509
|
+
c.hi.set(this.hi);
|
|
510
|
+
c._size = this._size;
|
|
511
|
+
return c;
|
|
512
|
+
}
|
|
513
|
+
_grow() {
|
|
514
|
+
const oldKeys = this.keys;
|
|
515
|
+
const oldLo = this.lo;
|
|
516
|
+
const oldHi = this.hi;
|
|
517
|
+
const oldCap = this._capacity;
|
|
518
|
+
this._capacity *= 2;
|
|
519
|
+
this._mask = this._capacity - 1;
|
|
520
|
+
this.keys = new Int32Array(this._capacity).fill(EMPTY);
|
|
521
|
+
this.lo = new Uint32Array(this._capacity);
|
|
522
|
+
this.hi = new Uint32Array(this._capacity);
|
|
523
|
+
this._size = 0;
|
|
524
|
+
for (let i = 0; i < oldCap; i++) {
|
|
525
|
+
if (oldKeys[i] !== EMPTY) {
|
|
526
|
+
const s = this.slot(oldKeys[i]);
|
|
527
|
+
this.keys[s] = oldKeys[i];
|
|
528
|
+
this.lo[s] = oldLo[i];
|
|
529
|
+
this.hi[s] = oldHi[i];
|
|
530
|
+
this._size++;
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
class SparseVoxelGrid {
|
|
536
|
+
constructor(nx, ny, nz) {
|
|
537
|
+
this.nx = nx;
|
|
538
|
+
this.ny = ny;
|
|
539
|
+
this.nz = nz;
|
|
540
|
+
this.nbx = nx >> 2;
|
|
541
|
+
this.nby = ny >> 2;
|
|
542
|
+
this.nbz = nz >> 2;
|
|
543
|
+
this.bStride = this.nbx * this.nby;
|
|
544
|
+
const totalBlocks = this.nbx * this.nby * this.nbz;
|
|
545
|
+
this.types = new Uint32Array(getBlockTypeWordCount(totalBlocks));
|
|
546
|
+
this.masks = new BlockMaskMap();
|
|
547
|
+
}
|
|
548
|
+
getVoxel(ix, iy, iz) {
|
|
549
|
+
const blockIdx = (ix >> 2) + (iy >> 2) * this.nbx + (iz >> 2) * this.bStride;
|
|
550
|
+
const bt = readBlockType(this.types, blockIdx);
|
|
551
|
+
if (bt === BLOCK_EMPTY) {
|
|
552
|
+
return 0;
|
|
553
|
+
}
|
|
554
|
+
if (bt === BLOCK_SOLID) {
|
|
555
|
+
return 1;
|
|
556
|
+
}
|
|
557
|
+
const s = this.masks.slot(blockIdx);
|
|
558
|
+
const bitIdx = (ix & 3) + ((iy & 3) << 2) + ((iz & 3) << 4);
|
|
559
|
+
return bitIdx < 32 ? (this.masks.lo[s] >>> bitIdx) & 1 : (this.masks.hi[s] >>> (bitIdx - 32)) & 1;
|
|
560
|
+
}
|
|
561
|
+
setVoxel(ix, iy, iz) {
|
|
562
|
+
const blockIdx = (ix >> 2) + (iy >> 2) * this.nbx + (iz >> 2) * this.bStride;
|
|
563
|
+
const bt = readBlockType(this.types, blockIdx);
|
|
564
|
+
if (bt === BLOCK_SOLID) {
|
|
565
|
+
return;
|
|
566
|
+
}
|
|
567
|
+
const bitIdx = (ix & 3) + ((iy & 3) << 2) + ((iz & 3) << 4);
|
|
568
|
+
if (bt === BLOCK_MIXED) {
|
|
569
|
+
const s = this.masks.slot(blockIdx);
|
|
570
|
+
if (bitIdx < 32) {
|
|
571
|
+
this.masks.lo[s] = (this.masks.lo[s] | (1 << bitIdx)) >>> 0;
|
|
572
|
+
}
|
|
573
|
+
else {
|
|
574
|
+
this.masks.hi[s] = (this.masks.hi[s] | (1 << (bitIdx - 32))) >>> 0;
|
|
575
|
+
}
|
|
576
|
+
if (this.masks.lo[s] === SOLID_LO && this.masks.hi[s] === SOLID_HI) {
|
|
577
|
+
this.masks.removeAt(s);
|
|
578
|
+
writeBlockType(this.types, blockIdx, BLOCK_SOLID);
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
else {
|
|
582
|
+
writeBlockType(this.types, blockIdx, BLOCK_MIXED);
|
|
583
|
+
this.masks.set(blockIdx, bitIdx < 32 ? (1 << bitIdx) >>> 0 : 0, bitIdx >= 32 ? (1 << (bitIdx - 32)) >>> 0 : 0);
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
orBlock(blockIdx, lo, hi) {
|
|
587
|
+
if (lo === 0 && hi === 0) {
|
|
588
|
+
return;
|
|
589
|
+
}
|
|
590
|
+
const bt = readBlockType(this.types, blockIdx);
|
|
591
|
+
if (bt === BLOCK_SOLID) {
|
|
592
|
+
return;
|
|
593
|
+
}
|
|
594
|
+
if (bt === BLOCK_MIXED) {
|
|
595
|
+
const s = this.masks.slot(blockIdx);
|
|
596
|
+
this.masks.lo[s] = (this.masks.lo[s] | lo) >>> 0;
|
|
597
|
+
this.masks.hi[s] = (this.masks.hi[s] | hi) >>> 0;
|
|
598
|
+
if (this.masks.lo[s] === SOLID_LO && this.masks.hi[s] === SOLID_HI) {
|
|
599
|
+
this.masks.removeAt(s);
|
|
600
|
+
writeBlockType(this.types, blockIdx, BLOCK_SOLID);
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
else {
|
|
604
|
+
if (lo >>> 0 === SOLID_LO && hi >>> 0 === SOLID_HI) {
|
|
605
|
+
writeBlockType(this.types, blockIdx, BLOCK_SOLID);
|
|
606
|
+
}
|
|
607
|
+
else {
|
|
608
|
+
writeBlockType(this.types, blockIdx, BLOCK_MIXED);
|
|
609
|
+
this.masks.set(blockIdx, lo >>> 0, hi >>> 0);
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
clear() {
|
|
614
|
+
this.types.fill(0);
|
|
615
|
+
this.masks.clear();
|
|
616
|
+
}
|
|
617
|
+
releaseStorage() {
|
|
618
|
+
this.types = new Uint32Array(0);
|
|
619
|
+
this.masks.releaseStorage();
|
|
620
|
+
}
|
|
621
|
+
clone() {
|
|
622
|
+
const g = new SparseVoxelGrid(this.nx, this.ny, this.nz);
|
|
623
|
+
g.types.set(this.types);
|
|
624
|
+
g.masks = this.masks.clone();
|
|
625
|
+
return g;
|
|
626
|
+
}
|
|
627
|
+
cropTo(cropMinBx, cropMinBy, cropMinBz, cropMaxBx, cropMaxBy, cropMaxBz, onProgress) {
|
|
628
|
+
const outNbx = cropMaxBx - cropMinBx;
|
|
629
|
+
const outNby = cropMaxBy - cropMinBy;
|
|
630
|
+
const outNbz = cropMaxBz - cropMinBz;
|
|
631
|
+
const out = new SparseVoxelGrid(outNbx * 4, outNby * 4, outNbz * 4);
|
|
632
|
+
const outBStride = outNbx * outNby;
|
|
633
|
+
const { nbx, nby } = this;
|
|
634
|
+
const totalBlocks = nbx * nby * this.nbz;
|
|
635
|
+
const types = this.types;
|
|
636
|
+
const masks = this.masks;
|
|
637
|
+
const outTypes = out.types;
|
|
638
|
+
const outMasks = out.masks;
|
|
639
|
+
if (out.nbx * out.nby * out.nbz === 0) {
|
|
640
|
+
if (onProgress) {
|
|
641
|
+
onProgress(0, 0);
|
|
642
|
+
}
|
|
643
|
+
return out;
|
|
644
|
+
}
|
|
645
|
+
const PROGRESS_INTERVAL = 1 << 13;
|
|
646
|
+
let nextTick = PROGRESS_INTERVAL;
|
|
647
|
+
for (let w = 0; w < types.length; w++) {
|
|
648
|
+
if (onProgress && w >= nextTick) {
|
|
649
|
+
onProgress(w, types.length);
|
|
650
|
+
nextTick = w + PROGRESS_INTERVAL;
|
|
651
|
+
}
|
|
652
|
+
const word = types[w];
|
|
653
|
+
if (word === 0) {
|
|
654
|
+
continue;
|
|
655
|
+
}
|
|
656
|
+
let nonEmpty = ((word & EVEN_BITS) | ((word >>> 1) & EVEN_BITS)) >>> 0;
|
|
657
|
+
const baseIdx = w * BLOCKS_PER_WORD;
|
|
658
|
+
let bx = baseIdx % nbx;
|
|
659
|
+
const byBz = (baseIdx / nbx) | 0;
|
|
660
|
+
let by = byBz % nby;
|
|
661
|
+
let bz = (byBz / nby) | 0;
|
|
662
|
+
let coordLane = 0;
|
|
663
|
+
while (nonEmpty) {
|
|
664
|
+
const bp = 31 - Math.clz32(nonEmpty & -nonEmpty);
|
|
665
|
+
const lane = bp >>> 1;
|
|
666
|
+
nonEmpty &= nonEmpty - 1;
|
|
667
|
+
const blockIdx = baseIdx + lane;
|
|
668
|
+
if (blockIdx >= totalBlocks) {
|
|
669
|
+
break;
|
|
670
|
+
}
|
|
671
|
+
bx += lane - coordLane;
|
|
672
|
+
coordLane = lane;
|
|
673
|
+
while (bx >= nbx) {
|
|
674
|
+
bx -= nbx;
|
|
675
|
+
by++;
|
|
676
|
+
if (by >= nby) {
|
|
677
|
+
by = 0;
|
|
678
|
+
bz++;
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
if (bx < cropMinBx ||
|
|
682
|
+
bx >= cropMaxBx ||
|
|
683
|
+
by < cropMinBy ||
|
|
684
|
+
by >= cropMaxBy ||
|
|
685
|
+
bz < cropMinBz ||
|
|
686
|
+
bz >= cropMaxBz) {
|
|
687
|
+
continue;
|
|
688
|
+
}
|
|
689
|
+
const outIdx = bx - cropMinBx + (by - cropMinBy) * outNbx + (bz - cropMinBz) * outBStride;
|
|
690
|
+
const bt = (word >>> (lane << 1)) & TYPE_MASK;
|
|
691
|
+
writeBlockType(outTypes, outIdx, bt);
|
|
692
|
+
if (bt === BLOCK_MIXED) {
|
|
693
|
+
const s = masks.slot(blockIdx);
|
|
694
|
+
outMasks.set(outIdx, masks.lo[s], masks.hi[s]);
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
if (onProgress) {
|
|
699
|
+
onProgress(types.length, types.length);
|
|
700
|
+
}
|
|
701
|
+
return out;
|
|
702
|
+
}
|
|
703
|
+
cropToInverted(cropMinBx, cropMinBy, cropMinBz, cropMaxBx, cropMaxBy, cropMaxBz, onProgress) {
|
|
704
|
+
const outNbx = cropMaxBx - cropMinBx;
|
|
705
|
+
const outNby = cropMaxBy - cropMinBy;
|
|
706
|
+
const outNbz = cropMaxBz - cropMinBz;
|
|
707
|
+
const out = new SparseVoxelGrid(outNbx * 4, outNby * 4, outNbz * 4);
|
|
708
|
+
const outBStride = outNbx * outNby;
|
|
709
|
+
const outTotalBlocks = outNbx * outNby * outNbz;
|
|
710
|
+
const outTypes = out.types;
|
|
711
|
+
const outMasks = out.masks;
|
|
712
|
+
if (outTotalBlocks === 0) {
|
|
713
|
+
if (onProgress) {
|
|
714
|
+
onProgress(0, 0);
|
|
715
|
+
}
|
|
716
|
+
return out;
|
|
717
|
+
}
|
|
718
|
+
const SOLID_WORD = 0x55555555 >>> 0;
|
|
719
|
+
outTypes.fill(SOLID_WORD);
|
|
720
|
+
const lastWord = outTypes.length - 1;
|
|
721
|
+
const lastLanes = outTotalBlocks - lastWord * BLOCKS_PER_WORD;
|
|
722
|
+
if (lastLanes < BLOCKS_PER_WORD) {
|
|
723
|
+
const validBits = (1 << (lastLanes * 2)) - 1;
|
|
724
|
+
outTypes[lastWord] = (outTypes[lastWord] & validBits) >>> 0;
|
|
725
|
+
}
|
|
726
|
+
const { nbx, nby } = this;
|
|
727
|
+
const types = this.types;
|
|
728
|
+
const masks = this.masks;
|
|
729
|
+
const totalBlocks = nbx * nby * this.nbz;
|
|
730
|
+
const PROGRESS_INTERVAL = 1 << 13;
|
|
731
|
+
let nextTick = PROGRESS_INTERVAL;
|
|
732
|
+
for (let w = 0; w < types.length; w++) {
|
|
733
|
+
if (onProgress && w >= nextTick) {
|
|
734
|
+
onProgress(w, types.length);
|
|
735
|
+
nextTick = w + PROGRESS_INTERVAL;
|
|
736
|
+
}
|
|
737
|
+
const word = types[w];
|
|
738
|
+
if (word === 0) {
|
|
739
|
+
continue;
|
|
740
|
+
}
|
|
741
|
+
let nonEmpty = ((word & EVEN_BITS) | ((word >>> 1) & EVEN_BITS)) >>> 0;
|
|
742
|
+
const baseIdx = w * BLOCKS_PER_WORD;
|
|
743
|
+
let bx = baseIdx % nbx;
|
|
744
|
+
const byBz = (baseIdx / nbx) | 0;
|
|
745
|
+
let by = byBz % nby;
|
|
746
|
+
let bz = (byBz / nby) | 0;
|
|
747
|
+
let coordLane = 0;
|
|
748
|
+
while (nonEmpty) {
|
|
749
|
+
const bp = 31 - Math.clz32(nonEmpty & -nonEmpty);
|
|
750
|
+
const lane = bp >>> 1;
|
|
751
|
+
nonEmpty &= nonEmpty - 1;
|
|
752
|
+
const blockIdx = baseIdx + lane;
|
|
753
|
+
if (blockIdx >= totalBlocks) {
|
|
754
|
+
break;
|
|
755
|
+
}
|
|
756
|
+
bx += lane - coordLane;
|
|
757
|
+
coordLane = lane;
|
|
758
|
+
while (bx >= nbx) {
|
|
759
|
+
bx -= nbx;
|
|
760
|
+
by++;
|
|
761
|
+
if (by >= nby) {
|
|
762
|
+
by = 0;
|
|
763
|
+
bz++;
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
if (bx < cropMinBx ||
|
|
767
|
+
bx >= cropMaxBx ||
|
|
768
|
+
by < cropMinBy ||
|
|
769
|
+
by >= cropMaxBy ||
|
|
770
|
+
bz < cropMinBz ||
|
|
771
|
+
bz >= cropMaxBz) {
|
|
772
|
+
continue;
|
|
773
|
+
}
|
|
774
|
+
const outIdx = bx - cropMinBx + (by - cropMinBy) * outNbx + (bz - cropMinBz) * outBStride;
|
|
775
|
+
const bt = (word >>> (lane << 1)) & TYPE_MASK;
|
|
776
|
+
if (bt === BLOCK_SOLID) {
|
|
777
|
+
writeBlockType(outTypes, outIdx, BLOCK_EMPTY);
|
|
778
|
+
}
|
|
779
|
+
else {
|
|
780
|
+
writeBlockType(outTypes, outIdx, BLOCK_MIXED);
|
|
781
|
+
const s = masks.slot(blockIdx);
|
|
782
|
+
outMasks.set(outIdx, ~masks.lo[s] >>> 0, ~masks.hi[s] >>> 0);
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
if (onProgress) {
|
|
787
|
+
onProgress(types.length, types.length);
|
|
788
|
+
}
|
|
789
|
+
return out;
|
|
790
|
+
}
|
|
791
|
+
static fromBuffer(acc, nx, ny, nz) {
|
|
792
|
+
const g = new SparseVoxelGrid(nx, ny, nz);
|
|
793
|
+
const solidBlocks = acc.getSolidBlocks();
|
|
794
|
+
const totalBlocks = g.nbx * g.nby * g.nbz;
|
|
795
|
+
for (let i = 0; i < solidBlocks.length; i++) {
|
|
796
|
+
const blockIdx = solidBlocks[i];
|
|
797
|
+
if (blockIdx < 0 || blockIdx >= totalBlocks) {
|
|
798
|
+
continue;
|
|
799
|
+
}
|
|
800
|
+
writeBlockType(g.types, blockIdx, BLOCK_SOLID);
|
|
801
|
+
}
|
|
802
|
+
const mixed = acc.getMixedBlocks();
|
|
803
|
+
for (let i = 0; i < mixed.blockIdx.length; i++) {
|
|
804
|
+
const blockIdx = mixed.blockIdx[i];
|
|
805
|
+
if (blockIdx < 0 || blockIdx >= totalBlocks) {
|
|
806
|
+
continue;
|
|
807
|
+
}
|
|
808
|
+
if (readBlockType(g.types, blockIdx) === BLOCK_SOLID) {
|
|
809
|
+
continue;
|
|
810
|
+
}
|
|
811
|
+
writeBlockType(g.types, blockIdx, BLOCK_MIXED);
|
|
812
|
+
g.masks.set(blockIdx, mixed.masks[i * 2], mixed.masks[i * 2 + 1]);
|
|
813
|
+
}
|
|
814
|
+
return g;
|
|
815
|
+
}
|
|
816
|
+
toBuffer(cropMinBx, cropMinBy, cropMinBz, cropMaxBx, cropMaxBy, cropMaxBz, defaultSolid = false) {
|
|
817
|
+
const out = new BlockMaskBuffer();
|
|
818
|
+
for (let bz = cropMinBz; bz < cropMaxBz; bz++) {
|
|
819
|
+
for (let by = cropMinBy; by < cropMaxBy; by++) {
|
|
820
|
+
for (let bx = cropMinBx; bx < cropMaxBx; bx++) {
|
|
821
|
+
const blockIdx = bx + by * this.nbx + bz * this.bStride;
|
|
822
|
+
const bt = readBlockType(this.types, blockIdx);
|
|
823
|
+
let lo;
|
|
824
|
+
let hi;
|
|
825
|
+
if (bt === BLOCK_SOLID) {
|
|
826
|
+
lo = SOLID_LO;
|
|
827
|
+
hi = SOLID_HI;
|
|
828
|
+
}
|
|
829
|
+
else if (bt === BLOCK_MIXED) {
|
|
830
|
+
const s = this.masks.slot(blockIdx);
|
|
831
|
+
lo = this.masks.lo[s];
|
|
832
|
+
hi = this.masks.hi[s];
|
|
833
|
+
}
|
|
834
|
+
else if (defaultSolid) {
|
|
835
|
+
lo = SOLID_LO;
|
|
836
|
+
hi = SOLID_HI;
|
|
837
|
+
}
|
|
838
|
+
else {
|
|
839
|
+
continue;
|
|
840
|
+
}
|
|
841
|
+
if ((lo | hi) !== 0) {
|
|
842
|
+
const outNbx = cropMaxBx - cropMinBx;
|
|
843
|
+
const outNby = cropMaxBy - cropMinBy;
|
|
844
|
+
out.addBlock(bx - cropMinBx + (by - cropMinBy) * outNbx + (bz - cropMinBz) * outNbx * outNby, lo, hi);
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
return out;
|
|
850
|
+
}
|
|
851
|
+
toBufferInverted(cropMinBx, cropMinBy, cropMinBz, cropMaxBx, cropMaxBy, cropMaxBz) {
|
|
852
|
+
const out = new BlockMaskBuffer();
|
|
853
|
+
for (let bz = cropMinBz; bz < cropMaxBz; bz++) {
|
|
854
|
+
for (let by = cropMinBy; by < cropMaxBy; by++) {
|
|
855
|
+
for (let bx = cropMinBx; bx < cropMaxBx; bx++) {
|
|
856
|
+
const blockIdx = bx + by * this.nbx + bz * this.bStride;
|
|
857
|
+
const bt = readBlockType(this.types, blockIdx);
|
|
858
|
+
let lo;
|
|
859
|
+
let hi;
|
|
860
|
+
if (bt === BLOCK_SOLID) {
|
|
861
|
+
continue;
|
|
862
|
+
}
|
|
863
|
+
if (bt === BLOCK_MIXED) {
|
|
864
|
+
const s = this.masks.slot(blockIdx);
|
|
865
|
+
lo = ~this.masks.lo[s] >>> 0;
|
|
866
|
+
hi = ~this.masks.hi[s] >>> 0;
|
|
867
|
+
}
|
|
868
|
+
else {
|
|
869
|
+
lo = SOLID_LO;
|
|
870
|
+
hi = SOLID_HI;
|
|
871
|
+
}
|
|
872
|
+
if ((lo | hi) !== 0) {
|
|
873
|
+
const outNbx = cropMaxBx - cropMinBx;
|
|
874
|
+
const outNby = cropMaxBy - cropMinBy;
|
|
875
|
+
out.addBlock(bx - cropMinBx + (by - cropMinBy) * outNbx + (bz - cropMinBz) * outNbx * outNby, lo, hi);
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
return out;
|
|
881
|
+
}
|
|
882
|
+
getOccupiedBlockBounds(onProgress) {
|
|
883
|
+
const { nbx, nby } = this;
|
|
884
|
+
const totalBlocks = nbx * nby * this.nbz;
|
|
885
|
+
let minBx = nbx, minBy = nby, minBz = this.nbz;
|
|
886
|
+
let maxBx = 0, maxBy = 0, maxBz = 0;
|
|
887
|
+
let found = false;
|
|
888
|
+
const PROGRESS_INTERVAL = 1 << 13;
|
|
889
|
+
let nextTick = PROGRESS_INTERVAL;
|
|
890
|
+
for (let w = 0; w < this.types.length; w++) {
|
|
891
|
+
if (onProgress && w >= nextTick) {
|
|
892
|
+
onProgress(w, this.types.length);
|
|
893
|
+
nextTick = w + PROGRESS_INTERVAL;
|
|
894
|
+
}
|
|
895
|
+
const word = this.types[w];
|
|
896
|
+
if (word === 0) {
|
|
897
|
+
continue;
|
|
898
|
+
}
|
|
899
|
+
let nonEmpty = ((word & EVEN_BITS) | ((word >>> 1) & EVEN_BITS)) >>> 0;
|
|
900
|
+
const baseIdx = w * BLOCKS_PER_WORD;
|
|
901
|
+
let bx = baseIdx % nbx;
|
|
902
|
+
const byBz = (baseIdx / nbx) | 0;
|
|
903
|
+
let by = byBz % nby;
|
|
904
|
+
let bz = (byBz / nby) | 0;
|
|
905
|
+
let coordLane = 0;
|
|
906
|
+
while (nonEmpty) {
|
|
907
|
+
const bitPos = 31 - Math.clz32(nonEmpty & -nonEmpty);
|
|
908
|
+
const lane = bitPos >>> 1;
|
|
909
|
+
const blockIdx = baseIdx + lane;
|
|
910
|
+
if (blockIdx >= totalBlocks) {
|
|
911
|
+
nonEmpty = 0;
|
|
912
|
+
break;
|
|
913
|
+
}
|
|
914
|
+
bx += lane - coordLane;
|
|
915
|
+
coordLane = lane;
|
|
916
|
+
while (bx >= nbx) {
|
|
917
|
+
bx -= nbx;
|
|
918
|
+
by++;
|
|
919
|
+
if (by >= nby) {
|
|
920
|
+
by = 0;
|
|
921
|
+
bz++;
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
if (bx < minBx) {
|
|
925
|
+
minBx = bx;
|
|
926
|
+
}
|
|
927
|
+
if (bx > maxBx) {
|
|
928
|
+
maxBx = bx;
|
|
929
|
+
}
|
|
930
|
+
if (by < minBy) {
|
|
931
|
+
minBy = by;
|
|
932
|
+
}
|
|
933
|
+
if (by > maxBy) {
|
|
934
|
+
maxBy = by;
|
|
935
|
+
}
|
|
936
|
+
if (bz < minBz) {
|
|
937
|
+
minBz = bz;
|
|
938
|
+
}
|
|
939
|
+
if (bz > maxBz) {
|
|
940
|
+
maxBz = bz;
|
|
941
|
+
}
|
|
942
|
+
found = true;
|
|
943
|
+
nonEmpty &= nonEmpty - 1;
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
if (onProgress) {
|
|
947
|
+
onProgress(this.types.length, this.types.length);
|
|
948
|
+
}
|
|
949
|
+
return found ? { minBx, minBy, minBz, maxBx, maxBy, maxBz } : null;
|
|
950
|
+
}
|
|
951
|
+
getNavigableBlockBounds(onProgress) {
|
|
952
|
+
const { nbx, nby } = this;
|
|
953
|
+
const totalBlocks = nbx * nby * this.nbz;
|
|
954
|
+
if (totalBlocks === 0) {
|
|
955
|
+
if (onProgress) {
|
|
956
|
+
onProgress(0, 0);
|
|
957
|
+
}
|
|
958
|
+
return null;
|
|
959
|
+
}
|
|
960
|
+
const SOLID_WORD = 0x55555555 >>> 0;
|
|
961
|
+
const lastWordIdx = this.types.length - 1;
|
|
962
|
+
const lastLanes = totalBlocks - lastWordIdx * BLOCKS_PER_WORD;
|
|
963
|
+
const lastNonEmptyMask = lastLanes >= BLOCKS_PER_WORD ? EVEN_BITS : (((1 << (lastLanes * 2)) - 1) >>> 0) & EVEN_BITS;
|
|
964
|
+
let minBx = nbx, minBy = nby, minBz = this.nbz;
|
|
965
|
+
let maxBx = -1, maxBy = 0, maxBz = 0;
|
|
966
|
+
const PROGRESS_INTERVAL = 1 << 13;
|
|
967
|
+
let nextTick = PROGRESS_INTERVAL;
|
|
968
|
+
for (let w = 0; w < this.types.length; w++) {
|
|
969
|
+
if (onProgress && w >= nextTick) {
|
|
970
|
+
onProgress(w, this.types.length);
|
|
971
|
+
nextTick = w + PROGRESS_INTERVAL;
|
|
972
|
+
}
|
|
973
|
+
const baseIdx = w * BLOCKS_PER_WORD;
|
|
974
|
+
const word = this.types[w];
|
|
975
|
+
const flipped = (word ^ SOLID_WORD) >>> 0;
|
|
976
|
+
let navMask = ((flipped & EVEN_BITS) | ((flipped >>> 1) & EVEN_BITS)) >>> 0;
|
|
977
|
+
if (w === lastWordIdx) {
|
|
978
|
+
navMask &= lastNonEmptyMask;
|
|
979
|
+
}
|
|
980
|
+
let bx = baseIdx % nbx;
|
|
981
|
+
const byBz = (baseIdx / nbx) | 0;
|
|
982
|
+
let by = byBz % nby;
|
|
983
|
+
let bz = (byBz / nby) | 0;
|
|
984
|
+
let coordLane = 0;
|
|
985
|
+
while (navMask) {
|
|
986
|
+
const bp = 31 - Math.clz32(navMask & -navMask);
|
|
987
|
+
const lane = bp >>> 1;
|
|
988
|
+
bx += lane - coordLane;
|
|
989
|
+
coordLane = lane;
|
|
990
|
+
while (bx >= nbx) {
|
|
991
|
+
bx -= nbx;
|
|
992
|
+
by++;
|
|
993
|
+
if (by >= nby) {
|
|
994
|
+
by = 0;
|
|
995
|
+
bz++;
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
if (bx < minBx) {
|
|
999
|
+
minBx = bx;
|
|
1000
|
+
}
|
|
1001
|
+
if (bx > maxBx) {
|
|
1002
|
+
maxBx = bx;
|
|
1003
|
+
}
|
|
1004
|
+
if (by < minBy) {
|
|
1005
|
+
minBy = by;
|
|
1006
|
+
}
|
|
1007
|
+
if (by > maxBy) {
|
|
1008
|
+
maxBy = by;
|
|
1009
|
+
}
|
|
1010
|
+
if (bz < minBz) {
|
|
1011
|
+
minBz = bz;
|
|
1012
|
+
}
|
|
1013
|
+
if (bz > maxBz) {
|
|
1014
|
+
maxBz = bz;
|
|
1015
|
+
}
|
|
1016
|
+
navMask &= navMask - 1;
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
if (onProgress) {
|
|
1020
|
+
onProgress(this.types.length, this.types.length);
|
|
1021
|
+
}
|
|
1022
|
+
return maxBx >= 0 ? { minBx, minBy, minBz, maxBx, maxBy, maxBz } : null;
|
|
1023
|
+
}
|
|
1024
|
+
static findNearestFreeCell(blocked, seedIx, seedIy, seedIz, maxRadius) {
|
|
1025
|
+
const { nx, ny, nz } = blocked;
|
|
1026
|
+
for (let r = 1; r <= maxRadius; r++) {
|
|
1027
|
+
for (let dz = -r; dz <= r; dz++) {
|
|
1028
|
+
for (let dy = -r; dy <= r; dy++) {
|
|
1029
|
+
for (let dx = -r; dx <= r; dx++) {
|
|
1030
|
+
if (Math.abs(dx) !== r && Math.abs(dy) !== r && Math.abs(dz) !== r) {
|
|
1031
|
+
continue;
|
|
1032
|
+
}
|
|
1033
|
+
const ix = seedIx + dx;
|
|
1034
|
+
const iy = seedIy + dy;
|
|
1035
|
+
const iz = seedIz + dz;
|
|
1036
|
+
if (ix < 0 || ix >= nx || iy < 0 || iy >= ny || iz < 0 || iz >= nz) {
|
|
1037
|
+
continue;
|
|
1038
|
+
}
|
|
1039
|
+
if (!blocked.getVoxel(ix, iy, iz)) {
|
|
1040
|
+
return { ix, iy, iz };
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
return null;
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
export const SOLID_LEAF_MARKER = 0xff000000 >>> 0;
|
|
1050
|
+
export const MAX_24BIT_OFFSET = 0x00ffffff;
|
|
1051
|
+
const DENSE_SOLID_STREAM_THRESHOLD = 8_000_000;
|
|
1052
|
+
export class SparseOctree24BitOverflowError extends Error {
|
|
1053
|
+
constructor(kind, actual, limit) {
|
|
1054
|
+
super(`Sparse octree ${kind} count (${actual}) exceeds the Laine-Karras 24-bit baseOffset limit (${limit}). Reduce the grid size or split the scene.`);
|
|
1055
|
+
this.name = 'SparseOctree24BitOverflowError';
|
|
1056
|
+
this.kind = kind;
|
|
1057
|
+
this.actual = actual;
|
|
1058
|
+
this.limit = limit;
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
export function getChildOffset(mask, octant) {
|
|
1062
|
+
const prefix = mask & ((1 << octant) - 1);
|
|
1063
|
+
let n = prefix >>> 0;
|
|
1064
|
+
n -= (n >>> 1) & 0x55555555;
|
|
1065
|
+
n = (n & 0x33333333) + ((n >>> 2) & 0x33333333);
|
|
1066
|
+
return (((n + (n >>> 4)) & 0x0f0f0f0f) * 0x01010101) >>> 24;
|
|
1067
|
+
}
|
|
1068
|
+
function bitCount(n) {
|
|
1069
|
+
let v = n >>> 0;
|
|
1070
|
+
v -= (v >>> 1) & 0x55555555;
|
|
1071
|
+
v = (v & 0x33333333) + ((v >>> 2) & 0x33333333);
|
|
1072
|
+
return (((v + (v >>> 4)) & 0x0f0f0f0f) * 0x01010101) >>> 24;
|
|
1073
|
+
}
|
|
1074
|
+
function sortMixedByMorton(mortons, masks, n = mortons.length) {
|
|
1075
|
+
if (n <= 1) {
|
|
1076
|
+
return;
|
|
1077
|
+
}
|
|
1078
|
+
const stackLo = [0];
|
|
1079
|
+
const stackHi = [n - 1];
|
|
1080
|
+
function swap(a, b) {
|
|
1081
|
+
const km = mortons[a];
|
|
1082
|
+
mortons[a] = mortons[b];
|
|
1083
|
+
mortons[b] = km;
|
|
1084
|
+
const alo = masks[a * 2];
|
|
1085
|
+
const ahi = masks[a * 2 + 1];
|
|
1086
|
+
masks[a * 2] = masks[b * 2];
|
|
1087
|
+
masks[a * 2 + 1] = masks[b * 2 + 1];
|
|
1088
|
+
masks[b * 2] = alo;
|
|
1089
|
+
masks[b * 2 + 1] = ahi;
|
|
1090
|
+
}
|
|
1091
|
+
while (stackLo.length > 0) {
|
|
1092
|
+
const lo = stackLo.pop();
|
|
1093
|
+
const hi = stackHi.pop();
|
|
1094
|
+
if (hi - lo < 16) {
|
|
1095
|
+
for (let i = lo + 1; i <= hi; i++) {
|
|
1096
|
+
const km = mortons[i];
|
|
1097
|
+
const m0 = masks[i * 2];
|
|
1098
|
+
const m1 = masks[i * 2 + 1];
|
|
1099
|
+
let j = i - 1;
|
|
1100
|
+
while (j >= lo && mortons[j] > km) {
|
|
1101
|
+
mortons[j + 1] = mortons[j];
|
|
1102
|
+
masks[(j + 1) * 2] = masks[j * 2];
|
|
1103
|
+
masks[(j + 1) * 2 + 1] = masks[j * 2 + 1];
|
|
1104
|
+
j--;
|
|
1105
|
+
}
|
|
1106
|
+
mortons[j + 1] = km;
|
|
1107
|
+
masks[(j + 1) * 2] = m0;
|
|
1108
|
+
masks[(j + 1) * 2 + 1] = m1;
|
|
1109
|
+
}
|
|
1110
|
+
continue;
|
|
1111
|
+
}
|
|
1112
|
+
const mid = (lo + hi) >>> 1;
|
|
1113
|
+
if (mortons[mid] < mortons[lo]) {
|
|
1114
|
+
swap(mid, lo);
|
|
1115
|
+
}
|
|
1116
|
+
if (mortons[hi] < mortons[lo]) {
|
|
1117
|
+
swap(hi, lo);
|
|
1118
|
+
}
|
|
1119
|
+
if (mortons[hi] < mortons[mid]) {
|
|
1120
|
+
swap(hi, mid);
|
|
1121
|
+
}
|
|
1122
|
+
const pivot = mortons[mid];
|
|
1123
|
+
let i = lo;
|
|
1124
|
+
let j = hi;
|
|
1125
|
+
while (i <= j) {
|
|
1126
|
+
while (mortons[i] < pivot) {
|
|
1127
|
+
i++;
|
|
1128
|
+
}
|
|
1129
|
+
while (mortons[j] > pivot) {
|
|
1130
|
+
j--;
|
|
1131
|
+
}
|
|
1132
|
+
if (i <= j) {
|
|
1133
|
+
if (i !== j) {
|
|
1134
|
+
swap(i, j);
|
|
1135
|
+
}
|
|
1136
|
+
i++;
|
|
1137
|
+
j--;
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
if (j - lo > hi - i) {
|
|
1141
|
+
if (lo < j) {
|
|
1142
|
+
stackLo.push(lo);
|
|
1143
|
+
stackHi.push(j);
|
|
1144
|
+
}
|
|
1145
|
+
if (i < hi) {
|
|
1146
|
+
stackLo.push(i);
|
|
1147
|
+
stackHi.push(hi);
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
else {
|
|
1151
|
+
if (i < hi) {
|
|
1152
|
+
stackLo.push(i);
|
|
1153
|
+
stackHi.push(hi);
|
|
1154
|
+
}
|
|
1155
|
+
if (lo < j) {
|
|
1156
|
+
stackLo.push(lo);
|
|
1157
|
+
stackHi.push(j);
|
|
1158
|
+
}
|
|
1159
|
+
}
|
|
1160
|
+
}
|
|
1161
|
+
}
|
|
1162
|
+
var OctreeNodeType;
|
|
1163
|
+
(function (OctreeNodeType) {
|
|
1164
|
+
OctreeNodeType[OctreeNodeType["Empty"] = 0] = "Empty";
|
|
1165
|
+
OctreeNodeType[OctreeNodeType["Solid"] = 1] = "Solid";
|
|
1166
|
+
OctreeNodeType[OctreeNodeType["Mixed"] = 2] = "Mixed";
|
|
1167
|
+
})(OctreeNodeType || (OctreeNodeType = {}));
|
|
1168
|
+
function createInteriorWave(initialCapacity) {
|
|
1169
|
+
const cap = Math.max(16, initialCapacity);
|
|
1170
|
+
return { pos: new Uint32Array(cap), li: new Uint32Array(cap), ii: new Uint32Array(cap), length: 0 };
|
|
1171
|
+
}
|
|
1172
|
+
function pushInteriorWave(wave, pos, li, ii) {
|
|
1173
|
+
if (wave.length === wave.pos.length) {
|
|
1174
|
+
const cap = wave.pos.length * 2;
|
|
1175
|
+
const grownPos = new Uint32Array(cap);
|
|
1176
|
+
const grownLi = new Uint32Array(cap);
|
|
1177
|
+
const grownIi = new Uint32Array(cap);
|
|
1178
|
+
grownPos.set(wave.pos);
|
|
1179
|
+
grownLi.set(wave.li);
|
|
1180
|
+
grownIi.set(wave.ii);
|
|
1181
|
+
wave.pos = grownPos;
|
|
1182
|
+
wave.li = grownLi;
|
|
1183
|
+
wave.ii = grownIi;
|
|
1184
|
+
}
|
|
1185
|
+
const i = wave.length++;
|
|
1186
|
+
wave.pos[i] = pos;
|
|
1187
|
+
wave.li[i] = li;
|
|
1188
|
+
wave.ii[i] = ii;
|
|
1189
|
+
}
|
|
1190
|
+
function shouldUseDenseMipBuild(totalBlocks, nSolid, nMixed) {
|
|
1191
|
+
return nSolid >= DENSE_SOLID_STREAM_THRESHOLD && nSolid > nMixed * 4 && nSolid > totalBlocks * 0.25;
|
|
1192
|
+
}
|
|
1193
|
+
function buildDenseTypeLevels(grid, maxDepth) {
|
|
1194
|
+
const levels = [
|
|
1195
|
+
{
|
|
1196
|
+
types: grid.types,
|
|
1197
|
+
nbx: grid.nbx,
|
|
1198
|
+
nby: grid.nby,
|
|
1199
|
+
nbz: grid.nbz,
|
|
1200
|
+
nonEmptyCount: 0,
|
|
1201
|
+
},
|
|
1202
|
+
];
|
|
1203
|
+
for (let li = 1; li <= maxDepth; li++) {
|
|
1204
|
+
const prev = levels[li - 1];
|
|
1205
|
+
const nbx = Math.max(1, Math.ceil(prev.nbx / 2));
|
|
1206
|
+
const nby = Math.max(1, Math.ceil(prev.nby / 2));
|
|
1207
|
+
const nbz = Math.max(1, Math.ceil(prev.nbz / 2));
|
|
1208
|
+
const total = nbx * nby * nbz;
|
|
1209
|
+
const types = new Uint32Array(getBlockTypeWordCount(total));
|
|
1210
|
+
const prevStride = prev.nbx * prev.nby;
|
|
1211
|
+
const stride = nbx * nby;
|
|
1212
|
+
let nonEmptyCount = 0;
|
|
1213
|
+
for (let pz = 0; pz < nbz; pz++) {
|
|
1214
|
+
const childZ0 = pz << 1;
|
|
1215
|
+
for (let py = 0; py < nby; py++) {
|
|
1216
|
+
const childY0 = py << 1;
|
|
1217
|
+
for (let px = 0; px < nbx; px++) {
|
|
1218
|
+
const childX0 = px << 1;
|
|
1219
|
+
let childMask = 0;
|
|
1220
|
+
let allSolid = true;
|
|
1221
|
+
let childCount = 0;
|
|
1222
|
+
for (let oct = 0; oct < 8; oct++) {
|
|
1223
|
+
const cx = childX0 + (oct & 1);
|
|
1224
|
+
const cy = childY0 + ((oct >> 1) & 1);
|
|
1225
|
+
const cz = childZ0 + ((oct >> 2) & 1);
|
|
1226
|
+
if (cx >= prev.nbx || cy >= prev.nby || cz >= prev.nbz) {
|
|
1227
|
+
continue;
|
|
1228
|
+
}
|
|
1229
|
+
const childIdx = cx + cy * prev.nbx + cz * prevStride;
|
|
1230
|
+
const bt = readBlockType(prev.types, childIdx);
|
|
1231
|
+
if (bt === BLOCK_EMPTY) {
|
|
1232
|
+
continue;
|
|
1233
|
+
}
|
|
1234
|
+
childMask |= 1 << oct;
|
|
1235
|
+
childCount++;
|
|
1236
|
+
if (bt !== BLOCK_SOLID) {
|
|
1237
|
+
allSolid = false;
|
|
1238
|
+
}
|
|
1239
|
+
}
|
|
1240
|
+
if (childMask !== 0) {
|
|
1241
|
+
const parentIdx = px + py * nbx + pz * stride;
|
|
1242
|
+
writeBlockType(types, parentIdx, allSolid && childCount === 8 ? BLOCK_SOLID : BLOCK_MIXED);
|
|
1243
|
+
nonEmptyCount++;
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1247
|
+
}
|
|
1248
|
+
levels.push({ types, nbx, nby, nbz, nonEmptyCount });
|
|
1249
|
+
if (nonEmptyCount === 0) {
|
|
1250
|
+
break;
|
|
1251
|
+
}
|
|
1252
|
+
if (nonEmptyCount === 1 && readBlockType(types, 0) !== BLOCK_EMPTY) {
|
|
1253
|
+
break;
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
return levels;
|
|
1257
|
+
}
|
|
1258
|
+
function lowerBoundF64(arr, target, n) {
|
|
1259
|
+
let lo = 0;
|
|
1260
|
+
let hi = n;
|
|
1261
|
+
while (lo < hi) {
|
|
1262
|
+
const mid = (lo + hi) >>> 1;
|
|
1263
|
+
if (arr[mid] < target) {
|
|
1264
|
+
lo = mid + 1;
|
|
1265
|
+
}
|
|
1266
|
+
else {
|
|
1267
|
+
hi = mid;
|
|
1268
|
+
}
|
|
1269
|
+
}
|
|
1270
|
+
return lo;
|
|
1271
|
+
}
|
|
1272
|
+
function flattenTreeFromLevels(interiorLevels, solidStream, mixedStream, mixedMasks, nSolid, nMixed, gridBounds, sceneBounds, voxelResolution, treeDepth) {
|
|
1273
|
+
if (interiorLevels.length === 0) {
|
|
1274
|
+
return {
|
|
1275
|
+
gridBounds,
|
|
1276
|
+
sceneBounds,
|
|
1277
|
+
voxelResolution,
|
|
1278
|
+
leafSize: LEAF_SIZE,
|
|
1279
|
+
treeDepth,
|
|
1280
|
+
numInteriorNodes: 0,
|
|
1281
|
+
numMixedLeaves: 0,
|
|
1282
|
+
nodes: new Uint32Array(0),
|
|
1283
|
+
leafData: new Uint32Array(0),
|
|
1284
|
+
};
|
|
1285
|
+
}
|
|
1286
|
+
const rootLevel = interiorLevels[interiorLevels.length - 1];
|
|
1287
|
+
let maxNodes = nSolid + nMixed;
|
|
1288
|
+
for (let l = 0; l < interiorLevels.length; l++) {
|
|
1289
|
+
maxNodes += interiorLevels[l].mortons.length;
|
|
1290
|
+
}
|
|
1291
|
+
const nodes = new Uint32Array(maxNodes);
|
|
1292
|
+
const leafData = new Uint32Array(nMixed * 2);
|
|
1293
|
+
let leafDataLen = 0;
|
|
1294
|
+
let numInteriorNodes = 0;
|
|
1295
|
+
let numMixedLeaves = 0;
|
|
1296
|
+
let emitPos = 0;
|
|
1297
|
+
let waveLi = [];
|
|
1298
|
+
let waveIi = [];
|
|
1299
|
+
const rootLi = interiorLevels.length - 1;
|
|
1300
|
+
for (let i = 0; i < rootLevel.mortons.length; i++) {
|
|
1301
|
+
waveLi.push(rootLi);
|
|
1302
|
+
waveIi.push(i);
|
|
1303
|
+
}
|
|
1304
|
+
const intPos = [];
|
|
1305
|
+
const intLi = [];
|
|
1306
|
+
const intIi = [];
|
|
1307
|
+
const intMask = [];
|
|
1308
|
+
while (waveLi.length > 0) {
|
|
1309
|
+
intPos.length = 0;
|
|
1310
|
+
intLi.length = 0;
|
|
1311
|
+
intIi.length = 0;
|
|
1312
|
+
intMask.length = 0;
|
|
1313
|
+
for (let w = 0; w < waveLi.length; w++) {
|
|
1314
|
+
const li = waveLi[w];
|
|
1315
|
+
const ii = waveIi[w];
|
|
1316
|
+
if (li === -1) {
|
|
1317
|
+
if (ii < nMixed) {
|
|
1318
|
+
const leafDataIndex = leafDataLen >> 1;
|
|
1319
|
+
if (leafDataIndex > MAX_24BIT_OFFSET) {
|
|
1320
|
+
throw new SparseOctree24BitOverflowError('mixed-leaf', leafDataIndex + 1, MAX_24BIT_OFFSET + 1);
|
|
1321
|
+
}
|
|
1322
|
+
leafData[leafDataLen++] = mixedMasks[ii * 2];
|
|
1323
|
+
leafData[leafDataLen++] = mixedMasks[ii * 2 + 1];
|
|
1324
|
+
nodes[emitPos] = leafDataIndex;
|
|
1325
|
+
numMixedLeaves++;
|
|
1326
|
+
}
|
|
1327
|
+
else {
|
|
1328
|
+
nodes[emitPos] = SOLID_LEAF_MARKER;
|
|
1329
|
+
}
|
|
1330
|
+
emitPos++;
|
|
1331
|
+
continue;
|
|
1332
|
+
}
|
|
1333
|
+
const level = interiorLevels[li];
|
|
1334
|
+
const type = level.types[ii];
|
|
1335
|
+
if (type === OctreeNodeType.Solid) {
|
|
1336
|
+
nodes[emitPos] = SOLID_LEAF_MARKER;
|
|
1337
|
+
}
|
|
1338
|
+
else {
|
|
1339
|
+
intPos.push(emitPos);
|
|
1340
|
+
intLi.push(li);
|
|
1341
|
+
intIi.push(ii);
|
|
1342
|
+
intMask.push(level.childMasks[ii]);
|
|
1343
|
+
numInteriorNodes++;
|
|
1344
|
+
nodes[emitPos] = 0;
|
|
1345
|
+
}
|
|
1346
|
+
emitPos++;
|
|
1347
|
+
}
|
|
1348
|
+
const nextWaveLi = [];
|
|
1349
|
+
const nextWaveIi = [];
|
|
1350
|
+
let nextChildStart = emitPos;
|
|
1351
|
+
for (let j = 0; j < intPos.length; j++) {
|
|
1352
|
+
const childMask = intMask[j];
|
|
1353
|
+
const childCount = bitCount(childMask);
|
|
1354
|
+
if (nextChildStart > MAX_24BIT_OFFSET) {
|
|
1355
|
+
throw new SparseOctree24BitOverflowError('node', nextChildStart + 1, MAX_24BIT_OFFSET + 1);
|
|
1356
|
+
}
|
|
1357
|
+
nodes[intPos[j]] = ((childMask & 0xff) << 24) | nextChildStart;
|
|
1358
|
+
const myLi = intLi[j];
|
|
1359
|
+
const myMorton = interiorLevels[myLi].mortons[intIi[j]];
|
|
1360
|
+
const childMortonBase = myMorton * 8;
|
|
1361
|
+
const childMortonEnd = childMortonBase + 8;
|
|
1362
|
+
if (myLi === 0) {
|
|
1363
|
+
let sIdx = lowerBoundF64(solidStream, childMortonBase, nSolid);
|
|
1364
|
+
let mIdx = lowerBoundF64(mixedStream, childMortonBase, nMixed);
|
|
1365
|
+
while (true) {
|
|
1366
|
+
const sM = sIdx < nSolid && solidStream[sIdx] < childMortonEnd
|
|
1367
|
+
? solidStream[sIdx]
|
|
1368
|
+
: Number.POSITIVE_INFINITY;
|
|
1369
|
+
const mM = mIdx < nMixed && mixedStream[mIdx] < childMortonEnd
|
|
1370
|
+
? mixedStream[mIdx]
|
|
1371
|
+
: Number.POSITIVE_INFINITY;
|
|
1372
|
+
if (!isFinite(sM) && !isFinite(mM)) {
|
|
1373
|
+
break;
|
|
1374
|
+
}
|
|
1375
|
+
if (sM < mM) {
|
|
1376
|
+
nextWaveLi.push(-1);
|
|
1377
|
+
nextWaveIi.push(nMixed + sIdx);
|
|
1378
|
+
sIdx++;
|
|
1379
|
+
}
|
|
1380
|
+
else {
|
|
1381
|
+
nextWaveLi.push(-1);
|
|
1382
|
+
nextWaveIi.push(mIdx);
|
|
1383
|
+
mIdx++;
|
|
1384
|
+
}
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
1387
|
+
else {
|
|
1388
|
+
const childLi = myLi - 1;
|
|
1389
|
+
const childLevel = interiorLevels[childLi];
|
|
1390
|
+
const childMortons = childLevel.mortons;
|
|
1391
|
+
let lo = 0;
|
|
1392
|
+
let hi = childMortons.length;
|
|
1393
|
+
while (lo < hi) {
|
|
1394
|
+
const mid = (lo + hi) >> 1;
|
|
1395
|
+
if (childMortons[mid] < childMortonBase) {
|
|
1396
|
+
lo = mid + 1;
|
|
1397
|
+
}
|
|
1398
|
+
else {
|
|
1399
|
+
hi = mid;
|
|
1400
|
+
}
|
|
1401
|
+
}
|
|
1402
|
+
while (lo < childMortons.length && childMortons[lo] < childMortonEnd) {
|
|
1403
|
+
nextWaveLi.push(childLi);
|
|
1404
|
+
nextWaveIi.push(lo);
|
|
1405
|
+
lo++;
|
|
1406
|
+
}
|
|
1407
|
+
}
|
|
1408
|
+
nextChildStart += childCount;
|
|
1409
|
+
}
|
|
1410
|
+
waveLi = nextWaveLi;
|
|
1411
|
+
waveIi = nextWaveIi;
|
|
1412
|
+
}
|
|
1413
|
+
return {
|
|
1414
|
+
gridBounds,
|
|
1415
|
+
sceneBounds,
|
|
1416
|
+
voxelResolution,
|
|
1417
|
+
leafSize: LEAF_SIZE,
|
|
1418
|
+
treeDepth,
|
|
1419
|
+
numInteriorNodes,
|
|
1420
|
+
numMixedLeaves,
|
|
1421
|
+
nodes: emitPos === maxNodes ? nodes : nodes.slice(0, emitPos),
|
|
1422
|
+
leafData: leafDataLen === leafData.length ? leafData : leafData.slice(0, leafDataLen),
|
|
1423
|
+
};
|
|
1424
|
+
}
|
|
1425
|
+
function flattenDenseLevels(levels, grid, gridBounds, sceneBounds, voxelResolution) {
|
|
1426
|
+
const treeDepth = Math.max(1, levels.length - 1);
|
|
1427
|
+
const rootLi = levels.length - 1;
|
|
1428
|
+
const rootLevel = levels[rootLi];
|
|
1429
|
+
const rootType = readBlockType(rootLevel.types, 0);
|
|
1430
|
+
if (rootType === BLOCK_EMPTY) {
|
|
1431
|
+
return {
|
|
1432
|
+
gridBounds,
|
|
1433
|
+
sceneBounds,
|
|
1434
|
+
voxelResolution,
|
|
1435
|
+
leafSize: LEAF_SIZE,
|
|
1436
|
+
treeDepth,
|
|
1437
|
+
numInteriorNodes: 0,
|
|
1438
|
+
numMixedLeaves: 0,
|
|
1439
|
+
nodes: new Uint32Array(0),
|
|
1440
|
+
leafData: new Uint32Array(0),
|
|
1441
|
+
};
|
|
1442
|
+
}
|
|
1443
|
+
let nodes = new Uint32Array(Math.max(1024, Math.min(MAX_24BIT_OFFSET + 1, grid.masks.size * 3)));
|
|
1444
|
+
let nodeLen = 0;
|
|
1445
|
+
let leafData = new Uint32Array(Math.max(1024, grid.masks.size * 2));
|
|
1446
|
+
let leafDataLen = 0;
|
|
1447
|
+
let numInteriorNodes = 0;
|
|
1448
|
+
let numMixedLeaves = 0;
|
|
1449
|
+
function appendNode(value) {
|
|
1450
|
+
if (nodeLen === nodes.length) {
|
|
1451
|
+
const grown = new Uint32Array(nodes.length * 2);
|
|
1452
|
+
grown.set(nodes);
|
|
1453
|
+
nodes = grown;
|
|
1454
|
+
}
|
|
1455
|
+
nodes[nodeLen] = value >>> 0;
|
|
1456
|
+
return nodeLen++;
|
|
1457
|
+
}
|
|
1458
|
+
function appendMixedLeaf(blockIdx) {
|
|
1459
|
+
const leafDataIndex = leafDataLen >> 1;
|
|
1460
|
+
if (leafDataIndex > MAX_24BIT_OFFSET) {
|
|
1461
|
+
throw new SparseOctree24BitOverflowError('mixed-leaf', leafDataIndex + 1, MAX_24BIT_OFFSET + 1);
|
|
1462
|
+
}
|
|
1463
|
+
if (leafDataLen + 2 > leafData.length) {
|
|
1464
|
+
const grown = new Uint32Array(leafData.length * 2);
|
|
1465
|
+
grown.set(leafData);
|
|
1466
|
+
leafData = grown;
|
|
1467
|
+
}
|
|
1468
|
+
const s = grid.masks.slot(blockIdx);
|
|
1469
|
+
leafData[leafDataLen++] = grid.masks.lo[s];
|
|
1470
|
+
leafData[leafDataLen++] = grid.masks.hi[s];
|
|
1471
|
+
appendNode(leafDataIndex);
|
|
1472
|
+
numMixedLeaves++;
|
|
1473
|
+
}
|
|
1474
|
+
let curWave = createInteriorWave(1);
|
|
1475
|
+
let nextWave = createInteriorWave(1024);
|
|
1476
|
+
function appendDenseNode(li, idx, wave) {
|
|
1477
|
+
const level = levels[li];
|
|
1478
|
+
const bt = readBlockType(level.types, idx);
|
|
1479
|
+
if (bt === BLOCK_SOLID) {
|
|
1480
|
+
appendNode(SOLID_LEAF_MARKER);
|
|
1481
|
+
}
|
|
1482
|
+
else if (bt === BLOCK_MIXED) {
|
|
1483
|
+
const pos = appendNode(0);
|
|
1484
|
+
pushInteriorWave(wave, pos, li, idx);
|
|
1485
|
+
numInteriorNodes++;
|
|
1486
|
+
}
|
|
1487
|
+
}
|
|
1488
|
+
appendDenseNode(rootLi, 0, curWave);
|
|
1489
|
+
while (curWave.length > 0) {
|
|
1490
|
+
nextWave.length = 0;
|
|
1491
|
+
const currentLi = curWave.li[0];
|
|
1492
|
+
for (let w = 0; w < curWave.length; w++) {
|
|
1493
|
+
const li = curWave.li[w];
|
|
1494
|
+
const parentLevel = levels[li];
|
|
1495
|
+
const childLevel = levels[li - 1];
|
|
1496
|
+
const parentIdx = curWave.ii[w];
|
|
1497
|
+
const px = parentIdx % parentLevel.nbx;
|
|
1498
|
+
const pyBz = (parentIdx / parentLevel.nbx) | 0;
|
|
1499
|
+
const py = pyBz % parentLevel.nby;
|
|
1500
|
+
const pz = (pyBz / parentLevel.nby) | 0;
|
|
1501
|
+
const childX0 = px << 1;
|
|
1502
|
+
const childY0 = py << 1;
|
|
1503
|
+
const childZ0 = pz << 1;
|
|
1504
|
+
const childStride = childLevel.nbx * childLevel.nby;
|
|
1505
|
+
const childStart = nodeLen;
|
|
1506
|
+
let childMask = 0;
|
|
1507
|
+
if (childStart > MAX_24BIT_OFFSET) {
|
|
1508
|
+
throw new SparseOctree24BitOverflowError('node', childStart + 1, MAX_24BIT_OFFSET + 1);
|
|
1509
|
+
}
|
|
1510
|
+
for (let oct = 0; oct < 8; oct++) {
|
|
1511
|
+
const cx = childX0 + (oct & 1);
|
|
1512
|
+
const cy = childY0 + ((oct >> 1) & 1);
|
|
1513
|
+
const cz = childZ0 + ((oct >> 2) & 1);
|
|
1514
|
+
if (cx >= childLevel.nbx || cy >= childLevel.nby || cz >= childLevel.nbz) {
|
|
1515
|
+
continue;
|
|
1516
|
+
}
|
|
1517
|
+
const childIdx = cx + cy * childLevel.nbx + cz * childStride;
|
|
1518
|
+
const bt = readBlockType(childLevel.types, childIdx);
|
|
1519
|
+
if (bt === BLOCK_EMPTY) {
|
|
1520
|
+
continue;
|
|
1521
|
+
}
|
|
1522
|
+
childMask |= 1 << oct;
|
|
1523
|
+
if (li === 1) {
|
|
1524
|
+
if (bt === BLOCK_SOLID) {
|
|
1525
|
+
appendNode(SOLID_LEAF_MARKER);
|
|
1526
|
+
}
|
|
1527
|
+
else {
|
|
1528
|
+
appendMixedLeaf(childIdx);
|
|
1529
|
+
}
|
|
1530
|
+
}
|
|
1531
|
+
else {
|
|
1532
|
+
appendDenseNode(li - 1, childIdx, nextWave);
|
|
1533
|
+
}
|
|
1534
|
+
}
|
|
1535
|
+
nodes[curWave.pos[w]] = ((childMask & 0xff) << 24) | childStart;
|
|
1536
|
+
}
|
|
1537
|
+
levels[currentLi] = null;
|
|
1538
|
+
const tmp = curWave;
|
|
1539
|
+
curWave = nextWave;
|
|
1540
|
+
nextWave = tmp;
|
|
1541
|
+
}
|
|
1542
|
+
return {
|
|
1543
|
+
gridBounds,
|
|
1544
|
+
sceneBounds,
|
|
1545
|
+
voxelResolution,
|
|
1546
|
+
leafSize: LEAF_SIZE,
|
|
1547
|
+
treeDepth,
|
|
1548
|
+
numInteriorNodes,
|
|
1549
|
+
numMixedLeaves,
|
|
1550
|
+
nodes: nodes.slice(0, nodeLen),
|
|
1551
|
+
leafData: leafData.slice(0, leafDataLen),
|
|
1552
|
+
};
|
|
1553
|
+
}
|
|
1554
|
+
function buildSparseOctreeDense(grid, gridBounds, sceneBounds, voxelResolution, maxDepth, consumeGrid) {
|
|
1555
|
+
const levels = buildDenseTypeLevels(grid, maxDepth);
|
|
1556
|
+
const result = flattenDenseLevels(levels, grid, gridBounds, sceneBounds, voxelResolution);
|
|
1557
|
+
if (consumeGrid) {
|
|
1558
|
+
grid.releaseStorage();
|
|
1559
|
+
}
|
|
1560
|
+
return result;
|
|
1561
|
+
}
|
|
1562
|
+
/**
|
|
1563
|
+
* Build a sparse octree from block masks using:
|
|
1564
|
+
* 1) mixed+solid SoA merge and Morton sort
|
|
1565
|
+
* 2) bottom-up level construction by parent Morton grouping
|
|
1566
|
+
* 3) BFS flatten to node/leafData arrays.
|
|
1567
|
+
*/
|
|
1568
|
+
export function buildSparseOctree(grid, gridBounds, sceneBounds, voxelResolution, options = {}) {
|
|
1569
|
+
const { nbx, nby, nbz, types: gridTypes, masks: gridMasks } = grid;
|
|
1570
|
+
const totalBlocks = nbx * nby * nbz;
|
|
1571
|
+
const blocksPerAxis = Math.max(nbx, nby, nbz);
|
|
1572
|
+
const treeDepth = Math.max(1, Math.ceil(Math.log2(blocksPerAxis)));
|
|
1573
|
+
const lastWordIdx = gridTypes.length - 1;
|
|
1574
|
+
const lastLanes = totalBlocks - lastWordIdx * BLOCKS_PER_WORD;
|
|
1575
|
+
const lastValidWordMask = lastLanes >= BLOCKS_PER_WORD ? 0xffffffff >>> 0 : ((1 << (lastLanes * 2)) - 1) >>> 0;
|
|
1576
|
+
let nSolid = 0;
|
|
1577
|
+
let nMixed = 0;
|
|
1578
|
+
for (let w = 0; w < gridTypes.length; w++) {
|
|
1579
|
+
let word = gridTypes[w];
|
|
1580
|
+
if (w === lastWordIdx) {
|
|
1581
|
+
word = (word & lastValidWordMask) >>> 0;
|
|
1582
|
+
}
|
|
1583
|
+
if (word === 0) {
|
|
1584
|
+
continue;
|
|
1585
|
+
}
|
|
1586
|
+
const solidMask = word & EVEN_BITS & ~((word >>> 1) & EVEN_BITS);
|
|
1587
|
+
const mixedMask = (word >>> 1) & EVEN_BITS & ~(word & EVEN_BITS);
|
|
1588
|
+
nSolid += bitCount(solidMask >>> 0);
|
|
1589
|
+
nMixed += bitCount(mixedMask >>> 0);
|
|
1590
|
+
}
|
|
1591
|
+
if (nSolid + nMixed === 0) {
|
|
1592
|
+
return {
|
|
1593
|
+
gridBounds,
|
|
1594
|
+
sceneBounds,
|
|
1595
|
+
voxelResolution,
|
|
1596
|
+
leafSize: LEAF_SIZE,
|
|
1597
|
+
treeDepth: 1,
|
|
1598
|
+
numInteriorNodes: 0,
|
|
1599
|
+
numMixedLeaves: 0,
|
|
1600
|
+
nodes: new Uint32Array(0),
|
|
1601
|
+
leafData: new Uint32Array(0),
|
|
1602
|
+
};
|
|
1603
|
+
}
|
|
1604
|
+
if (options.dense || shouldUseDenseMipBuild(totalBlocks, nSolid, nMixed)) {
|
|
1605
|
+
return buildSparseOctreeDense(grid, gridBounds, sceneBounds, voxelResolution, treeDepth, !!options.consumeGrid);
|
|
1606
|
+
}
|
|
1607
|
+
const solidStream = new Float64Array(nSolid);
|
|
1608
|
+
const mixedStream = new Float64Array(nMixed);
|
|
1609
|
+
const mixedMasks = new Uint32Array(nMixed * 2);
|
|
1610
|
+
let solidWriteIdx = 0;
|
|
1611
|
+
let mixedWriteIdx = 0;
|
|
1612
|
+
for (let w = 0; w < gridTypes.length; w++) {
|
|
1613
|
+
let word = gridTypes[w];
|
|
1614
|
+
if (w === lastWordIdx) {
|
|
1615
|
+
word = (word & lastValidWordMask) >>> 0;
|
|
1616
|
+
}
|
|
1617
|
+
if (word === 0) {
|
|
1618
|
+
continue;
|
|
1619
|
+
}
|
|
1620
|
+
let nonEmpty = ((word & EVEN_BITS) | ((word >>> 1) & EVEN_BITS)) >>> 0;
|
|
1621
|
+
const baseIdx = w * BLOCKS_PER_WORD;
|
|
1622
|
+
while (nonEmpty) {
|
|
1623
|
+
const bp = 31 - Math.clz32(nonEmpty & -nonEmpty);
|
|
1624
|
+
const lane = bp >>> 1;
|
|
1625
|
+
nonEmpty &= nonEmpty - 1;
|
|
1626
|
+
const blockIdx = baseIdx + lane;
|
|
1627
|
+
if (blockIdx >= totalBlocks) {
|
|
1628
|
+
break;
|
|
1629
|
+
}
|
|
1630
|
+
const bx = blockIdx % nbx;
|
|
1631
|
+
const byBz = (blockIdx / nbx) | 0;
|
|
1632
|
+
const by = byBz % nby;
|
|
1633
|
+
const bz = (byBz / nby) | 0;
|
|
1634
|
+
const morton = encodeMorton3(bx, by, bz);
|
|
1635
|
+
const bt = (word >>> (lane << 1)) & TYPE_MASK;
|
|
1636
|
+
if (bt === OctreeNodeType.Solid) {
|
|
1637
|
+
solidStream[solidWriteIdx++] = morton;
|
|
1638
|
+
}
|
|
1639
|
+
else if (bt === OctreeNodeType.Mixed) {
|
|
1640
|
+
mixedStream[mixedWriteIdx] = morton;
|
|
1641
|
+
const s = gridMasks.slot(blockIdx);
|
|
1642
|
+
mixedMasks[mixedWriteIdx * 2] = gridMasks.lo[s];
|
|
1643
|
+
mixedMasks[mixedWriteIdx * 2 + 1] = gridMasks.hi[s];
|
|
1644
|
+
mixedWriteIdx++;
|
|
1645
|
+
}
|
|
1646
|
+
}
|
|
1647
|
+
}
|
|
1648
|
+
if (options.consumeGrid) {
|
|
1649
|
+
grid.releaseStorage();
|
|
1650
|
+
}
|
|
1651
|
+
if (nSolid > 1) {
|
|
1652
|
+
solidStream.sort();
|
|
1653
|
+
}
|
|
1654
|
+
if (nMixed > 1) {
|
|
1655
|
+
sortMixedByMorton(mixedStream, mixedMasks, nMixed);
|
|
1656
|
+
}
|
|
1657
|
+
const interiorLevels = [];
|
|
1658
|
+
let curMortons = [];
|
|
1659
|
+
let curTypes = [];
|
|
1660
|
+
let curChildMasks = [];
|
|
1661
|
+
{
|
|
1662
|
+
let sI = 0;
|
|
1663
|
+
let mI = 0;
|
|
1664
|
+
while (sI < nSolid || mI < nMixed) {
|
|
1665
|
+
const sM0 = sI < nSolid ? solidStream[sI] : Number.POSITIVE_INFINITY;
|
|
1666
|
+
const mM0 = mI < nMixed ? mixedStream[mI] : Number.POSITIVE_INFINITY;
|
|
1667
|
+
const minMorton = sM0 < mM0 ? sM0 : mM0;
|
|
1668
|
+
const parentMorton = Math.floor(minMorton / 8);
|
|
1669
|
+
let childMask = 0;
|
|
1670
|
+
let allSolid = true;
|
|
1671
|
+
let childCount = 0;
|
|
1672
|
+
while (true) {
|
|
1673
|
+
const sM = sI < nSolid ? solidStream[sI] : Number.POSITIVE_INFINITY;
|
|
1674
|
+
const mM = mI < nMixed ? mixedStream[mI] : Number.POSITIVE_INFINITY;
|
|
1675
|
+
const cur = sM < mM ? sM : mM;
|
|
1676
|
+
if (!isFinite(cur) || Math.floor(cur / 8) !== parentMorton) {
|
|
1677
|
+
break;
|
|
1678
|
+
}
|
|
1679
|
+
childMask |= 1 << (cur % 8);
|
|
1680
|
+
childCount++;
|
|
1681
|
+
if (sM < mM) {
|
|
1682
|
+
sI++;
|
|
1683
|
+
}
|
|
1684
|
+
else {
|
|
1685
|
+
allSolid = false;
|
|
1686
|
+
mI++;
|
|
1687
|
+
}
|
|
1688
|
+
}
|
|
1689
|
+
curMortons.push(parentMorton);
|
|
1690
|
+
if (allSolid && childCount === 8) {
|
|
1691
|
+
curTypes.push(OctreeNodeType.Solid);
|
|
1692
|
+
curChildMasks.push(0);
|
|
1693
|
+
}
|
|
1694
|
+
else {
|
|
1695
|
+
curTypes.push(OctreeNodeType.Mixed);
|
|
1696
|
+
curChildMasks.push(childMask);
|
|
1697
|
+
}
|
|
1698
|
+
}
|
|
1699
|
+
}
|
|
1700
|
+
let actualDepth = treeDepth;
|
|
1701
|
+
if (curMortons.length === 0) {
|
|
1702
|
+
actualDepth = 1;
|
|
1703
|
+
}
|
|
1704
|
+
else if (curMortons.length === 1 && curMortons[0] === 0) {
|
|
1705
|
+
actualDepth = 1;
|
|
1706
|
+
interiorLevels.push({ mortons: curMortons, types: curTypes, childMasks: curChildMasks });
|
|
1707
|
+
}
|
|
1708
|
+
else {
|
|
1709
|
+
for (let level = 1; level < treeDepth; level++) {
|
|
1710
|
+
interiorLevels.push({ mortons: curMortons, types: curTypes, childMasks: curChildMasks });
|
|
1711
|
+
const n = curMortons.length;
|
|
1712
|
+
const nextMortons = [];
|
|
1713
|
+
const nextTypes = [];
|
|
1714
|
+
const nextChildMasks = [];
|
|
1715
|
+
let i = 0;
|
|
1716
|
+
while (i < n) {
|
|
1717
|
+
const parentMorton = Math.floor(curMortons[i] / 8);
|
|
1718
|
+
let childMask = 0;
|
|
1719
|
+
let allSolid = true;
|
|
1720
|
+
let childCount = 0;
|
|
1721
|
+
while (i < n && Math.floor(curMortons[i] / 8) === parentMorton) {
|
|
1722
|
+
const octant = curMortons[i] % 8;
|
|
1723
|
+
childMask |= 1 << octant;
|
|
1724
|
+
if (curTypes[i] !== OctreeNodeType.Solid) {
|
|
1725
|
+
allSolid = false;
|
|
1726
|
+
}
|
|
1727
|
+
childCount++;
|
|
1728
|
+
i++;
|
|
1729
|
+
}
|
|
1730
|
+
nextMortons.push(parentMorton);
|
|
1731
|
+
if (allSolid && childCount === 8) {
|
|
1732
|
+
nextTypes.push(OctreeNodeType.Solid);
|
|
1733
|
+
nextChildMasks.push(0);
|
|
1734
|
+
}
|
|
1735
|
+
else {
|
|
1736
|
+
nextTypes.push(OctreeNodeType.Mixed);
|
|
1737
|
+
nextChildMasks.push(childMask);
|
|
1738
|
+
}
|
|
1739
|
+
}
|
|
1740
|
+
curMortons = nextMortons;
|
|
1741
|
+
curTypes = nextTypes;
|
|
1742
|
+
curChildMasks = nextChildMasks;
|
|
1743
|
+
if (curMortons.length === 1 && curMortons[0] === 0) {
|
|
1744
|
+
actualDepth = level + 1;
|
|
1745
|
+
break;
|
|
1746
|
+
}
|
|
1747
|
+
}
|
|
1748
|
+
interiorLevels.push({ mortons: curMortons, types: curTypes, childMasks: curChildMasks });
|
|
1749
|
+
}
|
|
1750
|
+
return flattenTreeFromLevels(interiorLevels, solidStream, mixedStream, mixedMasks, nSolid, nMixed, gridBounds, sceneBounds, voxelResolution, actualDepth);
|
|
1751
|
+
}
|
|
1752
|
+
export { BLOCK_EMPTY, BLOCK_SOLID, BLOCK_MIXED, BLOCKS_PER_WORD, TYPE_MASK, EVEN_BITS, readBlockType, writeBlockType, SOLID_LO, SOLID_HI, SparseVoxelGrid, };
|