@needle-tools/engine 4.11.4 → 4.11.5-next.53316bf

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 (164) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/components.needle.json +1 -1
  3. package/dist/generateMeshBVH.worker-mO20N_b8.js +21 -0
  4. package/dist/{gltf-progressive-GwdQV1Qx.umd.cjs → gltf-progressive-DWcmTMCh.umd.cjs} +1 -1
  5. package/dist/{gltf-progressive-CftVUJy3.min.js → gltf-progressive-DZrY8VT6.min.js} +2 -2
  6. package/dist/{gltf-progressive-BvlZQAkt.js → gltf-progressive-DgYz5BYa.js} +19 -19
  7. package/dist/loader.worker-Dip-PthR.js +23 -0
  8. package/dist/{needle-engine.bundle-CQdk7IvU.min.js → needle-engine.bundle-BR5nmLCs.min.js} +155 -160
  9. package/dist/{needle-engine.bundle-BxK1-fWD.umd.cjs → needle-engine.bundle-Cb9jKt1y.umd.cjs} +154 -159
  10. package/dist/{needle-engine.bundle-DmMrUPFQ.js → needle-engine.bundle-D6G3NMe4.js} +5964 -5815
  11. package/dist/needle-engine.d.ts +17848 -1
  12. package/dist/needle-engine.js +336 -335
  13. package/dist/needle-engine.min.js +1 -1
  14. package/dist/needle-engine.umd.cjs +1 -1
  15. package/dist/{postprocessing-CJC0Npcd.js → postprocessing-BTW9pD_s.js} +1822 -1723
  16. package/dist/{postprocessing-DrM4PWU3.umd.cjs → postprocessing-CMgoN5t5.umd.cjs} +229 -158
  17. package/dist/{postprocessing-l7zsdO_Q.min.js → postprocessing-DYDtB188.min.js} +227 -156
  18. package/dist/rapier-B3oL1ap-.js +5217 -0
  19. package/dist/rapier-DJ-luMxr.min.js +1 -0
  20. package/dist/rapier-DQltNJbN.umd.cjs +1 -0
  21. package/dist/{three-BDW9I486.min.js → three-B7CT31Bt.min.js} +1 -5
  22. package/dist/{three-MHVqtJYj.js → three-DfMvBzXi.js} +0 -5
  23. package/dist/{three-examples-CgwGHSgz.umd.cjs → three-examples-CsW4_6LI.umd.cjs} +2 -7
  24. package/dist/{three-examples-fvEPSC8L.min.js → three-examples-D1P7eEhn.min.js} +2 -7
  25. package/dist/{three-examples-C5Ht-QFN.js → three-examples-D1SK93ek.js} +1 -7
  26. package/dist/{three-mesh-ui-BjWTTk1R.js → three-mesh-ui-C_uSB5dD.js} +1 -1
  27. package/dist/{three-mesh-ui-Bm32sS2a.umd.cjs → three-mesh-ui-DpATDXwU.umd.cjs} +1 -1
  28. package/dist/{three-mesh-ui-CLdkp21K.min.js → three-mesh-ui-LQ44s0AL.min.js} +1 -1
  29. package/dist/{three-iFaDq9U3.umd.cjs → three-qj71I7J3.umd.cjs} +2 -6
  30. package/dist/{vendor-CsyK1CFs.min.js → vendor-BKGa4GE0.min.js} +34 -39
  31. package/dist/{vendor-petGQl0N.js → vendor-D0zoswDa.js} +1626 -1605
  32. package/dist/{vendor-6kAXU6fm.umd.cjs → vendor-UCpFAwt1.umd.cjs} +30 -35
  33. package/lib/engine/api.d.ts +1 -1
  34. package/lib/engine/api.js +1 -1
  35. package/lib/engine/api.js.map +1 -1
  36. package/lib/engine/debug/debug_spector.d.ts +16 -0
  37. package/lib/engine/debug/debug_spector.js +28 -0
  38. package/lib/engine/debug/debug_spector.js.map +1 -0
  39. package/lib/engine/engine_addressables.d.ts +74 -11
  40. package/lib/engine/engine_addressables.js +74 -11
  41. package/lib/engine/engine_addressables.js.map +1 -1
  42. package/lib/engine/engine_application.d.ts +7 -0
  43. package/lib/engine/engine_application.js +8 -1
  44. package/lib/engine/engine_application.js.map +1 -1
  45. package/lib/engine/engine_camera.fit.d.ts +48 -3
  46. package/lib/engine/engine_camera.fit.js +29 -0
  47. package/lib/engine/engine_camera.fit.js.map +1 -1
  48. package/lib/engine/engine_context.d.ts +33 -8
  49. package/lib/engine/engine_context.js +47 -8
  50. package/lib/engine/engine_context.js.map +1 -1
  51. package/lib/engine/engine_loaders.d.ts +0 -6
  52. package/lib/engine/engine_loaders.js +5 -5
  53. package/lib/engine/engine_loaders.js.map +1 -1
  54. package/lib/engine/engine_physics.js +6 -2
  55. package/lib/engine/engine_physics.js.map +1 -1
  56. package/lib/engine/engine_physics_rapier.d.ts +11 -2
  57. package/lib/engine/engine_physics_rapier.js +9 -0
  58. package/lib/engine/engine_physics_rapier.js.map +1 -1
  59. package/lib/engine/engine_scenelighting.d.ts +12 -1
  60. package/lib/engine/engine_scenelighting.js +21 -1
  61. package/lib/engine/engine_scenelighting.js.map +1 -1
  62. package/lib/engine/engine_texture.d.ts +1 -1
  63. package/lib/engine/engine_tonemapping.d.ts +1 -1
  64. package/lib/engine/engine_types.d.ts +16 -0
  65. package/lib/engine/engine_typestore.d.ts +1 -0
  66. package/lib/engine/engine_typestore.js +5 -6
  67. package/lib/engine/engine_typestore.js.map +1 -1
  68. package/lib/engine/engine_utils_qrcode.js +1 -1
  69. package/lib/engine/engine_utils_qrcode.js.map +1 -1
  70. package/lib/engine/extensions/NEEDLE_components.d.ts +4 -4
  71. package/lib/engine/extensions/NEEDLE_components.js +36 -17
  72. package/lib/engine/extensions/NEEDLE_components.js.map +1 -1
  73. package/lib/engine/extensions/NEEDLE_lightmaps.js +2 -2
  74. package/lib/engine/extensions/NEEDLE_lightmaps.js.map +1 -1
  75. package/lib/engine/extensions/extensions.d.ts +2 -2
  76. package/lib/engine/extensions/extensions.js +11 -5
  77. package/lib/engine/extensions/extensions.js.map +1 -1
  78. package/lib/engine/webcomponents/buttons.d.ts +3 -1
  79. package/lib/engine/webcomponents/buttons.js +3 -1
  80. package/lib/engine/webcomponents/buttons.js.map +1 -1
  81. package/lib/engine/webcomponents/needle menu/needle-menu.d.ts +39 -2
  82. package/lib/engine/webcomponents/needle menu/needle-menu.js +39 -2
  83. package/lib/engine/webcomponents/needle menu/needle-menu.js.map +1 -1
  84. package/lib/engine/webcomponents/needle-engine.attributes.d.ts +1 -0
  85. package/lib/engine/webcomponents/needle-engine.d.ts +1 -0
  86. package/lib/engine/webcomponents/needle-engine.js +3 -0
  87. package/lib/engine/webcomponents/needle-engine.js.map +1 -1
  88. package/lib/engine/xr/NeedleXRSession.js +2 -1
  89. package/lib/engine/xr/NeedleXRSession.js.map +1 -1
  90. package/lib/engine-components/Component.d.ts +5 -0
  91. package/lib/engine-components/Component.js +7 -0
  92. package/lib/engine-components/Component.js.map +1 -1
  93. package/lib/engine-components/ReflectionProbe.js +16 -10
  94. package/lib/engine-components/ReflectionProbe.js.map +1 -1
  95. package/lib/engine-components/Renderer.js +16 -42
  96. package/lib/engine-components/Renderer.js.map +1 -1
  97. package/lib/engine-components/RendererInstancing.d.ts +5 -3
  98. package/lib/engine-components/RendererInstancing.js +64 -31
  99. package/lib/engine-components/RendererInstancing.js.map +1 -1
  100. package/lib/engine-components/RendererLightmap.d.ts +1 -0
  101. package/lib/engine-components/RendererLightmap.js +24 -18
  102. package/lib/engine-components/RendererLightmap.js.map +1 -1
  103. package/lib/engine-components/Skybox.d.ts +15 -5
  104. package/lib/engine-components/Skybox.js +37 -25
  105. package/lib/engine-components/Skybox.js.map +1 -1
  106. package/lib/engine-components/export/usdz/USDZExporter.d.ts +24 -3
  107. package/lib/engine-components/export/usdz/USDZExporter.js +36 -2
  108. package/lib/engine-components/export/usdz/USDZExporter.js.map +1 -1
  109. package/lib/engine-components/timeline/PlayableDirector.d.ts +2 -1
  110. package/lib/engine-components/timeline/PlayableDirector.js +16 -9
  111. package/lib/engine-components/timeline/PlayableDirector.js.map +1 -1
  112. package/lib/engine-components/web/CursorFollow.d.ts +1 -0
  113. package/lib/engine-components/web/CursorFollow.js +2 -0
  114. package/lib/engine-components/web/CursorFollow.js.map +1 -1
  115. package/lib/engine-components/webxr/WebXR.js +4 -0
  116. package/lib/engine-components/webxr/WebXR.js.map +1 -1
  117. package/lib/engine-components/webxr/controllers/XRControllerModel.js +1 -1
  118. package/lib/engine-components/webxr/controllers/XRControllerModel.js.map +1 -1
  119. package/lib/needle-engine.js +2 -1
  120. package/lib/needle-engine.js.map +1 -1
  121. package/package.json +15 -12
  122. package/plugins/types/needleConfig.d.ts +1 -1
  123. package/plugins/types/next.d.ts +1 -1
  124. package/plugins/types/userconfig.d.ts +1 -1
  125. package/src/engine/api.ts +1 -1
  126. package/src/engine/debug/debug_spector.ts +43 -0
  127. package/src/engine/engine_addressables.ts +75 -11
  128. package/src/engine/engine_application.ts +16 -1
  129. package/src/engine/engine_camera.fit.ts +49 -4
  130. package/src/engine/engine_context.ts +59 -10
  131. package/src/engine/engine_loaders.ts +6 -6
  132. package/src/engine/engine_physics.ts +6 -2
  133. package/src/engine/engine_physics_rapier.ts +11 -2
  134. package/src/engine/engine_scenelighting.ts +30 -8
  135. package/src/engine/engine_texture.ts +1 -1
  136. package/src/engine/engine_tonemapping.ts +1 -1
  137. package/src/engine/engine_types.ts +17 -0
  138. package/src/engine/engine_typestore.ts +5 -6
  139. package/src/engine/engine_utils_qrcode.ts +1 -1
  140. package/src/engine/extensions/NEEDLE_components.ts +47 -26
  141. package/src/engine/extensions/NEEDLE_lightmaps.ts +2 -2
  142. package/src/engine/extensions/extensions.ts +11 -5
  143. package/src/engine/webcomponents/buttons.ts +3 -1
  144. package/src/engine/webcomponents/needle menu/needle-menu.ts +40 -3
  145. package/src/engine/webcomponents/needle-engine.attributes.ts +2 -1
  146. package/src/engine/webcomponents/needle-engine.ts +4 -0
  147. package/src/engine/xr/NeedleXRSession.ts +3 -1
  148. package/src/engine-components/Component.ts +9 -1
  149. package/src/engine-components/ReflectionProbe.ts +18 -10
  150. package/src/engine-components/Renderer.ts +16 -44
  151. package/src/engine-components/RendererInstancing.ts +69 -33
  152. package/src/engine-components/RendererLightmap.ts +27 -17
  153. package/src/engine-components/Skybox.ts +47 -36
  154. package/src/engine-components/export/usdz/USDZExporter.ts +52 -5
  155. package/src/engine-components/timeline/PlayableDirector.ts +16 -10
  156. package/src/engine-components/web/CursorFollow.ts +3 -0
  157. package/src/engine-components/webxr/WebXR.ts +4 -0
  158. package/src/engine-components/webxr/controllers/XRControllerModel.ts +1 -1
  159. package/src/needle-engine.ts +4 -2
  160. package/dist/generateMeshBVH.worker-B9bjdr6J.js +0 -25
  161. package/dist/loader.worker-CiTwpNPW.js +0 -27
  162. package/dist/rapier-BqdcSmKY.umd.cjs +0 -1
  163. package/dist/rapier-Cg3w3nFI.min.js +0 -1
  164. package/dist/rapier-sU12SWAs.js +0 -5217
