@woosh/meep-engine 2.119.127 → 2.119.129

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.127",
8
+ "version": "2.119.129",
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":"AAQA,4BAA6B,CAAC,CAAC;AAC/B,6BAA8B,CAAC,CAAC;AAChC,6BAA8B,CAAC,CAAC;AAChC,4BAA6B,CAAC,CAAC;AAE/B;;;;;GAKG;AACH,+BAFU,MAAM,CAE+B;AAE/C;;;GAGG;AACH,wBAFU,MAAM,CAEoB;AAcpC;;;;;GAKG;AACH,iCAFU,MAAM,CAEqB;AAiBrC;;;;;;GAMG;AACH;IAEI;;;;OAIG;IACH,sBAA2E;IAE3E;;;;;OAKG;IACH,mBAFa,WAAW,CAIvB;IAED;;;;OAIG;IACH,uBAAsD;IAEtD;;;OAGG;IACH,oBAFY,YAAY,CAIvB;IAED;;;;OAIG;IACH,sBAAoD;IAEpD;;;;OAIG;IACH,mBAA8B;IAE9B;;;;OAIG;IACH,eAAW;IAEX;;;;OAIG;IACH,eAAY;IAEZ;;;;OAIG;IACH,uBAAmB;IAEnB;;;;OAIG;IACH,eAAmB;IAUnB;;;OAGG;IACH,YAFW,MAAM,EAIhB;IAdD;;;OAGG;IACH,YAFa,MAAM,CAIlB;IAUD;;;;OAIG;IACH,YAFY,MAAM,CAIjB;IAUD;;;OAGG;IACH,qBAFW,MAAM,EAQhB;IAlBD;;;OAGG;IACH,qBAFa,MAAM,CAIlB;IAcD,wBAgBC;IAED;;;;OAIG;IACH,uBA6BC;IAED;;OAEG;IACH,aAIC;IAED;;;OAGG;IACH,iBAFa,MAAM,CAqDlB;IAED;;;;OAIG;IACH,iBAFW,MAAM,QAMhB;IAED;;;;OAIG;IACH,iBAHW,MAAM,GACJ,OAAO,CAOnB;IAED;;;;OAIG;IACH,uBAHW,MAAM,GACJ,MAAM,CAKlB;IAED;;;;OAIG;IACH,uBAHW,MAAM,SACN,MAAM,QAOhB;IAED;;;;OAIG;IACH,oBAHW,MAAM,GACJ,MAAM,CAKlB;IAED;;;;OAIG;IACH,sBAHW,MAAM,UACN,MAAM,QAIhB;IAED;;;;OAIG;IACH,oBAHW,MAAM,GACJ,MAAM,CAKlB;IAED;;;;OAIG;IACH,sBAHW,MAAM,UACN,MAAM,QAIhB;IAED;;;;OAIG;IACH,oBAHW,MAAM,GACJ,MAAM,CAKlB;IAED;;;;OAIG;IACH,sBAHW,MAAM,UACN,MAAM,QAIhB;IAGD;;;;OAIG;IACH,oBAHW,MAAM,GACJ,MAAM,CAKlB;IAED;;;;OAIG;IACH,oBAHW,MAAM,UACN,MAAM,QAKhB;IAED;;;;OAIG;IACH,kBAHW,MAAM,UACN,MAAM,EAAE,GAAC,YAAY,QAe/B;IAED;;;;OAIG;IACH,kBAHW,MAAM,QACN,MAAM,EAAE,GAAC,SAAS,CAAC,MAAM,CAAC,GAAC,KAAK,QAsB1C;IAED;;;;OAIG;IACH,mBAHW,MAAM,QACN,MAAM,EAAE,QAWlB;IAED;;;;;;;;;OASG;IACH,4BARW,MAAM,MACN,MAAM,MACN,MAAM,MACN,MAAM,MACN,MAAM,MACN,MAAM,MACN,MAAM,QAmBhB;IAED;;;;OAIG;IACH,0BAHW,MAAM,GACJ,MAAM,CAoBlB;IAED;;;;;OAKG;IACH,wCAJW,MAAM,WACN,MAAM,GACJ,MAAM,CAoClB;IAED;;;;;OAKG;IACH,oCAJW,MAAM,WACN,MAAM,WACN,MAAM,QAwChB;IAED;;;;OAIG;IACH,kBAHW,MAAM,GACJ,IAAI,CA0GhB;IAED;;;;;OAKG;IACH,wBAqBC;IAED;;;;OAIG;IACH,yBA4BC;IAED;;;;OAIG;IACH,kBAHW,MAAM,GACJ,IAAI,CA6ChB;IAED;;;;;;OAMG;IACH,gBAoMC;IAED;;;OAGG;IACH,oBAIC;IAED;;;;OAIG;IACH,8BAFW,GAAC,QAgCX;IAED;;;;;OAKG;IACH,+BAJW,MAAM,EAAE,sBACR,MAAM,GACJ,MAAM,CAWlB;IAED;;;;;OAKG;IACH,0BA6BC;IAED;;;;;OAKG;IACH,cAJW,MAAM,KACN,MAAM,GACJ,OAAO,CAsCnB;CACJ"}
1
+ {"version":3,"file":"BVH.d.ts","sourceRoot":"","sources":["../../../../../src/core/bvh2/bvh3/BVH.js"],"names":[],"mappings":"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"}
@@ -1066,6 +1066,30 @@ export class BVH {
1066
1066
  return iA;
1067
1067
  }
