@woosh/meep-engine 2.74.0 → 2.75.0

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 (36) hide show
  1. package/build/bundle-worker-image-decoder.js +1 -1
  2. package/build/meep.cjs +183 -187
  3. package/build/meep.min.js +1 -1
  4. package/build/meep.module.js +183 -187
  5. package/package.json +1 -1
  6. package/src/core/binary/UINT32_MAX.js +5 -0
  7. package/src/core/bvh2/bvh3/BVH.js +44 -2
  8. package/src/core/bvh2/bvh3/BVH.spec.js +45 -0
  9. package/src/core/bvh2/bvh3/build_triangle_morton_codes.js +73 -0
  10. package/src/core/bvh2/bvh3/ebvh_build_for_geometry_morton.js +5 -101
  11. package/src/core/bvh2/bvh3/ebvh_build_hierarchy.js +59 -0
  12. package/src/core/bvh2/bvh3/query/bvh_query_user_data_nearest_to_point.js +31 -32
  13. package/src/core/bvh2/bvh3/query/bvh_query_user_data_nearest_to_point.spec.js +64 -0
  14. package/src/core/collection/SCRATCH_UINT32_TRAVERSAL_STACK.js +1 -0
  15. package/src/core/geom/3d/aabb/aabb3_signed_distance_sqr_to_point.js +1 -0
  16. package/src/core/geom/3d/aabb/aabb3_unsigned_distance_sqr_to_point.js +36 -0
  17. package/src/core/process/worker/OnDemandWorkerManager.js +5 -1
  18. package/src/engine/asset/loaders/ArrayBufferLoader.js +13 -15
  19. package/src/engine/asset/loaders/image/ImageDecoderWorker.js +1 -1
  20. package/src/engine/asset/loaders/image/ImageRGBADataLoader.js +5 -7
  21. package/src/engine/asset/loaders/image/codec/ThreadedImageDecoder.js +1 -1
  22. package/src/engine/asset/loaders/image/png/PNG.js +339 -332
  23. package/src/engine/asset/loaders/image/png/PNGReader.js +59 -16
  24. package/src/engine/asset/loaders/image/png/prototypePNG.js +13 -4
  25. package/src/engine/graphics/texture/virtual/v2/{SparseTexture.js → PageTexture.js} +62 -18
  26. package/src/engine/graphics/texture/virtual/v2/ResidentTileTexture.js +46 -0
  27. package/src/engine/graphics/texture/virtual/v2/{TileLoader.js → VirtualTextureTileLoader.js} +11 -8
  28. package/src/engine/graphics/texture/virtual/v2/{UsageMetadata.js → VirtualTextureUsage.js} +2 -8
  29. package/src/engine/graphics/texture/virtual/v2/{VirtualTextureManager.js → VirtualTextureUsageUpdater.js} +7 -5
  30. package/src/engine/graphics/texture/virtual/v2/debug/ResidencyDebugView.js +17 -5
  31. package/src/engine/graphics/texture/virtual/v2/debug/UsageDebugView.js +1 -1
  32. package/src/engine/graphics/texture/virtual/v2/debug/UsagePyramidDebugView.js +1 -1
  33. package/src/engine/graphics/texture/virtual/v2/prototype.js +78 -59
  34. package/src/engine/graphics/texture/virtual/v2/tile/{TextureTile.js → VirtualTextureTile.js} +2 -2
  35. package/src/engine/graphics/texture/virtual/v2/tile/compose_tile_address.js +4 -0
  36. /package/src/engine/graphics/texture/virtual/v2/{ShaderUsage.js → VirtualTextureUsageShader.js} +0 -0
@@ -53658,6 +53658,12 @@ class TerrainPreview {
53658
53658
  }
53659
53659
  }
53660
53660
 
53661
+ /**
53662
+ * equal to `Math.pow(2,32) - 1`
53663
+ * @type {number}
53664
+ */
53665
+ const UINT32_MAX = 4294967295;
53666
+
53661
53667
  /**
53662
53668
  * Assumes arrays have the same type
53663
53669
  * @param {TypedArray|Float32Array|Uint8Array|Uint32Array} source
@@ -53737,6 +53743,13 @@ const ELEMENT_WORD_COUNT = 10;
53737
53743
  */
