@woosh/meep-engine 2.34.33 → 2.34.34

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/core/assert.js CHANGED
@@ -316,6 +316,17 @@ assert.defined = function (value, name = "value") {
316
316
 
317
317
  };
318
318
 
319
+ /**
320
+ *
321
+ * @param {*} value
322
+ * @param {String} [name]
323
+ */
324
+ assert.isNull = function (value, name){
325
+ if (value !== null) {
326
+ throw new Error(`${name} is NOT null`);
327
+ }
328
+ }
329
+
319
330
  /**
320
331
  *
321
332
  * @param {*} value
@@ -8,7 +8,7 @@ import { arrayQuickSort } from "../collection/array/arrayQuickSort.js";
8
8
  import { max2 } from "../math/max2.js";
9
9
  import { min2 } from "../math/min2.js";
10
10
  import { computeSampleStandardDeviation } from "../math/statistics/computeSampleStandardDeviation.js";
11
- import { aabb3_intersect_aabb3, boxSurfaceArea2 } from "./aabb3/AABB3Math.js";
11
+ import { _boxSurfaceArea2, aabb3_intersect_aabb3, boxSurfaceArea2 } from "./aabb3/AABB3Math.js";
12
12
  import { isLeaf, LeafNode } from "./LeafNode.js";
13
13
  import { Node } from "./Node.js";
14
14
  import { surfaceAreaHeuristic } from "./sah/surfaceAreaHeuristic.js";
@@ -139,13 +139,135 @@ BinaryNode.prototype.setChildren = function (left, right) {
139
139
  right.parentNode = this;
140
140
  };
141
141
 
142
+ /**
143
+ *
144
+ * @param {Node} child
145
+ */
146
+ BinaryNode.prototype.setChildLeft = function (child) {
147
+ assert.isNull(this.left, 'left');
148
+
149
+ this.left = child;
150
+
151
+ this.updateLeafNodeCount();
152
+
153
+ this.refit();
154
+
155
+ child.parentNode = this;
156
+ }
157
+
158
+ /**
159
+ *
160
+ * @param {Node} child
161
+ */
162
+ BinaryNode.prototype.setChildRight = function (child) {
163
+ assert.isNull(this.right, 'right');
164
+
165
+ this.right = child;
166
+
167
+ this.updateLeafNodeCount();
168
+
169
+ this.refit();
170
+
171
+ child.parentNode = this;
172
+ }
173
+
174
+ /**
175
+ * @see "Dynamic BVH" by Erin Catto / Blizzard
176
+ * @param {AABB3} node
177
+ * @param {AABB3} addition
178
+ * @returns {number}
179
+ */
180
+ function inclusion_surface_area_heuristic(node, addition) {
181
+ const x0 = min2(node.x0, addition.x0);
182
+ const y0 = min2(node.y0, addition.y0);
183
+ const z0 = min2(node.z0, addition.z0);
184
+
185
+ const x1 = max2(node.x1, addition.x1);
186
+ const y1 = max2(node.y1, addition.y1);
187
+ const z1 = max2(node.z1, addition.z1);
188
+
189
+ return _boxSurfaceArea2(x0, y0, z0, x1, y1, z1) - boxSurfaceArea2(node);
190
+ }
191
+
192
+ /**
193
+ *
194
+ * @param {BinaryNode|LeafNode} node
195
+ * @param {AABB3} addition
196
+ */
197
+ function inclusion_cost_lower_bound(node, addition) {
198
+ let n = node;
199
+ let r = boxSurfaceArea2(addition);
200
+
201
+ while (n !== null) {
202
+
203
+ r += n.costForInclusion(addition);
204
+
205
+ n = n.parentNode;
206
+ }
207
+
208
+ return r;
209
+ }
210
+
211
+ /**
212
+ * For incremental insertion, a sibling needs to be found, newly inserted node will be parented together with this sibling
213
+ * @param {AABB3} box
214
+ * @returns {BinaryNode|LeafNode}
215
+ */
216
+ BinaryNode.prototype.__insertion_findSiblingFor = function (box) {
217
+
218
+ const open_set = [this];
219
+ let cursor = 1;
220
+
221
+ let best_cost = Infinity;
222
+ let best_node = this;
223
+
224
+ while (cursor > 0) {
225
+ const node = open_set[--cursor];
226
+
227
+ const cost = inclusion_surface_area_heuristic(node, box);
228
+
229
+ if (cost < best_cost) {
230
+ best_cost = cost;
231
+ best_node = node;
232
+ }
233
+
234
+ if (node.isBinaryNode) {
235
+
236
+ if (node.isProtected()) {
237
+ continue;
238
+ }
239
+
240
+ // consider pushing children into the queue
241
+
242
+ // compute lower bound for cost
243
+ const c_low = inclusion_cost_lower_bound(node, box);
244
+
245
+ if (c_low < best_cost) {
246
+ // worth-while to explore children
247
+ if (node.left !== null) {
248
+ open_set[cursor++] = node.left;
249
+ }
250
+ if (node.right !== null) {
251
+ open_set[cursor++] = node.right
252
+ }
253
+ }
254
+ }
255
+
256
+
257
+ }
258
+
259
+ return best_node;
260
+ }
261
+
142
262
  /**
143
263
  *
144
264
  * @param {AABB3} box
145
- * @returns {Node}
265
+ * @returns {Node|BinaryNode|LeafNode}
146
266
  */
