@woosh/meep-engine 2.74.0 → 2.75.1
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/build/bundle-worker-image-decoder.js +1 -1
- package/build/meep.cjs +202 -204
- package/build/meep.min.js +1 -1
- package/build/meep.module.js +202 -204
- package/package.json +1 -1
- package/src/core/binary/UINT32_MAX.js +5 -0
- package/src/core/bvh2/bvh3/BVH.js +44 -2
- package/src/core/bvh2/bvh3/BVH.spec.js +45 -0
- package/src/core/bvh2/bvh3/build_triangle_morton_codes.js +73 -0
- package/src/core/bvh2/bvh3/ebvh_build_for_geometry_morton.js +5 -101
- package/src/core/bvh2/bvh3/ebvh_build_hierarchy.js +59 -0
- package/src/core/bvh2/bvh3/query/bvh_query_user_data_nearest_to_point.js +31 -32
- package/src/core/bvh2/bvh3/query/bvh_query_user_data_nearest_to_point.spec.js +139 -0
- package/src/core/collection/SCRATCH_UINT32_TRAVERSAL_STACK.js +1 -0
- package/src/core/events/signal/SignalBinding.js +18 -16
- package/src/core/geom/3d/aabb/aabb3_signed_distance_sqr_to_point.js +1 -0
- package/src/core/geom/3d/aabb/aabb3_unsigned_distance_sqr_to_point.js +36 -0
- package/src/core/model/ObservedBoolean.js +1 -1
- package/src/core/process/worker/OnDemandWorkerManager.js +5 -1
- package/src/engine/asset/loaders/ArrayBufferLoader.js +13 -15
- package/src/engine/asset/loaders/image/ImageDecoderWorker.js +1 -1
- package/src/engine/asset/loaders/image/ImageRGBADataLoader.js +5 -7
- package/src/engine/asset/loaders/image/codec/ThreadedImageDecoder.js +1 -1
- package/src/engine/asset/loaders/image/png/PNG.js +339 -332
- package/src/engine/asset/loaders/image/png/PNGReader.js +59 -16
- package/src/engine/asset/loaders/image/png/prototypePNG.js +13 -4
- package/src/engine/graphics/texture/virtual/v2/{SparseTexture.js → PageTexture.js} +62 -18
- package/src/engine/graphics/texture/virtual/v2/ResidentTileTexture.js +46 -0
- package/src/engine/graphics/texture/virtual/v2/{TileLoader.js → VirtualTextureTileLoader.js} +11 -8
- package/src/engine/graphics/texture/virtual/v2/{UsageMetadata.js → VirtualTextureUsage.js} +2 -8
- package/src/engine/graphics/texture/virtual/v2/{VirtualTextureManager.js → VirtualTextureUsageUpdater.js} +7 -5
- package/src/engine/graphics/texture/virtual/v2/debug/ResidencyDebugView.js +17 -5
- package/src/engine/graphics/texture/virtual/v2/debug/UsageDebugView.js +1 -1
- package/src/engine/graphics/texture/virtual/v2/debug/UsagePyramidDebugView.js +1 -1
- package/src/engine/graphics/texture/virtual/v2/prototype.js +78 -59
- package/src/engine/graphics/texture/virtual/v2/tile/{TextureTile.js → VirtualTextureTile.js} +2 -2
- package/src/engine/graphics/texture/virtual/v2/tile/compose_tile_address.js +4 -0
- /package/src/engine/graphics/texture/virtual/v2/{ShaderUsage.js → VirtualTextureUsageShader.js} +0 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { assert } from "../../assert.js";
|
|
2
|
+
import { UINT32_MAX } from "../../binary/UINT32_MAX.js";
|
|
2
3
|
import { array_copy } from "../../collection/array/array_copy.js";
|
|
3
4
|
import { typed_array_copy } from "../../collection/array/typed/typed_array_copy.js";
|
|
4
5
|
import { aabb3_compute_surface_area } from "../../geom/3d/aabb/aabb3_compute_surface_area.js";
|
|
@@ -51,6 +52,13 @@ export const ELEMENT_WORD_COUNT = 10;
|
|
|
51
52
|
*/
|
|
52
53
|
const INITIAL_CAPACITY = 128;
|
|
53
54
|
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Tree can not contain more than this number of nodes
|
|
58
|
+
* @type {number}
|
|
59
|
+
*/
|
|
60
|
+
const NODE_CAPACITY_LIMIT = Math.floor(UINT32_MAX / (ELEMENT_WORD_COUNT * 4));
|
|
61
|
+
|
|
54
62
|
/**
|
|
55
63
|
* Bounding Volume Hierarchy implementation. Stores unsigned integer values at leaves, these are typically IDs or Index values.
|
|
56
64
|
* Highly optimized both in terms of memory usage and CPU. Most of the code inlined. No allocation are performed during usage (except for growing the tree capacity).
|
|
@@ -145,17 +153,26 @@ export class BVH {
|
|
|
145
153
|
}
|
|
146
154
|
|
|
147
155
|
__grow_capacity() {
|
|
148
|
-
|
|
156
|
+
if (this.__capacity >= NODE_CAPACITY_LIMIT) {
|
|
157
|
+
throw new Error('Can not grow capacity, already at maximum platform limit');
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
let new_capacity = Math.ceil(max2(
|
|
149
161
|
this.__capacity * CAPACITY_GROW_MULTIPLIER,
|
|
150
162
|
this.__capacity + CAPACITY_GROW_MIN_STEP
|
|
151
163
|
));
|
|
152
164
|
|
|
165
|
+
if (new_capacity > NODE_CAPACITY_LIMIT) {
|
|
166
|
+
// can not grow as much as we'd like, but we can still grow up to the limit
|
|
167
|
+
new_capacity = NODE_CAPACITY_LIMIT;
|
|
168
|
+
}
|
|
169
|
+
|
|
153
170
|
this.__set_capacity(new_capacity);
|
|
154
171
|
}
|
|
155
172
|
|
|
156
173
|
/**
|
|
157
174
|
*
|
|
158
|
-
* @param {number} new_capacity
|
|
175
|
+
* @param {number} new_capacity in number of nodes
|
|
159
176
|
* @private
|
|
160
177
|
*/
|
|
161
178
|
__set_capacity(new_capacity) {
|
|
@@ -707,6 +724,9 @@ export class BVH {
|
|
|
707
724
|
assert.notEqual(child1, NULL_NODE, 'child1 is null');
|
|
708
725
|
assert.notEqual(child2, NULL_NODE, 'child2 is null');
|
|
709
726
|
|
|
727
|
+
assert.notEqual(child1, index, 'child1 is equal to parent');
|
|
728
|
+
assert.notEqual(child2, index, 'child2 is equal to parent');
|
|
729
|
+
|
|
710
730
|
this.node_set_combined_aabb(index, child1, child2);
|
|
711
731
|
|
|
712
732
|
index = uint32[address + COLUMN_PARENT];
|
|
@@ -734,6 +754,9 @@ export class BVH {
|
|
|
734
754
|
assert.notEqual(child1, NULL_NODE, 'child1 is null');
|
|
735
755
|
assert.notEqual(child2, NULL_NODE, 'child2 is null');
|
|
736
756
|
|
|
757
|
+
assert.notEqual(child1, index, 'child1 is equal to parent');
|
|
758
|
+
assert.notEqual(child2, index, 'child2 is equal to parent');
|
|
759
|
+
|
|
737
760
|
uint32[node_address + COLUMN_HEIGHT] = 1 + max2(
|
|
738
761
|
uint32[child1 * ELEMENT_WORD_COUNT + COLUMN_HEIGHT],
|
|
739
762
|
uint32[child2 * ELEMENT_WORD_COUNT + COLUMN_HEIGHT],
|
|
@@ -815,6 +838,9 @@ export class BVH {
|
|
|
815
838
|
const iB = uint32[iA * ELEMENT_WORD_COUNT + COLUMN_CHILD_1];
|
|
816
839
|
const iC = uint32[iA * ELEMENT_WORD_COUNT + COLUMN_CHILD_2];
|
|
817
840
|
|
|
841
|
+
assert.notEqual(iA, iB, 'child1 equal to parent');
|
|
842
|
+
assert.notEqual(iA, iB, 'child2 equal to parent');
|
|
843
|
+
|
|
818
844
|
assert.greaterThanOrEqual(iB, 0);
|
|
819
845
|
assert.lessThan(iB, this.node_capacity);
|
|
820
846
|
assert.greaterThanOrEqual(iC, 0);
|
|
@@ -830,6 +856,9 @@ export class BVH {
|
|
|
830
856
|
const iF = uint32[iC * ELEMENT_WORD_COUNT + COLUMN_CHILD_1];
|
|
831
857
|
const iG = uint32[iC * ELEMENT_WORD_COUNT + COLUMN_CHILD_2];
|
|
832
858
|
|
|
859
|
+
assert.notEqual(iC, iF, 'child1 equal to parent');
|
|
860
|
+
assert.notEqual(iC, iG, 'child2 equal to parent');
|
|
861
|
+
|
|
833
862
|
// b2TreeNode* F = m_nodes + iF;
|
|
834
863
|
// b2TreeNode* G = m_nodes + iG;
|
|
835
864
|
|
|
@@ -848,6 +877,7 @@ export class BVH {
|
|
|
848
877
|
|
|
849
878
|
// A's old parent should point to C
|
|
850
879
|
if (a_parent !== NULL_NODE) {
|
|
880
|
+
assert.notEqual(a_parent, iC);
|
|
851
881
|
if (uint32[a_parent * ELEMENT_WORD_COUNT + COLUMN_CHILD_1] === iA) {
|
|
852
882
|
uint32[a_parent * ELEMENT_WORD_COUNT + COLUMN_CHILD_1] = iC;
|
|
853
883
|
} else {
|
|
@@ -900,6 +930,9 @@ export class BVH {
|
|
|
900
930
|
|
|
901
931
|
}
|
|
902
932
|
|
|
933
|
+
assert.notEqual(iC, uint32[iC * ELEMENT_WORD_COUNT + COLUMN_CHILD_1]);
|
|
934
|
+
assert.notEqual(iC, uint32[iC * ELEMENT_WORD_COUNT + COLUMN_CHILD_2]);
|
|
935
|
+
|
|
903
936
|
return iC;
|
|
904
937
|
}
|
|
905
938
|
|
|
@@ -908,6 +941,9 @@ export class BVH {
|
|
|
908
941
|
const iD = uint32[iB * ELEMENT_WORD_COUNT + COLUMN_CHILD_1];
|
|
909
942
|
const iE = uint32[iB * ELEMENT_WORD_COUNT + COLUMN_CHILD_2];
|
|
910
943
|
|
|
944
|
+
assert.notEqual(iB, iD, 'child1 equal to parent');
|
|
945
|
+
assert.notEqual(iB, iE, 'child2 equal to parent');
|
|
946
|
+
|
|
911
947
|
assert.greaterThanOrEqual(iD, 0);
|
|
912
948
|
assert.lessThan(iD, this.node_capacity);
|
|
913
949
|
assert.greaterThanOrEqual(iE, 0);
|
|
@@ -974,9 +1010,15 @@ export class BVH {
|
|
|
974
1010
|
|
|
975
1011
|
}
|
|
976
1012
|
|
|
1013
|
+
assert.notEqual(iB, uint32[iB * ELEMENT_WORD_COUNT + COLUMN_CHILD_1]);
|
|
1014
|
+
assert.notEqual(iB, uint32[iB * ELEMENT_WORD_COUNT + COLUMN_CHILD_2]);
|
|
1015
|
+
|
|
977
1016
|
return iB;
|
|
978
1017
|
}
|
|
979
1018
|
|
|
1019
|
+
assert.notEqual(iA, uint32[iA * ELEMENT_WORD_COUNT + COLUMN_CHILD_1]);
|
|
1020
|
+
assert.notEqual(iA, uint32[iA * ELEMENT_WORD_COUNT + COLUMN_CHILD_2]);
|
|
1021
|
+
|
|
980
1022
|
// no rotation
|
|
981
1023
|
return iA;
|
|
982
1024
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { randomFloatBetween } from "../../math/random/randomFloatBetween.js";
|
|
2
|
+
import { seededRandom } from "../../math/random/seededRandom.js";
|
|
1
3
|
import { BVH, NULL_NODE } from "./BVH.js";
|
|
2
4
|
|
|
3
5
|
test("constructor doesn't throw", () => {
|
|
@@ -302,3 +304,46 @@ test("move leaf node", () => {
|
|
|
302
304
|
|
|
303
305
|
expect(actual_bounds).toEqual(a_new_bounds);
|
|
304
306
|
});
|
|
307
|
+
|
|
308
|
+
test.skip("performance, insert 10k random nodes", () => {
|
|
309
|
+
|
|
310
|
+
const random = seededRandom(1);
|
|
311
|
+
|
|
312
|
+
const bvh = new BVH();
|
|
313
|
+
|
|
314
|
+
const min_bound = -1000;
|
|
315
|
+
const max_bound = 1000;
|
|
316
|
+
|
|
317
|
+
const OBJECT_COUNT = 1000000;
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
for (let i = 0; i < OBJECT_COUNT; i++) {
|
|
321
|
+
const node = bvh.allocate_node();
|
|
322
|
+
|
|
323
|
+
bvh.node_set_user_data(node, i);
|
|
324
|
+
|
|
325
|
+
const x = randomFloatBetween(random, min_bound, max_bound);
|
|
326
|
+
const y = randomFloatBetween(random, min_bound, max_bound);
|
|
327
|
+
const z = randomFloatBetween(random, min_bound, max_bound);
|
|
328
|
+
|
|
329
|
+
const extents = randomFloatBetween(random, 1, 10);
|
|
330
|
+
|
|
331
|
+
const x0 = x - extents;
|
|
332
|
+
const y0 = y - extents;
|
|
333
|
+
const z0 = z - extents;
|
|
334
|
+
|
|
335
|
+
const x1 = x + extents;
|
|
336
|
+
const y1 = y + extents;
|
|
337
|
+
const z1 = z + extents;
|
|
338
|
+
|
|
339
|
+
// console.log([x0, y0, z0, x1, y1, z1].join(', '))
|
|
340
|
+
|
|
341
|
+
bvh.node_set_aabb_primitive(
|
|
342
|
+
node,
|
|
343
|
+
x0, y0, z0,
|
|
344
|
+
x1, y1, z1
|
|
345
|
+
);
|
|
346
|
+
|
|
347
|
+
bvh.insert_leaf(node);
|
|
348
|
+
}
|
|
349
|
+
});
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import morton from "../../geom/3d/morton/Morton.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Compute morton codes for each triangle
|
|
5
|
+
* @param {number[]|Uint32Array} morton_codes
|
|
6
|
+
* @param {number} tri_count
|
|
7
|
+
* @param {number[]|Uint8Array|Uint16Array|Uint32Array} index_array
|
|
8
|
+
* @param {number[]|Float32Array} position_array
|
|
9
|
+
* @param {number} aabb_x0
|
|
10
|
+
* @param {number} aabb_y0
|
|
11
|
+
* @param {number} aabb_z0
|
|
12
|
+
* @param {number} aabb_x1
|
|
13
|
+
* @param {number} aabb_y1
|
|
14
|
+
* @param {number} aabb_z1
|
|
15
|
+
*/
|
|
16
|
+
export function build_triangle_morton_codes(
|
|
17
|
+
morton_codes,
|
|
18
|
+
tri_count,
|
|
19
|
+
index_array,
|
|
20
|
+
position_array,
|
|
21
|
+
aabb_x0, aabb_y0, aabb_z0,
|
|
22
|
+
aabb_x1, aabb_y1, aabb_z1
|
|
23
|
+
) {
|
|
24
|
+
|
|
25
|
+
const aabb_size_x = aabb_x1 - aabb_x0;
|
|
26
|
+
const aabb_size_y = aabb_y1 - aabb_y0;
|
|
27
|
+
const aabb_size_z = aabb_z1 - aabb_z0;
|
|
28
|
+
|
|
29
|
+
const morton_scale_x = aabb_size_x === 0 ? 0 : 1023 / aabb_size_x;
|
|
30
|
+
const morton_scale_y = aabb_size_y === 0 ? 0 : 1023 / aabb_size_y;
|
|
31
|
+
const morton_scale_z = aabb_size_z === 0 ? 0 : 1023 / aabb_size_z;
|
|
32
|
+
|
|
33
|
+
// compute morton codes
|
|
34
|
+
for (let i = 0; i < tri_count; i++) {
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
const i3 = i * 3;
|
|
38
|
+
|
|
39
|
+
const a = index_array[i3];
|
|
40
|
+
const b = index_array[i3 + 1];
|
|
41
|
+
const c = index_array[i3 + 2];
|
|
42
|
+
|
|
43
|
+
const a_address = a * 3;
|
|
44
|
+
const ax = position_array[a_address];
|
|
45
|
+
const ay = position_array[a_address + 1];
|
|
46
|
+
const az = position_array[a_address + 2];
|
|
47
|
+
|
|
48
|
+
const b_address = b * 3;
|
|
49
|
+
const bx = position_array[b_address];
|
|
50
|
+
const by = position_array[b_address + 1];
|
|
51
|
+
const bz = position_array[b_address + 2];
|
|
52
|
+
|
|
53
|
+
const c_address = c * 3;
|
|
54
|
+
const cx = position_array[c_address];
|
|
55
|
+
const cy = position_array[c_address + 1];
|
|
56
|
+
const cz = position_array[c_address + 2];
|
|
57
|
+
|
|
58
|
+
const center_x = (ax + bx + cx) * 0.333333333;
|
|
59
|
+
const center_y = (ay + by + cy) * 0.333333333;
|
|
60
|
+
const center_z = (az + bz + cz) * 0.333333333;
|
|
61
|
+
|
|
62
|
+
// normalize to bounds
|
|
63
|
+
const ncx = (center_x - aabb_x0) * morton_scale_x;
|
|
64
|
+
const ncy = (center_y - aabb_y0) * morton_scale_y;
|
|
65
|
+
const ncz = (center_z - aabb_z0) * morton_scale_z;
|
|
66
|
+
|
|
67
|
+
morton_codes[i] = morton(
|
|
68
|
+
Math.round(ncx),
|
|
69
|
+
Math.round(ncy),
|
|
70
|
+
Math.round(ncz)
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -2,9 +2,10 @@ import { compute_triangle_group_aabb3 } from "../../../engine/graphics/sh3/path_
|
|
|
2
2
|
import { arrayQuickSort } from "../../collection/array/arrayQuickSort.js";
|
|
3
3
|
import { AABB3 } from "../../geom/3d/aabb/AABB3.js";
|
|
4
4
|
import { aabb3_from_v3_array } from "../../geom/3d/aabb/aabb3_from_v3_array.js";
|
|
5
|
-
import morton from "../../geom/3d/morton/Morton.js";
|
|
6
5
|
import { max2 } from "../../math/max2.js";
|
|
6
|
+
import { build_triangle_morton_codes } from "./build_triangle_morton_codes.js";
|
|
7
7
|
import { NULL_NODE } from "./BVH.js";
|
|
8
|
+
import { ebvh_build_hierarchy } from "./ebvh_build_hierarchy.js";
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* Build the BVH bottom-up using spatial hash sorting
|
|
@@ -22,18 +23,6 @@ export function ebvh_build_for_geometry_morton(bvh, index_array, position_array)
|
|
|
22
23
|
// get bounds for the entire geometry
|
|
23
24
|
aabb3_from_v3_array(aabb3, position_array, position_array.length);
|
|
24
25
|
|
|
25
|
-
const aabb_x0 = aabb3.x0;
|
|
26
|
-
const aabb_y0 = aabb3.y0;
|
|
27
|
-
const aabb_z0 = aabb3.z0;
|
|
28
|
-
|
|
29
|
-
const aabb_size_x = aabb3.getExtentsX();
|
|
30
|
-
const aabb_size_y = aabb3.getExtentsY();
|
|
31
|
-
const aabb_size_z = aabb3.getExtentsZ();
|
|
32
|
-
|
|
33
|
-
const morton_scale_x = aabb_size_x === 0 ? 0 : 1023 / aabb_size_x;
|
|
34
|
-
const morton_scale_y = aabb_size_y === 0 ? 0 : 1023 / aabb_size_y;
|
|
35
|
-
const morton_scale_z = aabb_size_z === 0 ? 0 : 1023 / aabb_size_z;
|
|
36
|
-
|
|
37
26
|
// allocate nodes
|
|
38
27
|
const tri_count = index_array.length / 3;
|
|
39
28
|
const node_leaf_count = tri_count;
|
|
@@ -52,59 +41,15 @@ export function ebvh_build_for_geometry_morton(bvh, index_array, position_array)
|
|
|
52
41
|
nodes[i] = (node_total_count - 1) - i;
|
|
53
42
|
}
|
|
54
43
|
|
|
55
|
-
// for (let i = node_total_count - 1; i >= 0; i--) {
|
|
56
|
-
// // store nodes in reverse order so that top-level nodes end up on top
|
|
57
|
-
// nodes[i] = bvh.allocate_node();
|
|
58
|
-
// }
|
|
59
|
-
|
|
60
44
|
const morton_codes = new Uint32Array(tri_count);
|
|
61
45
|
const sorted_triangle_order = new Uint32Array(tri_count);
|
|
62
46
|
|
|
63
|
-
// compute morton codes
|
|
64
47
|
for (let i = 0; i < tri_count; i++) {
|
|
65
|
-
|
|
66
48
|
sorted_triangle_order[i] = i;
|
|
67
|
-
|
|
68
|
-
const i3 = i * 3;
|
|
69
|
-
|
|
70
|
-
const a = index_array[i3];
|
|
71
|
-
const b = index_array[i3 + 1];
|
|
72
|
-
const c = index_array[i3 + 2];
|
|
73
|
-
|
|
74
|
-
const a_address = a * 3;
|
|
75
|
-
const ax = position_array[a_address];
|
|
76
|
-
const ay = position_array[a_address + 1];
|
|
77
|
-
const az = position_array[a_address + 2];
|
|
78
|
-
|
|
79
|
-
const b_address = b * 3;
|
|
80
|
-
const bx = position_array[b_address];
|
|
81
|
-
const by = position_array[b_address + 1];
|
|
82
|
-
const bz = position_array[b_address + 2];
|
|
83
|
-
|
|
84
|
-
const c_address = c * 3;
|
|
85
|
-
const cx = position_array[c_address];
|
|
86
|
-
const cy = position_array[c_address + 1];
|
|
87
|
-
const cz = position_array[c_address + 2];
|
|
88
|
-
|
|
89
|
-
const center_x = (ax + bx + cx) * 0.333333333;
|
|
90
|
-
const center_y = (ay + by + cy) * 0.333333333;
|
|
91
|
-
const center_z = (az + bz + cz) * 0.333333333;
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
// normalize to bounds
|
|
95
|
-
const ncx = (center_x - aabb_x0) * morton_scale_x;
|
|
96
|
-
const ncy = (center_y - aabb_y0) * morton_scale_y;
|
|
97
|
-
const ncz = (center_z - aabb_z0) * morton_scale_z;
|
|
98
|
-
|
|
99
|
-
const morton_code = morton(
|
|
100
|
-
Math.round(ncx),
|
|
101
|
-
Math.round(ncy),
|
|
102
|
-
Math.round(ncz)
|
|
103
|
-
);
|
|
104
|
-
|
|
105
|
-
morton_codes[i] = morton_code;
|
|
106
49
|
}
|
|
107
50
|
|
|
51
|
+
build_triangle_morton_codes(morton_codes, tri_count, index_array, position_array, aabb3.x0, aabb3.y0, aabb3.z0, aabb3.x1, aabb3.y1, aabb3.z1);
|
|
52
|
+
|
|
108
53
|
// sort leaves by morton codes
|
|
109
54
|
arrayQuickSort(sorted_triangle_order, (triangle_index) => morton_codes[triangle_index], null, 0, tri_count - 1);
|
|
110
55
|
|
|
@@ -129,47 +74,6 @@ export function ebvh_build_for_geometry_morton(bvh, index_array, position_array)
|
|
|
129
74
|
unprocessed_nodes[i] = node;
|
|
130
75
|
}
|
|
131
76
|
|
|
132
|
-
// Assemble hierarchy
|
|
133
|
-
let unprocessed_node_count = tri_count;
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
while (used_index < node_total_count && unprocessed_node_count > 1) {
|
|
137
|
-
|
|
138
|
-
let added_nodes = 0;
|
|
139
|
-
let cursor = 0;
|
|
140
|
-
|
|
141
|
-
while (cursor + 1 < unprocessed_node_count) {
|
|
142
|
-
const child_1 = unprocessed_nodes[cursor++];
|
|
143
|
-
const child_2 = unprocessed_nodes[cursor++];
|
|
144
|
-
|
|
145
|
-
const parent = nodes[used_index++];
|
|
146
|
-
|
|
147
|
-
bvh.node_set_combined_aabb(parent, child_1, child_2);
|
|
148
|
-
|
|
149
|
-
bvh.node_set_parent(child_1, parent);
|
|
150
|
-
bvh.node_set_parent(child_2, parent);
|
|
151
|
-
|
|
152
|
-
bvh.node_set_child1(parent, child_1);
|
|
153
|
-
bvh.node_set_child2(parent, child_2);
|
|
154
|
-
|
|
155
|
-
bvh.node_set_height(parent,
|
|
156
|
-
1 + max2(
|
|
157
|
-
bvh.node_get_height(child_1),
|
|
158
|
-
bvh.node_get_height(child_2)
|
|
159
|
-
)
|
|
160
|
-
);
|
|
161
|
-
|
|
162
|
-
unprocessed_nodes[added_nodes++] = parent;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
while (cursor < unprocessed_node_count) {
|
|
166
|
-
// dangling nodes, push them onto the next level
|
|
167
|
-
unprocessed_nodes[added_nodes++] = unprocessed_nodes[cursor++];
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
unprocessed_node_count = added_nodes;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
77
|
// assign root
|
|
174
|
-
bvh.__root = nodes
|
|
78
|
+
bvh.__root = ebvh_build_hierarchy(bvh,unprocessed_nodes,tri_count,nodes,used_index);
|
|
175
79
|
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { max2 } from "../../math/max2.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Assumes nodes are spatially sorted
|
|
5
|
+
* NOTE: {@link unprocessed_nodes} will be modified during execution to save memory
|
|
6
|
+
* @param {BVH} bvh
|
|
7
|
+
* @param {number[]|Uint32Array} unprocessed_nodes contains nodes that need to be built into a hierarchy, these must not be present in the BVH hierarchy
|
|
8
|
+
* @param {number} input_node_count
|
|
9
|
+
* @param {number[]|Uint32Array} node_pool Contains node indices that can be used to build ancestry hierarchy, need to be pre-allocated before calling this method
|
|
10
|
+
* @param {number} node_pool_offset
|
|
11
|
+
* @returns {number} new root
|
|
12
|
+
*/
|
|
13
|
+
export function ebvh_build_hierarchy(bvh, unprocessed_nodes, input_node_count, node_pool, node_pool_offset) {
|
|
14
|
+
|
|
15
|
+
let used_index = node_pool_offset;
|
|
16
|
+
|
|
17
|
+
// Assemble hierarchy
|
|
18
|
+
let unprocessed_node_count = input_node_count;
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
while (unprocessed_node_count > 1) {
|
|
22
|
+
|
|
23
|
+
let added_nodes = 0;
|
|
24
|
+
let cursor = 0;
|
|
25
|
+
|
|
26
|
+
while (cursor + 1 < unprocessed_node_count) {
|
|
27
|
+
const child_1 = unprocessed_nodes[cursor++];
|
|
28
|
+
const child_2 = unprocessed_nodes[cursor++];
|
|
29
|
+
|
|
30
|
+
const parent = node_pool[used_index++];
|
|
31
|
+
|
|
32
|
+
bvh.node_set_combined_aabb(parent, child_1, child_2);
|
|
33
|
+
|
|
34
|
+
bvh.node_set_parent(child_1, parent);
|
|
35
|
+
bvh.node_set_parent(child_2, parent);
|
|
36
|
+
|
|
37
|
+
bvh.node_set_child1(parent, child_1);
|
|
38
|
+
bvh.node_set_child2(parent, child_2);
|
|
39
|
+
|
|
40
|
+
bvh.node_set_height(parent,
|
|
41
|
+
1 + max2(
|
|
42
|
+
bvh.node_get_height(child_1),
|
|
43
|
+
bvh.node_get_height(child_2)
|
|
44
|
+
)
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
unprocessed_nodes[added_nodes++] = parent;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
while (cursor < unprocessed_node_count) {
|
|
51
|
+
// dangling nodes, push them onto the next level
|
|
52
|
+
unprocessed_nodes[added_nodes++] = unprocessed_nodes[cursor++];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
unprocessed_node_count = added_nodes;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return unprocessed_nodes[0];
|
|
59
|
+
}
|
|
@@ -1,13 +1,6 @@
|
|
|
1
1
|
import { SCRATCH_UINT32_TRAVERSAL_STACK } from "../../../collection/SCRATCH_UINT32_TRAVERSAL_STACK.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { NULL_NODE } from "../BVH.js";
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
*
|
|
8
|
-
* @type {number[]}
|
|
9
|
-
*/
|
|
10
|
-
const scratch_aabb = [];
|
|
2
|
+
import { aabb3_unsigned_distance_sqr_to_point } from "../../../geom/3d/aabb/aabb3_unsigned_distance_sqr_to_point.js";
|
|
3
|
+
import { COLUMN_CHILD_1, COLUMN_CHILD_2, ELEMENT_WORD_COUNT, NULL_NODE } from "../BVH.js";
|
|
11
4
|
|
|
12
5
|
const stack = SCRATCH_UINT32_TRAVERSAL_STACK;
|
|
13
6
|
|
|
@@ -21,7 +14,7 @@ const stack = SCRATCH_UINT32_TRAVERSAL_STACK;
|
|
|
21
14
|
* @returns {number} user data of the leaf, or -1 if not found
|
|
22
15
|
*/
|
|
23
16
|
export function bvh_query_user_data_nearest_to_point(bvh, x, y, z, max_distance = Infinity) {
|
|
24
|
-
|
|
17
|
+
const root = bvh.root;
|
|
25
18
|
|
|
26
19
|
if (root === NULL_NODE) {
|
|
27
20
|
return -1;
|
|
@@ -35,10 +28,17 @@ export function bvh_query_user_data_nearest_to_point(bvh, x, y, z, max_distance
|
|
|
35
28
|
|
|
36
29
|
stack[stack_top] = root;
|
|
37
30
|
|
|
38
|
-
let
|
|
31
|
+
let best_leaf = -1;
|
|
39
32
|
let best_distance_sqr = max_distance * max_distance;
|
|
40
33
|
|
|
41
|
-
|
|
34
|
+
|
|
35
|
+
/*
|
|
36
|
+
For performance, we bind data directly to avoid extra copies required to read out AABB
|
|
37
|
+
*/
|
|
38
|
+
const float32 = bvh.__data_float32;
|
|
39
|
+
const uint32 = bvh.__data_uint32;
|
|
40
|
+
|
|
41
|
+
do {
|
|
42
42
|
stack.pointer--;
|
|
43
43
|
|
|
44
44
|
/**
|
|
@@ -47,42 +47,41 @@ export function bvh_query_user_data_nearest_to_point(bvh, x, y, z, max_distance
|
|
|
47
47
|
*/
|
|
48
48
|
const node = stack[stack.pointer];
|
|
49
49
|
|
|
50
|
-
|
|
50
|
+
const address = node * ELEMENT_WORD_COUNT;
|
|
51
51
|
|
|
52
52
|
// compute distance to box
|
|
53
|
-
const distance_sqr =
|
|
54
|
-
|
|
55
|
-
|
|
53
|
+
const distance_sqr = aabb3_unsigned_distance_sqr_to_point(
|
|
54
|
+
float32[address], float32[address + 1], float32[address + 2],
|
|
55
|
+
float32[address + 3], float32[address + 4], float32[address + 5],
|
|
56
56
|
x, y, z
|
|
57
|
-
)
|
|
57
|
+
);
|
|
58
58
|
|
|
59
|
-
if (distance_sqr
|
|
59
|
+
if (distance_sqr >= best_distance_sqr) {
|
|
60
60
|
// node is too far
|
|
61
61
|
continue;
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
if (!node_is_leaf) {
|
|
64
|
+
// get fist child to check if this is a leaf node or not
|
|
65
|
+
const child_1 = uint32[address + COLUMN_CHILD_1];
|
|
67
66
|
|
|
68
|
-
|
|
69
|
-
const child1 = bvh.node_get_child1(node);
|
|
70
|
-
const child2 = bvh.node_get_child2(node);
|
|
67
|
+
if (child_1 !== NULL_NODE) {
|
|
71
68
|
|
|
72
|
-
//
|
|
73
|
-
stack[stack.pointer++] =
|
|
74
|
-
stack[stack.pointer++] =
|
|
69
|
+
// this is not a leaf node, push children onto traversal stack
|
|
70
|
+
stack[stack.pointer++] = child_1;
|
|
71
|
+
stack[stack.pointer++] = uint32[address + COLUMN_CHILD_2];
|
|
75
72
|
|
|
76
73
|
} else {
|
|
77
74
|
|
|
78
75
|
// leaf node
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
best_leaf_data = bvh.node_get_user_data(node);
|
|
82
|
-
}
|
|
76
|
+
best_distance_sqr = distance_sqr;
|
|
77
|
+
best_leaf = node;
|
|
83
78
|
|
|
84
79
|
}
|
|
80
|
+
} while (stack.pointer > stack_top)
|
|
81
|
+
|
|
82
|
+
if (best_leaf === -1) {
|
|
83
|
+
return -1;
|
|
85
84
|
}
|
|
86
85
|
|
|
87
|
-
return
|
|
86
|
+
return bvh.node_get_user_data(best_leaf);
|
|
88
87
|
}
|