53738
53744
  const INITIAL_CAPACITY = 128;
53739
53745
 
53746
+
53747
+ /**
53748
+ * Tree can not contain more than this number of nodes
53749
+ * @type {number}
53750
+ */
53751
+ const NODE_CAPACITY_LIMIT = Math.floor(UINT32_MAX / (ELEMENT_WORD_COUNT * 4));
53752
+
53740
53753
  /**
53741
53754
  * Bounding Volume Hierarchy implementation. Stores unsigned integer values at leaves, these are typically IDs or Index values.
53742
53755
  * Highly optimized both in terms of memory usage and CPU. Most of the code inlined. No allocation are performed during usage (except for growing the tree capacity).
@@ -53831,17 +53844,26 @@ class BVH {
53831
53844
  }
53832
53845
 
53833
53846
  __grow_capacity() {
53834
- const new_capacity = Math.ceil(max2(
53847
+ if (this.__capacity >= NODE_CAPACITY_LIMIT) {
53848
+ throw new Error('Can not grow capacity, already at maximum platform limit');
53849
+ }
53850
+
53851
+ let new_capacity = Math.ceil(max2(
53835
53852
  this.__capacity * CAPACITY_GROW_MULTIPLIER,
53836
53853
  this.__capacity + CAPACITY_GROW_MIN_STEP
53837
53854
  ));
53838
53855
 
53856
+ if (new_capacity > NODE_CAPACITY_LIMIT) {
53857
+ // can not grow as much as we'd like, but we can still grow up to the limit
53858
+ new_capacity = NODE_CAPACITY_LIMIT;
53859
+ }
53860
+
53839
53861
  this.__set_capacity(new_capacity);
53840
53862
  }
53841
53863
 
53842
53864
  /**
53843
53865
  *
53844
- * @param {number} new_capacity
53866
+ * @param {number} new_capacity in number of nodes
53845
53867
  * @private
53846
53868
  */
