@woosh/meep-engine 2.46.31 → 2.46.33

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.
@@ -658,7 +658,7 @@ class Signal {
658
658
  * @param {*} [context]
659
659
  */
660
660
  addOne(h, context) {
661
- assert.equal(typeof h, "function", "Handler is not a function");
661
+ assert.isFunction( h, "handler");
662
662
 
663
663
  const handler = new SignalHandler(h, context);
664
664
 
@@ -706,8 +706,8 @@ class Signal {
706
706
  * Remove all handlers
707
707
  */
708
708
  removeAll() {
709
- const signalHandlers = this.handlers;
710
- signalHandlers.splice(0, signalHandlers.length);
709
+ const handlers = this.handlers;
710
+ handlers.splice(0, handlers.length);
711
711
  }
712
712
 
713
713
  /**
@@ -49812,7 +49812,7 @@ function arraySwapElements(array, index0, index1) {
49812
49812
  array[index1] = t;
49813
49813
  }
49814
49814
 
49815
- const stack$9 = [];
49815
+ const stack$8 = [];
49816
49816
 
49817
49817
  /**
49818
49818
  * @template T
@@ -49833,14 +49833,14 @@ function arrayQuickSort(data, score_function, score_function_context, start, end
49833
49833
  let stackPointer = 2;
49834
49834
  let i, j;
49835
49835
 
49836
- stack$9[0] = start;
49837
- stack$9[1] = end;
49836
+ stack$8[0] = start;
49837
+ stack$8[1] = end;
49838
49838
 
49839
49839
  while (stackPointer > 0) {
49840
49840
  stackPointer -= 2;
49841
49841
 
49842
- const right = stack$9[stackPointer + 1];
49843
- const left = stack$9[stackPointer];
49842
+ const right = stack$8[stackPointer + 1];
49843
+ const left = stack$8[stackPointer];
49844
49844
 
49845
49845
  i = left;
49846
49846
  j = right;
@@ -49873,12 +49873,12 @@ function arrayQuickSort(data, score_function, score_function_context, start, end
49873
49873
 
49874
49874
  /* recursion */
49875
49875
  if (left < j) {
49876
- stack$9[stackPointer++] = left;
49877
- stack$9[stackPointer++] = j;
49876
+ stack$8[stackPointer++] = left;
49877
+ stack$8[stackPointer++] = j;
49878
49878
  }
49879
49879
  if (i < right) {
49880
- stack$9[stackPointer++] = i;
49881
- stack$9[stackPointer++] = right;
49880
+ stack$8[stackPointer++] = i;
49881
+ stack$8[stackPointer++] = right;
49882
49882
  }
49883
49883
  }
49884
49884
  }
@@ -49900,14 +49900,14 @@ function array_quick_sort_by_comparator(data, compare_function, compare_function
49900
49900
  let stackPointer = 2;
49901
49901
  let i, j;
49902
49902
 
49903
- stack$9[0] = start;
49904
- stack$9[1] = end;
49903
+ stack$8[0] = start;
49904
+ stack$8[1] = end;
49905
49905
 
49906
49906
  while (stackPointer > 0) {
49907
49907
  stackPointer -= 2;
49908
49908
 
49909
- const right = stack$9[stackPointer + 1];
49910
- const left = stack$9[stackPointer];
49909
+ const right = stack$8[stackPointer + 1];
49910
+ const left = stack$8[stackPointer];
49911
49911
 
49912
49912
  i = left;
49913
49913
  j = right;
@@ -49943,12 +49943,12 @@ function array_quick_sort_by_comparator(data, compare_function, compare_function
49943
49943
 
49944
49944
  /* recursion */
49945
49945
  if (left < j) {
49946
- stack$9[stackPointer++] = left;
49947
- stack$9[stackPointer++] = j;
49946
+ stack$8[stackPointer++] = left;
49947
+ stack$8[stackPointer++] = j;
49948
49948
  }
49949
49949
  if (i < right) {
49950
- stack$9[stackPointer++] = i;
49951
- stack$9[stackPointer++] = right;
49950
+ stack$8[stackPointer++] = i;
49951
+ stack$8[stackPointer++] = right;
49952
49952
  }
49953
49953
  }
49954
49954
  }
@@ -50493,7 +50493,7 @@ function aabb3_intersects_frustum_array(x0, y0, z0, x1, y1, z1, frustum) {
50493
50493
  * NOTE: {@link result} and {@link aabb} must be different objects
50494
50494
  * @param {ArrayLike<number>|number[]|Float32Array} result
50495
50495
  * @param {ArrayLike<number>|number[]|Float32Array} aabb
50496
- * @param {number[]} matrix 4x4 matrix
50496
+ * @param {ArrayLike<number>|number[]|Float32Array} matrix 4x4 matrix
50497
50497
  */
50498
50498
  function aabb3_matrix4_project(result, aabb, matrix) {
50499
50499
  assert.notEqual(result, aabb, 'input and output must not be the same for algorithm to work correctly');
@@ -51780,8 +51780,8 @@ class BVHVisitor {
51780
51780
  }
51781
51781
  }
51782
51782
 
51783
- const stack$8 = [];
51784
- let stackPointer$4 = 0;
51783
+ const stack$7 = [];
51784
+ let stackPointer$3 = 0;
51785
51785
 
51786
51786
  /**
51787
51787
  *
@@ -51791,13 +51791,13 @@ let stackPointer$4 = 0;
51791
51791
  function traverseBinaryNodeUsingVisitor(node, visitor) {
51792
51792
  let n;
51793
51793
 
51794
- const stackOffset = stackPointer$4;
51794
+ const stackOffset = stackPointer$3;
51795
51795
 
51796
- stack$8[stackPointer$4++] = node;
51796
+ stack$7[stackPointer$3++] = node;
51797
51797
 
51798
- while (stackPointer$4-- > stackOffset) {
51798
+ while (stackPointer$3-- > stackOffset) {
51799
51799
 
51800
- n = stack$8[stackPointer$4];
51800
+ n = stack$7[stackPointer$3];
51801
51801
 
51802
51802
  if (n.isBinaryNode) {
51803
51803
  const traverseDeeper = visitor.visitBinary(n);
@@ -51808,7 +51808,7 @@ function traverseBinaryNodeUsingVisitor(node, visitor) {
51808
51808
 
51809
51809
  // has a LEFT child
51810
51810
 
51811
- stack$8[stackPointer$4++] = n.left;
51811
+ stack$7[stackPointer$3++] = n.left;
51812
51812
 
51813
51813
  }
51814
51814
 
@@ -51816,7 +51816,7 @@ function traverseBinaryNodeUsingVisitor(node, visitor) {
51816
51816
 
51817
51817
  // has a RIGHT child
51818
51818
 
51819
- stack$8[stackPointer$4++] = n.right;
51819
+ stack$7[stackPointer$3++] = n.right;
51820
51820
 
51821
51821
  }
51822
51822
 
@@ -51828,7 +51828,7 @@ function traverseBinaryNodeUsingVisitor(node, visitor) {
51828
51828
  }
51829
51829
 
51830
51830
  //drop the stack frame
51831
- stackPointer$4 = stackOffset;
51831
+ stackPointer$3 = stackOffset;
51832
51832
  }
51833
51833
 
51834
51834
  /**
@@ -51936,12 +51936,12 @@ function aabb3_combined_surface_area(a, b) {
51936
51936
  *
51937
51937
  * @type {number}
51938
51938
  */
51939
- let stackPointer$3 = 0;
51939
+ let stackPointer$2 = 0;
51940
51940
  /**
51941
51941
  *
51942
51942
  * @type {Node[]}
51943
51943
  */
51944
- const stack$7 = [];
51944
+ const stack$6 = [];
51945
51945
 
51946
51946
  class BinaryNode extends Node {
51947
51947
  constructor() {
@@ -51989,30 +51989,30 @@ class BinaryNode extends Node {
51989
51989
  traversePreOrderUsingStack(visitor, thisArg) {
51990
51990
  let visitCount = 0;
51991
51991
 
51992
- const stackOffset = stackPointer$3;
51992
+ const stackOffset = stackPointer$2;
51993
51993
 
51994
- stack$7[stackPointer$3++] = this;
51994
+ stack$6[stackPointer$2++] = this;
51995
51995
  let n;
51996
- while (stackPointer$3-- > stackOffset) {
51996
+ while (stackPointer$2-- > stackOffset) {
51997
51997
 
51998
51998
  visitCount++;
51999
51999
 
52000
- n = stack$7[stackPointer$3];
52000
+ n = stack$6[stackPointer$2];
52001
52001
 
52002
52002
  const traverseDeeper = visitor.call(thisArg, n);
52003
52003
 
52004
52004
  if (traverseDeeper !== false && n.isBinaryNode) {
52005
52005
  if (n.right !== null) {
52006
- stack$7[stackPointer$3++] = n.right;
52006
+ stack$6[stackPointer$2++] = n.right;
52007
52007
  }
52008
52008
  if (n.left !== null) {
52009
- stack$7[stackPointer$3++] = n.left;
52009
+ stack$6[stackPointer$2++] = n.left;
52010
52010
  }
52011
52011
  }
52012
52012
  }
52013
52013
 
52014
52014
  // drop stack frame
52015
- stackPointer$3 = stackOffset;
52015
+ stackPointer$2 = stackOffset;
52016
52016
 
52017
52017
  return visitCount;
52018
52018
  }
@@ -52025,33 +52025,33 @@ class BinaryNode extends Node {
52025
52025
  traverseLeavesPreOrderUsingStack(visitor, thisArg) {
52026
52026
  let visitCount = 0;
52027
52027
 
52028
- const stackOffset = stackPointer$3;
52028
+ const stackOffset = stackPointer$2;
52029
52029
 
52030
- stack$7[stackPointer$3++] = this;
52030
+ stack$6[stackPointer$2++] = this;
52031
52031
 
52032
52032
  let n;
52033
52033
 
52034
- while (stackPointer$3-- > stackOffset) {
52034
+ while (stackPointer$2-- > stackOffset) {
52035
52035
 
52036
52036
  visitCount++;
52037
52037
 
52038
- n = stack$7[stackPointer$3];
52038
+ n = stack$6[stackPointer$2];
52039
52039
 
52040
52040
  if (n.isLeafNode) {
52041
52041
  visitor.call(thisArg, n);
52042
52042
  } else {
52043
52043
  //a binary node
52044
52044
  if (n.right !== null) {
52045
- stack$7[stackPointer$3++] = n.right;
52045
+ stack$6[stackPointer$2++] = n.right;
52046
52046
  }
52047
52047
  if (n.left !== null) {
52048
- stack$7[stackPointer$3++] = n.left;
52048
+ stack$6[stackPointer$2++] = n.left;
52049
52049
  }
52050
52050
  }
52051
52051
  }
52052
52052
 
52053
52053
  // drop stack frame
52054
- stackPointer$3 = stackOffset;
52054
+ stackPointer$2 = stackOffset;
52055
52055
 
52056
52056
  return visitCount;
52057
52057
  }
@@ -52620,15 +52620,15 @@ class BinaryNode extends Node {
52620
52620
  * @returns {number} number of objects added to the result
52621
52621
  */
52622
52622
  requestLeafIntersectionsAABB3(result, x0, y0, z0, x1, y1, z1) {
52623
- const startOffset = stackPointer$3;
52623
+ const startOffset = stackPointer$2;
52624
52624
 
52625
- stack$7[stackPointer$3++] = this;
52625
+ stack$6[stackPointer$2++] = this;
52626
52626
 
52627
52627
  let count = 0;
52628
52628
 
52629
- while (stackPointer$3 > startOffset) {
52630
- stackPointer$3--;
52631
- const node = stack$7[stackPointer$3];
52629
+ while (stackPointer$2 > startOffset) {
52630
+ stackPointer$2--;
52631
+ const node = stack$6[stackPointer$2];
52632
52632
 
52633
52633
  if (!aabb3_intersect_aabb3(
52634
52634
  x0, y0, z0, x1, y1, z1,
@@ -52645,12 +52645,12 @@ class BinaryNode extends Node {
52645
52645
  } else {
52646
52646
  const left = node.left;
52647
52647
  if (left !== null) {
52648
- stack$7[stackPointer$3++] = left;
52648
+ stack$6[stackPointer$2++] = left;
52649
52649
  }
52650
52650
 
52651
52651
  const right = node.right;
52652
52652
  if (right !== null) {
52653
- stack$7[stackPointer$3++] = right;
52653
+ stack$6[stackPointer$2++] = right;
52654
52654
  }
52655
52655
  }
52656
52656
  }
@@ -56115,16 +56115,16 @@ class IndexedBinaryBVH {
56115
56115
  */
56116
56116
  traversePreOrderStack(visitor, startIndex) {
56117
56117
 
56118
- const stackOffset = stackPointer$2;
56118
+ const stackOffset = stackPointer$1;
56119
56119
 
56120
- stack$6[stackPointer$2++] = startIndex;
56120
+ stack$5[stackPointer$1++] = startIndex;
56121
56121
 
56122
56122
  const nodeThreshold = this.binaryNodeCount * 6;
56123
56123
  const endAddress = this.boxCount * 6;
56124
56124
 
56125
- while (stackPointer$2-- > stackOffset) {
56125
+ while (stackPointer$1-- > stackOffset) {
56126
56126
 
56127
- const index = stack$6[stackPointer$2];
56127
+ const index = stack$5[stackPointer$1];
56128
56128
 
56129
56129
  const address = index * 6;
56130
56130
 
@@ -56140,7 +56140,7 @@ class IndexedBinaryBVH {
56140
56140
  //right
56141
56141
  if (rightAddress < endAddress) {
56142
56142
  if (rightAddress < nodeThreshold) {
56143
- stack$6[stackPointer$2++] = rightIndex;
56143
+ stack$5[stackPointer$1++] = rightIndex;
56144
56144
  } else {
56145
56145
  visitor.visit(rightAddress, NodeType.LEAF);
56146
56146
  }
@@ -56149,7 +56149,7 @@ class IndexedBinaryBVH {
56149
56149
  //left
56150
56150
  if (leftAddress < endAddress) {
56151
56151
  if (leftAddress < nodeThreshold) {
56152
- stack$6[stackPointer$2++] = leftIndex;
56152
+ stack$5[stackPointer$1++] = leftIndex;
56153
56153
  } else {
56154
56154
  visitor.visit(leftAddress, NodeType.LEAF);
56155
56155
  }
@@ -56158,7 +56158,7 @@ class IndexedBinaryBVH {
56158
56158
  }
56159
56159
 
56160
56160
  //drop stack
56161
- stackPointer$2 = stackOffset;
56161
+ stackPointer$1 = stackOffset;
56162
56162
  }
56163
56163
 
56164
56164
  /**
@@ -56325,8 +56325,8 @@ function binaryNodeRefit(array, binaryNode, childNode0, childNode1) {
56325
56325
  }
56326
56326
 
56327
56327
 
56328
- const stack$6 = [];
56329
- let stackPointer$2 = 0;
56328
+ const stack$5 = [];
56329
+ let stackPointer$1 = 0;
56330
56330
 
56331
56331
  const rayLeafIntersectionVisitor = new RayLeafIntersectionVisitor();
56332
56332
 
@@ -56614,18 +56614,20 @@ function rayTriangleIntersection(result, rayOrigin, rayDirection, a, b, c) {
56614
56614
  /**
56615
56615
  *
56616
56616
  * @param {number[]|ArrayLike<number>|Float32Array} output 6 component vector, [origin_x, origin_y, origin_z, direction_x, direction_y, direction_z]
56617
+ * @param {number} output_offset
56617
56618
  * @param {number[]|ArrayLike<number>|Float32Array} input 6 component vector, [origin_x, origin_y, origin_z, direction_x, direction_y, direction_z]
56619
+ * @param {number} input_offset
56618
56620
  * @param {number[]|ArrayLike<number>|Float32Array} m4 4x4 matrix
56619
56621
  * @returns {boolean} false if matrix transformation is impossible, such as when scale of the matrix is 0
56620
56622
  */
56621
- function ray3_array_apply_matrix4(output, input, m4) {
56622
- const origin_x = input[0];
56623
- const origin_y = input[1];
56624
- const origin_z = input[2];
56623
+ function ray3_array_apply_matrix4(output, output_offset, input, input_offset, m4) {
56624
+ const origin_x = input[input_offset + 0];
56625
+ const origin_y = input[input_offset + 1];
56626
+ const origin_z = input[input_offset + 2];
56625
56627
 
56626
- const direction_x = input[3];
56627
- const direction_y = input[4];
56628
- const direction_z = input[5];
56628
+ const direction_x = input[input_offset + 3];
56629
+ const direction_y = input[input_offset + 4];
56630
+ const direction_z = input[input_offset + 5];
56629
56631
 
56630
56632
  // transform ray to local space (inlined for speed)
56631
56633
  const det = m4[3] * origin_x + m4[7] * origin_y + m4[11] * origin_z + m4[15];
@@ -56657,13 +56659,13 @@ function ray3_array_apply_matrix4(output, input, m4) {
56657
56659
  const out_direction_normalized_y = out_direction_y * out_direction_magnitude_inverse;
56658
56660
  const out_direction_normalized_z = out_direction_z * out_direction_magnitude_inverse;
56659
56661
 
56660
- output[0] = out_origin_x;
56661
- output[1] = out_origin_y;
56662
- output[2] = out_origin_z;
56662
+ output[output_offset + 0] = out_origin_x;
56663
+ output[output_offset + 1] = out_origin_y;
56664
+ output[output_offset + 2] = out_origin_z;
56663
56665
 
56664
- output[3] = out_direction_normalized_x;
56665
- output[4] = out_direction_normalized_y;
56666
- output[5] = out_direction_normalized_z;
56666
+ output[output_offset + 3] = out_direction_normalized_x;
56667
+ output[output_offset + 4] = out_direction_normalized_y;
56668
+ output[output_offset + 5] = out_direction_normalized_z;
56667
56669
 
56668
56670
  return true;
56669
56671
  }
@@ -56881,7 +56883,7 @@ class BVHGeometryRaycaster {
56881
56883
  directionX, directionY, directionZ
56882
56884
  );
56883
56885
 
56884
- ray3_array_apply_matrix4(ray_tmp, ray_tmp, m4_tmp);
56886
+ ray3_array_apply_matrix4(ray_tmp, 0,ray_tmp,0, m4_tmp);
56885
56887
 
56886
56888
  const _originX = ray_tmp[0];
56887
56889
  const _originY = ray_tmp[1];
@@ -60528,8 +60530,9 @@ class MapEntry {
60528
60530
  *
60529
60531
  * @param {K} key
60530
60532
  * @param {V} value
60533
+ * @param {number} hash
60531
60534
  */
60532
- constructor(key, value) {
60535
+ constructor(key, value, hash) {
60533
60536
  /**
60534
60537
  *
60535
60538
  * @type {K}
@@ -60540,6 +60543,12 @@ class MapEntry {
60540
60543
  * @type {V}
60541
60544
  */
60542
60545
  this.value = value;
60546
+
60547
+ /**
60548
+ *
60549
+ * @type {number}
60550
+ */
60551
+ this.hash = hash;
60543
60552
  }
60544
60553
  }
60545
60554
 
@@ -60736,7 +60745,11 @@ class HashMap {
60736
60745
 
60737
60746
  const entryKey = entry.key;
60738
60747
 
60739
- if (entryKey === key || this.keyEqualityFunction(entryKey, key)) {
60748
+
60749
+ if (
60750
+ entry.hash === raw_hash
60751
+ && (entryKey === key || this.keyEqualityFunction(entryKey, key))
60752
+ ) {
60740
60753
 
60741
60754
  // found record with matching key, replace the value
60742
60755
 
@@ -60751,7 +60764,7 @@ class HashMap {
60751
60764
 
60752
60765
  }
60753
60766
 
60754
- bucket.push(new MapEntry(key, value));
60767
+ bucket.push(new MapEntry(key, value,raw_hash));
60755
60768
 
60756
60769
  const old_size = this.size;
60757
60770
  const new_size = old_size + 1;
@@ -60786,8 +60799,10 @@ class HashMap {
60786
60799
 
60787
60800
  const entryKey = entry.key;
60788
60801
 
60789
- if (entryKey === key || this.keyEqualityFunction(entryKey, key)) {
60790
-
60802
+ if (
60803
+ entry.hash === raw_hash
60804
+ && (entryKey === key || this.keyEqualityFunction(entryKey, key))
60805
+ ) {
60791
60806
  return entry.value;
60792
60807
 
60793
60808
  }
@@ -60949,6 +60964,9 @@ class HashMap {
60949
60964
 
60950
60965
  //check hash
60951
60966
  const raw_hash = this.keyHashFunction(entry.key);
60967
+
60968
+ entry.hash = raw_hash;
60969
+
60952
60970
  const actual_bucket_index = this.__compute_bucket_index(raw_hash);
60953
60971
 
60954
60972
 
@@ -69970,6 +69988,70 @@ class GeometrySpatialQueryAccelerator {
69970
69988
  */
69971
69989
  GeometrySpatialQueryAccelerator.INSTANCE = new GeometrySpatialQueryAccelerator();
69972
69990
 
69991
+ /**
69992
+ * A convenience class to work with BVH leaf nodes
69993
+ */
69994
+ class EBBVHLeafProxy {
69995
+ /**
69996
+ *
69997
+ * @type {ExplicitBinaryBoundingVolumeHierarchy|null}
69998
+ */
69999
+ #tree = null;
70000
+
70001
+ /**
70002
+ *
70003
+ * @type {number}
70004
+ */
70005
+ #node_id = -1;
70006
+
70007
+ /**
70008
+ * @readonly
70009
+ * @type {number[]}
70010
+ */
70011
+ bounds = new Float32Array(6);
70012
+
70013
+ get is_linked() {
70014
+ return this.#node_id !== -1;
70015
+ }
70016
+
70017
+ /**
70018
+ *
70019
+ * @param {ExplicitBinaryBoundingVolumeHierarchy} tree
70020
+ * @param {number} data Must be a uint32
70021
+ */
70022
+ link(tree, data) {
70023
+ assert.defined(tree, 'tree');
70024
+ assert.isNonNegativeInteger(data, 'data');
70025
+
70026
+ this.#tree = tree;
70027
+
70028
+ const node_id = tree.allocate_node();
70029
+
70030
+ this.#node_id = node_id;
70031
+
70032
+ tree.node_set_aabb(node_id, this.bounds);
70033
+ tree.node_set_user_data(node_id, data);
70034
+ tree.insert_leaf(node_id);
70035
+ }
70036
+
70037
+ unlink() {
70038
+ this.#tree.remove_leaf(this.#node_id);
70039
+ this.#tree.release_node(this.#node_id);
70040
+
70041
+ this.#node_id = -1;
70042
+ this.#tree = null;
70043
+ }
70044
+
70045
+ write_bounds() {
70046
+ if (this.#tree === null) {
70047
+ // nothing to write to
70048
+ return;
70049
+ }
70050
+
70051
+ this.#tree.node_move_aabb(this.#node_id, this.bounds);
70052
+ }
70053
+ }
70054
+
69973
70055
  /**
69974
70056
  * @readonly
69975
70057
  * @type {Float32Array}
@@ -70018,26 +70100,7 @@ class ShadedGeometry {
70018
70100
  */
70019
70101
  this.depth_material = null;
70020
70102
 
70021
- /**
70022
- *
70023
- * @type {number}
70024
- * @private
70025
- */
70026
- this.__bvh_leaf_id = -1;
70027
-
70028
- /**
70029
- *
70030
- * @type {ExplicitBinaryBoundingVolumeHierarchy|null}
70031
- * @private
70032
- */
70033
- this.__bvh_tree = null;
70034
-
70035
- /**
70036
- * @readonly
70037
- * @type {number[]|Float32Array}
70038
- * @private
70039
- */
70040
- this.__bvh_aabb = new Float32Array(6);
70103
+ this.__bvh_leaf = new EBBVHLeafProxy();
70041
70104
 
70042
70105
  /**
70043
70106
  * Transient, assigned in the system
@@ -70203,7 +70266,7 @@ class ShadedGeometry {
70203
70266
  * @param {AABB3} destination
70204
70267
  */
70205
70268
  getBoundingBox(destination) {
70206
- const aabb = this.__bvh_aabb;
70269
+ const aabb = this.__bvh_leaf.bounds;
70207
70270
 
70208
70271
  destination.readFromArray(aabb);
70209
70272
  }
@@ -70243,7 +70306,7 @@ class ShadedGeometry {
70243
70306
  updateTransform() {
70244
70307
  this.update_bounds();
70245
70308
 
70246
- this.__bvh_tree.node_move_aabb(this.__bvh_leaf_id, this.__bvh_aabb);
70309
+ this.__bvh_leaf.write_bounds();
70247
70310
  }
70248
70311
 
70249
70312
  update_bounds() {
@@ -70280,7 +70343,7 @@ class ShadedGeometry {
70280
70343
  scratch_aabb3_array[4] = y1;
70281
70344
  scratch_aabb3_array[5] = z1;
70282
70345
 
70283
- aabb3_matrix4_project(this.__bvh_aabb, scratch_aabb3_array, this.transform);
70346
+ aabb3_matrix4_project(this.__bvh_leaf.bounds, scratch_aabb3_array, this.transform);
70284
70347
  }
70285
70348
 
70286
70349
  /**
@@ -70296,7 +70359,7 @@ class ShadedGeometry {
70296
70359
  invert(scratch_m4, transform_matrix4);
70297
70360
 
70298
70361
  // transform ray to local space
70299
- if (!ray3_array_apply_matrix4(scratch_ray_0$1, ray, scratch_m4)) {
70362
+ if (!ray3_array_apply_matrix4(scratch_ray_0$1,0, ray,0, scratch_m4)) {
70300
70363
  // invalid transform matrix
70301
70364
  return false;
70302
70365
  }
@@ -76253,15 +76316,11 @@ class SGMeshSystem extends System {
76253
76316
 
76254
76317
  entity_node.build(ecd);
76255
76318
 
76256
- transform.position.onChanged.add(copy_transform);
76257
- transform.rotation.onChanged.add(copy_transform);
76258
- transform.scale.onChanged.add(copy_transform);
76319
+ transform.subscribeAllChanges(copy_transform);
76259
76320
 
76260
76321
  entity_node.on.destroyed.addOne(() => {
76261
76322
 
76262
- transform.position.onChanged.remove(copy_transform);
76263
- transform.rotation.onChanged.remove(copy_transform);
76264
- transform.scale.onChanged.remove(copy_transform);
76323
+ transform.unsubscribeAllChanges(copy_transform);
76265
76324
 
76266
76325
  });
76267
76326
 
@@ -78827,6 +78886,7 @@ class ExplicitBinaryBoundingVolumeHierarchy {
78827
78886
  */
78828
78887
  node_set_user_data(id, value) {
78829
78888
  assert.isNonNegativeInteger(id, 'id');
78889
+ assert.isNonNegativeInteger(value, 'value');
78830
78890
 
78831
78891
  this.__data_uint32[ELEMENT_WORD_COUNT * id + COLUMN_USER_DATA] = value;
78832
78892
  }
@@ -80540,25 +80600,13 @@ class ShadedGeometrySystem extends System {
80540
80600
 
80541
80601
  sg.update_bounds();
80542
80602
 
80543
- t.position.onChanged.add(sg.updateTransform, sg);
80544
- t.rotation.onChanged.add(sg.updateTransform, sg);
80545
- t.scale.onChanged.add(sg.updateTransform, sg);
80603
+ t.subscribeAllChanges(sg.updateTransform, sg);
80546
80604
 
80547
80605
  // remember entity for lookups
80548
80606
  sg.__entity = entity;
80549
80607
 
80550
80608
  // insert BVH entry
80551
- const bvh = this.__bvh_binary;
80552
-
80553
- const bvh_node_id = bvh.allocate_node();
80554
-
80555
- bvh.node_set_aabb(bvh_node_id, sg.__bvh_aabb);
80556
- bvh.node_set_user_data(bvh_node_id, entity);
80557
- bvh.insert_leaf(bvh_node_id);
80558
-
80559
- sg.__bvh_tree = bvh;
80560
- sg.__bvh_leaf_id = bvh_node_id;
80561
-
80609
+ sg.__bvh_leaf.link(this.__bvh_binary, entity);
80562
80610
 
80563
80611
  // update usage count
80564
80612
  const geometry_id = sg.geometry.id;
@@ -80575,19 +80623,10 @@ class ShadedGeometrySystem extends System {
80575
80623
  * @param {number} entity
80576
80624
  */
80577
80625
  unlink(sg, t, entity) {
80578
- t.position.onChanged.remove(sg.updateTransform, sg);
80579
- t.rotation.onChanged.remove(sg.updateTransform, sg);
80580
- t.scale.onChanged.remove(sg.updateTransform, sg);
80626
+ t.unsubscribeAllChanges(sg.updateTransform, sg);
80581
80627
 
80582
80628
  // disconnect BVH
80583
- const node_id = sg.__bvh_leaf_id;
80584
-
80585
- this.__bvh_binary.remove_leaf(node_id);
80586
- this.__bvh_binary.release_node(node_id);
80587
-
80588
- // reset BVH references
80589
- sg.__bvh_leaf_id = -1;
80590
- sg.__bvh_tree = null;
80629
+ sg.__bvh_leaf.unlink();
80591
80630
 
80592
80631
  const geometry_id = sg.geometry.id;
80593
80632
  const count_existing = this.__geometry_usage_counters.get(geometry_id);
@@ -87565,8 +87604,8 @@ function compare_three_objects(a, b) {
87565
87604
  *
87566
87605
  * @type {(BinaryNode|LeafNode)[]}
87567
87606
  */
87568
- const stack$5 = [];
87569
- let stackPointer$1 = 0;
87607
+ const stack$4 = [];
87608
+ let stackPointer = 0;
87570
87609
 
87571
87610
  /**
87572
87611
  * @template T
@@ -87578,22 +87617,22 @@ function queryBinaryNode_CollectData(destination, destinationOffset, root) {
87578
87617
  let n;
87579
87618
  let i = 0;
87580
87619
 
87581
- const stackOffset = stackPointer$1;
87620
+ const stackOffset = stackPointer;
87582
87621
 
87583
- stack$5[stackPointer$1++] = root;
87622
+ stack$4[stackPointer++] = root;
87584
87623
 
87585
- while (stackPointer$1-- > stackOffset) {
87624
+ while (stackPointer-- > stackOffset) {
87586
87625
 
87587
- n = stack$5[stackPointer$1];
87626
+ n = stack$4[stackPointer];
87588
87627
 
87589
87628
  if (n.isBinaryNode) {
87590
87629
 
87591
87630
  if (n.right !== null) {
87592
- stack$5[stackPointer$1++] = n.right;
87631
+ stack$4[stackPointer++] = n.right;
87593
87632
  }
87594
87633
 
87595
87634
  if (n.left !== null) {
87596
- stack$5[stackPointer$1++] = n.left;
87635
+ stack$4[stackPointer++] = n.left;
87597
87636
  }
87598
87637
 
87599
87638
  } else {
@@ -87606,7 +87645,7 @@ function queryBinaryNode_CollectData(destination, destinationOffset, root) {
87606
87645
  }
87607
87646
 
87608
87647
  //drop the stack frame
87609
- stackPointer$1 = stackOffset;
87648
+ stackPointer = stackOffset;
87610
87649
 
87611
87650
  return i;
87612
87651
  }
@@ -87615,7 +87654,7 @@ function queryBinaryNode_CollectData(destination, destinationOffset, root) {
87615
87654
  *
87616
87655
  * @type {(BinaryNode|LeafNode)[]}
87617
87656
  */
87618
- const stack$4 = [];
87657
+ const stack$3 = [];
87619
87658
 
87620
87659
 
87621
87660
  /**
@@ -87635,13 +87674,13 @@ function queryBinaryNode_FrustumIntersections_Data(destination, destination_offs
87635
87674
  let offset = destination_offset;
87636
87675
  let stackPointer = 1;
87637
87676
 
87638
- stack$4[0] = root;
87677
+ stack$3[0] = root;
87639
87678
 
87640
87679
  while (stackPointer > 0) {
87641
87680
 
87642
87681
  stackPointer--;
87643
87682
 
87644
- n = stack$4[stackPointer];
87683
+ n = stack$3[stackPointer];
87645
87684
 
87646
87685
  const intersection_degree = aabb3_intersects_frustum_degree(
87647
87686
  n.x0, n.y0, n.z0,
@@ -87662,13 +87701,13 @@ function queryBinaryNode_FrustumIntersections_Data(destination, destination_offs
87662
87701
  const right = n.right;
87663
87702
 
87664
87703
  if (right !== null) {
87665
- stack$4[stackPointer++] = right;
87704
+ stack$3[stackPointer++] = right;
87666
87705
  }
87667
87706
 
87668
87707
  const left = n.left;
87669
87708
 
87670
87709
  if (left !== null) {
87671
- stack$4[stackPointer++] = left;
87710
+ stack$3[stackPointer++] = left;
87672
87711
  }
87673
87712
 
87674
87713
  } else {
@@ -88630,7 +88669,7 @@ const MetricsCategory = {
88630
88669
  System: "system"
88631
88670
  };
88632
88671
 
88633
- const stack$3 = [];
88672
+ const stack$2 = [];
88634
88673
 
88635
88674
  class ThreeBypassRenderer {
88636
88675
  constructor() {
@@ -88707,13 +88746,13 @@ class ThreeBypassRenderer {
88707
88746
  const object3D = input[i];
88708
88747
 
88709
88748
  if (input_filter(object3D)) {
88710
- stack$3[stack_top++] = object3D;
88749
+ stack$2[stack_top++] = object3D;
88711
88750
  }
88712
88751
 
88713
88752
  }
88714
88753
 
88715
88754
  while (stack_top > 0) {
88716
- const object3D = stack$3[--stack_top];
88755
+ const object3D = stack$2[--stack_top];
88717
88756
 
88718
88757
  if (
88719
88758
  object_filter(object3D)
@@ -88752,7 +88791,7 @@ class ThreeBypassRenderer {
88752
88791
  for (i = 0; i < child_count; i++) {
88753
88792
  const child = children[i];
88754
88793
 
88755
- stack$3[stack_top++] = child;
88794
+ stack$2[stack_top++] = child;
88756
88795
  }
88757
88796
  }
88758
88797
 
@@ -99902,6 +99941,9 @@ class ActionBehavior extends Behavior {
99902
99941
  }
99903
99942
  }
99904
99943
 
99944
+ /**
99945
+ * Abstract class
99946
+ */
99905
99947
  class CompositeBehavior extends Behavior {
99906
99948
  constructor() {
99907
99949
  super();
@@ -99919,12 +99961,25 @@ class CompositeBehavior extends Behavior {
99919
99961
  * @param {Behavior} child
99920
99962
  */
99921
99963
  addChild(child) {
99922
- assert.defined(child);
99923
- assert.ok(child.isBehavior, 'child is not a Behavior');
99964
+ assert.defined(child, 'child');
99965
+ assert.notNull(child, 'child');
99966
+ assert.equal(child.isBehavior, true, 'child is not a Behavior');
99924
99967
 
99925
99968
  this.__children.push(child);
99926
99969
  }
99927
99970
 
99971
+ /**
99972
+ *
99973
+ * @param {Behavior[]} many
99974
+ */
99975
+ addChildren(many) {
99976
+ const n = many.length;
99977
+ for (let i = 0; i < n; i++) {
99978
+ const e = many[i];
99979
+ r.addChild(e);
99980
+ }
99981
+ }
99982
+
99928
99983
  /**
99929
99984
  * NOTE: do not modify obtained value
99930
99985
  * @return {Behavior[]}
@@ -99962,6 +100017,10 @@ class CompositeBehavior extends Behavior {
99962
100017
  */
99963
100018
  CompositeBehavior.prototype.isCompositeBehavior = true;
99964
100019
 
100020
+ /**
100021
+ * Executes all contained behaviors one after another in a sequence, next behaviour in the sequence will not be started until the previous one signal success
100022
+ * If any of the contained behaviours fail - the whole sequence fails
100023
+ */
99965
100024
  class SequenceBehavior extends CompositeBehavior {
99966
100025
  constructor() {
99967
100026
  super();
@@ -100065,7 +100124,7 @@ class SequenceBehavior extends CompositeBehavior {
100065
100124
  static from(list) {
100066
100125
  const r = new SequenceBehavior();
100067
100126
 
100068
- list.forEach(b => r.addChild(b));
100127
+ r.addChildren(list);
100069
100128
 
100070
100129
  return r;
100071
100130
  }
@@ -115166,7 +115225,9 @@ var FrustumProjector = {
115166
115225
  projectInWorldSpace
115167
115226
  };
115168
115227
 
115169
- class PointLightData {
115228
+ class LightRenderMetadata {
115229
+
115230
+
115170
115231
  /**
115171
115232
  *
115172
115233
  * @param {AbstractLight} light
@@ -115184,12 +115245,12 @@ class PointLightData {
115184
115245
  */
115185
115246
  this.address = 0;
115186
115247
 
115187
- this.bvh_leaf_main = new LeafNode(this, 0, 0, 0, 0, 0, 0);
115248
+ this.bvh_leaf = new EBBVHLeafProxy();
115188
115249
  }
115189
115250
 
115190
115251
  /**
115191
115252
  *
115192
- * @param {PointLightData} other
115253
+ * @param {LightRenderMetadata} other
115193
115254
  * @returns {number}
115194
115255
  */
115195
115256
  compare(other) {
@@ -115197,17 +115258,20 @@ class PointLightData {
115197
115258
  }
115198
115259
 
115199
115260
  update() {
115261
+ const leaf = this.bvh_leaf;
115200
115262
 
115201
- const leaf = this.bvh_leaf_main;
115263
+ this.light.getAABB(leaf.bounds);
115202
115264
 
115203
- this.light.getAABB(leaf);
115204
-
115205
- if (leaf.parentNode !== null) {
115206
- leaf.parentNode.bubbleRefit();
115207
- }
115265
+ leaf.write_bounds();
115208
115266
  }
115209
115267
 
115210
- link() {
115268
+ /**
115269
+ *
115270
+ * @param {ExplicitBinaryBoundingVolumeHierarchy} bvh
115271
+ */
115272
+ link(bvh) {
115273
+ this.bvh_leaf.link(bvh, this.light.id);
115274
+
115211
115275
  this.update();
115212
115276
 
115213
115277
  this.light.onDimensionChanged(this.update, this);
@@ -115218,488 +115282,10 @@ class PointLightData {
115218
115282
  this.light.offDimensionChanged(this.update, this);
115219
115283
 
115220
115284
  // disconnect from bvh
115221
- this.bvh_leaf_main.disconnect();
115222
- }
115223
- }
115224
-
115225
- const _lut_array_buffer = new ArrayBuffer(84);
115226
- /**
115227
- * Mapping from 3 plane bitfield, corresponding to intersection point of the planes
115228
- * @type {number[]}
115229
- */
115230
- const triplanar_corner_mapping = new Uint8Array(_lut_array_buffer, 0, 43);
115231
-
115232
- triplanar_corner_mapping[0b010101] = 0;
115233
- triplanar_corner_mapping[0b100101] = 1;
115234
- triplanar_corner_mapping[0b011001] = 2;
115235
- triplanar_corner_mapping[0b101001] = 3;
115236
- triplanar_corner_mapping[0b010110] = 4;
115237
- triplanar_corner_mapping[0b100110] = 5;
115238
- triplanar_corner_mapping[0b011010] = 6;
115239
- triplanar_corner_mapping[0b101010] = 7;
115240
- /**
115241
- * Mapping from 2 plane bitfield to 2 corner bitfield, corresponding to corner indices of intersecting planes
115242
- * @type {number[]}
115243
- */
115244
- const biplanar_edge_mapping = new Uint8Array(_lut_array_buffer, 43, 41);
115245
-
115246
- biplanar_edge_mapping[0b000101] = 0 | (1 << 3);
115247
- biplanar_edge_mapping[0b000110] = 4 | (5 << 3);
115248
-
115249
- biplanar_edge_mapping[0b001001] = 2 | (3 << 3);
115250
- biplanar_edge_mapping[0b001010] = 6 | (7 << 3);
115251
-
115252
- biplanar_edge_mapping[0b010100] = 0 | (4 << 3);
115253
- biplanar_edge_mapping[0b011000] = 2 | (6 << 3);
115254
-
115255
- biplanar_edge_mapping[0b100100] = 1 | (5 << 3);
115256
- biplanar_edge_mapping[0b101000] = 3 | (7 << 3);
115257
-
115258
- biplanar_edge_mapping[0b010001] = 0 | (2 << 3);
115259
- biplanar_edge_mapping[0b010010] = 4 | (6 << 3);
115260
-
115261
- biplanar_edge_mapping[0b100001] = 1 | (3 << 3);
115262
- biplanar_edge_mapping[0b100010] = 5 | (7 << 3);
115263
-
115264
- /**
115265
- * Computes shortest distance between a line segment(defined by 2 points) and a point
115266
- * @param {number} x0
115267
- * @param {number} y0
115268
- * @param {number} z0
115269
- * @param {number} x1
115270
- * @param {number} y1
115271
- * @param {number} z1
115272
- * @param {number} px
115273
- * @param {number} py
115274
- * @param {number} pz
115275
- */
115276
- function line3_computeSegmentPointDistance_sqr(x0, y0, z0, x1, y1, z1, px, py, pz) {
115277
- // get line delta
115278
- const line_delta_x = x1 - x0;
115279
- const line_delta_y = y1 - y0;
115280
- const line_delta_z = z1 - z0;
115281
-
115282
- // find closest point
115283
- const sp0_x = px - x0;
115284
- const sp0_y = py - y0;
115285
- const sp0_z = pz - z0;
115286
-
115287
- //
115288
- const d2 = line_delta_x * line_delta_x + line_delta_y * line_delta_y + line_delta_z * line_delta_z;
115289
-
115290
- const d3 = line_delta_x * sp0_x + line_delta_y * sp0_y + line_delta_z * sp0_z;
115291
-
115292
- const t = clamp01(d3 / d2);
115293
-
115294
- // compute point on the line
115295
- const lp_x = line_delta_x * t + x0;
115296
- const lp_y = line_delta_y * t + y0;
115297
- const lp_z = line_delta_z * t + z0;
115298
-
115299
- // compute distance from the point in question to the point on the line
115300
- return v3_distance_sqr(px, py, pz, lp_x, lp_y, lp_z);
115301
- }
115302
-
115303
- /**
115304
- *
115305
- * @param {number} radius
115306
- * @param {number} light_x
115307
- * @param {number} light_y
115308
- * @param {number} light_z
115309
- * @param {number} outside_plane_count
115310
- * @param {number} outside_plane_bitfield
115311
- * @param {number[]} points
115312
- * @return {boolean}
115313
- */
115314
- function detailed_sphere_frustum_intersection_test(
115315
- radius, light_x, light_y, light_z,
115316
- outside_plane_count, outside_plane_bitfield,
115317
- points
115318
- ) {
115319
-
115320
- const radius_sqr = radius * radius;
115321
-
115322
- if (outside_plane_count === 3) {
115323
- // nearest point in a corner, find the corner
115324
- const point_index = triplanar_corner_mapping[outside_plane_bitfield];
115325
-
115326
- const point_address = point_index * 3;
115327
-
115328
- const point_x = points[point_address];
115329
- const point_y = points[point_address + 1];
115330
- const point_z = points[point_address + 2];
115331
-
115332
- const distance_to_light_sqr = v3_distance_sqr(
115333
- point_x, point_y, point_z,
115334
- light_x, light_y, light_z
115335
- );
115336
-
115337
- // query_bvh_frustum_from_objects.checks_sphere_frustum_corner++;
115338
-
115339
- // return distance_to_light_sqr <= radius_sqr;
115340
-
115341
- if (distance_to_light_sqr > radius_sqr) {
115342
-
115343
- // query_bvh_frustum_from_objects.rejection_sphere_count++;
115344
- return false;
115345
- }
115346
-
115347
- } else if (outside_plane_count === 2) {
115348
- // nearest point is on an edge of two plane intersection
115349
- const edge_bitfield = biplanar_edge_mapping[outside_plane_bitfield];
115350
-
115351
- // get 2 corners from edge bitfield
115352
- const corner_index_0 = edge_bitfield & 0b111;
115353
- const corner_address_0 = corner_index_0 * 3;
115354
-
115355
- const corner_index_1 = (edge_bitfield >> 3) & 0b111;
115356
- const corner_address_1 = corner_index_1 * 3;
115357
-
115358
- const point_0_x = points[corner_address_0];
115359
- const point_0_y = points[corner_address_0 + 1];
115360
- const point_0_z = points[corner_address_0 + 2];
115361
-
115362
- const point_1_x = points[corner_address_1];
115363
- const point_1_y = points[corner_address_1 + 1];
115364
- const point_1_z = points[corner_address_1 + 2];
115365
-
115366
- const distance_to_frustum_sqr = line3_computeSegmentPointDistance_sqr(
115367
- point_0_x, point_0_y, point_0_z,
115368
- point_1_x, point_1_y, point_1_z,
115369
- light_x, light_y, light_z
115370
- );
115371
-
115372
- // query_bvh_frustum_from_objects.checks_sphere_frustum_edge++;
115373
-
115374
- // return distance_to_frustum_sqr <= radius_sqr;
115375
-
115376
- if (distance_to_frustum_sqr > radius_sqr) {
115377
-
115378
- // query_bvh_frustum_from_objects.rejection_sphere_count++;
115379
- return false;
115380
- }
115285
+ this.bvh_leaf.unlink();
115381
115286
  }
115382
-
115383
- return true;
115384
115287
  }
115385
115288
 
115386
- /**
115387
- *
115388
- * @param {number} light_x
115389
- * @param {number} light_y
115390
- * @param {number} light_z
115391
- * @param {number} radius
115392
- * @param {number[]} points
115393
- * @param {number[]|Float32Array|Float64Array} planes
115394
- * @returns {boolean}
115395
- */
115396
- function point_light_inside_volume(
115397
- light_x, light_y, light_z, radius,
115398
- points, planes
115399
- ) {
115400
- let j = 0;
115401
- let outside_plane_count = 0;
115402
- let outside_plane_bitfield = 0;
115403
-
115404
- // validate if the sphere intersects the plane-bound space
115405
-
115406
- // query_bvh_frustum_from_objects.checks_sphere++;
115407
-
115408
- for (; j < 6; j++) {
115409
-
115410
- const plane_address = j * 4;
115411
-
115412
- const plane_normal_x = planes[plane_address];
115413
- const plane_normal_y = planes[plane_address + 1];
115414
- const plane_normal_z = planes[plane_address + 2];
115415
- const plane_constant = planes[plane_address + 3];
115416
-
115417
- /*
115418
- compute distance from the center of the sphere to the plane
115419
- NOTE: inlined computation of distance from point to plane
115420
- */
115421
- const distance = (plane_normal_x * light_x + plane_normal_y * light_y + plane_normal_z * light_z) + plane_constant;
115422
-
115423
- // query_bvh_frustum_from_objects.checks_sphere_plane++;
115424
-
115425
- if (distance < 0) {
115426
- // center of the sphere is below the plane
115427
-
115428
- if (distance >= -radius) {
115429
-
115430
- // sphere intersects the plane
115431
-
115432
- outside_plane_count++;
115433
-
115434
- const plane_bit_mask = 1 << j;
115435
-
115436
- outside_plane_bitfield |= plane_bit_mask;
115437
- } else {
115438
- // sphere is entirely below the plane
115439
-
115440
- // query_bvh_frustum_from_objects.rejection_sphere_count++;
115441
-
115442
- return false;
115443
- }
115444
- }
115445
-
115446
- }
115447
-
115448
- if (outside_plane_count < 2) {
115449
- return true;
115450
- } else {
115451
- return detailed_sphere_frustum_intersection_test(
115452
- radius, light_x, light_y, light_z,
115453
- outside_plane_count, outside_plane_bitfield,
115454
- points
115455
- );
115456
- }
115457
- }
115458
-
115459
- const V1 = new Vector3$1();
115460
- const V2 = new Vector3$1();
115461
-
115462
- /**
115463
- * 2,0,or -2; 2: above, -2 : below, 0 : intersects plane
115464
- * NOTE: used an article by Bart Wronski https://bartwronski.com/2017/04/13/cull-that-cone/
115465
- * @param {number} plane_normal_x
115466
- * @param {number} plane_normal_y
115467
- * @param {number} plane_normal_z
115468
- * @param {number} plane_constant
115469
- * @param {number} originX
115470
- * @param {number} originY
115471
- * @param {number} originZ
115472
- * @param {number} directionX
115473
- * @param {number} directionY
115474
- * @param {number} directionZ
115475
- * @param {number} angle
115476
- * @param {number} length
115477
- * @returns {number}
115478
- */
115479
- function computeConePlaneSide(
115480
- plane_normal_x, plane_normal_y, plane_normal_z,
115481
- plane_constant,
115482
- originX, originY, originZ,
115483
- directionX, directionY, directionZ,
115484
- angle,
115485
- length
115486
- ) {
115487
-
115488
- /*
115489
- ORIGINAL HLSL:
115490
- bool TestConeVsPlane(in float3 origin, in float3 forward, in float size, in float angle, in float4 testPlane)
115491
- {
115492
- const float3 V1 = cross(testPlane.xyz, forward);
115493
- const float3 V2 = cross(V1, forward);
115494
-
115495
- const float3 capRimPoint = origin +
115496
- size * cos(angle) * forward +
115497
- size * sin(angle) * V2;
115498
-
115499
- return dot(float4(capRimPoint, 1.0f), testPlane) >= 0.0f || dot(float4(origin, 1.0f), testPlane) >= 0.0f;
115500
- }
115501
- */
115502
-
115503
- V1._crossVectors(plane_normal_x, plane_normal_y, plane_normal_z, directionX, directionY, directionZ);
115504
- V2._crossVectors(V1.x, V1.y, V1.z, directionX, directionY, directionZ);
115505
-
115506
- const scaled_cos = length * Math.cos(angle);
115507
- const scaled_sin = length * Math.sin(angle);
115508
-
115509
- // compute cap rim point (closest point to the plane on the wide side of the cone)
115510
- V1.set(
115511
- originX + scaled_cos * directionX + scaled_sin * V2.x,
115512
- originY + scaled_cos * directionY + scaled_sin * V2.y,
115513
- originZ + scaled_cos * directionZ + scaled_sin * V2.z
115514
- );
115515
-
115516
- const rim_point_side = v4_dot(
115517
- V1.x, V1.y, V1.z, 1,
115518
- plane_normal_x, plane_normal_y, plane_normal_z, plane_constant
115519
- );
115520
-
115521
- const origin_point_side = v4_dot(
115522
- originX, originY, originZ, 1,
115523
- plane_normal_x, plane_normal_y, plane_normal_z, plane_constant
115524
- );
115525
-
115526
- let result = 0;
115527
-
115528
- if (rim_point_side >= 0) {
115529
- result += 1;
115530
- } else {
115531
- result -= 1;
115532
- }
115533
-
115534
- if (origin_point_side >= 0) {
115535
- result += 1;
115536
- } else {
115537
- result -= 1;
115538
- }
115539
-
115540
- return result;
115541
- }
115542
-
115543
- /**
115544
- *
115545
- * @param {SpotLight} light
115546
- * @param {number[]} points
115547
- * @param {number[]|Float32Array|Float64Array} planes
115548
- * @returns {boolean}
115549
- */
115550
- function spot_light_inside_volume(light, points, planes) {
115551
- for (let j = 0; j < 6; j++) {
115552
-
115553
- const plane_address = j * 4;
115554
-
115555
- const plane_normal_x = planes[plane_address];
115556
- const plane_normal_y = planes[plane_address + 1];
115557
- const plane_normal_z = planes[plane_address + 2];
115558
- const plane_constant = planes[plane_address + 3];
115559
-
115560
- const side = computeConePlaneSide(
115561
- plane_normal_x, plane_normal_y, plane_normal_z, plane_constant,
115562
- light.position.x, light.position.y, light.position.z,
115563
- light.direction.x, light.direction.y, light.direction.z,
115564
- light.angle.getValue(), light.distance.getValue()
115565
- );
115566
-
115567
- if (side < 0) {
115568
- return false;
115569
- }
115570
- }
115571
-
115572
- return true;
115573
- }
115574
-
115575
- /**
115576
- *
115577
- * @type {(BinaryNode|LeafNode)[]}
115578
- */
115579
- const stack$2 = [];
115580
-
115581
- let stackPointer = 0;
115582
-
115583
- /**
115584
- * Most of the data is in flat continuous array for cache coherence
115585
- * @param {PointLightData[]} destination
115586
- * @param {number} destination_offset
115587
- * @param {BinaryNode<PointLightData>} root
115588
- * @param {number[]|Float32Array|Float64Array} planes
115589
- * @param {number[]|Float32Array|Float64Array} points
115590
- * @returns {number} number of items added to the destination, non-negative integer
115591
- */
115592
- function query_bvh_frustum_from_objects(
115593
- destination,
115594
- destination_offset,
115595
- root, planes, points
115596
- ) {
115597
- let i = 0;
115598
- let j = 0;
115599
- // read out points
115600
- const stackOffset = stackPointer;
115601
-
115602
- stack$2[stackPointer] = root;
115603
-
115604
- stackPointer++;
115605
-
115606
- stack_loop:while (stackPointer-- > stackOffset) {
115607
-
115608
- // query_bvh_frustum_from_objects.iteration_count++;
115609
-
115610
- const n = stack$2[stackPointer];
115611
-
115612
- if (n.isBinaryNode === true) {
115613
-
115614
- for (j = 0; j < 6; j++) {
115615
-
115616
- const plane_address = j * 4;
115617
-
115618
- const plane_normal_x = planes[plane_address];
115619
- const plane_normal_y = planes[plane_address + 1];
115620
- const plane_normal_z = planes[plane_address + 2];
115621
- const plane_constant = planes[plane_address + 3];
115622
-
115623
- const distanceAbovePlane = aabb3_computeDistanceAbovePlane_max(
115624
- plane_normal_x, plane_normal_y, plane_normal_z, plane_constant,
115625
- n.x0, n.y0, n.z0,
115626
- n.x1, n.y1, n.z1
115627
- );
115628
-
115629
- if (distanceAbovePlane < 0) {
115630
- // node is below the plane
115631
-
115632
- // query_bvh_frustum_from_objects.rejection_count++;
115633
-
115634
- continue stack_loop;
115635
- }
115636
- }
115637
-
115638
-
115639
- if (n.right !== null) {
115640
- stack$2[stackPointer] = n.right;
115641
- stackPointer++;
115642
- }
115643
-
115644
- if (n.left !== null) {
115645
- stack$2[stackPointer] = n.left;
115646
- stackPointer++;
115647
- }
115648
-
115649
- } else {
115650
-
115651
- // leaf node
115652
-
115653
- /**
115654
- *
115655
- * @type {PointLightData}
115656
- */
115657
- const light_data = n.object;
115658
-
115659
- const light = light_data.light;
115660
-
115661
- if (light.isPointLight) {
115662
-
115663
- const light_position = light.position;
115664
-
115665
- const light_x = light_position.x;
115666
- const light_y = light_position.y;
115667
- const light_z = light_position.z;
115668
-
115669
- const radius = light.radius.getValue();
115670
-
115671
- if (!point_light_inside_volume(light_x, light_y, light_z, radius, points, planes)) {
115672
- continue;
115673
- }
115674
- } else if (light.isSpotLight) {
115675
- if (!spot_light_inside_volume(light, points, planes)) {
115676
- continue;
115677
- }
115678
- }
115679
-
115680
- destination[destination_offset + i] = light_data;
115681
-
115682
- i++;
115683
- }
115684
-
115685
- }
115686
-
115687
- //drop the stack frame
115688
- stackPointer = stackOffset;
115689
-
115690
- return i;
115691
- }
115692
-
115693
- query_bvh_frustum_from_objects.iteration_count = 0;
115694
- query_bvh_frustum_from_objects.rejection_count = 0;
115695
- query_bvh_frustum_from_objects.rejection_sphere_count = 0;
115696
- query_bvh_frustum_from_objects.plane_tests = 0;
115697
- query_bvh_frustum_from_objects.plane_tests_early_exits = 0;
115698
- query_bvh_frustum_from_objects.checks_sphere = 0;
115699
- query_bvh_frustum_from_objects.checks_sphere_plane = 0;
115700
- query_bvh_frustum_from_objects.checks_sphere_frustum_corner = 0;
115701
- query_bvh_frustum_from_objects.checks_sphere_frustum_edge = 0;
115702
-
115703
115289
  /**
115704
115290
  *
115705
115291
  * @param {number[]} result
@@ -116420,10 +116006,7 @@ function build_morton(data, address, matrix) {
116420
116006
 
116421
116007
  }
116422
116008
 
116423
- const scratch_box_0 = [];
116424
- const scratch_box_1 = [];
116425
- const scratch_box_2 = [];
116426
-
116009
+ const scratch_box_0 = new Float32Array(18);
116427
116010
  const stack$1 = [];
116428
116011
 
116429
116012
  class BinaryUint32BVH {
@@ -116595,16 +116178,16 @@ class BinaryUint32BVH {
116595
116178
  */
116596
116179
  __compute_bounds_area_of_3_boxes(a, b, c) {
116597
116180
  this.readBounds(a, scratch_box_0, 0);
116598
- this.readBounds(b, scratch_box_1, 0);
116599
- this.readBounds(c, scratch_box_2, 0);
116181
+ this.readBounds(b, scratch_box_0, 6);
116182
+ this.readBounds(c, scratch_box_0, 12);
116600
116183
 
116601
- const x0 = min3(scratch_box_0[0], scratch_box_1[0], scratch_box_2[0]);
116602
- const y0 = min3(scratch_box_0[1], scratch_box_1[1], scratch_box_2[1]);
116603
- const z0 = min3(scratch_box_0[2], scratch_box_1[2], scratch_box_2[2]);
116184
+ const x0 = min3(scratch_box_0[0], scratch_box_0[6], scratch_box_0[12]);
116185
+ const y0 = min3(scratch_box_0[1], scratch_box_0[7], scratch_box_0[13]);
116186
+ const z0 = min3(scratch_box_0[2], scratch_box_0[8], scratch_box_0[14]);
116604
116187
 
116605
- const x1 = max3(scratch_box_0[3], scratch_box_1[3], scratch_box_2[3]);
116606
- const y1 = max3(scratch_box_0[4], scratch_box_1[4], scratch_box_2[4]);
116607
- const z1 = max3(scratch_box_0[5], scratch_box_1[5], scratch_box_2[5]);
116188
+ const x1 = max3(scratch_box_0[3], scratch_box_0[9], scratch_box_0[15]);
116189
+ const y1 = max3(scratch_box_0[4], scratch_box_0[10], scratch_box_0[16]);
116190
+ const z1 = max3(scratch_box_0[5], scratch_box_0[11], scratch_box_0[17]);
116608
116191
 
116609
116192
  return aabb3_compute_half_surface_area(x0, y0, z0, x1, y1, z1);
116610
116193
  }
@@ -118872,7 +118455,6 @@ class CachingTextureAtlas extends AbstractTextureAtlas {
118872
118455
  /**
118873
118456
  *
118874
118457
  * @param {AbstractTextureAtlas} atlas
118875
- * @param {number} cache_size
118876
118458
  */
118877
118459
  constructor({
118878
118460
  atlas
@@ -119187,7 +118769,7 @@ class AbstractLight {
119187
118769
 
119188
118770
  /**
119189
118771
  *
119190
- * @param {AABB3} result
118772
+ * @param {number[]|ArrayLike<number>|Float32Array} result
119191
118773
  * @returns {void}
119192
118774
  */
119193
118775
  getAABB(result) {
@@ -119312,7 +118894,6 @@ function aabb3_matrix4_project_by_corners(result, aabb_corners, projection_matri
119312
118894
  }
119313
118895
 
119314
118896
  const corners = [];
119315
- const aabb_array = new Float32Array(6);
119316
118897
 
119317
118898
  class Decal extends AbstractLight {
119318
118899
  constructor() {
@@ -119344,7 +118925,7 @@ class Decal extends AbstractLight {
119344
118925
 
119345
118926
  /**
119346
118927
  *
119347
- * @param {number[]} m
118928
+ * @param {number[]|Float32Array|ArrayLike<number>} m
119348
118929
  */
119349
118930
  setTransform(m) {
119350
118931
  copy(this.transform, m);
@@ -119395,9 +118976,7 @@ class Decal extends AbstractLight {
119395
118976
  getAABB(result) {
119396
118977
  aabb3_build_corners(corners, 0, -0.5, -0.5, -0.5, 0.5, 0.5, 0.5);
119397
118978
 
119398
- aabb3_matrix4_project_by_corners(aabb_array, corners, this.transform);
119399
-
119400
- result.readFromArray(aabb_array, 0);
118979
+ aabb3_matrix4_project_by_corners(result, corners, this.transform);
119401
118980
  }
119402
118981
 
119403
118982
  toArray(destination, address) {
@@ -119553,16 +119132,6 @@ function v3_distance(x0, y0, z0, x1, y1, z1) {
119553
119132
  return Math.sqrt(v3_distance_sqr(x0, y0, z0, x1, y1, z1));
119554
119133
  }
119555
119134
 
119556
- /**
119557
- *
119558
- * @param {{id:number}} a
119559
- * @param {{id:number}} b
119560
- * @returns {number}
119561
- */
119562
- function compareObjectsByNumericId(a,b){
119563
- return a.id - b.id;
119564
- }
119565
-
119566
119135
  /**
119567
119136
  * TODO compute slices to minimize world-space volume difference between clusters
119568
119137
  * @param {number[]} input_frustum_corners corners of input frustum
@@ -119716,6 +119285,240 @@ function read_cluster_frustum_corners(output, i_z_0, tr_xy_1, i_y_0, tr_x_1, i_x
119716
119285
  read_frustum_corner(cluster_frustum_points, frustum_point_index_111, output, 21);
119717
119286
  }
119718
119287
 
119288
+ const _lut_array_buffer = new ArrayBuffer(84);
119289
+ /**
119290
+ * Mapping from 3 plane bitfield, corresponding to intersection point of the planes
119291
+ * @type {number[]}
119292
+ */
119293
+ const triplanar_corner_mapping = new Uint8Array(_lut_array_buffer, 0, 43);
119294
+
119295
+ triplanar_corner_mapping[0b010101] = 0;
119296
+ triplanar_corner_mapping[0b100101] = 1;
119297
+ triplanar_corner_mapping[0b011001] = 2;
119298
+ triplanar_corner_mapping[0b101001] = 3;
119299
+ triplanar_corner_mapping[0b010110] = 4;
119300
+ triplanar_corner_mapping[0b100110] = 5;
119301
+ triplanar_corner_mapping[0b011010] = 6;
119302
+ triplanar_corner_mapping[0b101010] = 7;
119303
+ /**
119304
+ * Mapping from 2 plane bitfield to 2 corner bitfield, corresponding to corner indices of intersecting planes
119305
+ * @type {number[]}
119306
+ */
119307
+ const biplanar_edge_mapping = new Uint8Array(_lut_array_buffer, 43, 41);
119308
+
119309
+ biplanar_edge_mapping[0b000101] = 0 | (1 << 3);
119310
+ biplanar_edge_mapping[0b000110] = 4 | (5 << 3);
119311
+
119312
+ biplanar_edge_mapping[0b001001] = 2 | (3 << 3);
119313
+ biplanar_edge_mapping[0b001010] = 6 | (7 << 3);
119314
+
119315
+ biplanar_edge_mapping[0b010100] = 0 | (4 << 3);
119316
+ biplanar_edge_mapping[0b011000] = 2 | (6 << 3);
119317
+
119318
+ biplanar_edge_mapping[0b100100] = 1 | (5 << 3);
119319
+ biplanar_edge_mapping[0b101000] = 3 | (7 << 3);
119320
+
119321
+ biplanar_edge_mapping[0b010001] = 0 | (2 << 3);
119322
+ biplanar_edge_mapping[0b010010] = 4 | (6 << 3);
119323
+
119324
+ biplanar_edge_mapping[0b100001] = 1 | (3 << 3);
119325
+ biplanar_edge_mapping[0b100010] = 5 | (7 << 3);
119326
+
119327
+ /**
119328
+ * Computes shortest distance between a line segment(defined by 2 points) and a point
119329
+ * @param {number} x0
119330
+ * @param {number} y0
119331
+ * @param {number} z0
119332
+ * @param {number} x1
119333
+ * @param {number} y1
119334
+ * @param {number} z1
119335
+ * @param {number} px
119336
+ * @param {number} py
119337
+ * @param {number} pz
119338
+ */
119339
+ function line3_computeSegmentPointDistance_sqr(x0, y0, z0, x1, y1, z1, px, py, pz) {
119340
+ // get line delta
119341
+ const line_delta_x = x1 - x0;
119342
+ const line_delta_y = y1 - y0;
119343
+ const line_delta_z = z1 - z0;
119344
+
119345
+ // find closest point
119346
+ const sp0_x = px - x0;
119347
+ const sp0_y = py - y0;
119348
+ const sp0_z = pz - z0;
119349
+
119350
+ //
119351
+ const d2 = line_delta_x * line_delta_x + line_delta_y * line_delta_y + line_delta_z * line_delta_z;
119352
+
119353
+ const d3 = line_delta_x * sp0_x + line_delta_y * sp0_y + line_delta_z * sp0_z;
119354
+
119355
+ const t = clamp01(d3 / d2);
119356
+
119357
+ // compute point on the line
119358
+ const lp_x = line_delta_x * t + x0;
119359
+ const lp_y = line_delta_y * t + y0;
119360
+ const lp_z = line_delta_z * t + z0;
119361
+
119362
+ // compute distance from the point in question to the point on the line
119363
+ return v3_distance_sqr(px, py, pz, lp_x, lp_y, lp_z);
119364
+ }
119365
+
119366
+ /**
119367
+ *
119368
+ * @param {number} radius
119369
+ * @param {number} light_x
119370
+ * @param {number} light_y
119371
+ * @param {number} light_z
119372
+ * @param {number} outside_plane_count
119373
+ * @param {number} outside_plane_bitfield
119374
+ * @param {number[]} points
119375
+ * @return {boolean}
119376
+ */
119377
+ function detailed_sphere_frustum_intersection_test(
119378
+ radius, light_x, light_y, light_z,
119379
+ outside_plane_count, outside_plane_bitfield,
119380
+ points
119381
+ ) {
119382
+
119383
+ const radius_sqr = radius * radius;
119384
+
119385
+ if (outside_plane_count === 3) {
119386
+ // nearest point in a corner, find the corner
119387
+ const point_index = triplanar_corner_mapping[outside_plane_bitfield];
119388
+
119389
+ const point_address = point_index * 3;
119390
+
119391
+ const point_x = points[point_address];
119392
+ const point_y = points[point_address + 1];
119393
+ const point_z = points[point_address + 2];
119394
+
119395
+ const distance_to_light_sqr = v3_distance_sqr(
119396
+ point_x, point_y, point_z,
119397
+ light_x, light_y, light_z
119398
+ );
119399
+
119400
+ // query_bvh_frustum_from_objects.checks_sphere_frustum_corner++;
119401
+
119402
+ // return distance_to_light_sqr <= radius_sqr;
119403
+
119404
+ if (distance_to_light_sqr > radius_sqr) {
119405
+
119406
+ // query_bvh_frustum_from_objects.rejection_sphere_count++;
119407
+ return false;
119408
+ }
119409
+
119410
+ } else if (outside_plane_count === 2) {
119411
+ // nearest point is on an edge of two plane intersection
119412
+ const edge_bitfield = biplanar_edge_mapping[outside_plane_bitfield];
119413
+
119414
+ // get 2 corners from edge bitfield
119415
+ const corner_index_0 = edge_bitfield & 0b111;
119416
+ const corner_address_0 = corner_index_0 * 3;
119417
+
119418
+ const corner_index_1 = (edge_bitfield >> 3) & 0b111;
119419
+ const corner_address_1 = corner_index_1 * 3;
119420
+
119421
+ const point_0_x = points[corner_address_0];
119422
+ const point_0_y = points[corner_address_0 + 1];
119423
+ const point_0_z = points[corner_address_0 + 2];
119424
+
119425
+ const point_1_x = points[corner_address_1];
119426
+ const point_1_y = points[corner_address_1 + 1];
119427
+ const point_1_z = points[corner_address_1 + 2];
119428
+
119429
+ const distance_to_frustum_sqr = line3_computeSegmentPointDistance_sqr(
119430
+ point_0_x, point_0_y, point_0_z,
119431
+ point_1_x, point_1_y, point_1_z,
119432
+ light_x, light_y, light_z
119433
+ );
119434
+
119435
+ // query_bvh_frustum_from_objects.checks_sphere_frustum_edge++;
119436
+
119437
+ // return distance_to_frustum_sqr <= radius_sqr;
119438
+
119439
+ if (distance_to_frustum_sqr > radius_sqr) {
119440
+
119441
+ // query_bvh_frustum_from_objects.rejection_sphere_count++;
119442
+ return false;
119443
+ }
119444
+ }
119445
+
119446
+ return true;
119447
+ }
119448
+
119449
+ /**
119450
+ *
119451
+ * @param {number} light_x
119452
+ * @param {number} light_y
119453
+ * @param {number} light_z
119454
+ * @param {number} radius
119455
+ * @param {number[]|Float32Array} points
119456
+ * @param {number[]|Float32Array|Float64Array} planes
119457
+ * @returns {boolean}
119458
+ */
119459
+ function point_light_inside_volume(
119460
+ light_x, light_y, light_z, radius,
119461
+ points, planes
119462
+ ) {
119463
+ let j = 0;
119464
+ let outside_plane_count = 0;
119465
+ let outside_plane_bitfield = 0;
119466
+
119467
+ // validate if the sphere intersects the plane-bound space
119468
+
119469
+ // query_bvh_frustum_from_objects.checks_sphere++;
119470
+
119471
+ for (; j < 6; j++) {
119472
+
119473
+ const plane_address = j * 4;
119474
+
119475
+ const plane_normal_x = planes[plane_address];
119476
+ const plane_normal_y = planes[plane_address + 1];
119477
+ const plane_normal_z = planes[plane_address + 2];
119478
+ const plane_constant = planes[plane_address + 3];
119479
+
119480
+ /*
119481
+ compute distance from the center of the sphere to the plane
119482
+ NOTE: inlined computation of distance from point to plane
119483
+ */
119484
+ const distance = (plane_normal_x * light_x + plane_normal_y * light_y + plane_normal_z * light_z) + plane_constant;
119485
+
119486
+ // query_bvh_frustum_from_objects.checks_sphere_plane++;
119487
+
119488
+ if (distance < 0) {
119489
+ // center of the sphere is below the plane
119490
+
119491
+ if (distance >= -radius) {
119492
+
119493
+ // sphere intersects the plane
119494
+
119495
+ outside_plane_count++;
119496
+
119497
+ const plane_bit_mask = 1 << j;
119498
+
119499
+ outside_plane_bitfield |= plane_bit_mask;
119500
+ } else {
119501
+ // sphere is entirely below the plane
119502
+
119503
+ // query_bvh_frustum_from_objects.rejection_sphere_count++;
119504
+
119505
+ return false;
119506
+ }
119507
+ }
119508
+
119509
+ }
119510
+
119511
+ if (outside_plane_count < 2) {
119512
+ return true;
119513
+ } else {
119514
+ return detailed_sphere_frustum_intersection_test(
119515
+ radius, light_x, light_y, light_z,
119516
+ outside_plane_count, outside_plane_bitfield,
119517
+ points
119518
+ );
119519
+ }
119520
+ }
119521
+
119719
119522
  /**
119720
119523
  *
119721
119524
  * @type {number[]}
@@ -120074,7 +119877,7 @@ function encode_light_descriptor(address, light) {
120074
119877
 
120075
119878
  /**
120076
119879
  *
120077
- * @param {IncrementalDeltaSet<PointLightData>} data_set
119880
+ * @param {IncrementalDeltaSet<LightRenderMetadata>} data_set
120078
119881
  * @param {BinaryUint32BVH} bvh
120079
119882
  */
120080
119883
  function build_light_data_bvh(data_set, bvh) {
@@ -120088,18 +119891,18 @@ function build_light_data_bvh(data_set, bvh) {
120088
119891
 
120089
119892
  /**
120090
119893
  *
120091
- * @type {PointLightData}
119894
+ * @type {LightRenderMetadata}
120092
119895
  */
120093
119896
  const datum = elements[i];
120094
119897
 
120095
- const bb = datum.bvh_leaf_main;
119898
+ const bb = datum.bvh_leaf.bounds;
120096
119899
 
120097
119900
  const payload = encode_light_descriptor(datum.address, datum.light);
120098
119901
 
120099
119902
  bvh.setLeafData(
120100
119903
  i, payload,
120101
- bb.x0, bb.y0, bb.z0,
120102
- bb.x1, bb.y1, bb.z1
119904
+ bb[0], bb[1], bb[2],
119905
+ bb[3], bb[4], bb[5],
120103
119906
  );
120104
119907
  }
120105
119908
 
@@ -120109,22 +119912,32 @@ function build_light_data_bvh(data_set, bvh) {
120109
119912
  bvh.build();
120110
119913
  }
120111
119914
 
119915
+ /**
119916
+ *
119917
+ * @param {LightRenderMetadata} a
119918
+ * @param {LightRenderMetadata} b
119919
+ * @returns {number}
119920
+ */
119921
+ function compareLRMByLightId(a, b) {
119922
+ return b.light.id - a.light.id;
119923
+ }
119924
+
120112
119925
  class LightManager {
120113
119926
 
120114
- constructor() {
119927
+ /**
119928
+ * @readonly
119929
+ * @type {ExplicitBinaryBoundingVolumeHierarchy}
119930
+ */
119931
+ #light_data_bvh = new ExplicitBinaryBoundingVolumeHierarchy();
120115
119932
 
120116
- /**
120117
- * @type {BinaryNode<PointLightData>}
120118
- * @private
120119
- */
120120
- this.__light_data_bvh = new BinaryNode();
119933
+ /**
119934
+ * Mapping from a light/decal object ID internal metadata
119935
+ * @type {Map<number,LightRenderMetadata>}
119936
+ */
119937
+ #metadata_map = new Map();
120121
119938
 
120122
- /**
120123
- *
120124
- * @type {Map<AbstractLight, PointLightData>}
120125
- * @private
120126
- */
120127
- this.__light_data_map = new Map();
119939
+
119940
+ constructor() {
120128
119941
 
120129
119942
  /**
120130
119943
  * Number of cluster slices along each dimension
@@ -120227,22 +120040,22 @@ class LightManager {
120227
120040
 
120228
120041
  /**
120229
120042
  *
120230
- * @type {IncrementalDeltaSet<PointLightData>}
120043
+ * @type {IncrementalDeltaSet<LightRenderMetadata>}
120231
120044
  * @readonly
120232
120045
  * @private
120233
120046
  */
120234
- this.__visible_lights = new IncrementalDeltaSet(compareObjectsByNumericId);
120047
+ this.__visible_lights = new IncrementalDeltaSet(compareLRMByLightId);
120235
120048
  /**
120236
120049
  *
120237
- * @type {IncrementalDeltaSet<PointLightData>}
120050
+ * @type {IncrementalDeltaSet<LightRenderMetadata>}
120238
120051
  * @readonly
120239
120052
  * @private
120240
120053
  */
120241
- this.__visible_decals = new IncrementalDeltaSet(compareObjectsByNumericId);
120054
+ this.__visible_decals = new IncrementalDeltaSet(compareLRMByLightId);
120242
120055
 
120243
120056
  /**
120244
120057
  *
120245
- * @type {PointLightData[]}
120058
+ * @type {LightRenderMetadata[]}
120246
120059
  * @private
120247
120060
  */
120248
120061
  this.__sorted_visible_lights = [];
@@ -120479,7 +120292,7 @@ class LightManager {
120479
120292
 
120480
120293
  /**
120481
120294
  *
120482
- * @param {PointLightData} data
120295
+ * @param {LightRenderMetadata} data
120483
120296
  * @private
120484
120297
  */
120485
120298
  __handle_visible_decal_added(data) {
@@ -120511,7 +120324,7 @@ class LightManager {
120511
120324
 
120512
120325
  /**
120513
120326
  *
120514
- * @param {PointLightData} data
120327
+ * @param {LightRenderMetadata} data
120515
120328
  * @private
120516
120329
  */
120517
120330
  __handle_visible_decal_removed(data) {
@@ -120548,7 +120361,7 @@ class LightManager {
120548
120361
 
120549
120362
  /**
120550
120363
  *
120551
- * @param {PointLightData} data
120364
+ * @param {LightRenderMetadata} data
120552
120365
  * @private
120553
120366
  */
120554
120367
  __handle_visible_light_added(data) {
@@ -120561,7 +120374,7 @@ class LightManager {
120561
120374
 
120562
120375
  /**
120563
120376
  *
120564
- * @param {PointLightData} data
120377
+ * @param {LightRenderMetadata} data
120565
120378
  * @private
120566
120379
  */
120567
120380
  __handle_visible_light_removed(data) {
@@ -120573,7 +120386,7 @@ class LightManager {
120573
120386
 
120574
120387
  /**
120575
120388
  *
120576
- * @param {PointLightData} light
120389
+ * @param {LightRenderMetadata} light
120577
120390
  * @returns {number}
120578
120391
  * @protected
120579
120392
  */
@@ -120591,7 +120404,7 @@ class LightManager {
120591
120404
 
120592
120405
  /**
120593
120406
  * DEBUG method
120594
- * @param {PointLightData} lights
120407
+ * @param {LightRenderMetadata} lights
120595
120408
  * @param {number} count
120596
120409
  * @returns {number} lower = better sorting score
120597
120410
  * @private
@@ -120688,7 +120501,7 @@ class LightManager {
120688
120501
 
120689
120502
  /**
120690
120503
  *
120691
- * @type {IncrementalDeltaSet<PointLightData>}
120504
+ * @type {IncrementalDeltaSet<LightRenderMetadata>}
120692
120505
  */
120693
120506
  const visible_lights = this.__visible_lights;
120694
120507
 
@@ -120702,20 +120515,54 @@ class LightManager {
120702
120515
 
120703
120516
  read_frustum_planes_to_array(this.__view_frustum.planes, scratch_frustum_planes);
120704
120517
 
120705
- const match_count = query_bvh_frustum_from_objects(nodes, 0, this.__light_data_bvh, scratch_frustum_planes, this.__view_frustum_points);
120518
+ /*
120519
+ Search is done in 2 phases:
120520
+ 1) broad phase using bounding boxes
120521
+ 2) granular phase where we use object-specific shape to check against frustum
120522
+ */
120523
+
120524
+ const broad_match_count = bvh_query_user_data_overlaps_frustum(nodes, 0, this.#light_data_bvh, scratch_frustum_planes);
120525
+
120526
+ for (let i = 0; i < broad_match_count; i++) {
120706
120527
 
120707
- for (let i = 0; i < match_count; i++) {
120528
+ const light_id = nodes[i];
120708
120529
 
120709
120530
  /**
120710
120531
  *
120711
- * @type {PointLightData}
120532
+ * @type {LightRenderMetadata}
120712
120533
  */
120713
- const light_data = nodes[i];
120534
+ const light_data = this.#metadata_map.get(light_id);
120535
+
120536
+ /**
120537
+ *
120538
+ * @type {PointLight|Decal}
120539
+ */
120540
+ const light = light_data.light;
120541
+
120542
+ if (light.isDecal === true) {
120543
+
120714
120544
 
120715
- if (light_data.light instanceof Decal) {
120716
120545
  // decals go into a separate bucket
120717
120546
  visible_decals.push(light_data);
120718
- } else {
120547
+
120548
+ } else if (light.isPointLight === true) {
120549
+ // perform granular check
120550
+
120551
+ const light_position = light.position;
120552
+
120553
+ const light_x = light_position.x;
120554
+ const light_y = light_position.y;
120555
+ const light_z = light_position.z;
120556
+
120557
+ const radius = light.radius.getValue();
120558
+
120559
+ if (!point_light_inside_volume(light_x, light_y, light_z, radius, this.__view_frustum_points, scratch_frustum_planes)) {
120560
+ // outside of view frustum
120561
+ continue;
120562
+ }
120563
+
120564
+
120565
+ // register as visible
120719
120566
  visible_lights.push(light_data);
120720
120567
  }
120721
120568
 
@@ -120730,7 +120577,7 @@ class LightManager {
120730
120577
 
120731
120578
  /**
120732
120579
  *
120733
- * @type {PointLightData[]}
120580
+ * @type {LightRenderMetadata[]}
120734
120581
  */
120735
120582
  const visible_lights = this.__sorted_visible_lights;
120736
120583
 
@@ -121074,17 +120921,17 @@ class LightManager {
121074
120921
  * @param {number} z
121075
120922
  */
121076
120923
  setTileMapResolution(x, y, z) {
121077
- assert.isNumber(x,'x');
121078
- assert.isNonNegativeInteger(x,'x');
121079
- assert.isFiniteNumber(x,'x');
120924
+ assert.isNumber(x, 'x');
120925
+ assert.isNonNegativeInteger(x, 'x');
120926
+ assert.isFiniteNumber(x, 'x');
121080
120927
 
121081
- assert.isNumber(y,'y');
121082
- assert.isNonNegativeInteger(y,'y');
121083
- assert.isFiniteNumber(y,'y');
120928
+ assert.isNumber(y, 'y');
120929
+ assert.isNonNegativeInteger(y, 'y');
120930
+ assert.isFiniteNumber(y, 'y');
121084
120931
 
121085
- assert.isNumber(z,'z');
121086
- assert.isNonNegativeInteger(z,'z');
121087
- assert.isFiniteNumber(z,'z');
120932
+ assert.isNumber(z, 'z');
120933
+ assert.isNonNegativeInteger(z, 'z');
120934
+ assert.isFiniteNumber(z, 'z');
121088
120935
 
121089
120936
  const r = this.__tiles_resolution;
121090
120937
 
@@ -121118,13 +120965,11 @@ class LightManager {
121118
120965
  * @param {AbstractLight} light
121119
120966
  */
121120
120967
  addLight(light) {
121121
- const lightData = new PointLightData(light);
121122
-
121123
- lightData.link();
120968
+ const lightData = new LightRenderMetadata(light);
121124
120969
 
121125
- this.__light_data_bvh.insertNode(lightData.bvh_leaf_main);
120970
+ lightData.link(this.#light_data_bvh);
121126
120971
 
121127
- this.__light_data_map.set(light, lightData);
120972
+ this.#metadata_map.set(light.id, lightData);
121128
120973
  }
121129
120974
 
121130
120975
  /**
@@ -121134,7 +120979,9 @@ class LightManager {
121134
120979
  */
121135
120980
  removeLight(light) {
121136
120981
 
121137
- const data = this.__light_data_map.get(light);
120982
+ const light_id = light.id;
120983
+
120984
+ const data = this.#metadata_map.get(light_id);
121138
120985
 
121139
120986
  if (data === undefined) {
121140
120987
  return false;
@@ -121142,7 +120989,7 @@ class LightManager {
121142
120989
 
121143
120990
  data.unlink();
121144
120991
 
121145
- this.__light_data_map.delete(light);
120992
+ this.#metadata_map.delete(light_id);
121146
120993
 
121147
120994
  return true;
121148
120995
  }
@@ -121771,6 +121618,7 @@ class ForwardPlusRenderingPlugin extends EnginePlugin {
121771
121618
  getLightManager() {
121772
121619
  return this.__light_manager;
121773
121620
  }
121621
+
121774
121622
  }
121775
121623
 
121776
121624
  /**
@@ -122408,10 +122256,6 @@ class PointLight extends AbstractLight {
122408
122256
  this.position.writeToArray(result, 0);
122409
122257
  }
122410
122258
 
122411
- /**
122412
- *
122413
- * @param {AABB3} result
122414
- */
122415
122259
  getAABB(result) {
122416
122260
  const r = this.radius.getValue();
122417
122261
  const p = this.position;
@@ -122420,15 +122264,13 @@ class PointLight extends AbstractLight {
122420
122264
  const center_y = p.y;
122421
122265
  const center_z = p.z;
122422
122266
 
122423
- result.setBounds(
122424
- center_x - r,
122425
- center_y - r,
122426
- center_z - r,
122267
+ result[0] = center_x - r;
122268
+ result[1] = center_y - r;
122269
+ result[2] = center_z - r;
122427
122270
 
122428
- center_x + r,
122429
- center_y + r,
122430
- center_z + r
122431
- );
122271
+ result[3] = center_x + r;
122272
+ result[4] = center_y + r;
122273
+ result[5] = center_z + r;
122432
122274
  }
122433
122275
 
122434
122276
  onDimensionChanged(listener, context) {
@@ -124889,6 +124731,12 @@ function browserInfo() {
124889
124731
  return cached;
124890
124732
  }
124891
124733
 
124734
+ const navigator = globalThis.navigator;
124735
+
124736
+ if(navigator === undefined){
124737
+ throw new Error('Not a browser, globalThis.navigator is undefined');
124738
+ }
124739
+
124892
124740
  let ua = navigator.userAgent, tem,
124893
124741
  M = ua.match(/(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i) || [];
124894
124742
 
@@ -125112,7 +124960,7 @@ function setListenerPositionNOOP(listener, position) {
125112
124960
 
125113
124961
  let setListenerPosition = setListenerPositionNOOP;
125114
124962
 
125115
- if (navigator !== undefined) {
124963
+ if (globalThis.navigator !== undefined) {
125116
124964
  const info = browserInfo();
125117
124965
  if (info.name === "Chrome") {
125118
124966
  if (info.version >= 64) {
@@ -125164,7 +125012,7 @@ function setListenerOrientation2(listener, forward, up) {
125164
125012
 
125165
125013
  let setListenerOrientation = noop;
125166
125014
 
125167
- if (navigator !== undefined) {
125015
+ if (globalThis.navigator !== undefined) {
125168
125016
  const info = browserInfo();
125169
125017
  if (info.name === "Chrome") {
125170
125018
  setListenerOrientation = setListenerOrientation1;
@@ -125924,6 +125772,9 @@ const ParallelBehaviorPolicy = {
125924
125772
  RequireAll: 1
125925
125773
  };
125926
125774
 
125775
+ /**
125776
+ * Executes all contained behaviors in parallel
125777
+ */
125927
125778
  class ParallelBehavior extends CompositeBehavior {
125928
125779
  /**
125929
125780
  *
@@ -125933,6 +125784,9 @@ class ParallelBehavior extends CompositeBehavior {
125933
125784
  constructor(successPolicy, failurePolicy) {
125934
125785
  super();
125935
125786
 
125787
+ assert.enum(successPolicy, ParallelBehaviorPolicy, 'successPolicy');
125788
+ assert.enum(failurePolicy, ParallelBehaviorPolicy, 'failurePolicy');
125789
+
125936
125790
  /**
125937
125791
  * @private
125938
125792
  * @type {ParallelBehaviorPolicy}
@@ -125946,6 +125800,7 @@ class ParallelBehavior extends CompositeBehavior {
125946
125800
  this.failurePolicy = failurePolicy;
125947
125801
 
125948
125802
  /**
125803
+ * @readonly
125949
125804
  * @private
125950
125805
  * @type {BitSet}
125951
125806
  */
@@ -125973,10 +125828,11 @@ class ParallelBehavior extends CompositeBehavior {
125973
125828
 
125974
125829
  /**
125975
125830
  *
125976
- * @param {ParallelBehaviorPolicy|number} v
125831
+ * @param {ParallelBehaviorPolicy|number} policy
125977
125832
  */
125978
- setSuccessPolicy(v) {
125979
- this.successPolicy = v;
125833
+ setSuccessPolicy(policy) {
125834
+ assert.enum(policy, ParallelBehaviorPolicy, 'policy');
125835
+ this.successPolicy = policy;
125980
125836
  }
125981
125837
 
125982
125838
  /**
@@ -125989,10 +125845,11 @@ class ParallelBehavior extends CompositeBehavior {
125989
125845
 
125990
125846
  /**
125991
125847
  *
125992
- * @param {ParallelBehaviorPolicy|number} v
125848
+ * @param {ParallelBehaviorPolicy|number} policy
125993
125849
  */
125994
- setFailurePolicy(v) {
125995
- this.failurePolicy = v;
125850
+ setFailurePolicy(policy) {
125851
+ assert.enum(policy, ParallelBehaviorPolicy, 'policy');
125852
+ this.failurePolicy = policy;
125996
125853
  }
125997
125854
 
125998
125855
  /**
@@ -126126,14 +125983,16 @@ class ParallelBehavior extends CompositeBehavior {
126126
125983
  /**
126127
125984
  *
126128
125985
  * @param {Behavior[]} elements
126129
- * @param {ParallelBehaviorPolicy} success
126130
- * @param {ParallelBehaviorPolicy} failure
125986
+ * @param {ParallelBehaviorPolicy} success how should successful completion be determined?
125987
+ * @param {ParallelBehaviorPolicy} failure how should failing completion be determined
126131
125988
  * @returns {ParallelBehavior}
126132
125989
  */
126133
125990
  static from(elements, success = ParallelBehaviorPolicy.RequireAll, failure = ParallelBehaviorPolicy.RequireOne) {
125991
+ assert.isArray(elements, 'elements');
125992
+
126134
125993
  const r = new ParallelBehavior(success, failure);
126135
125994
 
126136
- elements.forEach(e => r.addChild(e));
125995
+ r.addChildren(elements);
126137
125996
 
126138
125997
  return r;
126139
125998
  }