1068
1068
 
1069
+ /**
1070
+ * Utility method for assigning both children at once
1071
+ * Children must be valid nodes (non-null)
1072
+ * @param {number} parent
1073
+ * @param {number} child_1
1074
+ * @param {number} child_2
1075
+ */
1076
+ node_assign_children(parent, child_1, child_2) {
1077
+ this.node_set_child1(parent, child_1);
1078
+ this.node_set_child2(parent, child_2);
1079
+
1080
+ this.node_set_parent(child_1, parent);
1081
+ this.node_set_parent(child_2, parent);
1082
+
1083
+ this.node_set_height(parent,
1084
+ Math.max(
1085
+ this.node_get_height(child_1),
1086
+ this.node_get_height(child_2),
1087
+ ) + 1
1088
+ );
1089
+
1090
+ this.node_set_combined_aabb(parent, child_1, child_2);
1091
+ }
1092
+
1069
1093
  /**
1070
1094
  * Release all nodes, this essentially resets the tree to empty state
1071
1095
  * 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,7 +42,8 @@ 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
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
47
  const swap_distance = (i + 1) * 2;
48
48
 
49
49
  ebvh_nodes_sort_sah_local4(
@@ -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":"AAoNA;;;;;;;GAOG;AACH,2CAJW,GAAG,QACH,MAAM,iBACN,MAAM,QA0IhB"}
@@ -0,0 +1,357 @@
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_partitions = 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_partitions[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_partitions[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
+ //cost of traversing an intermediate node
58
+ const SAH_COST_INTERMEDIATE = 1.2;
59
+ // cost of performing ray/triangle test
60
+ const SAH_COST_TRIANGLE = 1;
61
+
62
+ /**
63
+ *
64
+ * @param {BVH} bvh
65
+ * @param {number} root
66
+ * @param {number[]|Uint32Array} leaves
67
+ * @param {number} leaf_count
68
+ */
69
+ function optimize_treelet(bvh, root, leaves, leaf_count) {
70
+ // console.log(`TREELET(${leaf_count}): ${leaves.slice(0, leaf_count).join(', ')}`); // DEBUG
71
+
72
+ // see "Fast Parallel Construction of High-Quality Bounding Volume Hierarchies", Algorithm 2
73
+
74
+ // Calculate surface area for each subset
75
+ for (let i = 0; i < leaf_count; i++) {
76
+ const leaf_0 = leaves[i];
77
+ for (let j = i + 1; j < leaf_count; j++) {
78
+ const leaf_1 = leaves[j];
79
+
80
+ const s = (1 << i) | (1 << j);
81
+
82
+ scratch_areas[s] = bvh.node_get_combined_surface_area(leaf_0, leaf_1)
83
+ }
84
+ }
85
+
86
+ // Initialize costs of individual leaves
87
+ for (let i = 0; i < leaf_count; i++) {
88
+ // TODO implement a full cost heuristic
89
+ const leaf = leaves[i];
90
+
91
+ // we use height, which is available, as a proxy for triangle count
92
+ const triangle_count = bvh.node_get_height(leaf);
93
+
94
+ scratch_cost[1 << i] = SAH_COST_TRIANGLE * bvh.node_get_surface_area(leaf) * triangle_count;
95
+ }
96
+
97
+ // Optimize every subset of leaves
98
+ for (let k = 2; k <= leaf_count; k++) {
99
+ for (let s = 1; s <= (1 << leaf_count) - 1; s++) {
100
+ if (bitCount(s) !== k) {
101
+ continue;
102
+ }
103
+
104
+ // Try each way of partitioning the leaves
105
+ let best_cost = Infinity;
106
+ let best_partition = 0;
107
+
108
+ const delta = (s - 1) & s;
109
+ let partition = (-delta) & s;
110
+
111
+ do {
112
+ const cost = scratch_cost[partition] + scratch_cost[s ^ partition];
113
+
114
+ if (cost < best_cost) {
115
+ best_cost = cost;
116
+ best_partition = partition;
117
+ }
118
+
119
+ partition = (partition - delta) & s;
120
+
121
+ } while (partition !== 0)
122
+
123
+ // Calculate final SAH cost (Equation 2)
124
+ const sah_heuristic = SAH_COST_INTERMEDIATE * scratch_areas[s] + best_cost
125
+
126
+ scratch_cost[s] = sah_heuristic;
127
+ scratch_partitions[s] = best_partition;
128
+ }
129
+ }
130
+
131
+ // build tree
132
+ rebuild_treelet(bvh, root, leaves, (1 << leaf_count) - 1);
133
+ }
134
+
135
+
136
+ /**
137
+ *
138
+ * @param {BVH} bvh
139
+ * @param {number} root
140
+ * @param {number} treelet_max_size
141
+ */
142
+ function optimize_at_node(bvh, root, treelet_max_size) {
143
+ if (bvh.node_is_leaf(root)) {
144
+ return;
145
+ }
146
+
147
+ const left = bvh.node_get_child1(root);
148
+ const right = bvh.node_get_child2(root);
149
+
150
+ let treelet_size = 0;
151
+
152
+ if (left !== NULL_NODE) {
153
+ scratch_treelet[treelet_size++] = left;
154
+ }
155
+
156
+ if (right !== NULL_NODE) {
157
+ scratch_treelet[treelet_size++] = right;
158
+ }
159
+
160
+ if (treelet_size === 0) {
161
+ return;
162
+ }
163
+
164
+ while (treelet_size < treelet_max_size) {
165
+ // pick node with the largest surface area
166
+ let best_node_index = -1;
167
+ let best_area = 0; //note that this is deliberate, expanding node with 0 area is pointless
168
+
169
+ for (let i = 0; i < treelet_size; i++) {
170
+ const n = scratch_treelet[i];
171
+
172
+ if (bvh.node_is_leaf(n)) {
173
+ // can't expand
174
+ continue;
175
+ }
176
+
177
+ // TODO maintain list of areas instead of computing them every time
178
+ const area = bvh.node_get_surface_area(n);
179
+ if (area > best_area) {
180
+ best_area = area;
181
+ best_node_index = i;
182
+ }
183
+ }
184
+
185
+ if (best_node_index === -1) {
186
+ break;
187
+ }
188
+
189
+ // expand candidate
190
+ const victim = scratch_treelet[best_node_index];
191
+
192
+ const c0 = bvh.node_get_child1(victim);
193
+ const c1 = bvh.node_get_child2(victim);
194
+
195
+ scratch_treelet[best_node_index] = c0;
196
+ scratch_treelet[treelet_size++] = c1;
197
+
198
+ bvh.release_node(victim);
199
+ }
200
+
201
+ if (treelet_size <= 2) {
202
+ // nothing to do
203
+ return;
204
+ }
205
+
206
+ optimize_treelet(bvh, root, scratch_treelet, treelet_size);
207
+ }
208
+
209
+ const CAME_FROM_TYPE_PARENT = 0;
210
+ const CAME_FROM_TYPE_CHILD_1 = 1;
211
+ const CAME_FROM_TYPE_CHILD_2 = 2;
212
+
213
+ /**
214
+ * Perform linear optimization across the tree, similar to rotations, but we support somewhat arbitrary depth and all possible permutations
215
+ * Note that the root does not move, but everything below the root is subject to change
216
+ * @see "Fast Parallel Construction of High-Quality Bounding Volume Hierarchies" by Kerras, NVIDIA 2013
217
+ * @param {BVH} bvh
218
+ * @param {number} root
219
+ * @param {number} [treelet_size]
220
+ */
221
+ export function ebvh_optimize_treelet(bvh, root, treelet_size = 7) {
222
+ assert.isNonNegativeInteger(treelet_size, 'treelet_size');
223
+ assert.lessThanOrEqual(treelet_size, 8, 'limit is 8');
224
+
225
+ if (root === NULL_NODE) {
226
+ // special case
227
+ return;
228
+ }
229
+
230
+ if (bvh.node_is_leaf(root)) {
231
+ // special case
232
+ return;
233
+ }
234
+
235
+ // we traverse depth-first using stack-less scheme
236
+ let came_from_node = root;
237
+
238
+ let came_from_type = CAME_FROM_TYPE_PARENT;
239
+
240
+ let node = bvh.node_get_child1(came_from_node);
241
+ if (node === NULL_NODE) {
242
+ node = bvh.node_get_child2(came_from_node);
243
+ }
244
+
245
+ if (node === NULL_NODE) {
246
+ // no children, done
247
+ return;
248
+ }
249
+
250
+ const min_height = Math.ceil(Math.log2(treelet_size));
251
+
252
+ while (true) {
253
+
254
+
255
+ if (
256
+ came_from_type === CAME_FROM_TYPE_CHILD_2
257
+ && bvh.node_get_height(node) >= min_height
258
+ ) {
259
+ // only form treelets when moving up
260
+ optimize_at_node(bvh, node, treelet_size);
261
+ }
262
+
263
+ // only add to treelet when traversing up, not down
264
+
265
+ if (bvh.node_is_leaf(node)) {
266
+
267
+ assert.equal(came_from_type, CAME_FROM_TYPE_PARENT);
268
+
269
+ const parent = came_from_node;
270
+ const parent_child_1 = bvh.node_get_child1(parent);
271
+
272
+ if (parent_child_1 === node) {
273
+ came_from_type = CAME_FROM_TYPE_CHILD_1;
274
+ } else {
275
+ came_from_type = CAME_FROM_TYPE_CHILD_2;
276
+ }
277
+
278
+ came_from_node = node;
279
+
280
+ node = parent;
281
+ } else if (came_from_type === CAME_FROM_TYPE_CHILD_2) {
282
+ // finishing traversal of this branch
283
+ came_from_node = node;
284
+ const parent = bvh.node_get_parent(node);
285
+
286
+ if (bvh.node_get_child1(parent) === node) {
287
+ came_from_type = CAME_FROM_TYPE_CHILD_1;
288
+ } else {
289
+ came_from_type = CAME_FROM_TYPE_CHILD_2;
290
+ }
291
+
292
+ if (node === root) {
293
+ // traversed all the way back up
294
+ break;
295
+ }
296
+
297
+ node = parent;
298
+
299
+ } else if (came_from_type === CAME_FROM_TYPE_CHILD_1) {
300
+ // traversing up from left child
301
+ const child2 = bvh.node_get_child2(node);
302
+
303
+ came_from_node = node;
304
+
305
+ if (child2 === NULL_NODE) {
306
+ // no right child, finish this branch
307
+ came_from_type = CAME_FROM_TYPE_CHILD_2; // indicate end
308
+
309
+ node = bvh.node_get_parent(node);
310
+ } else {
311
+ came_from_type = CAME_FROM_TYPE_PARENT;
312
+ node = child2;
313
+ }
314
+
315
+ } else if (came_from_type === CAME_FROM_TYPE_PARENT) {
316
+ const child1 = bvh.node_get_child1(node);
317
+ const child2 = bvh.node_get_child1(node);
318
+
319
+ if (child1 !== NULL_NODE) {
320
+ came_from_type = CAME_FROM_TYPE_PARENT;
321
+ came_from_node = node;
322
+ node = child1;
323
+ } else if (child2 !== NULL_NODE) {
324
+ came_from_type = CAME_FROM_TYPE_PARENT;
325
+ came_from_node = node;
326
+ node = child2;
327
+ } else {
328
+ // this should not happen, as this would mean that node is empty and completely pointless
329
+ // we remove the node and traverse up
330
+
331
+
332
+ const parent_child1 = bvh.node_get_child1(came_from_node);
333
+
334
+ bvh.release_node(node);
335
+
336
+ if (parent_child1 === node) {
337
+ // we are left child
338
+ bvh.node_set_child1(came_from_node, NULL_NODE);
339
+ came_from_node = NULL_NODE;
340
+ came_from_type = CAME_FROM_TYPE_CHILD_1;
341
+ } else {
342
+ bvh.node_set_child2(came_from_node, NULL_NODE);
343
+ came_from_node = NULL_NODE;
344
+ came_from_type = CAME_FROM_TYPE_CHILD_2;
345
+ }
346
+
347
+ node = came_from_node;
348
+ }
349
+
350
+
351
+ }
352
+
353
+ }
354
+
355
+ // optimize last collected treelet
356
+ optimize_at_node(bvh, root, treelet_size);
357
+ }