@needle-tools/engine 4.11.5-next.a900688 → 4.11.5-next.aa5e3b8

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 (75) hide show
  1. package/dist/generateMeshBVH.worker-D1Vr8UHG.js +21 -0
  2. package/dist/{gltf-progressive-GwdQV1Qx.umd.cjs → gltf-progressive-DWcmTMCh.umd.cjs} +1 -1
  3. package/dist/{gltf-progressive-CftVUJy3.min.js → gltf-progressive-DZrY8VT6.min.js} +2 -2
  4. package/dist/{gltf-progressive-BvlZQAkt.js → gltf-progressive-DgYz5BYa.js} +19 -19
  5. package/dist/loader.worker-Dip-PthR.js +23 -0
  6. package/dist/{needle-engine.bundle-vPWPv18K.min.js → needle-engine.bundle-DC-_T-N6.min.js} +115 -120
  7. package/dist/{needle-engine.bundle-Dsfd8BPk.umd.cjs → needle-engine.bundle-DCQk5ghP.umd.cjs} +124 -129
  8. package/dist/{needle-engine.bundle-Bwinyjsh.js → needle-engine.bundle-De0bbaDg.js} +2634 -2604
  9. package/dist/needle-engine.js +336 -335
  10. package/dist/needle-engine.min.js +1 -1
  11. package/dist/needle-engine.umd.cjs +1 -1
  12. package/dist/{postprocessing-CJC0Npcd.js → postprocessing-BTW9pD_s.js} +1822 -1723
  13. package/dist/{postprocessing-DrM4PWU3.umd.cjs → postprocessing-CMgoN5t5.umd.cjs} +229 -158
  14. package/dist/{postprocessing-l7zsdO_Q.min.js → postprocessing-DYDtB188.min.js} +227 -156
  15. package/dist/rapier-B3oL1ap-.js +5217 -0
  16. package/dist/rapier-DJ-luMxr.min.js +1 -0
  17. package/dist/rapier-DQltNJbN.umd.cjs +1 -0
  18. package/dist/{three-BDW9I486.min.js → three-B7CT31Bt.min.js} +1 -5
  19. package/dist/{three-MHVqtJYj.js → three-DfMvBzXi.js} +0 -5
  20. package/dist/{three-examples-CgwGHSgz.umd.cjs → three-examples-CsW4_6LI.umd.cjs} +2 -7
  21. package/dist/{three-examples-fvEPSC8L.min.js → three-examples-D1P7eEhn.min.js} +2 -7
  22. package/dist/{three-examples-C5Ht-QFN.js → three-examples-D1SK93ek.js} +1 -7
  23. package/dist/{three-mesh-ui-BjWTTk1R.js → three-mesh-ui-C_uSB5dD.js} +1 -1
  24. package/dist/{three-mesh-ui-Bm32sS2a.umd.cjs → three-mesh-ui-DpATDXwU.umd.cjs} +1 -1
  25. package/dist/{three-mesh-ui-CLdkp21K.min.js → three-mesh-ui-LQ44s0AL.min.js} +1 -1
  26. package/dist/{three-iFaDq9U3.umd.cjs → three-qj71I7J3.umd.cjs} +2 -6
  27. package/dist/{vendor-6kAXU6fm.umd.cjs → vendor-DhTcel4c.umd.cjs} +2 -7
  28. package/dist/{vendor-petGQl0N.js → vendor-Dkpn1a8s.js} +1 -7
  29. package/dist/{vendor-CsyK1CFs.min.js → vendor-DtTGRuXh.min.js} +2 -7
  30. package/lib/engine/debug/debug_spector.d.ts +16 -0
  31. package/lib/engine/debug/debug_spector.js +28 -0
  32. package/lib/engine/debug/debug_spector.js.map +1 -0
  33. package/lib/engine/engine_application.d.ts +7 -0
  34. package/lib/engine/engine_application.js +8 -1
  35. package/lib/engine/engine_application.js.map +1 -1
  36. package/lib/engine/engine_assetdatabase.d.ts +0 -6
  37. package/lib/engine/engine_assetdatabase.js +0 -15
  38. package/lib/engine/engine_assetdatabase.js.map +1 -1
  39. package/lib/engine/engine_context.d.ts +1 -0
  40. package/lib/engine/engine_context.js +5 -2
  41. package/lib/engine/engine_context.js.map +1 -1
  42. package/lib/engine/engine_gltf_builtin_components.js +1 -1
  43. package/lib/engine/engine_gltf_builtin_components.js.map +1 -1
  44. package/lib/engine/engine_loaders.js +24 -25
  45. package/lib/engine/engine_loaders.js.map +1 -1
  46. package/lib/engine/engine_physics.js +6 -2
  47. package/lib/engine/engine_physics.js.map +1 -1
  48. package/lib/engine/engine_physics_rapier.d.ts +11 -2
  49. package/lib/engine/engine_physics_rapier.js +9 -0
  50. package/lib/engine/engine_physics_rapier.js.map +1 -1
  51. package/lib/engine/engine_types.d.ts +16 -0
  52. package/lib/engine-components/RendererInstancing.d.ts +5 -3
  53. package/lib/engine-components/RendererInstancing.js +64 -31
  54. package/lib/engine-components/RendererInstancing.js.map +1 -1
  55. package/lib/engine-components/Skybox.js.map +1 -1
  56. package/lib/needle-engine.js +2 -1
  57. package/lib/needle-engine.js.map +1 -1
  58. package/package.json +1 -1
  59. package/src/engine/debug/debug_spector.ts +43 -0
  60. package/src/engine/engine_application.ts +16 -1
  61. package/src/engine/engine_assetdatabase.ts +0 -21
  62. package/src/engine/engine_context.ts +6 -2
  63. package/src/engine/engine_gltf_builtin_components.ts +1 -1
  64. package/src/engine/engine_loaders.ts +24 -26
  65. package/src/engine/engine_physics.ts +6 -2
  66. package/src/engine/engine_physics_rapier.ts +11 -2
  67. package/src/engine/engine_types.ts +17 -0
  68. package/src/engine-components/RendererInstancing.ts +69 -33
  69. package/src/engine-components/Skybox.ts +1 -1
  70. package/src/needle-engine.ts +4 -2
  71. package/dist/generateMeshBVH.worker-B9bjdr6J.js +0 -25
  72. package/dist/loader.worker-CiTwpNPW.js +0 -27
  73. package/dist/rapier-BqdcSmKY.umd.cjs +0 -1
  74. package/dist/rapier-Cg3w3nFI.min.js +0 -1
  75. package/dist/rapier-sU12SWAs.js +0 -5217