@@ -92,18 +92,24 @@ class PointerResolver {
92
92
  }
93
93
  }
94
94
 
95
- export async function registerExtensions(loader: GLTFLoader, context: Context, url: string) {
95
+ export async function registerExtensions(loader: GLTFLoader, context: Context, url: string, sourceId: SourceIdentifier) {
96
96
 
97
97
  // Make sure to remove any url parameters from the sourceId (because the source id in the renderer does not have a ?v=xxx so it will not be able to register the resolved lightmap otherwise)
98
98
  const idEnd = url.indexOf("?");
99
99
  if (idEnd >= 0) url = url.substring(0, idEnd);
100
+ if (!sourceId) {
101
+ sourceId = url;
102
+ }
103
+ if (sourceId.startsWith("blob:") || sourceId.startsWith("data:")) {
104
+ console.debug("[GLTFLoader] Suspicious sourceId detected");
105
+ }
100
106
 
101
107
  loader.register(p => new NEEDLE_gameobject_data(p));
102
108
  loader.register(p => new NEEDLE_persistent_assets(p));
103
- loader.register(p => new NEEDLE_lightmaps(p, context.lightmaps, url));
104
- loader.register(p => new NEEDLE_lighting_settings(p, url, context));
105
- loader.register(p => new NEEDLE_techniques_webgl(p, url));
106
- loader.register(p => new NEEDLE_render_objects(p, url));
109
+ loader.register(p => new NEEDLE_lightmaps(p, context.lightmaps, sourceId));
110
+ loader.register(p => new NEEDLE_lighting_settings(p, sourceId, context));
111
+ loader.register(p => new NEEDLE_techniques_webgl(p, sourceId));
112
+ loader.register(p => new NEEDLE_render_objects(p, sourceId));
107
113
  loader.register(p => new NEEDLE_progressive(p));
108
114
  loader.register(p => new EXT_texture_exr(p));
109
115
  if (isResourceTrackingEnabled()) loader.register(p => new InternalUsageTrackerPlugin(p))
@@ -8,7 +8,9 @@ import { getIconElement } from "./icons.js";
8
8
  * Use the ButtonsFactory to create buttons with icons and functionality
9
9
  * Get access to the default buttons by using `ButtonsFactory.instance`
10
10
  * The factory will create the buttons if they don't exist yet, and return the existing ones if they do (this allows you to reparent or modify created buttons)
11
- */
11
+ *
12
+ * @category HTML
13
+ */
12
14
  export class ButtonsFactory {
13
15
 
14
16
  private static _instance?: ButtonsFactory;
@@ -48,11 +48,13 @@ export declare type ButtonInfo = {
48
48
  }
49
49
 
50
50
  /**
51
- * The NeedleMenu is a menu that can be displayed in the needle engine webcomponent or in VR/AR sessions.
51
+ * The NeedleMenu is a menu that can be displayed in the needle engine webcomponent or in VR/AR sessions.
52
+ *
52
53
  * The menu can be used to add buttons to the needle engine that can be used to interact with the application.
53
- * The menu can be positioned at the top or the bottom of the needle engine webcomponent
54
54
  *
55
- * @example Create a button using the NeedleMenu
55
+ * The menu can be positioned at the top or the bottom of the <needle-engine> webcomponent.
56
+ *
57
+ * @example Add a new button using the NeedleMenu
56
58
  * ```typescript
57
59
  * onStart(ctx => {
58
60
  * ctx.menu.appendChild({
@@ -76,6 +78,20 @@ export declare type ButtonInfo = {
76
78
  * }
77
79
  * }, "*");
78
80
  * ```
81
+ *
82
+ * @example Access the menu from a component
83
+ * ```typescript
84
+ * import { Behaviour, OnStart } from '@needle-tools/engine';
85
+ *
86
+ * export class MyComponent extends Behaviour {
87
+ *
88
+ * start() {
89
+ * this.context.menu.appendChild({ ... });
90
+ * }
91
+ * }
92
+ * ```
93
+ *
94
+ * @category HTML
79
95
  */
80
96
  export class NeedleMenu {
81
97
  private readonly _context: Context;
@@ -909,6 +925,27 @@ export class NeedleMenuElement extends HTMLElement {
909
925
  }
910
926
  }
911
927
  }
928
+ /**
929
+ * Appends a button or HTML element to the needle-menu options.
930
+ * @param node a Node or ButtonInfo to create a button from
931
+ * @returns the appended Node
932
+ *
933
+ * @example Append a button
934
+ * ```javascript
935
+ * const button = document.createElement("button");
936
+ * button.textContent = "Click Me";
937
+ * needleMenu.appendChild(button);
938
+ * ```
939
+ * @example Append a button using ButtonInfo
940
+ * ```javascript
941
+ * needleMenu.appendChild({
942
+ * label: "Click Me",
943
+ * onClick: () => { alert("Button clicked!"); },
944
+ * icon: "info",
945
+ * title: "This is a button",
946
+ * });
947
+ * ```
948
+ */
912
949
  appendChild<T extends Node>(node: T | ButtonInfo): T {
913
950
 
914
951
  if (!(node instanceof Node)) {
@@ -47,7 +47,7 @@ type SkyboxAttributes = {
47
47
  /** @deprecated. Use background-image instead - URL to .exr, .hdr, .png, .jpg to be used as skybox */
48
48
  "skybox-image"?: string,
49
49
  /** URL to .exr, .hdr, .png, .jpg to be used as skybox */
50
- "background-image"?: string,
50
+ "background-image"?: string,
51
51
  /** Blurs the background image (if any) - value between 0 and 1
52
52
  * 0: no blur
53
53
  * 1: full blur
@@ -69,6 +69,7 @@ type RenderingAttributes = {
69
69
  "transparent"?: boolean,
70
70
  "contactshadows"?: boolean,
71
71
  "tone-mapping"?: TonemappingAttributeOptions,
72
+ "tonemapping"?: TonemappingAttributeOptions,
72
73
  "tone-mapping-exposure"?: number,
73
74
  }
74
75
 
@@ -296,6 +296,10 @@ export class NeedleEngineWebComponent extends HTMLElement implements INeedleEngi
296
296
  }
297
297
  }
298
298
 
299
+ connectedMoveCallback() {
300
+ // we dont want needle-engine to cleanup JUST because the element is moved in the DOM. See https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#custom_element_lifecycle_callbacks
301
+ }
302
+
299
303
  /**
300
304
  * @internal
301
305
  */
@@ -527,7 +527,9 @@ export class NeedleXRSession implements INeedleXRSession {
527
527
  else
528
528
  console.log("%c" + `Requesting ${mode} session`, "font-weight:bold;");
529
529
  for (const script of scripts) {
530
- if (script.onBeforeXR) script.onBeforeXR(mode, init);
530
+ if (script.onBeforeXR && script.activeAndEnabled && !script.destroyed) {
531
+ script.onBeforeXR(mode, init);
532
+ }
531
533
  }
532
534
  for (const listener of this._sessionRequestStartListeners) {
533
535
  listener({ mode, init });
@@ -11,6 +11,7 @@ import { syncDestroy, syncInstantiate, SyncInstantiateOptions } from "../engine/
11
11
  import { Context, FrameEvent } from "../engine/engine_setup.js";
12
12
  import * as threeutils from "../engine/engine_three_utils.js";
13
13
  import type { Collision, ComponentInit, Constructor, ConstructorConcrete, GuidsMap, ICollider, IComponent, IGameObject, SourceIdentifier } from "../engine/engine_types.js";
14
+ import { TypeStore } from "../engine/engine_typestore.js";
14
15
  import type { INeedleXRSessionEventReceiver, NeedleXRControllerEventArgs, NeedleXREventArgs } from "../engine/engine_xr.js";
15
16
  import { type IPointerEventHandler, PointerEventData } from "./ui/PointerEvents.js";
16
17
 
@@ -540,7 +541,8 @@ export abstract class GameObject extends Object3D implements Object3D, IGameObje
540
541
  }
541
542
  }
542
543
 
543
-
544
+ // DO NOT CHANGE THE SYMBOL NAME
545
+ const $componentName = Symbol("component-name");
544
546
 
545
547
  /**
546
548
  * Needle Engine component's are the main building blocks of the Needle Engine.
@@ -581,6 +583,12 @@ export abstract class Component implements IComponent, EventTarget,
581
583
  */
582
584
  get isComponent(): boolean { return true; }
583
585
 
586
+ /**
587
+ * Get the original component type name before minification (available if the component is registered in the TypeStore)
588
+ */
589
+ get [$componentName]() { return TypeStore.getKey(this.constructor as any) || undefined; }
590
+
591
+
584
592
  private __context: Context | undefined;
585
593
 
586
594
  /**
@@ -1,4 +1,4 @@
1
- import { EquirectangularReflectionMapping, LinearSRGBColorSpace, Material, MeshBasicMaterial, Object3D, SRGBColorSpace, Texture, Vector3 } from "three";
1
+ import { CubeReflectionMapping, CubeTexture, EquirectangularReflectionMapping, LinearSRGBColorSpace, Material, MeshBasicMaterial, Object3D, SRGBColorSpace, Texture, Vector3 } from "three";
2
2
 
3
3
  import { isDevEnvironment, showBalloonWarning } from "../engine/debug/index.js";
4
4
  import { serializable } from "../engine/engine_serialization.js";
@@ -63,13 +63,19 @@ export class ReflectionProbe extends Behaviour {
63
63
 
64
64
  // @serializable(Texture)
65
65
  set texture(tex: Texture) {
66
- if (tex && !(tex instanceof Texture)) {
67
- console.error("ReflectionProbe.texture must be a Texture", tex);
68
- return;
69
- }
66
+
67
+ if (this._texture === tex) return;
70
68
  this._texture = tex;
69
+
70
+ if (debug) console.debug("[ReflectionProbe] Set reflection probe texture " + (tex?.name || "(removed)"));
71
+
71
72
  if (tex) {
72
- tex.mapping = EquirectangularReflectionMapping;
73
+ if (tex instanceof CubeTexture) {
74
+ // cube textures use CubeReflectionMapping by default
75
+ }
76
+ else if (tex.mapping !== EquirectangularReflectionMapping) {
77
+ tex.mapping = EquirectangularReflectionMapping;
78
+ }
73
79
  tex.colorSpace = LinearSRGBColorSpace;
74
80
  tex.needsUpdate = true;
75
81
  }
@@ -110,9 +116,8 @@ export class ReflectionProbe extends Behaviour {
110
116
  }
111
117
  }
112
118
  start(): void {
113
- if (!this._texture && isDevEnvironment()) {
119
+ if (!this._texture) {
114
120
  console.warn(`[ReflectionProbe] Missing texture. Please assign a custom cubemap texture. To use reflection probes assign them to your renderer's "anchor" property.`);
115
- showBalloonWarning("ReflectionProbe configuration hint: See browser console for details")
116
121
  }
117
122
  }
118
123
 
@@ -206,8 +211,11 @@ export class ReflectionProbe extends Behaviour {
206
211
  /** this is the material that we copied and that has the reflection probe */
207
212
  const copy = cached?.copy;
208
213
 
209
- // make sure the reflection probe is assigned
210
- copy["envMap"] = this.texture;
214
+ if ("envMap" in copy) {
215
+ // make sure the reflection probe is assigned
216
+ copy.envMap = this.texture;
217
+ copy.needsUpdate = true;
218
+ }
211
219
 
212
220
  _rend.sharedMaterials[i] = copy;
213
221
  }
@@ -69,7 +69,7 @@ class SharedMaterialArray implements ISharedMaterials {
69
69
  set changed(value: boolean) {
70
70
  if (value === true) {
71
71
  if (debugRenderer)
72
- console.warn("SharedMaterials have changed: " + this._renderer.name, this);
72
+ console.warn("SharedMaterials have changed: " + this._renderer.name);
73
73
  }
74
74
  this._changed = value;
75
75
  }
@@ -358,11 +358,15 @@ export class Renderer extends Behaviour implements IRenderer {
358
358
  //@ts-ignore
359
359
  get sharedMaterials(): SharedMaterialArray {
360
360
 
361
- // @ts-ignore (original materials will be set during deserialization)
362
- if (this._originalMaterials === undefined) return null;
363
-
364
- // @ts-ignore during deserialization code might access this property *before* the setter and then create an empty array
365
- if (this.__isDeserializing === true) return null;
361
+ if (this._originalMaterials === undefined) {
362
+ if (!this.__didAwake) {
363
+ // @ts-ignore (original materials will be set during deserialization)
364
+ return null;
365
+ }
366
+ else {
367
+ this._originalMaterials = [];
368
+ }
369
+ }
366
370
 
367
371
  if (!this._sharedMaterials || !this._sharedMaterials.is(this)) {
368
372
  if (!this._originalMaterials) this._originalMaterials = [];
@@ -454,6 +458,7 @@ export class Renderer extends Behaviour implements IRenderer {
454
458
  this.context.addBeforeRenderListener(this.gameObject, this.onBeforeRenderThree);
455
459
  }
456
460
 
461
+ this._lightmaps = undefined;
457
462
  this.applyLightmapping();
458
463
 
459
464
  if (showWireframe) {
@@ -468,8 +473,8 @@ export class Renderer extends Behaviour implements IRenderer {
468
473
  }
469
474
 
470
475
  private applyLightmapping() {
471
- if (this.lightmapIndex >= 0) {
472
- const type = this.gameObject.type;
476
+ if (this.lightmapIndex >= 0 && !this._lightmaps) {
477
+ // const type = this.gameObject.type;
473
478
 
474
479
  // use the override lightmap if its not undefined
475
480
  const tex = this._lightmapTextureOverride !== undefined
@@ -477,45 +482,12 @@ export class Renderer extends Behaviour implements IRenderer {
477
482
  : this.context.lightmaps.tryGetLightmap(this.sourceId, this.lightmapIndex);
478
483
  if (tex) {
479
484
  if (!this._lightmaps) this._lightmaps = [];
480
-
481
-
482
485
  const rm = new RendererLightmap(this);
483
486
  rm.init(this.lightmapIndex, this.lightmapScaleOffset, tex);
484
487
  this._lightmaps.push(rm);
485
-
486
- // if (type === "Mesh") {
487
- // const mat = this.gameObject["material"];
488
- // if (!mat?.isMeshBasicMaterial) {
489
- // if (this._lightmaps.length <= 0) {
490
- // }
491
- // const rm = this._lightmaps[0];
492
- // rm.init(this.lightmapIndex, this.lightmapScaleOffset, tex);
493
- // }
494
- // else {
495
- // if (mat)
496
- // console.warn("Lightmapping is not supported on MeshBasicMaterial", mat.name)
497
- // }
498
- // }
499
- // // for multi materials we need to loop through children
500
- // // and then we add a lightmap renderer component to each of them
501
- // else if (this.isMultiMaterialObject(this.gameObject) && this.sharedMaterials.length > 0) {
502
- // for (let i = 0; i < this.gameObject.children.length; i++) {
503
- // const child = this.gameObject.children[i];
504
- // if (!child["material"]?.isMeshBasicMaterial) {
505
- // let rm: RendererLightmap | undefined = undefined;
506
- // if (i >= this._lightmaps.length) {
507
- // rm = new RendererLightmap(child as Mesh, this.context);
508
- // this._lightmaps.push(rm);
509
- // }
510
- // else
511
- // rm = this._lightmaps[i];
512
- // rm.init(this.lightmapIndex, this.lightmapScaleOffset, tex);
513
- // }
514
- // }
515
- // }
516
488
  }
517
489
  else {
518
- if (debugRenderer) console.warn("Lightmap not found", this.sourceId, this.lightmapIndex);
490
+ if (debugRenderer) console.warn(`[Renderer] No lightmaps found ${this.name} (${this.sourceId}, ${this.lightmapIndex})`);
519
491
  }
520
492
  }
521
493
 
@@ -730,7 +702,6 @@ export class Renderer extends Behaviour implements IRenderer {
730
702
  // If the material has a envMap and is NOT using a reflection probe we set the envMap to the scene environment
731
703
  if (mat && "envMap" in mat && "envMapIntensity" in mat && !ReflectionProbe.isUsingReflectionProbe(mat)) {
732
704
  mat.envMap = this.context.scene.environment;
733
- mat.envMapIntensity = this.context.scene.environmentIntensity;
734
705
  mat.envMapRotation = this.context.scene.environmentRotation;
735
706
  }
736
707
  }
@@ -741,8 +712,9 @@ export class Renderer extends Behaviour implements IRenderer {
741
712
  private onBeforeRenderThree = (_renderer, _scene, _camera, _geometry, material, _group) => {
742
713
  if (material.envMapIntensity !== undefined) {
743
714
  const factor = this.hasLightmap ? Math.PI : 1;
744
- const environmentIntensity = this.context.mainCameraComponent?.environmentIntensity ?? 1;
715
+ const environmentIntensity = this.context.scene.environmentIntensity;
745
716
  material.envMapIntensity = Math.max(0, environmentIntensity * this.context.sceneLighting.environmentIntensity / factor);
717
+ // console.log(this.context.sceneLighting.environmentIntensity);
746
718
  }
747
719
  if (this._lightmaps) {
748
720
  for (const lm of this._lightmaps) {
@@ -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);