@woosh/meep-engine 2.46.26 → 2.46.27

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 (30) hide show
  1. package/package.json +1 -1
  2. package/src/core/bvh2/binary/2/BinaryUint32BVH.js +9 -12
  3. package/src/core/bvh2/bvh3/EBBVHLeafProxy.js +60 -0
  4. package/src/core/bvh2/bvh3/ExplicitBinaryBoundingVolumeHierarchy.js +1 -0
  5. package/src/core/collection/HashMap.js +21 -5
  6. package/src/core/events/signal/Signal.js +3 -3
  7. package/src/core/geom/3d/cone/computeConeBoundingBox.js +7 -10
  8. package/src/core/geom/3d/ray/ray3_array_apply_matrix4.js +15 -13
  9. package/src/engine/graphics/ecs/mesh-v2/ShadedGeometry.js +6 -24
  10. package/src/engine/graphics/ecs/mesh-v2/ShadedGeometrySystem.js +4 -25
  11. package/src/engine/graphics/ecs/mesh-v2/aggregate/SGMeshSystem.js +2 -6
  12. package/src/engine/graphics/geometry/bvh/buffered/BVHGeometryRaycaster.js +1 -1
  13. package/src/engine/graphics/particles/particular/engine/parameter/ParameterLookupTable.d.ts +1 -1
  14. package/src/engine/graphics/particles/particular/engine/parameter/ParameterLookupTable.js +10 -43
  15. package/src/engine/graphics/render/forward_plus/LightManager.js +164 -56
  16. package/src/engine/graphics/render/forward_plus/{PointLightData.js → LightRenderMetadata.js} +18 -11
  17. package/src/engine/graphics/render/forward_plus/cluster/compute_light_data_hash.js +1 -1
  18. package/src/engine/graphics/render/forward_plus/model/AbstractLight.js +1 -1
  19. package/src/engine/graphics/render/forward_plus/model/Decal.js +1 -4
  20. package/src/engine/graphics/render/forward_plus/model/PointLight.js +6 -12
  21. package/src/engine/graphics/render/forward_plus/model/SpotLight.js +0 -4
  22. package/src/engine/graphics/render/forward_plus/plugin/ForwardPlusRenderingPlugin.d.ts +3 -0
  23. package/src/engine/graphics/render/forward_plus/plugin/ForwardPlusRenderingPlugin.js +12 -0
  24. package/src/engine/graphics/render/forward_plus/prototype/prototypeLightManager.js +3 -2
  25. package/src/engine/graphics/render/forward_plus/query/point_light_inside_volume.js +1 -1
  26. package/src/engine/graphics/render/forward_plus/query/query_bvh_frustum_from_objects.js +3 -3
  27. package/src/engine/graphics/sh3/path_tracer/PathTracedMesh.js +1 -1
  28. package/src/engine/graphics/texture/atlas/CachingTextureAtlas.js +0 -1
  29. package/src/engine/intelligence/behavior/util/BranchBehavior.js +5 -0
  30. package/src/engine/intelligence/behavior/util/SelectorBehavior.js +0 -83
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.46.26",
8
+ "version": "2.46.27",
9
9
  "main": "build/meep.module.js",
10
10
  "module": "build/meep.module.js",
