@woosh/meep-engine 2.138.3 → 2.138.4

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.
Files changed (24) hide show
  1. package/package.json +1 -1
  2. package/src/core/bvh2/bvh3/ebvh_build_hierarchy_radix.d.ts +13 -5
  3. package/src/core/bvh2/bvh3/ebvh_build_hierarchy_radix.d.ts.map +1 -1
  4. package/src/core/bvh2/bvh3/ebvh_build_hierarchy_radix.js +253 -280
  5. package/src/core/bvh2/bvh3/ebvh_update_hierarchy_bounds.js +1 -1
  6. package/src/core/cache/CacheV2.d.ts +6 -2
  7. package/src/core/cache/CacheV2.d.ts.map +1 -1
  8. package/src/core/cache/CacheV2.js +6 -2
  9. package/src/engine/graphics/ecs/light/LightSystem.d.ts.map +1 -1
  10. package/src/engine/graphics/ecs/light/LightSystem.js +67 -19
  11. package/src/engine/graphics/ecs/light/shadow/compute_view_frustum_aabb_in_space.d.ts +14 -0
  12. package/src/engine/graphics/ecs/light/shadow/compute_view_frustum_aabb_in_space.d.ts.map +1 -0
  13. package/src/engine/graphics/ecs/light/shadow/compute_view_frustum_aabb_in_space.js +62 -0
  14. package/src/engine/graphics/impostors/octahedral/prototypeBaker.js +6 -10
  15. package/src/engine/graphics/impostors/octahedral/shader/ImpostorShaderV0.d.ts.map +1 -1
  16. package/src/engine/graphics/impostors/octahedral/shader/ImpostorShaderV0.js +139 -57
  17. package/src/engine/graphics/impostors/octahedral/shader/ImpostorShaderWireframeV0.d.ts.map +1 -1
  18. package/src/engine/graphics/impostors/octahedral/shader/ImpostorShaderWireframeV0.js +123 -38
  19. package/src/engine/graphics/impostors/octahedral/util/makeImpostorAtlasPreview.d.ts +1 -1
  20. package/src/engine/graphics/impostors/octahedral/util/makeImpostorAtlasPreview.d.ts.map +1 -1
  21. package/src/engine/graphics/impostors/octahedral/util/makeImpostorAtlasPreview.js +7 -3
  22. package/src/engine/graphics/ecs/camera/FrustumProjector.d.ts +0 -14
  23. package/src/engine/graphics/ecs/camera/FrustumProjector.d.ts.map +0 -1
  24. package/src/engine/graphics/ecs/camera/FrustumProjector.js +0 -86
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "description": "Pure JavaScript game engine. Fully featured and production ready.",
6
6
  "type": "module",
7
7
  "author": "Alexander Goldring",
8
- "version": "2.138.3",
8
+ "version": "2.138.4",
9
9
  "main": "build/meep.module.js",
10
10
  "module": "build/meep.module.js",
