@woosh/meep-engine 2.119.126 → 2.119.128

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/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "description": "Fully featured ECS game engine written in JavaScript",
6
6
  "type": "module",
7
7
  "author": "Alexander Goldring",
8
- "version": "2.119.126",
8
+ "version": "2.119.128",
9
9
  "main": "build/meep.module.js",
10
10
  "module": "build/meep.module.js",
11
11
  "exports": {
@@ -1 +1 @@
1
- {"version":3,"file":"BVH.d.ts","sourceRoot":"","sources":["../../../../../src/core/bvh2/bvh3/BVH.js"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"BVH.d.ts","sourceRoot":"","sources":["../../../../../src/core/bvh2/bvh3/BVH.js"],"names":[],"mappings":"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"}
@@ -1066,6 +1066,29 @@ export class BVH {
1066
1066
  return iA;
1067
1067
  }
1068
1068
 
1069
+ /**
1070
+ *
1071
+ * @param {number} parent
1072
+ * @param {number} child_1
1073
+ * @param {number} child_2
1074
+ */
1075
+ node_assign_children(parent, child_1, child_2) {
1076
+ this.node_set_child1(parent, child_1);
1077
+ this.node_set_child2(parent, child_2);
1078
+
1079
+ this.node_set_parent(child_1, parent);
1080
+ this.node_set_parent(child_2, parent);
1081
+
1082
+ this.node_set_height(parent,
1083
+ Math.max(
1084
+ this.node_get_height(child_1),
1085
+ this.node_get_height(child_2),
1086
+ ) + 1
1087
+ );
1088
+
1089
+ this.node_set_combined_aabb(parent, child_1, child_2);
1090
+ }
1091
+
1069
1092
  /**
1070
1093
  * Release all nodes, this essentially resets the tree to empty state
1071
1094
  * NOTE: For performance reasons, released memory is not reset, this means that attempting to access cleared nodes' memory will yield garbage data
@@ -1 +1 @@
1
- {"version":3,"file":"ebvh_build_hierarchy.d.ts","sourceRoot":"","sources":["../../../../../src/core/bvh2/bvh3/ebvh_build_hierarchy.js"],"names":[],"mappings":"AAMA;;;;;;;;;;;;GAYG;AACH,0CATW,GAAG,qBACH,MAAM,EAAE,GAAC,WAAW,oBACpB,MAAM,aACN,MAAM,EAAE,GAAC,WAAW,oBACpB,MAAM,2BACN,MAAM,0BACN,MAAM,GACJ,MAAM,CAkFlB"}
1
+ {"version":3,"file":"ebvh_build_hierarchy.d.ts","sourceRoot":"","sources":["../../../../../src/core/bvh2/bvh3/ebvh_build_hierarchy.js"],"names":[],"mappings":"AAKA;;;;;;;;;;;;GAYG;AACH,0CATW,GAAG,qBACH,MAAM,EAAE,GAAC,WAAW,oBACpB,MAAM,aACN,MAAM,EAAE,GAAC,WAAW,oBACpB,MAAM,2BACN,MAAM,0BACN,MAAM,GACJ,MAAM,CAmFlB"}
@@ -3,7 +3,6 @@ import { max2 } from "../../math/max2.js";
3
3
  import { NULL_NODE } from "./BVH.js";
4
4
  import { ebvh_nodes_sort_sah_local4 } from "./ebvh_nodes_sort_sah_local4.js";
5
5
 
6
-
7
6
  /**
8
7
  * Given a set of leaves, build intermediate node hierarchy on top, all the way up to the root
9
8
  * Assumes nodes are spatially sorted, if not - no guarantees can be made about quality
@@ -43,8 +42,9 @@ export function ebvh_build_hierarchy(
43
42
 
44
43
  for (let i = 0; i < sah_optimization_cycle_count; i++) {
45
44
 
46
- // sort intermediate nodes using small locality and SAH metric
47
- const swap_distance = 1 << ((i & 0b111) + 1);
45
+ // Sort intermediate nodes using small locality and SAH metric
46
+ // Sorting progressively larger distance pairs yields best results, based on empirical tests (AG - 2024/12/28)
47
+ const swap_distance = (i + 1) * 2;
48
48
 
49
49
  ebvh_nodes_sort_sah_local4(
50
50
  bvh,
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Perform linear optimization across the tree, similar to rotations, but we support somewhat arbitrary depth and all possible permutations
3
+ * Note that the root does not move, but everything below the root is subject to change
4
+ * @see "Fast Parallel Construction of High-Quality Bounding Volume Hierarchies" by Kerras, NVIDIA 2013
5
+ * @param {BVH} bvh
6
+ * @param {number} root
7
+ * @param {number} [treelet_size]
8
+ */
9
+ export function ebvh_optimize_treelet(bvh: BVH, root: number, treelet_size?: number): void;
10
+ //# sourceMappingURL=ebvh_optimize_treelet.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ebvh_optimize_treelet.d.ts","sourceRoot":"","sources":["../../../../../src/core/bvh2/bvh3/ebvh_optimize_treelet.js"],"names":[],"mappings":"AAyMA;;;;;;;GAOG;AACH,2CAJW,GAAG,QACH,MAAM,iBACN,MAAM,QA0IhB"}
@@ -0,0 +1,346 @@
1
+ import { assert } from "../../assert.js";
2
+ import { lsb_32 } from "../../binary/lsb_32.js";
3
+ import { bitCount } from "../../binary/operations/bitCount.js";
4
+ import { NULL_NODE } from "./BVH.js";
5
+
6
+ const scratch_areas = new Float32Array(256);
7
+ const scratch_cost = new Float32Array(256);
8
+ const scratch_pairings = new Uint32Array(256);
9
+ const scratch_treelet = new Uint32Array(16);
10
+
11
+ /**
12
+ *
13
+ * @param {BVH} bvh
14
+ * @param {number[]} leaves
15
+ * @param {number} mask
16
+ */
17
+ function assemble_mask(bvh, leaves, mask) {
18
+
19
+ const leaf_count = bitCount(mask);
20
+
21
+ if (leaf_count === 1) {
22
+ const leaf_index = lsb_32(mask);
23
+ return leaves[leaf_index];
24
+ }
25
+
26
+ let partitioning_left = scratch_pairings[mask];
27
+ let partition_right = mask & ~(partitioning_left);
28
+
29
+ const parent = bvh.allocate_node();
30
+
31
+ const left_node = assemble_mask(bvh, leaves, partitioning_left);
32
+ const right_node = assemble_mask(bvh, leaves, partition_right);
33
+
34
+ bvh.node_assign_children(parent, left_node, right_node);
35
+
36
+ return parent;
37
+ }
38
+
39
+ /**
40
+ *
41
+ * @param {BVH} bvh
42
+ * @param {number} root
43
+ * @param {number[]} leaves
44
+ * @param {number} mask
45
+ */
46
+ function rebuild_treelet(bvh, root, leaves, mask) {
47
+
48
+ let partitioning_left = scratch_pairings[mask];
49
+ let partition_right = mask & ~(partitioning_left);
50
+
51
+ bvh.node_assign_children(root,
52
+ assemble_mask(bvh, leaves, partitioning_left),
53
+ assemble_mask(bvh, leaves, partition_right),
54
+ );
55
+ }
56
+
57
+ /**
58
+ *
59
+ * @param {BVH} bvh
60
+ * @param {number} root
61
+ * @param {number[]|Uint32Array} leaves
62
+ * @param {number} leaf_count
63
+ */
64
+ function optimize_treelet(bvh, root, leaves, leaf_count) {
65
+ // console.log(`TREELET(${leaf_count}): ${leaves.slice(0, leaf_count).join(', ')}`); // DEBUG
66
+
67
+ // see "Fast Parallel Construction of High-Quality Bounding Volume Hierarchies", Algorithm 2
68
+
69
+ // Calculate surface area for each subset
70
+ for (let i = 0; i < leaf_count; i++) {
71
+ const leaf_0 = leaves[i];
72
+ for (let j = i + 1; j < leaf_count; j++) {
73
+ const leaf_1 = leaves[j];
74
+
75
+ const s = (1 << i) | (1 << j);
76
+
77
+ scratch_areas[s] = bvh.node_get_combined_surface_area(leaf_0, leaf_1)
78
+ }
79
+ }
80
+
81
+ // Initialize costs of individual leaves
82
+ for (let i = 0; i < leaf_count; i++) {
83
+ scratch_cost[1 << i] = bvh.node_get_surface_area(leaves[i]);
84
+ }
85
+
86
+ // Optimize every subset of leaves
87
+ for (let k = 2; k <= leaf_count; k++) {
88
+ for (let s = 1; s <= (1 << leaf_count) - 1; s++) {
89
+ if (bitCount(s) !== k) {
90
+ continue;
91
+ }
92
+
93
+ // Try each way of partitioning the leaves
94
+ let best_cost = Infinity;
95
+ let best_pairing = 0;
96
+
97
+ const delta = (s - 1) & s;
98
+ let p = (-delta) & s;
99
+
100
+ do {
101
+ const cost = scratch_cost[p] + scratch_cost[s ^ p];
102
+
103
+ if (cost < best_cost) {
104
+ best_cost = cost;
105
+ best_pairing = p;
106
+ }
107
+
108
+ p = (p - delta) & s;
109
+
110
+ } while (p !== 0)
111
+
112
+ // Calculate final SAH cost (Equation 2)
113
+ scratch_cost[s] = scratch_areas[s] + best_cost;
114
+ scratch_pairings[s] = best_pairing;
115
+ }
116
+ }
117
+
118
+ // build tree
119
+ rebuild_treelet(bvh, root, leaves, (1 << leaf_count) - 1);
120
+ }
121
+
122
+
123
+
124
+
125
+ /**
126
+ *
127
+ * @param {BVH} bvh
128
+ * @param {number} root
129
+ * @param {number} treelet_max_size
130
+ */
131
+ function optimize_at_node(bvh, root, treelet_max_size) {
132
+ if (bvh.node_is_leaf(root)) {
133
+ return;
134
+ }
135
+
136
+ const left = bvh.node_get_child1(root);
137
+ const right = bvh.node_get_child2(root);
138
+
139
+ let treelet_size = 0;
140
+
141
+ if (left !== NULL_NODE) {
142
+ scratch_treelet[treelet_size++] = left;
143
+ }
144
+
145
+ if (right !== NULL_NODE) {
146
+ scratch_treelet[treelet_size++] = right;
147
+ }
148
+
149
+ if (treelet_size === 0) {
150
+ return;
151
+ }
152
+
153
+ while (treelet_size < treelet_max_size) {
154
+ // pick node with the largest surface area
155
+ let best_node_index = -1;
156
+ let best_area = 0; //note that this is deliberate, expanding node with 0 area is pointless
157
+
158
+ for (let i = 0; i < treelet_size; i++) {
159
+ const n = scratch_treelet[i];
160
+
161
+ if (bvh.node_is_leaf(n)) {
162
+ // can't expand
163
+ continue;
164
+ }
165
+
166
+ // TODO maintain list of areas instead of computing them every time
167
+ const area = bvh.node_get_surface_area(n);
168
+ if (area > best_area) {
169
+ best_area = area;
170
+ best_node_index = i;
171
+ }
172
+ }
173
+
174
+ if (best_node_index === -1) {
175
+ break;
176
+ }
177
+
178
+ // expand candidate
179
+ const victim = scratch_treelet[best_node_index];
180
+
181
+ const c0 = bvh.node_get_child1(victim);
182
+ const c1 = bvh.node_get_child2(victim);
183
+
184
+ scratch_treelet[best_node_index] = c0;
185
+ scratch_treelet[treelet_size++] = c1;
186
+
187
+ bvh.release_node(victim);
188
+ }
189
+
190
+ if (treelet_size <= 2) {
191
+ // nothing to do
192
+ return;
193
+ }
194
+
195
+ optimize_treelet(bvh, root, scratch_treelet, treelet_size);
196
+ }
197
+
198
+ const CAME_FROM_TYPE_PARENT = 0;
199
+ const CAME_FROM_TYPE_CHILD_1 = 1;
200
+ const CAME_FROM_TYPE_CHILD_2 = 2;
201
+
202
+ /**
203
+ * Perform linear optimization across the tree, similar to rotations, but we support somewhat arbitrary depth and all possible permutations
204
+ * Note that the root does not move, but everything below the root is subject to change
205
+ * @see "Fast Parallel Construction of High-Quality Bounding Volume Hierarchies" by Kerras, NVIDIA 2013
206
+ * @param {BVH} bvh
207
+ * @param {number} root
208
+ * @param {number} [treelet_size]
209
+ */
210
+ export function ebvh_optimize_treelet(bvh, root, treelet_size = 7) {
211
+ assert.isNonNegativeInteger(treelet_size, 'treelet_size');
212
+ assert.lessThanOrEqual(treelet_size, 8, 'limit is 8');
213
+
214
+ if (root === NULL_NODE) {
215
+ // special case
216
+ return;
217
+ }
218
+
219
+ if (bvh.node_is_leaf(root)) {
220
+ // special case
221
+ return;
222
+ }
223
+
224
+ // we traverse depth-first using stack-less scheme
225
+ let came_from_node = root;
226
+
227
+ let came_from_type = CAME_FROM_TYPE_PARENT;
228
+
229
+ let node = bvh.node_get_child1(came_from_node);
230
+ if (node === NULL_NODE) {
231
+ node = bvh.node_get_child2(came_from_node);
232
+ }
233
+
234
+ if (node === NULL_NODE) {
235
+ // no children, done
236
+ return;
237
+ }
238
+
239
+ const min_height = Math.ceil(Math.log2(treelet_size));
240
+
241
+ while (true) {
242
+
243
+
244
+ if (
245
+ came_from_type === CAME_FROM_TYPE_CHILD_2
246
+ && bvh.node_get_height(node) >= min_height
247
+ ) {
248
+ // only form treelets when moving up
249
+ optimize_at_node(bvh, node, treelet_size);
250
+ }
251
+
252
+ // only add to treelet when traversing up, not down
253
+
254
+ if (bvh.node_is_leaf(node)) {
255
+
256
+ assert.equal(came_from_type, CAME_FROM_TYPE_PARENT);
257
+
258
+ const parent = came_from_node;
259
+ const parent_child_1 = bvh.node_get_child1(parent);
260
+
261
+ if (parent_child_1 === node) {
262
+ came_from_type = CAME_FROM_TYPE_CHILD_1;
263
+ } else {
264
+ came_from_type = CAME_FROM_TYPE_CHILD_2;
265
+ }
266
+
267
+ came_from_node = node;
268
+
269
+ node = parent;
270
+ } else if (came_from_type === CAME_FROM_TYPE_CHILD_2) {
271
+ // finishing traversal of this branch
272
+ came_from_node = node;
273
+ const parent = bvh.node_get_parent(node);
274
+
275
+ if (bvh.node_get_child1(parent) === node) {
276
+ came_from_type = CAME_FROM_TYPE_CHILD_1;
277
+ } else {
278
+ came_from_type = CAME_FROM_TYPE_CHILD_2;
279
+ }
280
+
281
+ if (node === root) {
282
+ // traversed all the way back up
283
+ break;
284
+ }
285
+
286
+ node = parent;
287
+
288
+ } else if (came_from_type === CAME_FROM_TYPE_CHILD_1) {
289
+ // traversing up from left child
290
+ const child2 = bvh.node_get_child2(node);
291
+
292
+ came_from_node = node;
293
+
294
+ if (child2 === NULL_NODE) {
295
+ // no right child, finish this branch
296
+ came_from_type = CAME_FROM_TYPE_CHILD_2; // indicate end
297
+
298
+ node = bvh.node_get_parent(node);
299
+ } else {
300
+ came_from_type = CAME_FROM_TYPE_PARENT;
301
+ node = child2;
302
+ }
303
+
304
+ } else if (came_from_type === CAME_FROM_TYPE_PARENT) {
305
+ const child1 = bvh.node_get_child1(node);
306
+ const child2 = bvh.node_get_child1(node);
307
+
308
+ if (child1 !== NULL_NODE) {
309
+ came_from_type = CAME_FROM_TYPE_PARENT;
310
+ came_from_node = node;
311
+ node = child1;
312
+ } else if (child2 !== NULL_NODE) {
313
+ came_from_type = CAME_FROM_TYPE_PARENT;
314
+ came_from_node = node;
315
+ node = child2;
316
+ } else {
317
+ // this should not happen, as this would mean that node is empty and completely pointless
318
+ // we remove the node and traverse up
319
+
320
+
321
+ const parent_child1 = bvh.node_get_child1(came_from_node);
322
+
323
+ bvh.release_node(node);
324
+
325
+ if (parent_child1 === node) {
326
+ // we are left child
327
+ bvh.node_set_child1(came_from_node, NULL_NODE);
328
+ came_from_node = NULL_NODE;
329
+ came_from_type = CAME_FROM_TYPE_CHILD_1;
330
+ } else {
331
+ bvh.node_set_child2(came_from_node, NULL_NODE);
332
+ came_from_node = NULL_NODE;
333
+ came_from_type = CAME_FROM_TYPE_CHILD_2;
334
+ }
335
+
336
+ node = came_from_node;
337
+ }
338
+
339
+
340
+ }
341
+
342
+ }
343
+
344
+ // optimize last collected treelet
345
+ optimize_at_node(bvh, root, treelet_size);
346
+ }