147
267
  BinaryNode.prototype.findParentFor = function (box) {
148
268
  let n = this;
269
+ let aCost = 0;
270
+ let bCost = 0;
149
271
 
150
272
  for (; ;) {
151
273
 
@@ -181,8 +303,8 @@ BinaryNode.prototype.findParentFor = function (box) {
181
303
  }
182
304
  }
183
305
 
184
- let aCost = a.costForInclusion(box);
185
- let bCost = b.costForInclusion(box);
306
+ aCost = a.costForInclusion(box);
307
+ bCost = b.costForInclusion(box);
186
308
 
187
309
  if (aCost === bCost) {
188
310
 
@@ -196,9 +318,9 @@ BinaryNode.prototype.findParentFor = function (box) {
196
318
 
197
319
  }
198
320
 
199
- //change costs to be surface areas instead
200
- aCost = a.computeSAH();
201
- bCost = b.computeSAH();
321
+ //change costs to be based on balance
322
+ aCost = a.leafNodeCount / boxSurfaceArea2(a);
323
+ bCost = b.leafNodeCount / boxSurfaceArea2(b);
202
324
  }
203
325
 
204
326
  if (aCost === bCost) {
@@ -518,26 +640,29 @@ function countLeaves(node) {
518
640
  }
519
641
 
520
642
  /**
521
- *
643
+ * @deprecated Incorrect usage of SAH
522
644
  * @return {number}
523
645
  */
524
646
  BinaryNode.prototype.computeSAH = function () {
525
647
  let leftLeaves, rightLeaves, leftArea, rightArea;
526
648
 
527
- if (this.left === null) {
649
+ const left = this.left;
650
+
651
+ if (left === null) {
528
652
  leftArea = 0;
529
653
  leftLeaves = 0;
530
654
  } else {
531
- leftArea = boxSurfaceArea2(this.left);
532
- leftLeaves = this.left.leafNodeCount;
655
+ leftArea = boxSurfaceArea2(left);
656
+ leftLeaves = left.leafNodeCount;
533
657
  }
534
658
 
535
- if (this.right === null) {
659
+ const right = this.right;
660
+ if (right === null) {
536
661
  rightArea = 0;
537
662
  rightLeaves = 0;
538
663
  } else {
539
- rightArea = boxSurfaceArea2(this.right);
540
- rightLeaves = this.right.leafNodeCount;
664
+ rightArea = boxSurfaceArea2(right);
665
+ rightLeaves = right.leafNodeCount;
541
666
  }
542
667
 
543
668
  const thisArea = boxSurfaceArea2(this);
@@ -1065,34 +1190,96 @@ function transplantNewCommonParent(node, child) {
1065
1190
 
1066
1191
  parent.bubbleExpandToFit(bNode);
1067
1192
 
1193
+ parent.updateLeafNodeCount();
1194
+
1068
1195
  return bNode;
1069
1196
  }
1070
1197
 
1198
+
1071
1199
  /**
1072
1200
  *
1073
1201
  * @param {Node|BinaryNode|LeafNode} child
1074
1202
  */
1075
1203
  BinaryNode.prototype.insertNode = function (child) {
1076
- assert.notEqual(child, null, "child node is null");
1077
- assert.notEqual(child, undefined, "child node is undefined");
1078
- assert.ok(child instanceof Node, undefined, "child node is not an instance of Node");
1204
+
1205
+ assert.notNull(child, "child");
1206
+ assert.defined(child, "child");
1207
+ assert.isInstanceOf(child, Node, 'child', "Node");
1208
+ assert.equal(child.parentNode, null, "parentNode must be null, otherwise Node still belongs to another tree");
1209
+
1210
+ const sibling = this.__insertion_findSiblingFor(child);
1211
+
1212
+ const sibling_parent = sibling.parentNode;
1213
+ if (sibling_parent === null) {
1214
+ // root node
1215
+
1216
+ if (sibling.left === null) {
1217
+ sibling.setChildLeft(child);
1218
+ } else if (sibling.right === null) {
1219
+ sibling.setChildRight(child);
1220
+ } else {
1221
+ const parent = new BinaryNode();
1222
+
1223
+ parent.setChildren(sibling.right, child);
1224
+ sibling.right = parent;
1225
+ parent.parentNode = sibling;
1226
+ }
1227
+
1228
+ } else {
1229
+
1230
+ // non-root
1231
+
1232
+ if (sibling_parent.left === sibling && sibling_parent.right === null) {
1233
+
1234
+ // right node is free, take it
1235
+ sibling_parent.setChildRight(child);
1236
+
1237
+ } else if (sibling_parent.right === sibling && sibling_parent.left === null) {
1238
+
1239
+ // left node is free, take it
1240
+ sibling_parent.setChildLeft(child);
1241
+
1242
+ } else {
1243
+
1244
+ transplantNewCommonParent(sibling, child)
1245
+
1246
+ }
1247
+
1248
+ sibling_parent.bubbleRefit();
1249
+
1250
+ }
1251
+ }
1252
+
1253
+ /**
1254
+ * @deprecated
1255
+ * @param {Node|BinaryNode|LeafNode} child
1256
+ */
1257
+ BinaryNode.prototype.insertNode_old = function (child) {
1258
+ assert.notNull(child, "child");
1259
+ assert.defined(child, "child");
1260
+ assert.isInstanceOf(child, Node, 'child', "Node");
1079
1261
  assert.equal(child.parentNode, null, "parentNode must be null, otherwise Node still belongs to another tree");
1080
1262
 
1081
1263
  const node = this.findParentFor(child);
1082
1264
 
1083
1265
  let bNode;
1084
1266
 
1085
- if (node.isBinaryNode) {
1086
- if ((node.isProtected() && node !== this)) {
1087
- transplantNewCommonParent(node, child);
1088
- } else if (node.left === null) {
1267
+ if (
1268
+ node.isBinaryNode &&
1269
+ !(node.isProtected() && node !== this) // protected node, can't insert into this subtree
1270
+ ) {
1271
+
1272
+ if (node.left === null) {
1089
1273
  node.left = child;
1090
1274
  child.parentNode = node;
1091
1275
  node.bubbleExpandToFit(child);
1276
+
1092
1277
  } else if (node.right === null) {
1278
+
1093
1279
  node.right = child;
1094
1280
  child.parentNode = node;
1095
1281
  node.bubbleExpandToFit(child);
1282
+
1096
1283
  } else {
1097
1284
  //take right child and insert another binary node there
1098
1285
  bNode = new BinaryNode();
@@ -1106,18 +1293,17 @@ BinaryNode.prototype.insertNode = function (child) {
1106
1293
  node.updateLeafNodeCount();
1107
1294
  } else {
1108
1295
  //need to do transplanting and introduce a new common parent
1109
- bNode = transplantNewCommonParent(node, child);
1296
+ transplantNewCommonParent(node, child);
1110
1297
 
1111
- bNode.updateLeafNodeCount();
1112
1298
  }
1113
1299
 
1114
1300
  };
1115
1301
 
1116
1302
  /**
1117
- *
1303
+ * @param {boolean} deep
1118
1304
  * @returns {BinaryNode}
1119
1305
  */
1120
- BinaryNode.prototype.clone = function (deep) {
1306
+ BinaryNode.prototype.clone = function (deep = false) {
1121
1307
  const clone = new BinaryNode();
1122
1308
 
1123
1309
  clone.x0 = this.x0;
@@ -6,7 +6,7 @@
6
6
  import { aabb3_computeDistanceAbovePlane } from "../../geom/3d/aabb/aabb3_computeDistanceAbovePlane.js";
7
7
  import { computeAABB3PlaneSide } from "../../geom/3d/aabb/computeAABB3PlaneSide.js";
8
8
  import computeMortonCode from "../../geom/3d/morton/Morton.js";
9
- import { _boxSurfaceArea2, intersectRay, intersectSegment } from "./AABB3Math.js";
9
+ import { boxSurfaceArea, intersectRay, intersectSegment } from "./AABB3Math.js";
10
10
  import { aabb3_build_corners } from "../../geom/3d/aabb/aabb3_build_corners.js";
11
11
  import { assert } from "../../assert.js";
12
12
 
@@ -81,7 +81,7 @@ AABB3.prototype.computeMortonCode = function () {
81
81
  * @returns {number}
82
82
  */
83
83
  AABB3.prototype.computeSurfaceArea = function () {
84
- return _boxSurfaceArea2(this.x0, this.y0, this.z0, this.x1, this.y1, this.z1);
84
+ return boxSurfaceArea(this.x0, this.y0, this.z0, this.x1, this.y1, this.z1);
85
85
  };
86
86
 
87
87
  /**
@@ -296,7 +296,8 @@ AABB3.prototype.costForInclusion = function (other) {
296
296
  };
297
297
 
298
298
  /**
299
- *
299
+ * Surface area delta when including a given AABB
300
+ * 0 means that including a given AABB would not cause any change to total surface area
300
301
  * @param {number} x0
301
302
  * @param {number} y0
302
303
  * @param {number} z0
@@ -540,7 +540,7 @@ function boxSurfaceArea(x0, y0, z0, x1, y1, z1) {
540
540
  const dx = x1 - x0;
541
541
  const dy = y1 - y0;
542
542
  const dz = z1 - z0;
543
- return (dx * dy + dy * dz + dz * dx) * 2; //2 of each side
543
+ return (dy * (dx + dz) + dz * dx) * 2; //2 of each side
544
544
  }
545
545
 
546
546
  /**
@@ -719,40 +719,6 @@ List.prototype.copy = function (other) {
719
719
  }
720
720
  };
721
721
 
722
- /**
723
- *
724
- * @param {List} other
725
- * @param {function} elementComparator
726
- * @returns {number} -1, 0 or 1
727
- */
728
- List.prototype.compare = function (other, elementComparator) {
729
- const thisLength = this.length;
730
- const otherLength = other.length;
731
-
732
- const deltaLength = thisLength - otherLength;
733
-
734
- if (deltaLength !== 0) {
735
- return deltaLength;
736
- }
737
-
738
- for (let i = 0; i < thisLength; i++) {
739
- const thisElement = this.get(i);
740
- const otherElement = other.get(i);
741
-
742
- if (thisElement === otherElement) {
743
- continue;
744
- }
745
-
746
- const elementDelta = elementComparator(thisElement, otherElement);
747
-
748
- if (elementDelta !== 0) {
749
- return elementDelta;
750
- }
751
- }
752
-
753
- return 0;
754
- };
755
-
756
722
  /**
757
723
  * NOTE: do not modify resulting array
758
724
  * @returns {T[]}
@@ -4,6 +4,7 @@ import { GameAssetType } from "../../../../asset/GameAssetType.js";
4
4
  import { EngineConfiguration } from "../../../../EngineConfiguration.js";
5
5
  import { GLTFAssetLoader } from "../../../../asset/loaders/GLTFAssetLoader.js";
6
6
  import { three_object_to_entity_composition } from "../three_object_to_entity_composition.js";
7
+ import { AttachmentSystem } from "../../../../ecs/attachment/AttachmentSystem.js";
7
8
 
8
9
  const eh = new EngineHarness();
9
10
 
@@ -18,6 +19,7 @@ async function init(harness) {
18
19
  const config = new EngineConfiguration();
19
20
 
20
21
  config.addSystem(new ShadedGeometrySystem(engine));
22
+ config.addSystem(new AttachmentSystem());
21
23
  config.addLoader(GameAssetType.ModelGLTF_JSON, new GLTFAssetLoader());
22
24
 
23
25
  await config.apply(engine);
@@ -10,10 +10,9 @@ import { ShadedGeometry } from "./ShadedGeometry.js";
10
10
  export function three_object_to_entity_composition(root) {
11
11
  const node = new EntityNode();
12
12
 
13
- const transform = new Transform();
14
- transform.fromMatrix4(root.matrix.elements);
13
+ node.transform.fromMatrix4(root.matrix.elements);
15
14
 
16
- node.entity.add(transform);
15
+ node.entity.add(new Transform());
17
16
 
18
17
  if (root.isMesh) {
19
18
  if (root.isSkinnedMesh) {
@@ -30,6 +29,7 @@ export function three_object_to_entity_composition(root) {
30
29
 
31
30
  const child_node = three_object_to_entity_composition(child);
32
31
 
32
+
33
33
  node.addChild(child_node);
34
34
  }
35
35
 
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "productName": "Meep",
6
6
  "description": "production-ready JavaScript game engine based on Entity Component System Architecture",
7
7
  "author": "Alexander Goldring",
8
- "version": "2.34.33",
8
+ "version": "2.34.34",
9
9
  "dependencies": {
10
10
  "gl-matrix": "3.4.3",
11
11
  "fast-levenshtein": "2.0.6",