11
11
  "exports": {
@@ -1,11 +1,19 @@
1
1
  /**
2
- * DO NOT USE, currently broken
2
+ * Build a binary radix tree over the given leaves and Morton codes.
3
+ *
3
4
  * @param {BVH} bvh
4
5
  * @param {number[]|Uint32Array} leaf_nodes
5
- * @param {number[]} sorted_morton_codes
6
+ * Pre-allocated leaf node IDs, ordered to match `sorted_morton_codes`
7
+ * (i.e. leaf_nodes[i]'s code is sorted_morton_codes[i]).
8
+ * @param {number[]|Uint32Array} sorted_morton_codes
9
+ * Morton codes in non-decreasing order. May contain duplicates.
6
10
  * @param {number} leaf_count
7
- * @param {number[]} internal_nodes
8
- * @returns {number} new root
11
+ * Number of leaves (must equal the length of `leaf_nodes` and
12
+ * `sorted_morton_codes` that the function will actually look at).
13
+ * @param {number[]|Uint32Array} internal_nodes
14
+ * Pre-allocated internal node IDs, of length at least `leaf_count - 1`.
15
+ * Ignored when leaf_count < 2.
16
+ * @returns {number} The root node id, or NULL_NODE if leaf_count is 0.
9
17
  */
10
- export function ebvh_build_hierarchy_radix(bvh: BVH, leaf_nodes: number[] | Uint32Array, sorted_morton_codes: number[], leaf_count: number, internal_nodes: number[]): number;
18
+ export function ebvh_build_hierarchy_radix(bvh: BVH, leaf_nodes: number[] | Uint32Array, sorted_morton_codes: number[] | Uint32Array, leaf_count: number, internal_nodes: number[] | Uint32Array): number;
11
19
  //# sourceMappingURL=ebvh_build_hierarchy_radix.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ebvh_build_hierarchy_radix.d.ts","sourceRoot":"","sources":["../../../../../src/core/bvh2/bvh3/ebvh_build_hierarchy_radix.js"],"names":[],"mappings":"AA2MA;;;;;;;;GAQG;AACH,iEANW,MAAM,EAAE,GAAC,WAAW,uBACpB,MAAM,EAAE,cACR,MAAM,kBACN,MAAM,EAAE,GACN,MAAM,CAqElB"}
1
+ {"version":3,"file":"ebvh_build_hierarchy_radix.d.ts","sourceRoot":"","sources":["../../../../../src/core/bvh2/bvh3/ebvh_build_hierarchy_radix.js"],"names":[],"mappings":"AA4KA;;;;;;;;;;;;;;;;GAgBG;AACH,iEAbW,MAAM,EAAE,GAAC,WAAW,uBAGpB,MAAM,EAAE,GAAC,WAAW,cAEpB,MAAM,kBAGN,MAAM,EAAE,GAAC,WAAW,GAGlB,MAAM,CAiElB"}
@@ -1,280 +1,253 @@
1
- import { assert } from "../../assert.js";
2
- import { clamp } from "../../math/clamp.js";
3
- import { NULL_NODE } from "./BVH.js";
4
- import { ebvh_update_hierarchy_bounds } from "./ebvh_update_hierarchy_bounds.js";
5
-
6
- /**
7
- *
8
- * @see "Thinking Parallel, Part III: Tree Construction on the GPU", 2012 by Tero Karras
9
- *
10
- * @param {number[]|Uint32Array} sortedMortonCodes
11
- * @param {number} first
12
- * @param {number} last
13
- * @return {number}
14
- */
15
- function find_split(
16
- sortedMortonCodes,
17
- first,
18
- last
19
- ) {
20
- // Identical Morton codes => split the range in the middle.
21
-
22
- const firstCode = sortedMortonCodes[first];
23
- const lastCode = sortedMortonCodes[last];
24
-
25
- if (firstCode === lastCode) {
26
- return (first + last) >>> 1;
27
- }
28
-
29
- // Calculate the number of highest bits that are the same
30
- // for all objects, using the count-leading-zeros intrinsic.
31
-
32
- const commonPrefix = Math.clz32(firstCode ^ lastCode);
33
-
34
- // Use binary search to find where the next bit differs.
35
- // Specifically, we are looking for the highest object that
36
- // shares more than commonPrefix bits with the first one.
37
-
38
- let split = first; // initial guess
39
- let step = last - first;
40
-
41
- do {
42
- step = (step + 1) >>> 1; // exponential decrease
43
- const newSplit = split + step; // proposed new position
44
-
45
- if (newSplit < last) {
46
- const splitCode = sortedMortonCodes[newSplit];
47
- const splitPrefix = Math.clz32(firstCode ^ splitCode);
48
- if (splitPrefix > commonPrefix) {
49
- split = newSplit; // accept proposal
50
- }
51
- }
52
- }
53
- while (step > 1);
54
-
55
- return split;
56
- }
57
-
58
- /**
59
- *
60
- * @param {number} indexA
61
- * @param {number} indexB
62
- * @param {number} elementCount
63
- * @param {number[]} mortonCodes
64
- * @return {number}
65
- * @see https://github.com/turanszkij/WickedEngine/blob/506749de321c2ab66fd33fbe41efb95afbbb7ff8/WickedEngine/shaders/bvh_hierarchyCS.hlsl#L28C1-L48C2
66
- */
67
- function GetLongestCommonPrefix(indexA, indexB, elementCount, mortonCodes) {
68
- assert.isNonNegativeInteger(indexA, 'indexA');
69
- assert.isNonNegativeInteger(indexB, 'indexB');
70
-
71
- if (indexA >= elementCount || indexB >= elementCount) {
72
- return -1;
73
- } else {
74
- const mortonCodeA = mortonCodes[indexA];
75
- const mortonCodeB = mortonCodes[indexB];
76
- if (mortonCodeA !== mortonCodeB) {
77
- return Math.clz32(mortonCodeA ^ mortonCodeB);
78
- } else {
79
- // TODO: Technically this should be primitive ID
80
- return Math.clz32(indexA ^ indexB) + 31;
81
- }
82
- }
83
- }
84
-
85
- /**
86
- * @see https://github.com/turanszkij/WickedEngine/blob/506749de321c2ab66fd33fbe41efb95afbbb7ff8/WickedEngine/shaders/bvh_hierarchyCS.hlsl#L28C1-L48C2
87
- * @param {number[]|Uint32Array} output
88
- * @param {number[]} sortedMortonCodes
89
- * @param {number} numTriangles
90
- * @param {number} idx
91
- */
92
- function determineRangeW(output, sortedMortonCodes, numTriangles, idx) {
93
- if (idx === 0) {
94
- // root
95
- output[0] = 0;
96
- output[1] = numTriangles - 1;
97
- return;
98
- }
99
-
100
- let d = GetLongestCommonPrefix(idx, idx + 1, numTriangles, sortedMortonCodes) - GetLongestCommonPrefix(idx, idx - 1, numTriangles, sortedMortonCodes);
101
- d = clamp(d, -1, 1);
102
-
103
- let minPrefix = GetLongestCommonPrefix(idx, idx - d, numTriangles, sortedMortonCodes);
104
-
105
- // TODO: Consider starting this at a higher number
106
- let maxLength = 2;
107
- while (GetLongestCommonPrefix(idx, idx + maxLength * d, numTriangles, sortedMortonCodes) > minPrefix) {
108
- maxLength <<= 2;
109
- }
110
-
111
- let length = 0;
112
- for (let t = maxLength >>> 1; t > 0; t >>>= 1) {
113
- if (GetLongestCommonPrefix(idx, idx + (length + t) * d, numTriangles, sortedMortonCodes) > minPrefix) {
114
- length = length + t;
115
- }
116
- }
117
-
118
- let j = idx + length * d;
119
-
120
- output[0] = Math.min(idx, j);
121
- output[1] = Math.max(idx, j);
122
-
123
- }
124
-
125
-
126
- /**
127
- * @see https://github.com/mbartling/cuda-bvh/blob/7f2f98d9d29956c3559632e59104ba66f31f80b8/kernels/bvh.cu#L276C1-L352C2
128
- * @param {number[]|Uint32Array} output
129
- * @param {number[]} sortedMortonCodes
130
- * @param {number} numTriangles
131
- * @param {number} idx
132
- */
133
- function determineRange(output, sortedMortonCodes, numTriangles, idx) {
134
-
135
- //determine the range of keys covered by each internal node (as well as its children)
136
- //direction is found by looking at the neighboring keys ki-1 , ki , ki+1
137
- //the index is either the beginning of the range or the end of the range
138
- let direction = 0;
139
- let common_prefix_with_left = 0;
140
- let common_prefix_with_right = 0;
141
-
142
- common_prefix_with_right = Math.clz32(sortedMortonCodes[idx] ^ sortedMortonCodes[idx + 1]);
143
-
144
- if (idx === 0) {
145
- common_prefix_with_left = -1;
146
- } else {
147
- common_prefix_with_left = Math.clz32(sortedMortonCodes[idx] ^ sortedMortonCodes[idx - 1]);
148
-
149
- }
150
-
151
- direction = ((common_prefix_with_right - common_prefix_with_left) > 0) ? 1 : -1;
152
-
153
- let min_prefix_range = 0;
154
-
155
- if (idx === 0) {
156
- min_prefix_range = -1;
157
-
158
- } else {
159
- min_prefix_range = Math.clz32(sortedMortonCodes[idx] ^ sortedMortonCodes[idx - direction]);
160
- }
161
-
162
- let lmax = 2;
163
- let next_key = idx + lmax * direction;
164
-
165
- while ((next_key >= 0) && (next_key < numTriangles) && (Math.clz32(sortedMortonCodes[idx] ^ sortedMortonCodes[next_key]) > min_prefix_range)) {
166
- lmax *= 2;
167
- next_key = idx + lmax * direction;
168
- }
169
- //find the other end using binary search
170
- let l = 0;
171
-
172
- do {
173
- lmax = (lmax + 1) >> 1; // exponential decrease
174
- const new_val = idx + (l + lmax) * direction;
175
-
176
- if (new_val >= 0 && new_val < numTriangles) {
177
- const Code = sortedMortonCodes[new_val];
178
- const Prefix = Math.clz32(sortedMortonCodes[idx] ^ Code);
179
- if (Prefix > min_prefix_range) {
180
- l = l + lmax;
181
- }
182
- }
183
- } while (lmax > 1);
184
-
185
- const j = idx + l * direction;
186
-
187
- let left = 0;
188
- let right = 0;
189
-
190
- if (idx < j) {
191
- left = idx;
192
- right = j;
193
- } else {
194
- left = j;
195
- right = idx;
196
- }
197
-
198
- // printf("idx : (%d) returning range (%d, %d) \n" , idx , left, right);
199
-
200
- output[0] = left
201
- output[1] = right;
202
- }
203
-
204
- /**
205
- * DO NOT USE, currently broken
206
- * @param {BVH} bvh
207
- * @param {number[]|Uint32Array} leaf_nodes
208
- * @param {number[]} sorted_morton_codes
209
- * @param {number} leaf_count
210
- * @param {number[]} internal_nodes
211
- * @returns {number} new root
212
- */
213
- export function ebvh_build_hierarchy_radix(
214
- bvh,
215
- leaf_nodes,
216
- sorted_morton_codes,
217
- leaf_count,
218
- internal_nodes,
219
- ) {
220
- console.warn("IMPLEMENTATION IS INCOMPLETE, DO NOT USE")
221
-
222
- assert.isNonNegativeInteger(leaf_count, 'leaf_count');
223
-
224
- // Construct leaf nodes.
225
- // Note: This step can be avoided by storing
226
- // the tree in a slightly different way.
227
-
228
- const range = new Uint32Array(2);
229
-
230
- // Construct internal nodes.
231
- const intermediate_node_count = leaf_count - 1;
232
- for (let idx = 0; idx < intermediate_node_count; idx++) // in parallel
233
- {
234
- // Find out which range of objects the node corresponds to.
235
- // (This is where the magic happens!)
236
-
237
- determineRange(range, sorted_morton_codes, leaf_count, idx);
238
-
239
- const first = range[0];
240
- const last = range[1];
241
-
242
- // Determine where to split the range.
243
-
244
- const split = find_split(sorted_morton_codes, first, last);
245
- assert.isNonNegativeInteger(split, 'split');
246
-
247
- // Select childA.
248
-
249
- let childA;
250
- if (split === first) {
251
- childA = leaf_nodes[split];
252
- } else {
253
- childA = internal_nodes[split];
254
- }
255
-
256
- // Select childB.
257
-
258
- let childB;
259
- if (split + 1 === last) {
260
- childB = leaf_nodes[split + 1];
261
- } else {
262
- childB = internal_nodes[split + 1];
263
- }
264
-
265
- // Record parent-child relationships.
266
- const parent = internal_nodes[idx];
267
-
268
- bvh.node_assign_children_only(parent, childA, childB);
269
- }
270
-
271
- // Node 0 is the root.
272
- const root = internal_nodes[0];
273
-
274
- bvh.node_set_parent(root, NULL_NODE);
275
-
276
- // update bounds
277
- ebvh_update_hierarchy_bounds(bvh, root);
278
-
279
- return root;
280
- }
1
+ import { assert } from "../../assert.js";
2
+ import { NULL_NODE } from "./BVH.js";
3
+
4
+ /**
5
+ * Karras 2012 — "Maximizing Parallelism in the Construction of BVHs, Octrees, and k-d Trees".
6
+ *
7
+ * For a sorted array of N Morton codes, build a binary radix tree of N-1 internal
8
+ * nodes and N leaves. Each internal node corresponds to the longest common prefix
9
+ * of a contiguous range of Morton codes; its split point is where the first bit
10
+ * after that prefix flips from 0 to 1.
11
+ *
12
+ * The caller must pre-allocate:
13
+ * - `leaf_count` leaf nodes in `bvh`, each with its AABB and user-data set;
14
+ * their IDs are passed in `leaf_nodes`, sorted by `sorted_morton_codes`.
15
+ * - `leaf_count - 1` internal nodes (when `leaf_count >= 2`); their IDs are
16
+ * passed in `internal_nodes`, in arbitrary order.
17
+ *
18
+ * The returned root index is wired into the tree; internal-node bounds and
19
+ * heights are filled in by a bottom-up refit after structural construction.
20
+ *
21
+ * @see "Thinking Parallel, Part III: Tree Construction on the GPU", 2012 by Tero Karras
22
+ */
23
+
24
+ /**
25
+ * Compute the position of the first bit where the codes at `first` and `last`
26
+ * differ in their longest-common-prefix sense — i.e. the split point for the
27
+ * range [first, last]. Returns an index `s` such that all codes in [first, s]
28
+ * share more leading bits with the firstCode than codes in [s+1, last] do.
29
+ *
30
+ * @param {number[]|Uint32Array} sortedMortonCodes
31
+ * @param {number} first
32
+ * @param {number} last
33
+ * @return {number}
34
+ */
35
+ function find_split(sortedMortonCodes, first, last) {
36
+ const firstCode = sortedMortonCodes[first];
37
+ const lastCode = sortedMortonCodes[last];
38
+
39
+ // Identical Morton codes (multiple leaves at the same point): split the
40
+ // range in the middle so we still build a balanced subtree.
41
+ if (firstCode === lastCode) {
42
+ return (first + last) >>> 1;
43
+ }
44
+
45
+ // Number of highest bits that match between first and last.
46
+ const commonPrefix = Math.clz32(firstCode ^ lastCode);
47
+
48
+ // Binary search for the highest index that still shares more than
49
+ // `commonPrefix` bits with the firstCode.
50
+ let split = first;
51
+ let step = last - first;
52
+
53
+ do {
54
+ step = (step + 1) >>> 1;
55
+ const newSplit = split + step;
56
+
57
+ if (newSplit < last) {
58
+ const splitCode = sortedMortonCodes[newSplit];
59
+ const splitPrefix = Math.clz32(firstCode ^ splitCode);
60
+ if (splitPrefix > commonPrefix) {
61
+ split = newSplit;
62
+ }
63
+ }
64
+ } while (step > 1);
65
+
66
+ return split;
67
+ }
68
+
69
+ /**
70
+ * Determine the [first, last] code range covered by internal node `idx`.
71
+ *
72
+ * @see https://github.com/mbartling/cuda-bvh/blob/7f2f98d9d29956c3559632e59104ba66f31f80b8/kernels/bvh.cu#L276
73
+ * @param {number[]|Uint32Array} output 2-element output [first, last]
74
+ * @param {number[]|Uint32Array} sortedMortonCodes
75
+ * @param {number} leaf_count
76
+ * @param {number} idx
77
+ */
78
+ function determineRange(output, sortedMortonCodes, leaf_count, idx) {
79
+ // For the root (idx === 0) the range is unambiguously [0, leaf_count - 1].
80
+ // We short-circuit so callers don't have to special-case it.
81
+ if (idx === 0) {
82
+ output[0] = 0;
83
+ output[1] = leaf_count - 1;
84
+ return;
85
+ }
86
+
87
+ const codeIdx = sortedMortonCodes[idx];
88
+ const prefixRight = Math.clz32(codeIdx ^ sortedMortonCodes[idx + 1]);
89
+ const prefixLeft = Math.clz32(codeIdx ^ sortedMortonCodes[idx - 1]);
90
+
91
+ const direction = (prefixRight - prefixLeft) > 0 ? 1 : -1;
92
+
93
+ // Minimum prefix length we require to stay in this range — anything that
94
+ // shares fewer bits than this is "outside" the node's subtree.
95
+ const min_prefix_range = Math.clz32(codeIdx ^ sortedMortonCodes[idx - direction]);
96
+
97
+ // Expand maximum range with exponential growth, bounded by array.
98
+ let lmax = 2;
99
+ let next_key = idx + lmax * direction;
100
+
101
+ while (
102
+ next_key >= 0 &&
103
+ next_key < leaf_count &&
104
+ Math.clz32(codeIdx ^ sortedMortonCodes[next_key]) > min_prefix_range
105
+ ) {
106
+ lmax *= 2;
107
+ next_key = idx + lmax * direction;
108
+ }
109
+
110
+ // Binary-search for the actual length within [0, lmax].
111
+ let length = 0;
112
+ do {
113
+ lmax = (lmax + 1) >> 1;
114
+ const new_val = idx + (length + lmax) * direction;
115
+
116
+ if (new_val >= 0 && new_val < leaf_count) {
117
+ const prefix = Math.clz32(codeIdx ^ sortedMortonCodes[new_val]);
118
+ if (prefix > min_prefix_range) {
119
+ length += lmax;
120
+ }
121
+ }
122
+ } while (lmax > 1);
123
+
124
+ const j = idx + length * direction;
125
+
126
+ output[0] = Math.min(idx, j);
127
+ output[1] = Math.max(idx, j);
128
+ }
129
+
130
+ /**
131
+ * Bottom-up bounds and height refit for a radix-built tree.
132
+ *
133
+ * Walks the tree once in pre-order to collect every reachable node, then
134
+ * processes them in reverse — which is post-order for trees — combining
135
+ * each internal node's AABB from its children and updating its height.
136
+ *
137
+ * @param {BVH} bvh
138
+ * @param {number} root
139
+ */
140
+ function refit_radix_tree(bvh, root) {
141
+ if (root === NULL_NODE) {
142
+ return;
143
+ }
144
+
145
+ const order = [];
146
+ const stack = [root];
147
+
148
+ while (stack.length > 0) {
149
+ const node = stack.pop();
150
+ order.push(node);
151
+
152
+ if (!bvh.node_is_leaf(node)) {
153
+ stack.push(bvh.node_get_child1(node));
154
+ stack.push(bvh.node_get_child2(node));
155
+ }
156
+ }
157
+
158
+ for (let i = order.length - 1; i >= 0; i--) {
159
+ const node = order[i];
160
+ if (bvh.node_is_leaf(node)) {
161
+ continue;
162
+ }
163
+ const c1 = bvh.node_get_child1(node);
164
+ const c2 = bvh.node_get_child2(node);
165
+ bvh.node_set_combined_aabb(node, c1, c2);
166
+ bvh.node_set_height(
167
+ node,
168
+ 1 + Math.max(bvh.node_get_height(c1), bvh.node_get_height(c2))
169
+ );
170
+ }
171
+ }
172
+
173
+ /**
174
+ * Build a binary radix tree over the given leaves and Morton codes.
175
+ *
176
+ * @param {BVH} bvh
177
+ * @param {number[]|Uint32Array} leaf_nodes
178
+ * Pre-allocated leaf node IDs, ordered to match `sorted_morton_codes`
179
+ * (i.e. leaf_nodes[i]'s code is sorted_morton_codes[i]).
180
+ * @param {number[]|Uint32Array} sorted_morton_codes
181
+ * Morton codes in non-decreasing order. May contain duplicates.
182
+ * @param {number} leaf_count
183
+ * Number of leaves (must equal the length of `leaf_nodes` and
184
+ * `sorted_morton_codes` that the function will actually look at).
185
+ * @param {number[]|Uint32Array} internal_nodes
186
+ * Pre-allocated internal node IDs, of length at least `leaf_count - 1`.
187
+ * Ignored when leaf_count < 2.
188
+ * @returns {number} The root node id, or NULL_NODE if leaf_count is 0.
189
+ */
190
+ export function ebvh_build_hierarchy_radix(
191
+ bvh,
192
+ leaf_nodes,
193
+ sorted_morton_codes,
194
+ leaf_count,
195
+ internal_nodes
196
+ ) {
197
+ assert.isNonNegativeInteger(leaf_count, 'leaf_count');
198
+
199
+ // Empty input: nothing to build.
200
+ if (leaf_count === 0) {
201
+ return NULL_NODE;
202
+ }
203
+
204
+ // Single leaf is its own root; no internal node needed.
205
+ if (leaf_count === 1) {
206
+ const root = leaf_nodes[0];
207
+ bvh.node_set_parent(root, NULL_NODE);
208
+ return root;
209
+ }
210
+
211
+ // General case: build leaf_count - 1 internal nodes per Karras 2012.
212
+ const range = new Uint32Array(2);
213
+ const internal_node_count = leaf_count - 1;
214
+
215
+ for (let idx = 0; idx < internal_node_count; idx++) {
216
+ determineRange(range, sorted_morton_codes, leaf_count, idx);
217
+
218
+ const first = range[0];
219
+ const last = range[1];
220
+
221
+ const split = find_split(sorted_morton_codes, first, last);
222
+ assert.isNonNegativeInteger(split, 'split');
223
+
224
+ // child A: the [first, split] subrange's owner. If that subrange is a
225
+ // single element it's a leaf; otherwise it's the internal node at
226
+ // index `split` (Karras's convention: internal node k owns the range
227
+ // whose left boundary is at code-index k).
228
+ const childA = (split === first)
229
+ ? leaf_nodes[split]
230
+ : internal_nodes[split];
231
+
232
+ // child B: the [split+1, last] subrange's owner.
233
+ const childB = (split + 1 === last)
234
+ ? leaf_nodes[split + 1]
235
+ : internal_nodes[split + 1];
236
+
237
+ const parent = internal_nodes[idx];
238
+ bvh.node_assign_children_only(parent, childA, childB);
239
+ }
240
+
241
+ // Internal node 0 covers [0, leaf_count - 1] — the whole tree — and is
242
+ // therefore the root.
243
+ const root = internal_nodes[0];
244
+ bvh.node_set_parent(root, NULL_NODE);
245
+
246
+ // Walk back up to fill in correct AABBs and heights for every internal
247
+ // node. Heights set during construction by node_assign_children_only are
248
+ // stale because children may not have been built yet at parent-creation
249
+ // time.
250
+ refit_radix_tree(bvh, root);
251
+
252
+ return root;
253
+ }
@@ -107,7 +107,7 @@ export function ebvh_update_hierarchy_bounds(bvh, root = bvh.root) {
107
107
 
108
108
  } else if (came_from_type === CAME_FROM_TYPE_PARENT) {
109
109
  const child1 = bvh.node_get_child1(node);
110
- const child2 = bvh.node_get_child1(node);
110
+ const child2 = bvh.node_get_child2(node);
111
111
 
112
112
  if (child1 !== NULL_NODE) {
113
113
  came_from_type = CAME_FROM_TYPE_PARENT;
@@ -1,6 +1,10 @@
1
1
  /**
2
- * UNTESTED, DO NOT USE.
3
- * Use {@link Cache} or {@link LoadingCache} instead
2
+ * NOT PRODUCTION READY. Work in progress, behaviour is incomplete and
3
+ * unverified do not depend on it from product code, and do not add tests
4
+ * to lock in its current half-finished shape. Use {@link Cache} or
5
+ * {@link LoadingCache} for any real caching need until this class is
6
+ * finished and reviewed.
7
+ *
4
8
  * @template K,V
5
9
  */
6
10
  export class CacheV2<K, V> {
@@ -1 +1 @@
1
- {"version":3,"file":"CacheV2.d.ts","sourceRoot":"","sources":["../../../../src/core/cache/CacheV2.js"],"names":[],"mappings":"AAkEA;;;;GAIG;AACH;IAKQ;;;;OAIG;IACH,iCAA2C;IAC3C;;;;OAIG;IACH,oCAA8C;IAE9C,6BAA6B;IAE7B,yBAAyB;IACzB,yBAAyB;IAG7B;;;OAGG;IACH,0BAHa,MAAM,CAKlB;IAED;;;OAGG;IACH,6CAEC;IAED,6BAEC;IAED,mCAEC;IAED,0CAEC;IAED;;;OAGG;IACH,8BAEC;IAED,kCAEC;IAED,oCAEC;IAED;;;;OAIG;IACH,wBAFW,MAAM,QAyBhB;IAED;;;OAGG;IACH,qBAGC;IAED;;;;;;;;;OASG;IACH,sBAJW,MAAM,eACN,MAAM,GACJ,OAAO,CAmBnB;IAGD,0BAyBC;IAED,sBAEC;CACJ"}
1
+ {"version":3,"file":"CacheV2.d.ts","sourceRoot":"","sources":["../../../../src/core/cache/CacheV2.js"],"names":[],"mappings":"AAkEA;;;;;;;;GAQG;AACH;IAKQ;;;;OAIG;IACH,iCAA2C;IAC3C;;;;OAIG;IACH,oCAA8C;IAE9C,6BAA6B;IAE7B,yBAAyB;IACzB,yBAAyB;IAG7B;;;OAGG;IACH,0BAHa,MAAM,CAKlB;IAED;;;OAGG;IACH,6CAEC;IAED,6BAEC;IAED,mCAEC;IAED,0CAEC;IAED;;;OAGG;IACH,8BAEC;IAED,kCAEC;IAED,oCAEC;IAED;;;;OAIG;IACH,wBAFW,MAAM,QAyBhB;IAED;;;OAGG;IACH,qBAGC;IAED;;;;;;;;;OASG;IACH,sBAJW,MAAM,eACN,MAAM,GACJ,OAAO,CAmBnB;IAGD,0BAyBC;IAED,sBAEC;CACJ"}
@@ -65,8 +65,12 @@ class Node {
65
65
  }
66
66
 
67
67
  /**
68
- * UNTESTED, DO NOT USE.
69
- * Use {@link Cache} or {@link LoadingCache} instead
68
+ * NOT PRODUCTION READY. Work in progress, behaviour is incomplete and
69
+ * unverified do not depend on it from product code, and do not add tests
70
+ * to lock in its current half-finished shape. Use {@link Cache} or
71
+ * {@link LoadingCache} for any real caching need until this class is
72
+ * finished and reviewed.
73
+ *
70
74
  * @template K,V
71
75
  */
72
76
  export class CacheV2 {
@@ -1 +1 @@
1
- {"version":3,"file":"LightSystem.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/graphics/ecs/light/LightSystem.js"],"names":[],"mappings":"AAmQA;;;;;GAKG;AACH,2DAHW,MAAM,SACN,KAAK,QAwCf;;uBAxSsB,qBAAqB;sBAGtB,YAAY;AAOlC;IACI;;;;;OAKG;IACH,2CAsFC;IA1EG,kDAAsC;IAGtC,6DAEC;IAGD,eAAoB;IACpB,WAA2B;IAC3B,aAAwB;IAExB;;;;OAIG;IACH,mBAA0B;IAE1B;;;;OAIG;IACH,wBAA2B;IAE3B,gBAAkB;IAElB;;;;OAIG;IACH,kBAAoC;IAGpC;;;;OAIG;IACH,2BAA8B;IAC9B;;;;OAIG;IACH,iBAAoB;IAGpB;;;;OAIG;IACH,4BAAgD;IAqBpD,mCAEC;IAED;;;;OAIG;IACH,uBAHW,MAAM,SACN,MAAM,GAAC,MAAM,GAAC,OAAO,QAkB/B;IAED;;;;OAIG;IACH,4BAHW,KAAK,UACL,MAAM,QAMhB;IAED,2CAOC;IAED,4CASC;IAGD,4CAOC;IAED;;;;;OAKG;IACH,wCAmCC;IAED;;;;;;OAMG;IACH,sCAwBC;CACJ;sCA3PqC,8CAA8C;0BAC1D,qCAAqC;4CAFnB,uDAAuD"}
1
+ {"version":3,"file":"LightSystem.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/graphics/ecs/light/LightSystem.js"],"names":[],"mappings":"AAuQA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,2DANW,YAAY,SACZ,WAAW,oBACX,MAAM,QAmEhB;;uBAvVsB,qBAAqB;sBAEtB,YAAY;AAQlC;IACI;;;;;OAKG;IACH,2CAsFC;IA1EG,kDAAsC;IAGtC,6DAEC;IAGD,eAAoB;IACpB,WAA2B;IAC3B,aAAwB;IAExB;;;;OAIG;IACH,mBAA0B;IAE1B;;;;OAIG;IACH,wBAA2B;IAE3B,gBAAkB;IAElB;;;;OAIG;IACH,kBAAoC;IAGpC;;;;OAIG;IACH,2BAA8B;IAC9B;;;;OAIG;IACH,iBAAoB;IAGpB;;;;OAIG;IACH,4BAAgD;IAqBpD,mCAEC;IAED;;;;OAIG;IACH,uBAHW,MAAM,SACN,MAAM,GAAC,MAAM,GAAC,OAAO,QAkB/B;IAED;;;;OAIG;IACH,4BAHW,KAAK,UACL,MAAM,QAMhB;IAED,2CAOC;IAED,4CASC;IAGD,4CAOC;IAED;;;;;OAKG;IACH,wCAmCC;IAED;;;;;;OAMG;IACH,sCAwBC;CACJ;sCA3PqC,8CAA8C;0BAC1D,qCAAqC;4CAFnB,uDAAuD"}