11
11
  "exports": {
@@ -119,10 +119,7 @@ function build_morton(data, address, matrix) {
119
119
 
120
120
  }
121
121
 
122
- const scratch_box_0 = [];
123
- const scratch_box_1 = [];
124
- const scratch_box_2 = [];
125
-
122
+ const scratch_box_0 = new Float32Array(18);
126
123
  const stack = [];
127
124
 
128
125
  export class BinaryUint32BVH {
@@ -294,16 +291,16 @@ export class BinaryUint32BVH {
294
291
  */
295
292
  __compute_bounds_area_of_3_boxes(a, b, c) {
296
293
  this.readBounds(a, scratch_box_0, 0);
297
- this.readBounds(b, scratch_box_1, 0);
298
- this.readBounds(c, scratch_box_2, 0);
294
+ this.readBounds(b, scratch_box_0, 6);
295
+ this.readBounds(c, scratch_box_0, 12);
299
296
 
300
- const x0 = min3(scratch_box_0[0], scratch_box_1[0], scratch_box_2[0]);
301
- const y0 = min3(scratch_box_0[1], scratch_box_1[1], scratch_box_2[1]);
302
- const z0 = min3(scratch_box_0[2], scratch_box_1[2], scratch_box_2[2]);
297
+ const x0 = min3(scratch_box_0[0], scratch_box_0[6], scratch_box_0[12]);
298
+ const y0 = min3(scratch_box_0[1], scratch_box_0[7], scratch_box_0[13]);
299
+ const z0 = min3(scratch_box_0[2], scratch_box_0[8], scratch_box_0[14]);
303
300
 
304
- const x1 = max3(scratch_box_0[3], scratch_box_1[3], scratch_box_2[3]);
305
- const y1 = max3(scratch_box_0[4], scratch_box_1[4], scratch_box_2[4]);
306
- const z1 = max3(scratch_box_0[5], scratch_box_1[5], scratch_box_2[5]);
301
+ const x1 = max3(scratch_box_0[3], scratch_box_0[9], scratch_box_0[15]);
302
+ const y1 = max3(scratch_box_0[4], scratch_box_0[10], scratch_box_0[16]);
303
+ const z1 = max3(scratch_box_0[5], scratch_box_0[11], scratch_box_0[17]);
307
304
 
308
305
  return aabb3_compute_half_surface_area(x0, y0, z0, x1, y1, z1);
309
306
  }
@@ -0,0 +1,60 @@
1
+ import { assert } from "../../assert.js";
2
+
3
+ /**
4
+ * A convenience class to work with BVH leaf nodes
5
+ */
6
+ export class EBBVHLeafProxy {
7
+ /**
8
+ *
9
+ * @type {ExplicitBinaryBoundingVolumeHierarchy|null}
10
+ */
11
+ #tree = null;
12
+
13
+ /**
14
+ *
15
+ * @type {number}
16
+ */
17
+ #node_id = -1;
18
+
19
+ /**
20
+ * @readonly
21
+ * @type {number[]}
22
+ */
23
+ bounds = new Float32Array(6);
24
+
25
+ get is_linked(){
26
+ return this.#node_id !== -1;
27
+ }
28
+
29
+ /**
30
+ *
31
+ * @param {ExplicitBinaryBoundingVolumeHierarchy} tree
32
+ * @param {number} data Must be a uint32
33
+ */
34
+ link(tree, data) {
35
+ assert.defined(tree, 'tree');
36
+ assert.isNonNegativeInteger(data, 'data');
37
+
38
+ this.#tree = tree;
39
+
40
+ const node_id = tree.allocate_node();
41
+
42
+ this.#node_id = node_id;
43
+
44
+ tree.node_set_aabb(node_id, this.bounds);
45
+ tree.node_set_user_data(node_id, data);
46
+ tree.insert_leaf(node_id);
47
+ }
48
+
49
+ unlink() {
50
+ this.#tree.remove_leaf(this.#node_id);
51
+ this.#tree.release_node(this.#node_id);
52
+
53
+ this.#node_id = -1;
54
+ this.#tree = null;
55
+ }
56
+
57
+ write_bounds() {
58
+ this.#tree.node_move_aabb(this.#node_id, this.bounds);
59
+ }
60
+ }
@@ -267,6 +267,7 @@ export class ExplicitBinaryBoundingVolumeHierarchy {
267
267
  */
268
268
  node_set_user_data(id, value) {
269
269
  assert.isNonNegativeInteger(id, 'id');
270
+ assert.isNonNegativeInteger(value, 'value');
270
271
 
271
272
  this.__data_uint32[ELEMENT_WORD_COUNT * id + COLUMN_USER_DATA] = value;
272
273
  }
@@ -11,8 +11,9 @@ class MapEntry {
11
11
  *
12
12
  * @param {K} key
13
13
  * @param {V} value
14
+ * @param {number} hash
14
15
  */
15
- constructor(key, value) {
16
+ constructor(key, value, hash) {
16
17
  /**
17
18
  *
18
19
  * @type {K}
@@ -23,6 +24,12 @@ class MapEntry {
23
24
  * @type {V}
24
25
  */
25
26
  this.value = value;
27
+
28
+ /**
29
+ *
30
+ * @type {number}
31
+ */
32
+ this.hash = hash;
26
33
  }
27
34
  }
28
35
 
@@ -219,7 +226,11 @@ export class HashMap {
219
226
 
220
227
  const entryKey = entry.key;
221
228
 
222
- if (entryKey === key || this.keyEqualityFunction(entryKey, key)) {
229
+
230
+ if (
231
+ entry.hash === raw_hash
232
+ && (entryKey === key || this.keyEqualityFunction(entryKey, key))
233
+ ) {
223
234
 
224
235
  // found record with matching key, replace the value
225
236
 
@@ -234,7 +245,7 @@ export class HashMap {
234
245
 
235
246
  }
236
247
 
237
- bucket.push(new MapEntry(key, value));
248
+ bucket.push(new MapEntry(key, value,raw_hash));
238
249
 
239
250
  const old_size = this.size;
240
251
  const new_size = old_size + 1;
@@ -269,8 +280,10 @@ export class HashMap {
269
280
 
270
281
  const entryKey = entry.key;
271
282
 
272
- if (entryKey === key || this.keyEqualityFunction(entryKey, key)) {
273
-
283
+ if (
284
+ entry.hash === raw_hash
285
+ && (entryKey === key || this.keyEqualityFunction(entryKey, key))
286
+ ) {
274
287
  return entry.value;
275
288
 
276
289
  }
@@ -432,6 +445,9 @@ export class HashMap {
432
445
 
433
446
  //check hash
434
447
  const raw_hash = this.keyHashFunction(entry.key);
448
+
449
+ entry.hash = raw_hash;
450
+
435
451
  const actual_bucket_index = this.__compute_bucket_index(raw_hash);
436
452
 
437
453
 
@@ -147,7 +147,7 @@ export class Signal {
147
147
  * @param {*} [context]
148
148
  */
149
149
  addOne(h, context) {
150
- assert.equal(typeof h, "function", "Handler is not a function");
150
+ assert.isFunction( h, "handler");
151
151
 
152
152
  const handler = new SignalHandler(h, context);
153
153
 
@@ -195,8 +195,8 @@ export class Signal {
195
195
  * Remove all handlers
196
196
  */
197
197
  removeAll() {
198
- const signalHandlers = this.handlers;
199
- signalHandlers.splice(0, signalHandlers.length);
198
+ const handlers = this.handlers;
199
+ handlers.splice(0, handlers.length);
200
200
  }
201
201
 
202
202
  /**
@@ -4,7 +4,7 @@ import { v3_angle_between } from "../../v3_angle_between.js";
4
4
 
5
5
  /**
6
6
  * NOTE: used stackoverflow answer as a basis for circle part: https://stackoverflow.com/questions/2592011/bounding-boxes-for-circle-and-arcs-in-3d
7
- * @param {AABB3} result
7
+ * @param {number[]|ArrayLike<number>|Float32Array} result
8
8
  * @param {number} originX
9
9
  * @param {number} originY
10
10
  * @param {number} originZ
@@ -41,14 +41,11 @@ export function computeConeBoundingBox(result, originX, originY, originZ, direct
41
41
  const cz1 = centerZ + rZ;
42
42
 
43
43
  //combine bounds of the origin and base of the cone
44
- const x0 = min2(originX, cx0);
45
- const y0 = min2(originY, cy0);
46
- const z0 = min2(originZ, cz0);
44
+ result[0] = min2(originX, cx0);
45
+ result[1] = min2(originY, cy0);
46
+ result[2] = min2(originZ, cz0);
47
47
 
48
- const x1 = max2(originX, cx1);
49
- const y1 = max2(originY, cy1);
50
- const z1 = max2(originZ, cz1);
51
-
52
- //write result
53
- result.setBounds(x0, y0, z0, x1, y1, z1);
48
+ result[3] = max2(originX, cx1);
49
+ result[4] = max2(originY, cy1);
50
+ result[5] = max2(originZ, cz1);
54
51
  }
@@ -1,18 +1,20 @@
1
1
  /**
2
2
  *
3
3
  * @param {number[]|ArrayLike<number>|Float32Array} output 6 component vector, [origin_x, origin_y, origin_z, direction_x, direction_y, direction_z]
4
+ * @param {number} output_offset
4
5
  * @param {number[]|ArrayLike<number>|Float32Array} input 6 component vector, [origin_x, origin_y, origin_z, direction_x, direction_y, direction_z]
6
+ * @param {number} input_offset
5
7
  * @param {number[]|ArrayLike<number>|Float32Array} m4 4x4 matrix
6
8
  * @returns {boolean} false if matrix transformation is impossible, such as when scale of the matrix is 0
7
9
  */
8
- export function ray3_array_apply_matrix4(output, input, m4) {
9
- const origin_x = input[0];
10
- const origin_y = input[1];
11
- const origin_z = input[2];
10
+ export function ray3_array_apply_matrix4(output, output_offset, input, input_offset, m4) {
11
+ const origin_x = input[input_offset + 0];
12
+ const origin_y = input[input_offset + 1];
13
+ const origin_z = input[input_offset + 2];
12
14
 
13
- const direction_x = input[3];
14
- const direction_y = input[4];
15
- const direction_z = input[5];
15
+ const direction_x = input[input_offset + 3];
16
+ const direction_y = input[input_offset + 4];
17
+ const direction_z = input[input_offset + 5];
16
18
 
17
19
  // transform ray to local space (inlined for speed)
18
20
  const det = m4[3] * origin_x + m4[7] * origin_y + m4[11] * origin_z + m4[15];
@@ -44,13 +46,13 @@ export function ray3_array_apply_matrix4(output, input, m4) {
44
46
  const out_direction_normalized_y = out_direction_y * out_direction_magnitude_inverse;
45
47
  const out_direction_normalized_z = out_direction_z * out_direction_magnitude_inverse;
46
48
 
47
- output[0] = out_origin_x;
48
- output[1] = out_origin_y;
49
- output[2] = out_origin_z;
49
+ output[output_offset + 0] = out_origin_x;
50
+ output[output_offset + 1] = out_origin_y;
51
+ output[output_offset + 2] = out_origin_z;
50
52
 
51
- output[3] = out_direction_normalized_x;
52
- output[4] = out_direction_normalized_y;
53
- output[5] = out_direction_normalized_z;
53
+ output[output_offset + 3] = out_direction_normalized_x;
54
+ output[output_offset + 4] = out_direction_normalized_y;
55
+ output[output_offset + 5] = out_direction_normalized_z;
54
56
 
55
57
  return true;
56
58
  }
@@ -6,6 +6,7 @@ import { mat4 } from "gl-matrix";
6
6
  import { ray3_array_apply_matrix4 } from "../../../../core/geom/3d/ray/ray3_array_apply_matrix4.js";
7
7
  import { GeometrySpatialQueryAccelerator } from "../../geometry/buffered/query/GeometrySpatialQueryAccelerator.js";
8
8
  import { assert } from "../../../../core/assert.js";
9
+ import { EBBVHLeafProxy } from "../../../../core/bvh2/bvh3/EBBVHLeafProxy.js";
9
10
 
10
11
  /**
11
12
  * @readonly
@@ -55,26 +56,7 @@ export class ShadedGeometry {
55
56
  */
56
57
  this.depth_material = null;
57
58
 
58
- /**
59
- *
60
- * @type {number}
61
- * @private
62
- */
63
- this.__bvh_leaf_id = -1;
64
-
65
- /**
66
- *
67
- * @type {ExplicitBinaryBoundingVolumeHierarchy|null}
68
- * @private
69
- */
70
- this.__bvh_tree = null;
71
-
72
- /**
73
- * @readonly
74
- * @type {number[]|Float32Array}
75
- * @private
76
- */
77
- this.__bvh_aabb = new Float32Array(6);
59
+ this.__bvh_leaf = new EBBVHLeafProxy();
78
60
 
79
61
  /**
80
62
  * Transient, assigned in the system
@@ -240,7 +222,7 @@ export class ShadedGeometry {
240
222
  * @param {AABB3} destination
241
223
  */
242
224
  getBoundingBox(destination) {
243
- const aabb = this.__bvh_aabb;
225
+ const aabb = this.__bvh_leaf.bounds;
244
226
 
245
227
  destination.readFromArray(aabb);
246
228
  }
@@ -280,7 +262,7 @@ export class ShadedGeometry {
280
262
  updateTransform() {
281
263
  this.update_bounds();
282
264
 
283
- this.__bvh_tree.node_move_aabb(this.__bvh_leaf_id, this.__bvh_aabb);
265
+ this.__bvh_leaf.write_bounds();
284
266
  }
285
267
 
286
268
  update_bounds() {
@@ -317,7 +299,7 @@ export class ShadedGeometry {
317
299
  scratch_aabb3_array[4] = y1;
318
300
  scratch_aabb3_array[5] = z1;
319
301
 
320
- aabb3_matrix4_project(this.__bvh_aabb, scratch_aabb3_array, this.transform);
302
+ aabb3_matrix4_project(this.__bvh_leaf.bounds, scratch_aabb3_array, this.transform);
321
303
  }
322
304
 
323
305
  /**
@@ -333,7 +315,7 @@ export class ShadedGeometry {
333
315
  mat4.invert(scratch_m4, transform_matrix4);
334
316
 
335
317
  // transform ray to local space
336
- if (!ray3_array_apply_matrix4(scratch_ray_0, ray, scratch_m4)) {
318
+ if (!ray3_array_apply_matrix4(scratch_ray_0,0, ray,0, scratch_m4)) {
337
319
  // invalid transform matrix
338
320
  return false;
339
321
  }
@@ -289,25 +289,13 @@ export class ShadedGeometrySystem extends System {
289
289
 
290
290
  sg.update_bounds();
291
291
 
292
- t.position.onChanged.add(sg.updateTransform, sg);
293
- t.rotation.onChanged.add(sg.updateTransform, sg);
294
- t.scale.onChanged.add(sg.updateTransform, sg);
292
+ t.subscribeAllChanges(sg.updateTransform, sg);
295
293
 
296
294
  // remember entity for lookups
297
295
  sg.__entity = entity;
298
296
 
299
297
  // insert BVH entry
300
- const bvh = this.__bvh_binary;
301
-
302
- const bvh_node_id = bvh.allocate_node();
303
-
304
- bvh.node_set_aabb(bvh_node_id, sg.__bvh_aabb);
305
- bvh.node_set_user_data(bvh_node_id, entity);
306
- bvh.insert_leaf(bvh_node_id);
307
-
308
- sg.__bvh_tree = bvh;
309
- sg.__bvh_leaf_id = bvh_node_id;
310
-
298
+ sg.__bvh_leaf.link(this.__bvh_binary, entity);
311
299
 
312
300
  // update usage count
313
301
  const geometry_id = sg.geometry.id;
@@ -324,19 +312,10 @@ export class ShadedGeometrySystem extends System {
324
312
  * @param {number} entity
325
313
  */
326
314
  unlink(sg, t, entity) {
327
- t.position.onChanged.remove(sg.updateTransform, sg);
328
- t.rotation.onChanged.remove(sg.updateTransform, sg);
329
- t.scale.onChanged.remove(sg.updateTransform, sg);
315
+ t.unsubscribeAllChanges(sg.updateTransform, sg);
330
316
 
331
317
  // disconnect BVH
332
- const node_id = sg.__bvh_leaf_id;
333
-
334
- this.__bvh_binary.remove_leaf(node_id);
335
- this.__bvh_binary.release_node(node_id);
336
-
337
- // reset BVH references
338
- sg.__bvh_leaf_id = -1;
339
- sg.__bvh_tree = null;
318
+ sg.__bvh_leaf.unlink();
340
319
 
341
320
  const geometry_id = sg.geometry.id;
342
321
  const count_existing = this.__geometry_usage_counters.get(geometry_id);
@@ -162,15 +162,11 @@ export class SGMeshSystem extends System {
162
162
 
163
163
  entity_node.build(ecd);
164
164
 
165
- transform.position.onChanged.add(copy_transform);
166
- transform.rotation.onChanged.add(copy_transform);
167
- transform.scale.onChanged.add(copy_transform);
165
+ transform.subscribeAllChanges(copy_transform);
168
166
 
169
167
  entity_node.on.destroyed.addOne(() => {
170
168
 
171
- transform.position.onChanged.remove(copy_transform);
172
- transform.rotation.onChanged.remove(copy_transform);
173
- transform.scale.onChanged.remove(copy_transform);
169
+ transform.unsubscribeAllChanges(copy_transform);
174
170
 
175
171
  });
176
172
 
@@ -194,7 +194,7 @@ export class BVHGeometryRaycaster {
194
194
  directionX, directionY, directionZ
195
195
  );
196
196
 
197
- ray3_array_apply_matrix4(ray_tmp, ray_tmp, m4_tmp);
197
+ ray3_array_apply_matrix4(ray_tmp, 0,ray_tmp,0, m4_tmp);
198
198
 
199
199
  const _originX = ray_tmp[0];
200
200
  const _originY = ray_tmp[1];
@@ -11,5 +11,5 @@ export class ParameterLookupTable {
11
11
 
12
12
  write(value: number[], positions: number[]): void
13
13
 
14
- static from(itemSize: number, value: number[], positions: number[]): ParameterLookupTable
14
+ static from(itemSize: number, value: number[], positions?: number[]): ParameterLookupTable
15
15
  }
@@ -6,6 +6,7 @@ import { ParameterLookupTableFlags } from "./ParameterLookupTableFlags.js";
6
6
  import { ParameterLookupTableSerializationAdapter } from "../emitter/serde/ParameterLookupTableSerializationAdapter.js";
7
7
  import { computeHashIntegerArray } from "../../../../../../core/collection/array/computeHashIntegerArray.js";
8
8
  import { computeHashFloatArray } from "../../../../../../core/math/hash/computeHashFloatArray.js";
9
+ import { isArrayEqualStrict } from "../../../../../../core/collection/array/isArrayEqualStrict.js";
9
10
 
10
11
 
11
12
  export class ParameterLookupTable {
@@ -211,7 +212,7 @@ export class ParameterLookupTable {
211
212
 
212
213
  if (positions === undefined) {
213
214
 
214
- console.warn('positions are undefined, assuming uniform distribution');
215
+ //console.warn('positions are undefined, assuming uniform distribution');
215
216
  this.computeUniformPositions();
216
217
 
217
218
  } else {
@@ -309,52 +310,18 @@ export class ParameterLookupTable {
309
310
  * @returns {boolean}
310
311
  */
311
312
  equals(other) {
312
- if (this.itemSize !== other.itemSize) {
313
- return false;
314
- }
315
-
316
- const thisData = this.data;
317
- const otherData = other.data;
318
-
319
- const thisDataLength = thisData.length;
320
- const otherDataLength = otherData.length;
321
-
322
- if (thisDataLength !== otherDataLength) {
323
- return false;
313
+ if(this === other){
314
+ // identity shortcut
315
+ return true;
324
316
  }
325
317
 
326
- const thisPositions = this.positions;
327
- const otherPositions = other.positions;
328
-
329
- const thisPositionsLength = thisPositions.length;
330
- const otherPositionsLength = otherPositions.length;
331
-
332
- if (thisPositionsLength !== otherPositionsLength) {
318
+ if (this.itemSize !== other.itemSize) {
333
319
  return false;
334
320
  }
335
321
 
336
- let i;
337
-
338
- for (i = 0; i < thisPositionsLength; i++) {
339
- const pA = thisPositions[i];
340
- const pB = otherPositions[i];
341
-
342
- if (pA !== pB) {
343
- return false;
344
- }
345
- }
346
-
347
- for (i = 0; i < thisDataLength; i++) {
348
- const dA = thisData[i];
349
- const dB = otherData[i];
350
-
351
- if (dA !== dB) {
352
- return false;
353
- }
354
- }
355
-
356
- //equal
357
- return true;
322
+ return isArrayEqualStrict(this.data, other.data)
323
+ && isArrayEqualStrict(this.positions, other.positions)
324
+ ;
358
325
  }
359
326
 
360
327
  validate() {
@@ -408,7 +375,7 @@ export class ParameterLookupTable {
408
375
  static from(itemSize, values, positions) {
409
376
  const r = new ParameterLookupTable(itemSize);
410
377
 
411
- r.write(values, positions)
378
+ r.write(values, positions);
412
379
 
413
380
  return r;
414
381
  }
@@ -1,5 +1,4 @@
1
- import { BinaryNode } from "../../../../core/bvh2/BinaryNode.js";
2
- import { PointLightData } from "./PointLightData.js";
1
+ import { LightRenderMetadata } from "./LightRenderMetadata.js";
3
2
  import {
4
3
  ClampToEdgeWrapping,
5
4
  DataTexture,
@@ -14,7 +13,6 @@ import {
14
13
  UnsignedShortType
15
14
  } from "three";
16
15
  import Vector3 from "../../../../core/geom/Vector3.js";
17
- import { query_bvh_frustum_from_objects } from "./query/query_bvh_frustum_from_objects.js";
18
16
  import { IncrementalDeltaSet } from "../visibility/IncrementalDeltaSet.js";
19
17
  import { computeFrustumCorners } from "./computeFrustumCorners.js";
20
18
  import { read_plane_pair } from "./cluster/read_plane_pair.js";
@@ -42,10 +40,19 @@ import { array_copy } from "../../../../core/collection/array/copyArray.js";
42
40
  import { arrayQuickSort } from "../../../../core/collection/array/arrayQuickSort.js";
43
41
  import { frustum_from_camera } from "../../ecs/camera/frustum_from_camera.js";
44
42
  import { arraySwapElements } from "../../../../core/collection/array/arraySwapElements.js";
45
- import { compareObjectsByNumericId } from "../../../../core/model/object/compareObjectsByNumericId.js";
46
43
  import { slice_frustum_linear_to_points } from "../../../../core/geom/3d/frustum/slice_frustum_linear_to_points.js";
47
44
  import { read_cluster_frustum_corners } from "../../../../core/geom/3d/frustum/read_cluster_frustum_corners.js";
48
45
  import { assign_cluster, LOOKUP_CACHE, scratch_corners, scratch_frustum_planes } from "./assign_cluster.js";
46
+ import {
47
+ ExplicitBinaryBoundingVolumeHierarchy
48
+ } from "../../../../core/bvh2/bvh3/ExplicitBinaryBoundingVolumeHierarchy.js";
49
+ import {
50
+ bvh_query_user_data_overlaps_frustum
51
+ } from "../../../../core/bvh2/bvh3/query/bvh_query_user_data_overlaps_frustum.js";
52
+ import { point_light_inside_volume } from "./query/point_light_inside_volume.js";
53
+ import { bvh_query_leaves_ray } from "../../../../core/bvh2/bvh3/query/bvh_query_leaves_ray.js";
54
+ import { ray3_array_apply_matrix4 } from "../../../../core/geom/3d/ray/ray3_array_apply_matrix4.js";
55
+ import { aabb3_intersects_ray } from "../../../../core/bvh2/aabb3/aabb3_intersects_ray.js";
49
56
 
50
57
 
51
58
  /**
@@ -82,7 +89,7 @@ function encode_light_descriptor(address, light) {
82
89
 
83
90
  /**
84
91
  *
85
- * @param {IncrementalDeltaSet<PointLightData>} data_set
92
+ * @param {IncrementalDeltaSet<LightRenderMetadata>} data_set
86
93
  * @param {BinaryUint32BVH} bvh
87
94
  */
88
95
  function build_light_data_bvh(data_set, bvh) {
@@ -96,18 +103,18 @@ function build_light_data_bvh(data_set, bvh) {
96
103
 
97
104
  /**
98
105
  *
99
- * @type {PointLightData}
106
+ * @type {LightRenderMetadata}
100
107
  */
101
108
  const datum = elements[i];
102
109
 
103
- const bb = datum.bvh_leaf_main;
110
+ const bb = datum.bvh_leaf.bounds;
104
111
 
105
112
  const payload = encode_light_descriptor(datum.address, datum.light);
106
113
 
107
114
  bvh.setLeafData(
108
115
  i, payload,
109
- bb.x0, bb.y0, bb.z0,
110
- bb.x1, bb.y1, bb.z1
116
+ bb[0], bb[1], bb[2],
117
+ bb[3], bb[4], bb[5],
111
118
  );
112
119
  }
113
120
 
@@ -117,22 +124,32 @@ function build_light_data_bvh(data_set, bvh) {
117
124
  bvh.build();
118
125
  }
119
126
 
127
+ /**
128
+ *
129
+ * @param {LightRenderMetadata} a
130
+ * @param {LightRenderMetadata} b
131
+ * @returns {number}
132
+ */
133
+ function compareLRMByLightId(a, b) {
134
+ return b.light.id - a.light.id;
135
+ }
136
+
120
137
  export class LightManager {
121
138
 
122
- constructor() {
139
+ /**
140
+ * @readonly
141
+ * @type {ExplicitBinaryBoundingVolumeHierarchy}
142
+ */
143
+ #light_data_bvh = new ExplicitBinaryBoundingVolumeHierarchy();
123
144
 
124
- /**
125
- * @type {BinaryNode<PointLightData>}
126
- * @private
127
- */
128
- this.__light_data_bvh = new BinaryNode();
145
+ /**
146
+ * Mapping from a light/decal object ID internal metadata
147
+ * @type {Map<number,LightRenderMetadata>}
148
+ */
149
+ #metadata_map = new Map();
129
150
 
130
- /**
131
- *
132
- * @type {Map<AbstractLight, PointLightData>}
133
- * @private
134
- */
135
- this.__light_data_map = new Map();
151
+
152
+ constructor() {
136
153
 
137
154
  /**
138
155
  * Number of cluster slices along each dimension
@@ -235,22 +252,22 @@ export class LightManager {
235
252
 
236
253
  /**
237
254
  *
238
- * @type {IncrementalDeltaSet<PointLightData>}
255
+ * @type {IncrementalDeltaSet<LightRenderMetadata>}
239
256
  * @readonly
240
257
  * @private
241
258
  */
242
- this.__visible_lights = new IncrementalDeltaSet(compareObjectsByNumericId);
259
+ this.__visible_lights = new IncrementalDeltaSet(compareLRMByLightId);
243
260
  /**
244
261
  *
245
- * @type {IncrementalDeltaSet<PointLightData>}
262
+ * @type {IncrementalDeltaSet<LightRenderMetadata>}
246
263
  * @readonly
247
264
  * @private
248
265
  */
249
- this.__visible_decals = new IncrementalDeltaSet(compareObjectsByNumericId);
266
+ this.__visible_decals = new IncrementalDeltaSet(compareLRMByLightId);
250
267
 
251
268
  /**
252
269
  *
253
- * @type {PointLightData[]}
270
+ * @type {LightRenderMetadata[]}
254
271
  * @private
255
272
  */
256
273
  this.__sorted_visible_lights = [];
@@ -487,7 +504,7 @@ export class LightManager {
487
504
 
488
505
  /**
489
506
  *
490
- * @param {PointLightData} data
507
+ * @param {LightRenderMetadata} data
491
508
  * @private
492
509
  */
493
510
  __handle_visible_decal_added(data) {
@@ -519,7 +536,7 @@ export class LightManager {
519
536
 
520
537
  /**
521
538
  *
522
- * @param {PointLightData} data
539
+ * @param {LightRenderMetadata} data
523
540
  * @private
524
541
  */
525
542
  __handle_visible_decal_removed(data) {
@@ -556,7 +573,7 @@ export class LightManager {
556
573
 
557
574
  /**
558
575
  *
559
- * @param {PointLightData} data
576
+ * @param {LightRenderMetadata} data
560
577
  * @private
561
578
  */
562
579
  __handle_visible_light_added(data) {
@@ -569,7 +586,7 @@ export class LightManager {
569
586
 
570
587
  /**
571
588
  *
572
- * @param {PointLightData} data
589
+ * @param {LightRenderMetadata} data
573
590
  * @private
574
591
  */
575
592
  __handle_visible_light_removed(data) {
@@ -581,7 +598,7 @@ export class LightManager {
581
598
 
582
599
  /**
583
600
  *
584
- * @param {PointLightData} light
601
+ * @param {LightRenderMetadata} light
585
602
  * @returns {number}
586
603
  * @protected
587
604
  */
@@ -599,7 +616,7 @@ export class LightManager {
599
616
 
600
617
  /**
601
618
  * DEBUG method
602
- * @param {PointLightData} lights
619
+ * @param {LightRenderMetadata} lights
603
620
  * @param {number} count
604
621
  * @returns {number} lower = better sorting score
605
622
  * @private
@@ -696,7 +713,7 @@ export class LightManager {
696
713
 
697
714
  /**
698
715
  *
699
- * @type {IncrementalDeltaSet<PointLightData>}
716
+ * @type {IncrementalDeltaSet<LightRenderMetadata>}
700
717
  */
701
718
  const visible_lights = this.__visible_lights;
702
719
 
@@ -710,20 +727,54 @@ export class LightManager {
710
727
 
711
728
  read_frustum_planes_to_array(this.__view_frustum.planes, scratch_frustum_planes)
712
729
 
713
- const match_count = query_bvh_frustum_from_objects(nodes, 0, this.__light_data_bvh, scratch_frustum_planes, this.__view_frustum_points);
730
+ /*
731
+ Search is done in 2 phases:
732
+ 1) broad phase using bounding boxes
733
+ 2) granular phase where we use object-specific shape to check against frustum
734
+ */
735
+
736
+ const broad_match_count = bvh_query_user_data_overlaps_frustum(nodes, 0, this.#light_data_bvh, scratch_frustum_planes);
737
+
738
+ for (let i = 0; i < broad_match_count; i++) {
714
739
 
715
- for (let i = 0; i < match_count; i++) {
740
+ const light_id = nodes[i];
716
741
 
717
742
  /**
718
743
  *
719
- * @type {PointLightData}
744
+ * @type {LightRenderMetadata}
720
745
  */
721
- const light_data = nodes[i];
746
+ const light_data = this.#metadata_map.get(light_id);
747
+
748
+ /**
749
+ *
750
+ * @type {PointLight|Decal}
751
+ */
752
+ const light = light_data.light;
753
+
754
+ if (light.isDecal === true) {
755
+
722
756
 
723
- if (light_data.light instanceof Decal) {
724
757
  // decals go into a separate bucket
725
758
  visible_decals.push(light_data);
726
- } else {
759
+
760
+ } else if (light.isPointLight === true) {
761
+ // perform granular check
762
+
763
+ const light_position = light.position;
764
+
765
+ const light_x = light_position.x;
766
+ const light_y = light_position.y;
767
+ const light_z = light_position.z;
768
+
769
+ const radius = light.radius.getValue();
770
+
771
+ if (!point_light_inside_volume(light_x, light_y, light_z, radius, this.__view_frustum_points, scratch_frustum_planes)) {
772
+ // outside of view frustum
773
+ continue;
774
+ }
775
+
776
+
777
+ // register as visible
727
778
  visible_lights.push(light_data);
728
779
  }
729
780
 
@@ -738,7 +789,7 @@ export class LightManager {
738
789
 
739
790
  /**
740
791
  *
741
- * @type {PointLightData[]}
792
+ * @type {LightRenderMetadata[]}
742
793
  */
743
794
  const visible_lights = this.__sorted_visible_lights;
744
795
 
@@ -1082,17 +1133,17 @@ export class LightManager {
1082
1133
  * @param {number} z
1083
1134
  */
1084
1135
  setTileMapResolution(x, y, z) {
1085
- assert.isNumber(x,'x');
1086
- assert.isNonNegativeInteger(x,'x');
1087
- assert.isFiniteNumber(x,'x');
1136
+ assert.isNumber(x, 'x');
1137
+ assert.isNonNegativeInteger(x, 'x');
1138
+ assert.isFiniteNumber(x, 'x');
1088
1139
 
1089
- assert.isNumber(y,'y');
1090
- assert.isNonNegativeInteger(y,'y');
1091
- assert.isFiniteNumber(y,'y');
1140
+ assert.isNumber(y, 'y');
1141
+ assert.isNonNegativeInteger(y, 'y');
1142
+ assert.isFiniteNumber(y, 'y');
1092
1143
 
1093
- assert.isNumber(z,'z');
1094
- assert.isNonNegativeInteger(z,'z');
1095
- assert.isFiniteNumber(z,'z');
1144
+ assert.isNumber(z, 'z');
1145
+ assert.isNonNegativeInteger(z, 'z');
1146
+ assert.isFiniteNumber(z, 'z');
1096
1147
 
1097
1148
  const r = this.__tiles_resolution;
1098
1149
 
@@ -1126,13 +1177,11 @@ export class LightManager {
1126
1177
  * @param {AbstractLight} light
1127
1178
  */
1128
1179
  addLight(light) {
1129
- const lightData = new PointLightData(light);
1130
-
1131
- lightData.link();
1180
+ const lightData = new LightRenderMetadata(light);
1132
1181
 
1133
- this.__light_data_bvh.insertNode(lightData.bvh_leaf_main);
1182
+ lightData.link(this.#light_data_bvh);
1134
1183
 
1135
- this.__light_data_map.set(light, lightData);
1184
+ this.#metadata_map.set(light.id, lightData);
1136
1185
  }
1137
1186
 
1138
1187
  /**
@@ -1142,7 +1191,9 @@ export class LightManager {
1142
1191
  */
1143
1192
  removeLight(light) {
1144
1193
 
1145
- const data = this.__light_data_map.get(light);
1194
+ const light_id = light.id;
1195
+
1196
+ const data = this.#metadata_map.get(light_id);
1146
1197
 
1147
1198
  if (data === undefined) {
1148
1199
  return false;
@@ -1150,8 +1201,65 @@ export class LightManager {
1150
1201
 
1151
1202
  data.unlink();
1152
1203
 
1153
- this.__light_data_map.delete(light);
1204
+ this.#metadata_map.delete(light_id);
1154
1205
 
1155
1206
  return true;
1156
1207
  }
1208
+
1209
+ /**
1210
+ *
1211
+ * @param {Decal[]} result
1212
+ * @param {number} result_offset
1213
+ * @param {number[]} query
1214
+ * @param {number} query_offset
1215
+ * @returns {number} number of elements added to the result
1216
+ */
1217
+ query_decals_by_ray(result, result_offset, query, query_offset) {
1218
+
1219
+ const leaves = [];
1220
+ const local_ray = new Float32Array(6);
1221
+
1222
+ const hit_count = bvh_query_leaves_ray(this.#light_data_bvh, leaves, 0,
1223
+ query[query_offset], query[query_offset + 1], query[query_offset + 2],
1224
+ query[query_offset + 3], query[query_offset + 4], query[query_offset + 5]
1225
+ );
1226
+
1227
+ let result_count = 0;
1228
+
1229
+ for (let i = 0; i < hit_count; i++) {
1230
+ const node = leaves[i];
1231
+ const light_id = this.#light_data_bvh.node_get_user_data(node);
1232
+
1233
+ const metadata = this.#metadata_map.get(light_id);
1234
+
1235
+ /**
1236
+ *
1237
+ * @type {Decal}
1238
+ */
1239
+ const prop = metadata.light;
1240
+
1241
+ if (prop.isDecal !== true) {
1242
+ // wrong prop type
1243
+ continue;
1244
+ }
1245
+
1246
+ // transform ray into decal's local space
1247
+ ray3_array_apply_matrix4(local_ray, 0, query, query_offset, prop.transform_inverse);
1248
+
1249
+ if (aabb3_intersects_ray(
1250
+ -0.5, -0.5, -0.5,
1251
+ 0.5, 0.5, 0.5,
1252
+
1253
+ local_ray[0], local_ray[1], local_ray[2],
1254
+ local_ray[3], local_ray[4], local_ray[5],
1255
+ )) {
1256
+ result[result_offset + result_count] = prop;
1257
+
1258
+ result_count++;
1259
+ }
1260
+
1261
+ }
1262
+
1263
+ return result_count;
1264
+ }
1157
1265
  }
@@ -1,6 +1,8 @@
1
- import { LeafNode } from "../../../../core/bvh2/LeafNode.js";
1
+ import { EBBVHLeafProxy } from "../../../../core/bvh2/bvh3/EBBVHLeafProxy.js";
2
+
3
+ export class LightRenderMetadata {
4
+
2
5
 
3
- export class PointLightData {
4
6
  /**
5
7
  *
6
8
  * @param {AbstractLight} light
@@ -18,12 +20,12 @@ export class PointLightData {
18
20
  */
19
21
  this.address = 0;
20
22
 
21
- this.bvh_leaf_main = new LeafNode(this, 0, 0, 0, 0, 0, 0);
23
+ this.bvh_leaf = new EBBVHLeafProxy();
22
24
  }
23
25
 
24
26
  /**
25
27
  *
26
- * @param {PointLightData} other
28
+ * @param {LightRenderMetadata} other
27
29
  * @returns {number}
28
30
  */
29
31
  compare(other) {
@@ -31,17 +33,22 @@ export class PointLightData {
31
33
  }
32
34
 
33
35
  update() {
36
+ const leaf = this.bvh_leaf;
34
37
 
35
- const leaf = this.bvh_leaf_main;
38
+ this.light.getAABB(leaf.bounds);
36
39
 
37
- this.light.getAABB(leaf);
38
-
39
- if (leaf.parentNode !== null) {
40
- leaf.parentNode.bubbleRefit();
40
+ if (leaf.is_linked) {
41
+ leaf.write_bounds();
41
42
  }
42
43
  }
43
44
 
44
- link() {
45
+ /**
46
+ *
47
+ * @param {ExplicitBinaryBoundingVolumeHierarchy} bvh
48
+ */
49
+ link(bvh) {
50
+ this.bvh_leaf.link(bvh, this.light.id);
51
+
45
52
  this.update();
46
53
 
47
54
  this.light.onDimensionChanged(this.update, this);
@@ -52,6 +59,6 @@ export class PointLightData {
52
59
  this.light.offDimensionChanged(this.update, this);
53
60
 
54
61
  // disconnect from bvh
55
- this.bvh_leaf_main.disconnect();
62
+ this.bvh_leaf.unlink();
56
63
  }
57
64
  }
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  *
3
- * @param {PointLightData[]} nodes
3
+ * @param {LightRenderMetadata[]} nodes
4
4
  * @param {number} count
5
5
  */
6
6
  export function compute_light_data_hash(nodes, count) {
@@ -40,7 +40,7 @@ export class AbstractLight {
40
40
 
41
41
  /**
42
42
  *
43
- * @param {AABB3} result
43
+ * @param {number[]|ArrayLike<number>|Float32Array} result
44
44
  * @returns {void}
45
45
  */
46
46
  getAABB(result) {
@@ -6,7 +6,6 @@ import { mat4 } from "gl-matrix";
6
6
  import Signal from "../../../../../core/events/signal/Signal.js";
7
7
 
8
8
  const corners = [];
9
- const aabb_array = new Float32Array(6);
10
9
 
11
10
  export class Decal extends AbstractLight {
12
11
  constructor() {
@@ -89,9 +88,7 @@ export class Decal extends AbstractLight {
89
88
  getAABB(result) {
90
89
  aabb3_build_corners(corners, 0, -0.5, -0.5, -0.5, 0.5, 0.5, 0.5);
91
90
 
92
- aabb3_matrix4_project_by_corners(aabb_array, corners, this.transform);
93
-
94
- result.readFromArray(aabb_array, 0);
91
+ aabb3_matrix4_project_by_corners(result, corners, this.transform);
95
92
  }
96
93
 
97
94
  toArray(destination, address) {
@@ -36,10 +36,6 @@ export class PointLight extends AbstractLight {
36
36
  this.position.writeToArray(result, 0);
37
37
  }
38
38
 
39
- /**
40
- *
41
- * @param {AABB3} result
42
- */
43
39
  getAABB(result) {
44
40
  const r = this.radius.getValue();
45
41
  const p = this.position;
@@ -48,15 +44,13 @@ export class PointLight extends AbstractLight {
48
44
  const center_y = p.y;
49
45
  const center_z = p.z;
50
46
 
51
- result.setBounds(
52
- center_x - r,
53
- center_y - r,
54
- center_z - r,
47
+ result[0] = center_x - r;
48
+ result[1] = center_y - r;
49
+ result[2] = center_z - r;
55
50
 
56
- center_x + r,
57
- center_y + r,
58
- center_z + r
59
- );
51
+ result[3] = center_x + r;
52
+ result[4] = center_y + r;
53
+ result[5] = center_z + r;
60
54
  }
61
55
 
62
56
  onDimensionChanged(listener, context) {
@@ -65,10 +65,6 @@ export class SpotLight extends AbstractLight {
65
65
  result[2] = p.z + d.z * m;
66
66
  }
67
67
 
68
- /**
69
- *
70
- * @param {AABB3} result
71
- */
72
68
  getAABB(result) {
73
69
  const position = this.position;
74
70
  const direction = this.direction;
@@ -1,5 +1,8 @@
1
1
  import {EnginePlugin} from "../../../../plugin/EnginePlugin";
2
+ import {Decal} from "../../../ecs/decal/v2/Decal";
2
3
 
3
4
  export class ForwardPlusRenderingPlugin extends EnginePlugin {
4
5
  setClusterResolution(x: number, y: number, z: number): void
6
+
7
+ queryDecalsByRay(result: Decal[], result_offset: number, query: ArrayLike<number>, query_offset?: number): number
5
8
  }
@@ -88,4 +88,16 @@ export class ForwardPlusRenderingPlugin extends EnginePlugin {
88
88
  getLightManager() {
89
89
  return this.__light_manager;
90
90
  }
91
+
92
+ /**
93
+ *
94
+ * @param {Decal[]} result
95
+ * @param {number} result_offset
96
+ * @param {number[]} query
97
+ * @param {number} query_offset
98
+ * @returns {number} number of elements added to the result
99
+ */
100
+ queryDecalsByRay(result, result_offset, query, query_offset=0){
101
+ return this.__light_manager.query_decals_by_ray(result,result_offset, query,query_offset);
102
+ }
91
103
  }
@@ -1559,11 +1559,12 @@ function draw_camera_view_planes() {
1559
1559
  }
1560
1560
 
1561
1561
  // prepare_scene_2();
1562
- prepare_scene_decal_0();
1562
+ // prepare_scene_decal_0();
1563
1563
  // prepare_scene_decal_1();
1564
1564
  // prepare_scene_decal_2();
1565
1565
  // prepare_scene_9();
1566
- // prepare_scene_0();
1566
+ // prepare_scene_0(); // stress test
1567
+ prepare_scene_4(); // sponza
1567
1568
  animate();
1568
1569
 
1569
1570
  // draw_camera_view_planes();
@@ -6,7 +6,7 @@ import { detailed_sphere_frustum_intersection_test } from "./detailed_sphere_fru
6
6
  * @param {number} light_y
7
7
  * @param {number} light_z
8
8
  * @param {number} radius
9
- * @param {number[]} points
9
+ * @param {number[]|Float32Array} points
10
10
  * @param {number[]|Float32Array|Float64Array} planes
11
11
  * @returns {boolean}
12
12
  */
@@ -14,9 +14,9 @@ let stackPointer = 0;
14
14
 
15
15
  /**
16
16
  * Most of the data is in flat continuous array for cache coherence
17
- * @param {PointLightData[]} destination
17
+ * @param {LightRenderMetadata[]} destination
18
18
  * @param {number} destination_offset
19
- * @param {BinaryNode<PointLightData>} root
19
+ * @param {BinaryNode<LightRenderMetadata>} root
20
20
  * @param {number[]|Float32Array|Float64Array} planes
21
21
  * @param {number[]|Float32Array|Float64Array} points
22
22
  * @returns {number} number of items added to the destination, non-negative integer
@@ -84,7 +84,7 @@ export function query_bvh_frustum_from_objects(
84
84
 
85
85
  /**
86
86
  *
87
- * @type {PointLightData}
87
+ * @type {LightRenderMetadata}
88
88
  */
89
89
  const light_data = n.object;
90
90
 
@@ -69,7 +69,7 @@ export class PathTracedMesh {
69
69
  hit(out, ray, min_distance, max_distance) {
70
70
  //transform ray
71
71
  const m4 = this.__transform_inverse;
72
- ray3_array_apply_matrix4(local_ray, ray, m4);
72
+ ray3_array_apply_matrix4(local_ray,0, ray,0, m4);
73
73
 
74
74
  const scale_d = this.__local_scale_inverse;
75
75
 
@@ -29,7 +29,6 @@ export class CachingTextureAtlas extends AbstractTextureAtlas {
29
29
  /**
30
30
  *
31
31
  * @param {AbstractTextureAtlas} atlas
32
- * @param {number} cache_size
33
32
  */
34
33
  constructor({
35
34
  atlas
@@ -5,6 +5,11 @@ import { BehaviorStatus } from "../BehaviorStatus.js";
5
5
 
6
6
  const DEFAULT = new SucceedingBehavior();
7
7
 
8
+ /**
9
+ * Utility behavior that works just like an IF/ELSE statement
10
+ * If you are not sure if this is the right behavior for your use-case, consider using a selector behavior instead,
11
+ * as selector is a more commonly applicable behavior type
12
+ */
8
13
  export class BranchBehavior extends Behavior {
9
14
  #successBranch = DEFAULT
10
15
  #failureBranch = DEFAULT
@@ -1,83 +0,0 @@
1
- import { CompositeBehavior } from "../composite/CompositeBehavior.js";
2
- import { BehaviorStatus } from "../BehaviorStatus.js";
3
-
4
- export class SelectorBehavior extends CompositeBehavior {
5
- constructor() {
6
- super();
7
-
8
- this.__index = 0;
9
- }
10
-
11
- /**
12
- *
13
- * @param {Behavior[]} children
14
- * @return {SelectorBehavior}
15
- */
16
- static from(children) {
17
- const r = new SelectorBehavior();
18
-
19
- r.__children = children.slice();
20
-
21
- return r;
22
- }
23
-
24
- tick(timeDelta) {
25
- const children = this.__children;
26
- const child_count = children.length;
27
-
28
- if (child_count === 0) {
29
- // arbitrarily decide success
30
- return BehaviorStatus.Succeeded;
31
- }
32
-
33
- for (; ;) {
34
- const child = children[this.__index];
35
-
36
- const s = child.tick(timeDelta);
37
-
38
- //if child succeeds or keeps running, do the same
39
- if (s !== BehaviorStatus.Failed) {
40
- return s;
41
- }
42
-
43
-
44
- //continue search for fallback behavior until the last child
45
- this.__index++;
46
-
47
- if (this.__index >= child_count) {
48
- return BehaviorStatus.Failed;
49
- }
50
-
51
- //initialize new child
52
- const next = children[this.__index];
53
-
54
- child.finalize();
55
- next.initialize(this.context);
56
- }
57
-
58
- //we should never reach this point
59
- return BehaviorStatus.Invalid;
60
- }
61
-
62
- finalize() {
63
- super.finalize();
64
-
65
- if (this.__children.length > 0) {
66
- const b = this.__children[this.__index];
67
-
68
- b.finalize();
69
- }
70
- }
71
-
72
-
73
- initialize(context) {
74
- super.initialize(context);
75
-
76
- this.__index = 0;
77
-
78
- if (this.__children.length > 0) {
79
- this.__children[0].initialize(context);
80
- }
81
- }
82
-
83
- }