53847
53869
  __set_capacity(new_capacity) {
@@ -54393,6 +54415,9 @@ class BVH {
54393
54415
  assert.notEqual(child1, NULL_NODE, 'child1 is null');
54394
54416
  assert.notEqual(child2, NULL_NODE, 'child2 is null');
54395
54417
 
54418
+ assert.notEqual(child1, index, 'child1 is equal to parent');
54419
+ assert.notEqual(child2, index, 'child2 is equal to parent');
54420
+
54396
54421
  this.node_set_combined_aabb(index, child1, child2);
54397
54422
 
54398
54423
  index = uint32[address + COLUMN_PARENT];
@@ -54420,6 +54445,9 @@ class BVH {
54420
54445
  assert.notEqual(child1, NULL_NODE, 'child1 is null');
54421
54446
  assert.notEqual(child2, NULL_NODE, 'child2 is null');
54422
54447
 
54448
+ assert.notEqual(child1, index, 'child1 is equal to parent');
54449
+ assert.notEqual(child2, index, 'child2 is equal to parent');
54450
+
54423
54451
  uint32[node_address + COLUMN_HEIGHT] = 1 + max2(
54424
54452
  uint32[child1 * ELEMENT_WORD_COUNT + COLUMN_HEIGHT],
54425
54453
  uint32[child2 * ELEMENT_WORD_COUNT + COLUMN_HEIGHT],
@@ -54501,6 +54529,9 @@ class BVH {
54501
54529
  const iB = uint32[iA * ELEMENT_WORD_COUNT + COLUMN_CHILD_1];
54502
54530
  const iC = uint32[iA * ELEMENT_WORD_COUNT + COLUMN_CHILD_2];
54503
54531
 
54532
+ assert.notEqual(iA, iB, 'child1 equal to parent');
54533
+ assert.notEqual(iA, iB, 'child2 equal to parent');
54534
+
54504
54535
  assert.greaterThanOrEqual(iB, 0);
54505
54536
  assert.lessThan(iB, this.node_capacity);
54506
54537
  assert.greaterThanOrEqual(iC, 0);
@@ -54516,6 +54547,9 @@ class BVH {
54516
54547
  const iF = uint32[iC * ELEMENT_WORD_COUNT + COLUMN_CHILD_1];
54517
54548
  const iG = uint32[iC * ELEMENT_WORD_COUNT + COLUMN_CHILD_2];
54518
54549
 
54550
+ assert.notEqual(iC, iF, 'child1 equal to parent');
54551
+ assert.notEqual(iC, iG, 'child2 equal to parent');
54552
+
54519
54553
  // b2TreeNode* F = m_nodes + iF;
54520
54554
  // b2TreeNode* G = m_nodes + iG;
54521
54555
 
@@ -54534,6 +54568,7 @@ class BVH {
54534
54568
 
54535
54569
  // A's old parent should point to C
54536
54570
  if (a_parent !== NULL_NODE) {
54571
+ assert.notEqual(a_parent, iC);
54537
54572
  if (uint32[a_parent * ELEMENT_WORD_COUNT + COLUMN_CHILD_1] === iA) {
54538
54573
  uint32[a_parent * ELEMENT_WORD_COUNT + COLUMN_CHILD_1] = iC;
54539
54574
  } else {
@@ -54586,6 +54621,9 @@ class BVH {
54586
54621
 
54587
54622
  }
54588
54623
 
54624
+ assert.notEqual(iC, uint32[iC * ELEMENT_WORD_COUNT + COLUMN_CHILD_1]);
54625
+ assert.notEqual(iC, uint32[iC * ELEMENT_WORD_COUNT + COLUMN_CHILD_2]);
54626
+
54589
54627
  return iC;
54590
54628
  }
54591
54629
 
@@ -54594,6 +54632,9 @@ class BVH {
54594
54632
  const iD = uint32[iB * ELEMENT_WORD_COUNT + COLUMN_CHILD_1];
54595
54633
  const iE = uint32[iB * ELEMENT_WORD_COUNT + COLUMN_CHILD_2];
54596
54634
 
54635
+ assert.notEqual(iB, iD, 'child1 equal to parent');
54636
+ assert.notEqual(iB, iE, 'child2 equal to parent');
54637
+
54597
54638
  assert.greaterThanOrEqual(iD, 0);
54598
54639
  assert.lessThan(iD, this.node_capacity);
54599
54640
  assert.greaterThanOrEqual(iE, 0);
@@ -54660,9 +54701,15 @@ class BVH {
54660
54701
 
54661
54702
  }
54662
54703
 
54704
+ assert.notEqual(iB, uint32[iB * ELEMENT_WORD_COUNT + COLUMN_CHILD_1]);
54705
+ assert.notEqual(iB, uint32[iB * ELEMENT_WORD_COUNT + COLUMN_CHILD_2]);
54706
+
54663
54707
  return iB;
54664
54708
  }
54665
54709
 
54710
+ assert.notEqual(iA, uint32[iA * ELEMENT_WORD_COUNT + COLUMN_CHILD_1]);
54711
+ assert.notEqual(iA, uint32[iA * ELEMENT_WORD_COUNT + COLUMN_CHILD_2]);
54712
+
54666
54713
  // no rotation
54667
54714
  return iA;
54668
54715
  }
@@ -54823,6 +54870,7 @@ const SCRATCH_UINT32_TRAVERSAL_STACK = new Uint32Array(781250);
54823
54870
 
54824
54871
  /**
54825
54872
  * Pointer used to track current top of the stack, make sure to unwind once your traversal is done
54873
+ * Must be a positive integer
54826
54874
  * @type {number}
54827
54875
  */
54828
54876
  SCRATCH_UINT32_TRAVERSAL_STACK.pointer = 0;
@@ -68900,50 +68948,49 @@ function bvh_query_user_data_overlaps_frustum(
68900
68948
  return result_cursor - result_offset;
68901
68949
  }
68902
68950
 
68951
+ const PATH_SEPARATOR = '/';
68952
+
68903
68953
  /**
68904
- * @template CTX
68905
- * @template Asset
68954
+ * Strips all directories from the path.
68955
+ * @example 'a/b/c' -> 'c'
68956
+ * @param {string} path
68957
+ * @returns {string}
68906
68958
  */
68907
- class AssetLoader {
68908
- /**
68909
- *
68910
- * @type {AssetManager}
68911
- */
68912
- assetManager = null;
68959
+ function computePathBase(path) {
68960
+ if (typeof path !== "string") {
68961
+ throw new Error('path is not a string');
68962
+ }
68913
68963
 
68914
- /**
68915
- *
68916
- * @type {CTX}
68917
- */
68918
- context = null;
68964
+ const lastSlashIndex = path.lastIndexOf(PATH_SEPARATOR);
68919
68965
 
68920
- /**
68921
- *
68922
- * @param {AssetManager} assetManager
68923
- * @param {CTX} context
68924
- */
68925
- async link(assetManager, context) {
68926
- this.assetManager = assetManager;
68927
- this.context = context;
68966
+ if (lastSlashIndex !== -1) {
68967
+ return path.substring(lastSlashIndex + 1);
68968
+ } else {
68969
+ return path;
68970
+ }
68971
+ }
68972
+
68973
+ /**
68974
+ *
68975
+ * @param {string} path
68976
+ * @returns {String|null}
68977
+ */
68978
+ function computeFileExtension(path) {
68979
+ const type_of_path = typeof path;
68980
+ if (type_of_path !== "string") {
68981
+ throw new Error(`path must be a string, instead was '${type_of_path}'`);
68928
68982
  }
68929
68983
 
68930
- /**
68931
- * Release any held resources, finalizing operation of the loader
68932
- */
68933
- async unlink() {
68984
+ //get base
68985
+ const pathBase = computePathBase(path);
68934
68986
 
68935
- }
68987
+ const lastDotIndex = pathBase.lastIndexOf('.');
68936
68988
 
68937
- /**
68938
- *
68939
- * @param {AssetRequestScope} scope
68940
- * @param {string} path
68941
- * @param {function(Asset)} success
68942
- * @param {function} failure
68943
- * @param {function(current:number, total:number)} progress
68944
- */
68945
- load(scope, path, success, failure, progress) {
68946
- failure('Not Implemented');
68989
+ if (lastDotIndex !== -1) {
68990
+ return pathBase.substring(lastDotIndex + 1);
68991
+ } else {
68992
+ //no extension
68993
+ return null;
68947
68994
  }
68948
68995
  }
68949
68996
 
@@ -69154,49 +69201,6 @@ class Asset {
69154
69201
 
69155
69202
  Asset.prototype.byteSize = 0;
69156
69203
 
69157
- class ImageRGBADataAsset extends Asset {
69158
- /**
69159
- *
69160
- * @param {number[]|Uint8Array} data
69161
- * @param {number} width
69162
- * @param {number} height
69163
- * @param {number} [itemSize]
69164
- */
69165
- constructor(data, width, height, itemSize = 4) {
69166
- super();
69167
-
69168
- /**
69169
- *
69170
- * @type {number[]}
69171
- */
69172
- this.data = data;
69173
-
69174
- /**
69175
- *
69176
- * @type {number}
69177
- */
69178
- this.width = width;
69179
-
69180
- /**
69181
- *
69182
- * @type {number}
69183
- */
69184
- this.height = height;
69185
-
69186
- /**
69187
- * Number of channels
69188
- * @type {number}
69189
- */
69190
- this.itemSize = itemSize;
69191
-
69192
- this.byteSize = data.length;
69193
- }
69194
-
69195
- create() {
69196
- return new Sampler2D(this.data, this.itemSize, this.width, this.height);
69197
- }
69198
- }
69199
-
69200
69204
  const CrossOriginKind = {
69201
69205
  UseCredentials: 'use-credentials',
69202
69206
  Anonymous: 'anonymous'
@@ -69212,6 +69216,53 @@ class CrossOriginConfig {
69212
69216
  const default_coc = new CrossOriginConfig();
69213
69217
  CrossOriginConfig.default = Object.freeze(default_coc);
69214
69218
 
69219
+ /**
69220
+ * @template CTX
69221
+ * @template Asset
69222
+ */
69223
+ class AssetLoader {
69224
+ /**
69225
+ *
69226
+ * @type {AssetManager}
69227
+ */
69228
+ assetManager = null;
69229
+
69230
+ /**
69231
+ *
69232
+ * @type {CTX}
69233
+ */
69234
+ context = null;
69235
+
69236
+ /**
69237
+ *
69238
+ * @param {AssetManager} assetManager
69239
+ * @param {CTX} context
69240
+ */
69241
+ async link(assetManager, context) {
69242
+ this.assetManager = assetManager;
69243
+ this.context = context;
69244
+ }
69245
+
69246
+ /**
69247
+ * Release any held resources, finalizing operation of the loader
69248
+ */
69249
+ async unlink() {
69250
+
69251
+ }
69252
+
69253
+ /**
69254
+ *
69255
+ * @param {AssetRequestScope} scope
69256
+ * @param {string} path
69257
+ * @param {function(Asset)} success
69258
+ * @param {function} failure
69259
+ * @param {function(current:number, total:number)} progress
69260
+ */
69261
+ load(scope, path, success, failure, progress) {
69262
+ failure('Not Implemented');
69263
+ }
69264
+ }
69265
+
69215
69266
  class ArrayBufferLoader extends AssetLoader {
69216
69267
  /**
69217
69268
  *
@@ -69296,33 +69347,33 @@ class ArrayBufferLoader extends AssetLoader {
69296
69347
  }
69297
69348
 
69298
69349
  const reader = response.body.getReader();
69299
- response.headers.get('Content-Length');
69350
+ const contentLength = response.headers.get('Content-Length');
69351
+ const total = contentLength ? parseInt(contentLength) : 0;
69300
69352
  let loaded = 0;
69301
69353
 
69302
69354
  // periodically read data into the new stream tracking while download progress
69303
69355
  const stream = new ReadableStream({
69356
+ type: "bytes",
69304
69357
  start(controller) {
69305
69358
 
69306
- readData();
69359
+ pump();
69307
69360
 
69308
- function readData() {
69361
+ function pump() {
69309
69362
 
69310
69363
  reader.read().then(({ done, value }) => {
69311
69364
 
69312
69365
  if (done) {
69313
-
69366
+ // no more data, we're done
69314
69367
  controller.close();
69368
+ return;
69369
+ }
69315
69370
 
69316
- } else {
69317
-
69318
- loaded += value.byteLength;
69319
-
69320
- progress(loaded, length);
69371
+ loaded += value.byteLength;
69321
69372
 
69322
- controller.enqueue(value);
69323
- readData();
69373
+ progress(loaded, total);
69324
69374
 
69325
- }
69375
+ controller.enqueue(value);
69376
+ pump();
69326
69377
 
69327
69378
  });
69328
69379
 
@@ -69466,6 +69517,10 @@ class OnDemandWorkerManager {
69466
69517
  this.worker = worker;
69467
69518
  }
69468
69519
 
69520
+ /**
69521
+ * In milliseconds
69522
+ * @param {number} v
69523
+ */
69469
69524
  setTimeout(v) {
69470
69525
  assert.isNonNegativeInteger(v, 'v');
69471
69526
 
@@ -69572,7 +69627,7 @@ class ThreadedImageDecoder extends Codec {
69572
69627
  */
69573
69628
  this.worker = new OnDemandWorkerManager(workerBuilder.build());
69574
69629
 
69575
- this.worker.setTimeout(200);
69630
+ this.worker.setTimeout(1200);
69576
69631
  }
69577
69632
 
69578
69633
  async test(data) {
@@ -69591,104 +69646,46 @@ class ThreadedImageDecoder extends Codec {
69591
69646
  }
69592
69647
  }
69593
69648
 
69594
- /**
69595
- *
69596
- * @param {Image|ImageBitmap|HTMLImageElement} img
69597
- * @returns {Uint8Array}
69598
- */
69599
- function decode(img) {
69600
- const imgWidth = img.width;
69601
- const imgHeight = img.height;
69602
-
69603
- //
69604
- const canvas = document.createElement('canvas');
69605
-
69606
- canvas.width = imgWidth;
69607
- canvas.height = imgHeight;
69608
-
69609
- const context = canvas.getContext('2d');
69610
-
69611
- context.drawImage(img, 0, 0, imgWidth, imgHeight);
69612
-
69613
- const imgd = context.getImageData(0, 0, imgWidth, imgHeight);
69614
-
69615
- return imgd.data;
69616
- }
69617
-
69618
- class NativeImageDecoder extends Codec {
69619
- async decode(data) {
69620
- const image = new Image();
69621
-
69622
- // convert binary data to image URL that we can load
69623
- const blob = new Blob([data]);
69624
- const url = URL.createObjectURL(blob);
69625
-
69626
- image.src = url;
69627
-
69628
- // give browser a chance to decode image in async
69629
- await image.decode();
69630
-
69631
- const rgba_data = decode(image);
69649
+ class ImageRGBADataAsset extends Asset {
69650
+ /**
69651
+ *
69652
+ * @param {number[]|Uint8Array} data
69653
+ * @param {number} width
69654
+ * @param {number} height
69655
+ * @param {number} [itemSize]
69656
+ */
69657
+ constructor(data, width, height, itemSize = 4) {
69658
+ super();
69632
69659
 
69633
- const width = image.width;
69634
- const height = image.height;
69660
+ /**
69661
+ *
69662
+ * @type {number[]}
69663
+ */
69664
+ this.data = data;
69635
69665
 
69636
- // release resources
69637
- URL.revokeObjectURL(url);
69666
+ /**
69667
+ *
69668
+ * @type {number}
69669
+ */
69670
+ this.width = width;
69638
69671
 
69639
- return {
69640
- data: rgba_data,
69641
- width: width,
69642
- height: height,
69643
- itemSize: 4,
69644
- bitDepth: 8
69645
- };
69646
- }
69647
- }
69648
-
69649
- const PATH_SEPARATOR = '/';
69650
-
69651
- /**
69652
- * Strips all directories from the path.
69653
- * @example 'a/b/c' -> 'c'
69654
- * @param {string} path
69655
- * @returns {string}
69656
- */
69657
- function computePathBase(path) {
69658
- if (typeof path !== "string") {
69659
- throw new Error('path is not a string');
69660
- }
69672
+ /**
69673
+ *
69674
+ * @type {number}
69675
+ */
69676
+ this.height = height;
69661
69677
 
69662
- const lastSlashIndex = path.lastIndexOf(PATH_SEPARATOR);
69678
+ /**
69679
+ * Number of channels
69680
+ * @type {number}
69681
+ */
69682
+ this.itemSize = itemSize;
69663
69683
 
69664
- if (lastSlashIndex !== -1) {
69665
- return path.substring(lastSlashIndex + 1);
69666
- } else {
69667
- return path;
69668
- }
69669
- }
69670
-
69671
- /**
69672
- *
69673
- * @param {string} path
69674
- * @returns {String|null}
69675
- */
69676
- function computeFileExtension(path) {
69677
- const type_of_path = typeof path;
69678
- if (type_of_path !== "string") {
69679
- throw new Error(`path must be a string, instead was '${type_of_path}'`);
69684
+ this.byteSize = data.length;
69680
69685
  }
69681
69686
 
69682
- //get base
69683
- const pathBase = computePathBase(path);
69684
-
69685
- const lastDotIndex = pathBase.lastIndexOf('.');
69686
-
69687
- if (lastDotIndex !== -1) {
69688
- return pathBase.substring(lastDotIndex + 1);
69689
- } else {
69690
- //no extension
69691
- return null;
69687
+ create() {
69688
+ return new Sampler2D(this.data, this.itemSize, this.width, this.height);
69692
69689
  }
69693
69690
  }
69694
69691
 
@@ -69700,7 +69697,7 @@ class ImageRGBADataLoader extends AssetLoader {
69700
69697
 
69701
69698
  this.decoder = new CodecWithFallback(
69702
69699
  new ThreadedImageDecoder(),
69703
- new NativeImageDecoder()
69700
+ // new NativeImageDecoder()
69704
69701
  );
69705
69702
  }
69706
69703
 
@@ -69742,7 +69739,6 @@ class ImageRGBADataLoader extends AssetLoader {
69742
69739
 
69743
69740
  const bitmap = await this.__decode_via_worker(path, scope);
69744
69741
 
69745
-
69746
69742
  let data;
69747
69743
 
69748
69744
  const bitDepth = bitmap.bitDepth;
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.74.0",
8
+ "version": "2.75.0",
9
9
  "main": "build/meep.module.js",
10
10
  "module": "build/meep.module.js",
11
11
  "exports": {
@@ -0,0 +1,5 @@
1
+ /**
2
+ * equal to `Math.pow(2,32) - 1`
3
+ * @type {number}
4
+ */
5
+ export const UINT32_MAX = 4294967295;
@@ -1,4 +1,5 @@
1
1
  import { assert } from "../../assert.js";
2
+ import { UINT32_MAX } from "../../binary/UINT32_MAX.js";
2
3
  import { array_copy } from "../../collection/array/array_copy.js";
3
4
  import { typed_array_copy } from "../../collection/array/typed/typed_array_copy.js";
4
5
  import { aabb3_compute_surface_area } from "../../geom/3d/aabb/aabb3_compute_surface_area.js";
@@ -51,6 +52,13 @@ export const ELEMENT_WORD_COUNT = 10;
51
52
  */
52
53
  const INITIAL_CAPACITY = 128;
53
54
 
55
+
56
+ /**
57
+ * Tree can not contain more than this number of nodes
58
+ * @type {number}
59
+ */
60
+ const NODE_CAPACITY_LIMIT = Math.floor(UINT32_MAX / (ELEMENT_WORD_COUNT * 4));
61
+
54
62
  /**
55
63
  * Bounding Volume Hierarchy implementation. Stores unsigned integer values at leaves, these are typically IDs or Index values.
56
64
  * Highly optimized both in terms of memory usage and CPU. Most of the code inlined. No allocation are performed during usage (except for growing the tree capacity).
@@ -145,17 +153,26 @@ export class BVH {
145
153
  }
146
154
 
147
155
  __grow_capacity() {
148
- const new_capacity = Math.ceil(max2(
156
+ if (this.__capacity >= NODE_CAPACITY_LIMIT) {
157
+ throw new Error('Can not grow capacity, already at maximum platform limit');
158
+ }
159
+
160
+ let new_capacity = Math.ceil(max2(
149
161
  this.__capacity * CAPACITY_GROW_MULTIPLIER,
150
162
  this.__capacity + CAPACITY_GROW_MIN_STEP
151
163
  ));
152
164
 
165
+ if (new_capacity > NODE_CAPACITY_LIMIT) {
166
+ // can not grow as much as we'd like, but we can still grow up to the limit
167
+ new_capacity = NODE_CAPACITY_LIMIT;
168
+ }
169
+
153
170
  this.__set_capacity(new_capacity);
154
171
  }
155
172
 
156
173
  /**
157
174
  *
158
- * @param {number} new_capacity
175
+ * @param {number} new_capacity in number of nodes
159
176
  * @private
160
177
  */
161
178
  __set_capacity(new_capacity) {
@@ -707,6 +724,9 @@ export class BVH {
707
724
  assert.notEqual(child1, NULL_NODE, 'child1 is null');
708
725
  assert.notEqual(child2, NULL_NODE, 'child2 is null');
709
726
 
727
+ assert.notEqual(child1, index, 'child1 is equal to parent');
728
+ assert.notEqual(child2, index, 'child2 is equal to parent');
729
+
710
730
  this.node_set_combined_aabb(index, child1, child2);
711
731
 
712
732
  index = uint32[address + COLUMN_PARENT];
@@ -734,6 +754,9 @@ export class BVH {
734
754
  assert.notEqual(child1, NULL_NODE, 'child1 is null');
735
755
  assert.notEqual(child2, NULL_NODE, 'child2 is null');
736
756
 
757
+ assert.notEqual(child1, index, 'child1 is equal to parent');
758
+ assert.notEqual(child2, index, 'child2 is equal to parent');
759
+
737
760
  uint32[node_address + COLUMN_HEIGHT] = 1 + max2(
738
761
  uint32[child1 * ELEMENT_WORD_COUNT + COLUMN_HEIGHT],
739
762
  uint32[child2 * ELEMENT_WORD_COUNT + COLUMN_HEIGHT],
@@ -815,6 +838,9 @@ export class BVH {
815
838
  const iB = uint32[iA * ELEMENT_WORD_COUNT + COLUMN_CHILD_1];
816
839
  const iC = uint32[iA * ELEMENT_WORD_COUNT + COLUMN_CHILD_2];
817
840
 
841
+ assert.notEqual(iA, iB, 'child1 equal to parent');
842
+ assert.notEqual(iA, iB, 'child2 equal to parent');
843
+
818
844
  assert.greaterThanOrEqual(iB, 0);
819
845
  assert.lessThan(iB, this.node_capacity);
820
846
  assert.greaterThanOrEqual(iC, 0);
@@ -830,6 +856,9 @@ export class BVH {
830
856
  const iF = uint32[iC * ELEMENT_WORD_COUNT + COLUMN_CHILD_1];
831
857
  const iG = uint32[iC * ELEMENT_WORD_COUNT + COLUMN_CHILD_2];
832
858
 
859
+ assert.notEqual(iC, iF, 'child1 equal to parent');
860
+ assert.notEqual(iC, iG, 'child2 equal to parent');
861
+
833
862
  // b2TreeNode* F = m_nodes + iF;
834
863
  // b2TreeNode* G = m_nodes + iG;
835
864
 
@@ -848,6 +877,7 @@ export class BVH {
848
877
 
849
878
  // A's old parent should point to C
850
879
  if (a_parent !== NULL_NODE) {
880
+ assert.notEqual(a_parent, iC);
851
881
  if (uint32[a_parent * ELEMENT_WORD_COUNT + COLUMN_CHILD_1] === iA) {
852
882
  uint32[a_parent * ELEMENT_WORD_COUNT + COLUMN_CHILD_1] = iC;
853
883
  } else {
@@ -900,6 +930,9 @@ export class BVH {
900
930
 
901
931
  }
902
932
 
933
+ assert.notEqual(iC, uint32[iC * ELEMENT_WORD_COUNT + COLUMN_CHILD_1]);
934
+ assert.notEqual(iC, uint32[iC * ELEMENT_WORD_COUNT + COLUMN_CHILD_2]);
935
+
903
936
  return iC;
904
937
  }
905
938
 
@@ -908,6 +941,9 @@ export class BVH {
908
941
  const iD = uint32[iB * ELEMENT_WORD_COUNT + COLUMN_CHILD_1];
909
942
  const iE = uint32[iB * ELEMENT_WORD_COUNT + COLUMN_CHILD_2];
910
943
 
944
+ assert.notEqual(iB, iD, 'child1 equal to parent');
945
+ assert.notEqual(iB, iE, 'child2 equal to parent');
946
+
911
947
  assert.greaterThanOrEqual(iD, 0);
912
948
  assert.lessThan(iD, this.node_capacity);
913
949
  assert.greaterThanOrEqual(iE, 0);
@@ -974,9 +1010,15 @@ export class BVH {
974
1010
 
975
1011
  }
976
1012
 
1013
+ assert.notEqual(iB, uint32[iB * ELEMENT_WORD_COUNT + COLUMN_CHILD_1]);
1014
+ assert.notEqual(iB, uint32[iB * ELEMENT_WORD_COUNT + COLUMN_CHILD_2]);
1015
+
977
1016
  return iB;
978
1017
  }
979
1018
 
1019
+ assert.notEqual(iA, uint32[iA * ELEMENT_WORD_COUNT + COLUMN_CHILD_1]);
1020
+ assert.notEqual(iA, uint32[iA * ELEMENT_WORD_COUNT + COLUMN_CHILD_2]);
1021
+
980
1022
  // no rotation
981
1023
  return iA;
982
1024
  }