@@ -119,7 +119,7 @@ declare type IHasResolveGuids = {
119
119
  resolveGuids: (guidsMap: GuidsMap) => void;
120
120
  }
121
121
 
122
- const originalComponentNameKey = Symbol("original-component-name");
122
+ const originalComponentNameKey = "needle-component-name";// Symbol("original-component-name");
123
123
 
124
124
  /**
125
125
  * We want to create one id provider per component
@@ -236,20 +236,20 @@ function onBeforeLoad(loader: Loader | CustomLoader, context: Context): { compon
236
236
 
237
237
 
238
238
  /** Call after a 3d model has been loaded to compile shaders and construct the needle engine model structure with relevant metadata (if necessary) */
239
- async function onAfterLoaded(loader: Loader | CustomLoader, context: Context, pathOrUrl: string, asset: ValidLoaderReturnType, seed: number | null | UIDProvider, componentsExtension: NEEDLE_components | null): Promise<Model> {
240
- if (printGltf) console.warn("Loaded", pathOrUrl, asset);
239
+ async function onAfterLoaded(loader: Loader | CustomLoader, context: Context, gltfId: string, model: ValidLoaderReturnType, seed: number | null | UIDProvider, componentsExtension: NEEDLE_components | null): Promise<Model> {
240
+ if (printGltf) console.warn("Loaded", gltfId, model);
241
241
 
242
242
  // Handle loader was registered but no model was returned - should not completely break the engine
243
- if (asset == null) {
244
- console.error(`Loaded model is null '${pathOrUrl}' - please make sure the loader is registered correctly`);
243
+ if (model == null) {
244
+ console.error(`Loaded model is null '${gltfId}' - please make sure the loader is registered correctly`);
245
245
  return {
246
246
  scene: new Object3D(),
247
247
  animations: [],
248
248
  scenes: []
249
249
  };
250
250
  }
251
- else if (typeof asset !== "object") {
252
- console.error(`Loaded model is not an object '${pathOrUrl}' - please make sure the loader is registered correctly`);
251
+ else if (typeof model !== "object") {
252
+ console.error(`Loaded model is not an object '${gltfId}' - please make sure the loader is registered correctly`);
253
253
  return {
254
254
  scene: new Object3D(),
255
255
  animations: [],
@@ -258,50 +258,48 @@ async function onAfterLoaded(loader: Loader | CustomLoader, context: Context, pa
258
258
  }
259
259
 
260
260
  // Handle OBJ or FBX loader results
261
- if (asset instanceof Object3D) {
262
- asset = {
263
- scene: asset,
264
- animations: asset.animations,
265
- scenes: [asset]
261
+ if (model instanceof Object3D) {
262
+ model = {
263
+ scene: model,
264
+ animations: model.animations,
265
+ scenes: [model]
266
266
  }
267
267
  }
268
268
  // Handle STL loader results
269
- else if (asset instanceof BufferGeometry) {
269
+ else if (model instanceof BufferGeometry) {
270
270
  const mat = new MeshStandardMaterial({
271
271
  color: new Color(0xdddddd)
272
272
  });
273
- const mesh = new Mesh(asset, mat);
274
- asset = {
273
+ const mesh = new Mesh(model, mat);
274
+ model = {
275
275
  scene: mesh,
276
276
  animations: [],
277
277
  scenes: [mesh]
278
278
  }
279
279
  }
280
- else if (Array.isArray(asset.scenes) === false) {
281
- console.error(`[Needle Engine] The loaded model object does not have a scenes property '${pathOrUrl}' - please make sure the loader is registered correctly and three.js is not imported multiple times.`);
280
+ else if (Array.isArray(model.scenes) === false) {
281
+ console.error(`[Needle Engine] The loaded model object does not have a scenes property '${gltfId}' - please make sure the loader is registered correctly and three.js is not imported multiple times.`);
282
282
  }
283
283
 
284
284
 
285
285
  // Remove query parameters from gltfId
286
- if (pathOrUrl.includes("?")) {
287
- pathOrUrl = pathOrUrl.split("?")[0];
286
+ if (gltfId.includes("?")) {
287
+ gltfId = gltfId.split("?")[0];
288
288
  }
289
289
 
290
290
  // E.g. fbx material cleanup
291
- postprocessLoadedFile(loader, asset);
292
-
293
- context.assets.add(pathOrUrl, asset);
291
+ postprocessLoadedFile(loader, model);
294
292
 
295
293
  // load components
296
- if (isGLTFModel(asset)) {
297
- invokeLoadedImportPluginHooks(pathOrUrl, asset, context);
298
- await getLoader().createBuiltinComponents(context, pathOrUrl, asset, seed, componentsExtension || undefined);
294
+ if (isGLTFModel(model)) {
295
+ invokeLoadedImportPluginHooks(gltfId, model, context);
296
+ await getLoader().createBuiltinComponents(context, gltfId, model, seed, componentsExtension || undefined);
299
297
  }
300
298
 
301
299
  // Warmup the scene
302
- await compileAsync(asset.scene, context, context.mainCamera);
300
+ await compileAsync(model.scene, context, context.mainCamera);
303
301
 
304
- return asset;
302
+ return model;
305
303
  }
306
304
 
307
305
  async function compileAsync(scene: Object3D, context: Context, camera?: Camera | null) {
@@ -13,6 +13,7 @@ import type { IPhysicsEngine } from './engine_types.js';
13
13
  import { getParam } from "./engine_utils.js"
14
14
 
15
15
  const debugPhysics = getParam("debugphysics");
16
+ const debugWorker = getParam("debugworker");
16
17
  const layerMaskHelper: Layers = new Layers();
17
18
 
18
19
 
@@ -654,6 +655,9 @@ namespace NEMeshBVH {
654
655
  // if there are no workers available, create a new one
655
656
  if (!workerInstance && workerInstances.length < 3) {
656
657
  try {
658
+ if (debugWorker) {
659
+ console.warn("[GenerateMeshBVHWorker] Creating worker with import.meta.url:", import.meta.url);
660
+ }
657
661
  workerInstance = new _GenerateMeshBVHWorker();
658
662
  workerInstances.push(workerInstance);
659
663
  }
@@ -665,8 +669,8 @@ namespace NEMeshBVH {
665
669
  failedToCreateMeshBVHWorker += 10;
666
670
  }
667
671
  else {
668
- console.error("Failed to create MeshBVH worker");
669
- console.debug(err);
672
+ console.error("Failed to create MeshBVH worker. Please see below for more details:");
673
+ console.log(err);
670
674
  }
671
675
  failedToCreateMeshBVHWorker++;
672
676
  }
@@ -67,7 +67,7 @@ export class RapierPhysics implements IPhysicsEngine {
67
67
  debugRenderRaycasts: boolean = false;
68
68
 
69
69
  removeBody(obj: IComponent) {
70
- if(debugPhysics) console.log("REMOVE BODY", obj?.name, obj[$bodyKey]);
70
+ if (debugPhysics) console.log("REMOVE BODY", obj?.name, obj[$bodyKey]);
71
71
  if (!obj) return;
72
72
  this.validate();
73
73
  const body = obj[$bodyKey];
@@ -817,7 +817,16 @@ export class RapierPhysics implements IPhysicsEngine {
817
817
  return component;
818
818
  }
819
819
 
820
- private createCollider(collider: ICollider, desc: ColliderDesc) {
820
+ /**
821
+ * Creates a collider in the physics world.
822
+ *
823
+ * @param collider - The collider component.
824
+ * @param desc - The collider description.
825
+ * @returns The created collider.
826
+ *
827
+ * @throws Will throw an error if the physics world is not initialized. Make sure to call `initialize()` before creating colliders.
828
+ */
829
+ createCollider(collider: ICollider, desc: ColliderDesc) {
821
830
  if (!this.world) throw new Error("Physics world not initialized");
822
831
  const matrix = this._tempMatrix;
823
832
  let rigidBody: RigidBody | undefined = undefined;
@@ -566,6 +566,23 @@ export interface IPhysicsEngine {
566
566
  */
567
567
  boxOverlap(point: Vector3, size: Vector3, rotation: Vector4Like | null): Array<ShapeOverlapResult>;
568
568
 
569
+ /**
570
+ * Creates a collider in the physics world.
571
+ *
572
+ * @param collider - The collider component.
573
+ * @param desc - The collider description.
574
+ * @returns The created collider.
575
+ *
576
+ * @throws Will throw an error if the physics world is not initialized. Make sure to call `initialize()` before creating colliders.
577
+ *
578
+ * @example
579
+ * ```typescript
580
+ * const boxColliderDesc = NEEDLE_ENGINE_MODULES.RAPIER_PHYSICS.MODULE.ColliderDesc.cuboid(1, 1, 1);
581
+ * const collider = physicsEngine.createCollider(myBoxColliderComponent, boxColliderDesc);
582
+ * ```
583
+ */
584
+ createCollider(collider: ICollider, desc: any): any;
585
+
569
586
  // Collider methods
570
587
  /**
571
588
  * Adds a sphere collider to the physics world
@@ -29,8 +29,9 @@ export class InstancingHandler {
29
29
  * (The instancing mesh renderer will grow x2 if the max instance count is reached)
30
30
  * @default 4
31
31
  * @returns The initial instance count
32
- * */
33
- static getStartInstanceCount = (_obj: Object3D) => {
32
+ */
33
+ // @ts-ignore (ignore the unused parameter warning)
34
+ static getStartInstanceCount = (obj: Object3D) => {
34
35
  return 4;
35
36
  };
36
37
 
@@ -45,20 +46,24 @@ export class InstancingHandler {
45
46
  if (res) {
46
47
  if (handlesArray === null) handlesArray = [];
47
48
  handlesArray.push(res);
48
- // load texture lods
49
- NEEDLE_progressive.assignTextureLOD(res.renderer.material, 0);
50
-
51
- // Load mesh lods
52
- // TODO: technically for multi meshes we do this work multiple times (we search for meshes in children and then use the renderer sharedMeshes... that doesnt make sense)
53
- for (let i = 0; i < renderer.sharedMeshes.length; i++) {
54
- const mesh = renderer.sharedMeshes[i];
55
- const geometry = mesh.geometry;
56
- NEEDLE_progressive.assignMeshLOD(mesh, 0).then(lod => {
57
- if (lod && renderer.activeAndEnabled && geometry != lod) {
58
- res.setGeometry(lod);
59
- }
60
- });
49
+
50
+ // Load LOD for textures
51
+ const mat = res.object.material;
52
+ if(Array.isArray(mat)) {
53
+ mat.forEach(m => NEEDLE_progressive.assignTextureLOD(m, 0));
54
+ }
55
+ else {
56
+ NEEDLE_progressive.assignTextureLOD(mat, 0);
61
57
  }
58
+
59
+ // Load LOD for geometry
60
+ const mesh = res.object;
61
+ const geometry = mesh.geometry;
62
+ NEEDLE_progressive.assignMeshLOD(mesh, 0).then(lod => {
63
+ if (lod && geometry != lod) {
64
+ res.setGeometry(lod);
65
+ }
66
+ });
62
67
  }
63
68
 
64
69
  else if (level <= 0 && obj.type !== "Mesh") {
@@ -349,7 +354,7 @@ class InstancedMeshRenderer {
349
354
  private _context: Context;
350
355
  private _batchedMesh: BatchedMesh;
351
356
  private _handles: (InstanceHandle | null)[] = [];
352
- private readonly _geometryIds: Map<BufferGeometry, number> = new Map();
357
+ private _geometryIds = new WeakMap<BufferGeometry, number>();
353
358
  private _maxInstanceCount: number;
354
359
 
355
360
  private _currentInstanceCount = 0;
@@ -439,6 +444,10 @@ class InstancedMeshRenderer {
439
444
  private _needUpdateBounds: boolean = false;
440
445
  private _debugMaterial: MeshStandardMaterial | null = null;
441
446
 
447
+ private getBatchedMeshName() {
448
+ return this.name ? `${this.name} (BatchedMesh)` : "BatchedMesh";
449
+ }
450
+
442
451
  constructor(name: string, geo: BufferGeometry, material: Material, initialMaxCount: number, context: Context) {
443
452
  this.name = name;
444
453
  this.geometry = geo;
@@ -452,6 +461,7 @@ class InstancedMeshRenderer {
452
461
  this._maxVertexCount = estimate.vertexCount;
453
462
  this._maxIndexCount = estimate.indexCount;
454
463
  this._batchedMesh = new BatchedMesh(this._maxInstanceCount, this._maxVertexCount, this._maxIndexCount, this._debugMaterial ?? this.material);
464
+ this._batchedMesh.name = this.getBatchedMeshName();
455
465
  // this.inst = new InstancedMesh(geo, material, count);
456
466
  this._batchedMesh[$instancingAutoUpdateBounds] = true;
457
467
  // this.inst.count = 0;
@@ -475,7 +485,7 @@ class InstancedMeshRenderer {
475
485
  context.post_render_callbacks.push(this.onAfterRender);
476
486
 
477
487
  if (debugInstancing) {
478
- console.log(`Instanced renderer created with ${this._maxInstanceCount} instances, ${this._maxVertexCount} max vertices and ${this._maxIndexCount} max indices for \"${name}\"`)
488
+ console.log(`Instanced renderer (${this.name}) created with ${this._maxInstanceCount} instances, ${this._maxVertexCount} max vertices and ${this._maxIndexCount} max indices for \"${name}\"`)
479
489
  }
480
490
  }
481
491
 
@@ -522,7 +532,8 @@ class InstancedMeshRenderer {
522
532
  return false;
523
533
  }
524
534
 
525
- if (this.mustGrow(geo)) {
535
+ const newInstanceCount = this._currentInstanceCount + 1;
536
+ if (newInstanceCount > this._maxInstanceCount || this.mustGrow(geo)) {
526
537
  if (this.allowResize) {
527
538
  this.grow(geo);
528
539
  }
@@ -644,34 +655,42 @@ class InstancedMeshRenderer {
644
655
  private mustGrow(geo?: BufferGeometry): boolean {
645
656
  if (this.count >= this._maxInstanceCount) return true;
646
657
  if (!geo || !geo.attributes) return false;
658
+
659
+ const isKnownGeometry = this._geometryIds.has(geo);
660
+ if (isKnownGeometry) return false;
661
+
647
662
  const meshInfo = getMeshInformation(geo);
648
663
  const newVertexCount = meshInfo.vertexCount;
649
664
  const newIndexCount = meshInfo.indexCount;
650
665
  return this._currentVertexCount + newVertexCount > this._maxVertexCount || this._currentIndexCount + newIndexCount > this._maxIndexCount;
651
666
  }
652
667
 
668
+ private _growId = 0;
653
669
  private grow(geometry: BufferGeometry) {
670
+ const id = ++this._growId;
654
671
  const growFactor = 2;
655
672
  const newSize = Math.ceil(this._maxInstanceCount * growFactor);
656
673
 
657
674
  // create a new BatchedMesh instance
675
+ // TODO: we should keep track of how many instances for each geometry we have and consider that when estimating new space
658
676
  const estimatedSpace = this.tryEstimateVertexCountSize(newSize, [geometry]);// geometry.attributes.position.count;
659
677
  // const indices = geometry.index ? geometry.index.count : 0;
660
678
  const newMaxVertexCount = Math.max(this._maxVertexCount, estimatedSpace.vertexCount);
661
- const newMaxIndexCount = Math.max(this._maxIndexCount, estimatedSpace.indexCount, Math.ceil(this._maxVertexCount * growFactor));
679
+ const newMaxIndexCount = Math.max(this._maxIndexCount, estimatedSpace.indexCount);//, Math.ceil(this._maxVertexCount * growFactor));
662
680
 
663
681
  if (debugInstancing) {
664
682
  const geometryInfo = getMeshInformation(geometry);
665
- console.warn(`[Instancing] Growing Buffer\nMesh: \"${this.name}${geometry.name?.length ? "/" + geometry.name : ""}\"\n${geometryInfo.vertexCount} vertices, ${geometryInfo.indexCount} indices\nMax count ${this._maxInstanceCount} → ${newSize}\nMax vertex count ${this._maxVertexCount} -> ${newMaxVertexCount}\nMax index count ${this._maxIndexCount} -> ${newMaxIndexCount}`);
683
+ console.warn(`[Instancing] Growing Buffer\nMesh: \"${this.name}${geometry.name?.length ? "/" + geometry.name : ""}\" (${geometryInfo.vertexCount.toLocaleString()} vertices, ${geometryInfo.indexCount.toLocaleString()} indices)\nMax count ${this._maxInstanceCount.toLocaleString()} → ${newSize.toLocaleString()}\nMax vertex count ${this._maxVertexCount.toLocaleString()} -> ${newMaxVertexCount.toLocaleString()}\nMax index count ${this._maxIndexCount.toLocaleString()} -> ${newMaxIndexCount.toLocaleString()}`);
666
684
  this._debugMaterial = createDebugMaterial();
667
685
  }
668
686
  else if (isDevEnvironment()) {
669
- console.debug(`[Instancing] Growing Buffer\nMesh: \"${this.name}${geometry.name?.length ? "/" + geometry.name : ""}\"\nMax count ${this._maxInstanceCount} → ${newSize}\nMax vertex count ${this._maxVertexCount} -> ${newMaxVertexCount}\nMax index count ${this._maxIndexCount} -> ${newMaxIndexCount}`);
687
+ console.debug(`[Instancing] Growing Buffer\nMesh: \"${this.name}${geometry.name?.length ? "/" + geometry.name : ""}\"\nMax count ${this._maxInstanceCount} → ${newSize}\nMax vertex count ${this._maxVertexCount.toLocaleString()} -> ${newMaxVertexCount.toLocaleString()}\nMax index count ${this._maxIndexCount.toLocaleString()} -> ${newMaxIndexCount.toLocaleString()}`);
670
688
  }
671
689
 
672
690
  this._maxVertexCount = newMaxVertexCount;
673
691
  this._maxIndexCount = newMaxIndexCount;
674
692
  const newInst = new BatchedMesh(newSize, this._maxVertexCount, this._maxIndexCount, this._debugMaterial ?? this.material);
693
+ newInst.name = this.getBatchedMeshName();
675
694
  newInst.layers = this._batchedMesh.layers;
676
695
  newInst.castShadow = this._batchedMesh.castShadow;
677
696
  newInst.receiveShadow = this._batchedMesh.receiveShadow;
@@ -686,7 +705,7 @@ class InstancedMeshRenderer {
686
705
  // dispose the old batched mesh
687
706
  this._batchedMesh.dispose();
688
707
  this._batchedMesh.removeFromParent();
689
- this._geometryIds.clear();
708
+ this._geometryIds = new WeakMap<BufferGeometry, number>();
690
709
 
691
710
  this._batchedMesh = newInst;
692
711
  this._maxInstanceCount = newSize;
@@ -698,6 +717,11 @@ class InstancedMeshRenderer {
698
717
  const original = [...this._handles];
699
718
  this._handles = [];
700
719
  for (const handle of original) {
720
+ if (id !== this._growId) {
721
+ // another grow happened in the meantime
722
+ if (debugInstancing) console.warn("[Instancing] Aborting grow since another grow happened in the meantime");
723
+ return;
724
+ }
701
725
  if (handle && handle.__instanceIndex >= 0) {
702
726
  this.addGeometry(handle);
703
727
  this._handles[handle.__instanceIndex] = handle;
@@ -722,23 +746,31 @@ class InstancedMeshRenderer {
722
746
  entry.count += 1;
723
747
  }
724
748
 
749
+ if (_newGeometries && _newGeometries?.length > 0) {
750
+ const index = _newGeometries.indexOf(handle.object.geometry as BufferGeometry);
751
+ if (index !== -1) {
752
+ _newGeometries.splice(index, 1);
753
+ }
754
+ }
725
755
  }
726
756
  }
727
757
 
728
758
  // then calculate the total vertex count
729
759
  let totalVertices = 0;
730
760
  let totalIndices = 0;
761
+ let totalGeometries = 0;
731
762
  // let maxVertices = 0;
732
763
  for (const [_geo, data] of usedGeometries) {
733
- totalVertices += data.vertexCount * data.count;
734
- totalIndices += data.indexCount * data.count;
764
+ totalGeometries += 1;
765
+ totalVertices += data.vertexCount;
766
+ totalIndices += data.indexCount;
735
767
  // maxVertices = Math.max(maxVertices, geo.attributes.position.count * count);
736
768
  }
737
769
  // we calculate the average to make an educated guess of how many vertices will be needed with the new buffer count
738
- const averageVerts = Math.ceil(totalVertices / Math.max(1, this._currentInstanceCount));
739
- let maxVertexCount = averageVerts * newMaxInstances;
740
- const averageIndices = Math.ceil(totalIndices / Math.max(1, this._currentInstanceCount));
741
- let maxIndexCount = averageIndices * newMaxInstances * 2;
770
+ const averageVerts = Math.ceil(totalVertices / Math.max(1, totalGeometries));
771
+ let maxVertexCount = averageVerts * totalGeometries;
772
+ const averageIndices = Math.ceil(totalIndices / Math.max(1, totalGeometries));
773
+ let maxIndexCount = averageIndices * totalGeometries;
742
774
 
743
775
  // if new geometries are provided we *know* that they will be added
744
776
  // so we make sure to include them in the calculation
@@ -753,6 +785,10 @@ class InstancedMeshRenderer {
753
785
  }
754
786
  }
755
787
 
788
+ if (debugInstancing) {
789
+ console.log(`[Instancing] Estimated size for new buffer ${this.name}\nGeometries: ${totalGeometries} (New: ${_newGeometries?.length || 0})\nInstances: ${newMaxInstances}\nEstimated Vertices: ${maxVertexCount.toLocaleString()}\nEstimated Indices: ${maxIndexCount.toLocaleString()}`);
790
+ }
791
+
756
792
  return { vertexCount: maxVertexCount, indexCount: maxIndexCount };
757
793
  }
758
794
 
@@ -770,16 +806,16 @@ class InstancedMeshRenderer {
770
806
  let geometryId = this._geometryIds.get(geo);
771
807
  if (geometryId === undefined || geometryId === null) {
772
808
  if (debugInstancing)
773
- console.debug(`[Instancing] > ADD NEW GEOMETRY \"${handle.name} (${geo.name}; ${geo.uuid})\"\n${this._currentInstanceCount} instances, ${handle.maxVertexCount} max vertices, ${handle.maxIndexCount} max indices`);
809
+ console.warn(`[Instancing] > ADD NEW GEOMETRY \"${handle.name} (${geo.name}; ${geo.uuid})\"\nCurrent Instances: ${this._currentInstanceCount}\nMax Vertices: ${handle.maxVertexCount.toLocaleString()}\nMax Indices: ${handle.maxIndexCount.toLocaleString()}\nMax Triangles: ${(handle.maxIndexCount / 3).toLocaleString()}`);
774
810
 
775
811
  geometryId = this._batchedMesh.addGeometry(geo, handle.maxVertexCount, handle.maxIndexCount);
776
812
  this._geometryIds.set(geo, geometryId);
813
+ this._currentVertexCount += handle.maxVertexCount;
814
+ this._currentIndexCount += handle.maxIndexCount;
777
815
  }
778
816
  else {
779
817
  if (debugInstancing === "verbose") console.log(`[Instancing] > ADD INSTANCE \"${handle.name}\"\nGEOMETRY_ID=${geometryId}\n${this._currentInstanceCount} instances`);
780
818
  }
781
- this._currentVertexCount += handle.maxVertexCount;
782
- this._currentIndexCount += handle.maxIndexCount;
783
819
  const i = this._batchedMesh.addInstance(geometryId);
784
820
  handle.__geometryIndex = geometryId;
785
821
  handle.__instanceIndex = i;
@@ -787,7 +823,7 @@ class InstancedMeshRenderer {
787
823
  handle.__reservedIndexRange = handle.maxIndexCount;
788
824
  this._batchedMesh.setMatrixAt(i, handle.object.matrixWorld);
789
825
  if (debugInstancing)
790
- console.debug(`[Instancing] > ADDED INSTANCE \"${handle.name}\"\nGEOMETRY_ID=${geometryId}\n${this._currentInstanceCount} instances\nIndex: ${handle.__instanceIndex}`);
826
+ console.debug(`[Instancing] > ADDED INSTANCE \"${handle.name}\"\nGEOMETRY_ID=${geometryId}\n${this._currentInstanceCount} instances\nIndex: ${handle.__instanceIndex}\nVertices: ${this._currentVertexCount.toLocaleString()}/${this._maxVertexCount.toLocaleString()},\nIndices: ${this._currentIndexCount.toLocaleString()}/${this._maxIndexCount.toLocaleString()}`);
791
827
 
792
828
  }
793
829
 
@@ -803,7 +839,7 @@ class InstancedMeshRenderer {
803
839
  // this.inst.deleteGeometry(handle.__instanceIndex);
804
840
  // else
805
841
  // this._batchedMesh.setVisibleAt(handle.__instanceIndex, false);
806
- if(debugInstancing) {
842
+ if (debugInstancing) {
807
843
  console.debug(`[Instancing] < REMOVE INSTANCE \"${handle.name}\" at [${handle.__instanceIndex}]\nGEOMETRY_ID=${handle.__geometryIndex}\n${this._currentInstanceCount} instances\nIndex: ${handle.__instanceIndex}`);
808
844
  }
809
845
  this._batchedMesh.deleteInstance(handle.__instanceIndex);
@@ -1,6 +1,6 @@
1
1
  import { CompressedCubeTexture, CubeTexture, CubeUVReflectionMapping, EquirectangularRefractionMapping, Texture } from "three"
2
- import { isDevEnvironment } from "../engine/debug/debug.js";
3
2
 
3
+ import { isDevEnvironment } from "../engine/debug/debug.js";
4
4
  import { ContextEvent, ContextRegistry } from "../engine/engine_context_registry.js";
5
5
  import { syncField } from "../engine/engine_networking_auto.js";
6
6
  import { loadPMREM } from "../engine/engine_pmrem.js";
@@ -7,7 +7,7 @@ export * from "./engine-components-experimental/api.js";
7
7
  export * from "./engine-schemes/api.js";
8
8
 
9
9
  // make accessible for external javascript
10
- import { Context, loadAsset, NeedleXRSession, onAfterRender, onBeforeRender, onClear, onDestroy, onInitialized, onStart, onUpdate, VERSION } from "./engine/api.js";
10
+ import { Context, loadAsset, NeedleXRSession, onAfterRender, onBeforeRender, onClear, onDestroy, onInitialized, onStart, onUpdate, TypeStore, VERSION } from "./engine/api.js";
11
11
  const Needle = {
12
12
  VERSION: VERSION,
13
13
  Context: Context,
@@ -15,6 +15,7 @@ const Needle = {
15
15
  assets: {
16
16
  loadFromURL: loadAsset,
17
17
  },
18
+ types: TypeStore,
18
19
  onStart: onStart,
19
20
  onUpdate: onUpdate,
20
21
  onBeforeRender: onBeforeRender,
@@ -22,7 +23,8 @@ const Needle = {
22
23
  onInitializedContext: onInitialized,
23
24
  onDestroyContext: onDestroy,
24
25
  onClearContext: onClear,
25
- };
26
+ } as const;
27
+
26
28
  if (globalThis["Needle"]?.VERSION !== undefined) {
27
29
  console.warn(`Needle Engine is already imported: ${globalThis["Needle"].VERSION}`);
28
30
  }