@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
@@ -13,6 +13,7 @@ import * as Stats from 'three/examples/jsm/libs/stats.module.js';
13
13
  import type { EffectComposer as ThreeEffectComposer } from "three/examples/jsm/postprocessing/EffectComposer.js";
14
14
  import { nodeFrame } from "three/examples/jsm/renderers/webgl-legacy/nodes/WebGLNodeBuilder.js";
15
15
 
16
+ import { initSpectorIfAvailable } from './debug/debug_spector.js';
16
17
  import { isDevEnvironment, LogType, showBalloonError, showBalloonMessage } from './debug/index.js';
17
18
  import { Addressables } from './engine_addressables.js';
18
19
  import { AnimationsRegistry } from './engine_animation.js';
@@ -37,7 +38,7 @@ import { RendererData as SceneLighting } from './engine_scenelighting.js';
37
38
  import { getTempColor, logHierarchy } from './engine_three_utils.js';
38
39
  import { Time } from './engine_time.js';
39
40
  import { patchTonemapping } from './engine_tonemapping.js';
40
- import type { CoroutineData, ICamera, IComponent, IContext, ILight, LoadedModel, Model, Vec2 } from "./engine_types.js";
41
+ import type { CoroutineData, ICamera, IComponent, IContext, ILight, LoadedModel, Model, SourceIdentifier, Vec2 } from "./engine_types.js";
41
42
  import { deepClone, delay, DeviceUtilities, getParam } from './engine_utils.js';
42
43
  import type { INeedleXRSessionEventReceiver, NeedleXRSession } from './engine_xr.js';
43
44
  import { NeedleMenu } from './webcomponents/needle menu/needle-menu.js';
@@ -129,9 +130,12 @@ export function registerComponent(script: IComponent, context?: Context) {
129
130
  }
130
131
 
131
132
  /**
132
- * The context is the main object that holds all the data and state of the Needle Engine.
133
- * It can be used to access the scene, renderer, camera, input, physics, networking, and more.
134
- * @example
133
+ * The Needle Engine context is the main access point that holds all the data and state of a Needle Engine application.
134
+ * It can be used to access the {@link Context.scene}, {@link Context.renderer}, {@link Context.mainCamera}, {@link Context.input}, {@link Context.physics}, {@link Context.time}, {@link Context.connection} (networking), and more.
135
+ *
136
+ * The context is automatically created when using the `<needle-engine>` web component.
137
+ *
138
+ * @example Accessing the context from a [component](https://engine.needle.tools/docs/api/Behaviour):
135
139
  * ```typescript
136
140
  * import { Behaviour } from "@needle-tools/engine";
137
141
  * import { Mesh, BoxGeometry, MeshBasicMaterial } from "three";
@@ -142,6 +146,18 @@ export function registerComponent(script: IComponent, context?: Context) {
142
146
  * }
143
147
  * }
144
148
  * ```
149
+ *
150
+ * @example Accessing the context from a [hook](https://engine.needle.tools/docs/scripting.html#hooks) without a component e.g. from a javascript module or svelte or react component.
151
+ *
152
+ * ```typescript
153
+ * import { onStart } from "@needle-tools/engine";
154
+ *
155
+ * onStart((context) => {
156
+ * console.log("Hello from onStart hook");
157
+ * context.scene.add(new Mesh(new BoxGeometry(), new MeshBasicMaterial()));
158
+ * });
159
+ * ```
160
+ *
145
161
  */
146
162
  export class Context implements IContext {
147
163
 
@@ -357,6 +373,7 @@ export class Context implements IContext {
357
373
  */
358
374
  composer: EffectComposer | ThreeEffectComposer | null = null;
359
375
 
376
+ // #region internal script lists
360
377
  /**
361
378
  * @internal All known components. Don't use directly
362
379
  */
@@ -424,6 +441,9 @@ export class Context implements IContext {
424
441
  /** @internal */
425
442
  readonly new_scripts_xr: INeedleXRSessionEventReceiver[] = [];
426
443
 
444
+ // #endregion
445
+ // #region Properties
446
+
427
447
  /**
428
448
  * The **main camera component** of the scene - this camera is used for rendering.
429
449
  * Use `setCurrentCamera` for updating the main camera.
@@ -468,25 +488,36 @@ export class Context implements IContext {
468
488
  physics: Physics;
469
489
  /** access networking methods (use it to send or listen to messages or join a networking backend) */
470
490
  connection: NetworkConnection;
471
- /**
472
- * @deprecated AssetDataBase is deprecated
473
- */
491
+ /** @deprecated AssetDatabase is deprecated */
474
492
  assets: AssetDatabase;
475
493
  /** The main light in the scene */
476
494
  mainLight: ILight | null = null;
477
495
  /** @deprecated Use sceneLighting */
478
496
  get rendererData() { return this.sceneLighting }
497
+ /** Access the scene lighting manager to control lighting settings in the context */
479
498
  sceneLighting: SceneLighting;
480
499
  addressables: Addressables;
481
500
  lightmaps: ILightDataRegistry;
482
501
  players: PlayerViewManager;
502
+
503
+ /** Access the LODs manager to control LOD behavior in the context */
483
504
  readonly lodsManager: LODsManager;
484
505
  /** Access the needle menu to add or remove buttons to the menu element */
485
506
  readonly menu: NeedleMenu;
486
507
 
487
- /** @returns true if the context is fully created and ready */
508
+ /**
509
+ * Checks if the context is fully created and ready
510
+ * @returns true if the context is fully created and ready
511
+ */
488
512
  get isCreated() { return this._isCreated; }
489
513
 
514
+ /**
515
+ * The source identifier(s) of the root scene(s) loaded into this context.
516
+ * When using `<needle-engine>` web component this will be the `src` attribute(s).
517
+ * @returns The source identifier for of the root scene
518
+ */
519
+ get rootSourceId(): SourceIdentifier | undefined { return this.rootSceneSourceIdentifiers[0] || undefined; }
520
+
490
521
  private _needsUpdateSize: boolean = false;
491
522
  private _isCreated: boolean = false;
492
523
  private _isCreating: boolean = false;
@@ -541,6 +572,7 @@ export class Context implements IContext {
541
572
  ContextRegistry.register(this);
542
573
  }
543
574
 
575
+ // #region Renderer
544
576
  /**
545
577
  * Calling this function will dispose the current renderer and create a new one which will then be assigned to the context. It can be used to create a new renderer with custom WebGLRendererParameters.
546
578
  * **Note**: Instead you can also modify the static `Context.DefaultWebGlRendererParameters` before the context is created.
@@ -591,6 +623,8 @@ export class Context implements IContext {
591
623
 
592
624
  this.input.bindEvents();
593
625
 
626
+ initSpectorIfAvailable(this, this.renderer.domElement);
627
+
594
628
  return this.renderer;
595
629
  }
596
630
 
@@ -894,6 +928,7 @@ export class Context implements IContext {
894
928
  }
895
929
  }
896
930
 
931
+ // #region onBeforeRender / onAfterRender listeners
897
932
  private readonly _onBeforeRenderListeners = new Map<string, OnRenderCallback[]>();
898
933
  private readonly _onAfterRenderListeners = new Map<string, OnRenderCallback[]>();
899
934
 
@@ -991,6 +1026,8 @@ export class Context implements IContext {
991
1026
  private _lastStyleComputedResult: boolean | undefined = undefined;
992
1027
 
993
1028
  private _createId: number = 0;
1029
+
1030
+ // #region internal create
994
1031
  private async internalOnCreate(opts?: ContextCreateArgs): Promise<boolean> {
995
1032
  const createId = ++this._createId;
996
1033
 
@@ -1200,6 +1237,11 @@ export class Context implements IContext {
1200
1237
  if (opts?.abortSignal?.aborted) {
1201
1238
  return false;
1202
1239
  }
1240
+
1241
+
1242
+ const mainIdentifier = this.rootSourceId;
1243
+ if (mainIdentifier) this.sceneLighting.enable(mainIdentifier);
1244
+
1203
1245
  invokeLifecycleFunctions(this, ContextEvent.ContextCreated);
1204
1246
  if (debug) console.log("Context Created...", this.renderer, this.renderer.domElement)
1205
1247
 
@@ -1209,13 +1251,18 @@ export class Context implements IContext {
1209
1251
  return true;
1210
1252
  }
1211
1253
 
1254
+
1255
+ private readonly rootSceneSourceIdentifiers: SourceIdentifier[] = [];
1212
1256
  private async internalLoadInitialContent(createId: number, args: ContextCreateArgs): Promise<Array<LoadedModel>> {
1257
+ this.rootSceneSourceIdentifiers.length = 0;
1258
+
1213
1259
  const results = new Array<LoadedModel>();
1214
1260
  // early out if we dont have any files to load
1215
1261
  if (args.files.length === 0) return results;
1216
1262
 
1217
-
1218
1263
  const files = [...args.files];
1264
+ this.rootSceneSourceIdentifiers.push(...files);
1265
+
1219
1266
  const progressArg: LoadingProgressArgs = {
1220
1267
  name: "",
1221
1268
  progress: null!,
@@ -1223,6 +1270,7 @@ export class Context implements IContext {
1223
1270
  count: files.length
1224
1271
  }
1225
1272
 
1273
+
1226
1274
  const loader = getLoader();
1227
1275
  // this hash should be constant since it is used to initialize the UIDProvider per initially loaded scene
1228
1276
  const loadingHash = 0;
@@ -1661,7 +1709,8 @@ export class Context implements IContext {
1661
1709
 
1662
1710
  if (this._stats) {
1663
1711
  this._stats.end();
1664
- if (this.time.frameCount % 150 === 0)
1712
+ const dt = this.time.fps < 20 ? 50 : 150;
1713
+ if (this.time.frameCount % dt === 0)
1665
1714
  console.log(this.renderer.info.render.calls + " DrawCalls", "\nRender:", { ...this.renderer.info.render }, "\nMemory:", { ...this.renderer.info.memory }, "\nTarget Framerate: " + this.targetFrameRate);
1666
1715
  }
1667
1716
 
@@ -13,7 +13,7 @@ import { registerPrewarmObject } from "./engine_mainloop_utils.js";
13
13
  import { SerializationContext } from "./engine_serialization_core.js";
14
14
  import { Context } from "./engine_setup.js"
15
15
  import { postprocessFBXMaterials } from "./engine_three_utils.js";
16
- import { CustomModel, isGLTFModel, Model, type UIDProvider } from "./engine_types.js";
16
+ import { CustomModel, isGLTFModel, Model, SourceIdentifier, type UIDProvider } from "./engine_types.js";
17
17
  import * as utils from "./engine_utils.js";
18
18
  import { tryDetermineMimetypeFromURL } from "./engine_utils_format.js"
19
19
  import { invokeLoadedImportPluginHooks, registerComponentExtension, registerExtensions } from "./extensions/extensions.js";
@@ -43,7 +43,7 @@ const debugFileTypes = utils.getParam("debugfileformat");
43
43
 
44
44
 
45
45
 
46
- export async function onCreateLoader(url: string, context: Context): Promise<CustomLoader | GLTFLoader | FBXLoader | USDZLoader | OBJLoader | null> {
46
+ async function onCreateLoader(url: string, context: Context, sourceId: SourceIdentifier): Promise<CustomLoader | GLTFLoader | FBXLoader | USDZLoader | OBJLoader | null> {
47
47
 
48
48
  const type = await tryDetermineMimetypeFromURL(url, { useExtension: true }) || "unknown";
49
49
  if (debugFileTypes) console.debug(`Determined file type: '${type}' for url '${url}'`, { registeredModelLoaderCallbacks });
@@ -67,7 +67,7 @@ export async function onCreateLoader(url: string, context: Context): Promise<Cus
67
67
  {
68
68
  console.warn(`Unknown file type (${type}). Needle Engine will fallback to the GLTFLoader - To support more model formats please create a Needle loader plugin.\nUse import { NeedleEngineModelLoader } from \"@needle-tools/engine\" namespace to register your loader.`, url);
69
69
  const loader = new GLTFLoader();
70
- await registerExtensions(loader, context, url);
70
+ await registerExtensions(loader, context, url, sourceId);
71
71
  return loader;
72
72
  }
73
73
  case "model/fbx":
@@ -88,7 +88,7 @@ export async function onCreateLoader(url: string, context: Context): Promise<Cus
88
88
  case "model/vrm":
89
89
  {
90
90
  const loader = new GLTFLoader();
91
- await registerExtensions(loader, context, url);
91
+ await registerExtensions(loader, context, url, sourceId);
92
92
  return loader;
93
93
  }
94
94
  }
@@ -117,7 +117,7 @@ export async function parseSync(context: Context, data: string | ArrayBuffer, pa
117
117
  path = "";
118
118
  }
119
119
  if (printGltf) console.log("Parse glTF", path)
120
- const loader = await onCreateLoader(path, context);
120
+ const loader = await onCreateLoader(path, context, path);
121
121
  if (!loader) {
122
122
  return undefined;
123
123
  }
@@ -193,7 +193,7 @@ export async function loadSync(context: Context, url: string, sourceId: string,
193
193
  // but due to the async nature and potentially triggering multiple loads at the same time
194
194
  // we need to make sure the extensions dont override each other
195
195
  // creating new loaders should not be expensive as well
196
- const loader = await onCreateLoader(url, context);
196
+ const loader = await onCreateLoader(url, context, sourceId);
197
197
  if (!loader) {
198
198
  return undefined;
199
199
  }
@@ -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;
@@ -1,4 +1,4 @@
1
- import { EquirectangularReflectionMapping, LightProbe, SphericalHarmonics3, SRGBColorSpace,Texture, Vector4, WebGLCubeRenderTarget } from "three";
1
+ import { EquirectangularReflectionMapping, LightProbe, Source, SphericalHarmonics3, SRGBColorSpace, Texture, Vector4, WebGLCubeRenderTarget } from "three";
2
2
 
3
3
  import { AssetReference } from "./engine_addressables.js";
4
4
  import { Context } from "./engine_setup.js";
@@ -44,9 +44,16 @@ export class RendererData {
44
44
  this.context.pre_update_callbacks.push(this.preUpdate.bind(this))
45
45
  }
46
46
 
47
+ /**
48
+ * The id of the currently active scene light settings (source identifier).
49
+ */
47
50
  private _currentLightSettingsId?: SourceIdentifier;
48
51
  private _sceneLightSettings?: Map<SourceIdentifier, SceneLightSettings>;
49
52
 
53
+ get currentLightSettingsId(): SourceIdentifier | undefined {
54
+ return this._currentLightSettingsId;
55
+ }
56
+
50
57
  private preUpdate() {
51
58
  const time = this.context.time;
52
59
  this._timevec4.x = time.time;
@@ -82,11 +89,11 @@ export class RendererData {
82
89
 
83
90
  /** set the scene lighting from a specific scene. Will disable any previously enabled lighting settings */
84
91
  enable(sourceId: SourceIdentifier | AssetReference) {
85
- if(sourceId instanceof AssetReference)
92
+ if (sourceId instanceof AssetReference)
86
93
  sourceId = sourceId.url;
87
94
  const settings = this._sceneLightSettings?.get(sourceId);
88
95
  if (!settings) {
89
- if(debug) console.warn("No light settings found for", sourceId);
96
+ if (debug) console.warn("No light settings found for", sourceId);
90
97
  return false;
91
98
  }
92
99
  if (debug) console.log("Enable scene light settings", sourceId, settings);
@@ -100,7 +107,7 @@ export class RendererData {
100
107
 
101
108
  /** disable the lighting of a specific scene, will only have any effect if it is currently active */
102
109
  disable(sourceId: SourceIdentifier | AssetReference) {
103
- if(sourceId instanceof AssetReference)
110
+ if (sourceId instanceof AssetReference)
104
111
  sourceId = sourceId.url;
105
112
  if (sourceId === null || sourceId === undefined) return false;
106
113
  const settings = this._sceneLightSettings?.get(sourceId);
@@ -112,8 +119,22 @@ export class RendererData {
112
119
  return true;
113
120
  }
114
121
 
115
- /** Disables the currently active scene lighting (if any), returns the id of the previously active lighting */
116
- disableCurrent() : SourceIdentifier | null {
122
+ /**
123
+ * Enables the currently active scene lighting (if any), returns the id of the enabled lighting.
124
+ * @returns The id of the enabled lighting, or null if no lighting is currently active.
125
+ */
126
+ enableCurrent(): SourceIdentifier | null {
127
+ if (this._currentLightSettingsId) {
128
+ this.enable(this._currentLightSettingsId);
129
+ return this._currentLightSettingsId ?? null;
130
+ }
131
+ return null;
132
+ }
133
+
134
+ /** Disables the currently active scene lighting (if any), returns the id of the previously active lighting
135
+ * @returns The id of the previously active lighting, or null if no lighting was active.
136
+ */
137
+ disableCurrent(): SourceIdentifier | null {
117
138
  if (this._currentLightSettingsId) {
118
139
  const prev = this._currentLightSettingsId;
119
140
  this.disable(this._currentLightSettingsId);
@@ -162,7 +183,7 @@ export class RendererData {
162
183
  private __currentReflectionId: SourceIdentifier | null = null;
163
184
 
164
185
  /** @internal */
165
- internalEnableReflection(sourceId: SourceIdentifier) : Texture | null {
186
+ internalEnableReflection(sourceId: SourceIdentifier): Texture | null {
166
187
  this.__currentReflectionId = sourceId;
167
188
  const settings = this._sceneLightSettings?.get(sourceId);
168
189
 
@@ -181,6 +202,7 @@ export class RendererData {
181
202
  const tex = existing.Source;
182
203
  tex.mapping = EquirectangularReflectionMapping;
183
204
  scene.environment = tex;
205
+ scene.environmentIntensity = this.environmentIntensity || 1;
184
206
  return tex;
185
207
  }
186
208
  else if (debug) console.warn("Could not find reflection for source", sourceId);
@@ -216,7 +238,7 @@ export class RendererData {
216
238
  /** @internal */
217
239
  internalDisableReflection(sourceId?: SourceIdentifier) {
218
240
  if (sourceId && sourceId !== this.__currentReflectionId) {
219
- if(debug) console.log("Not disabling reflection for", sourceId, "because it is not the current light settings id", this.__currentReflectionId)
241
+ if (debug) console.log("Not disabling reflection for", sourceId, "because it is not the current light settings id", this.__currentReflectionId)
220
242
  return;
221
243
  }
222
244
  if (debug) console.log("Disable reflection", sourceId)
@@ -1,6 +1,6 @@
1
1
  import type { EffectComposer } from "postprocessing";
2
2
  import { Camera, Mesh, Object3D, Texture, WebGLRenderer, WebGLRenderTarget } from "three";
3
- import type { EffectComposer as ThreeEffectComposer } from "three/examples/jsm/postprocessing/EffectComposer";
3
+ import type { EffectComposer as ThreeEffectComposer } from "three/examples/jsm/postprocessing/EffectComposer.js";
4
4
 
5
5
  import { findResourceUsers } from "./engine_assetdatabase.js";
6
6
 
@@ -1,7 +1,7 @@
1
1
  import { ACESFilmicToneMapping, AgXToneMapping, NeutralToneMapping, NoToneMapping, ShaderChunk, ToneMapping } from "three";
2
2
 
3
3
  import { isDevEnvironment } from "./debug/index.js";
4
- import type { Context } from "./engine_setup";
4
+ import type { Context } from "./engine_setup.js";
5
5
 
6
6
  let patchedTonemapping = false;
7
7
  export function patchTonemapping(_ctx?: Context) {
@@ -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
@@ -7,6 +7,7 @@ declare type Type = new (...args: any[]) => any;
7
7
  class _TypeStore {
8
8
 
9
9
  private _types: Map<string, Type> = new Map();
10
+ private _reverseTypes: Map<Type, string> = new Map();
10
11
 
11
12
  constructor() {
12
13
  if (debug) console.warn("TypeStore: Created", this);
@@ -18,8 +19,10 @@ class _TypeStore {
18
19
  public add(key: string, type: Type) {
19
20
  if (debug) console.warn("ADD TYPE", key);
20
21
  const existing = this._types.get(key);
21
- if (!existing)
22
+ if (!existing) {
22
23
  this._types.set(key, type);
24
+ this._reverseTypes.set(type, key);
25
+ }
23
26
  else {
24
27
  if (debug) {
25
28
  if (existing !== type) {
@@ -40,11 +43,7 @@ class _TypeStore {
40
43
  * @returns the key/name for the given type if registered
41
44
  */
42
45
  public getKey(type: Type): string | null {
43
- for (const [key, value] of this._types) {
44
- if (value === type)
45
- return key;
46
- }
47
- return null;
46
+ return this._reverseTypes.get(type) || null;
48
47
  }
49
48
  }
50
49
 
@@ -166,7 +166,7 @@ async function internalRenderQRCodeOverlays(canvas: HTMLCanvasElement, args: { s
166
166
  haveLogo = await new Promise((resolve, _reject) => {
167
167
  image.onload = () => resolve(true);
168
168
  image.onerror = (err) => {
169
- let errorUrl = logoSrc !== needleLogoOnlySVG ? "'" + logoSrc + "'" : null;
169
+ const errorUrl = logoSrc !== needleLogoOnlySVG ? "'" + logoSrc + "'" : null;
170
170
  console.error("[QR Code] Error loading logo image for QR code", errorUrl, isDevEnvironment() ? err : "");
171
171
  resolve(false);
172
172
  };
@@ -2,12 +2,13 @@ import { Object3D } from "three";
2
2
  import { GLTFExporter } from 'three/examples/jsm/exporters/GLTFExporter.js';
3
3
  import { type GLTF, type GLTFLoaderPlugin, GLTFParser } from "three/examples/jsm/loaders/GLTFLoader.js";
4
4
 
5
+ import { isDevEnvironment } from "../debug/debug.js";
5
6
  import { builtinComponentKeyName } from "../engine_constants.js";
6
7
  import { debugExtension } from "../engine_default_parameters.js";
7
8
  import { getLoader } from "../engine_gltf.js";
8
9
  import { type NodeToObjectMap, type ObjectToNodeMap, SerializationContext } from "../engine_serialization_core.js";
9
10
  import { apply } from "../js-extensions/index.js";
10
- import { maskGltfAssociation,resolveReferences } from "./extension_utils.js";
11
+ import { maskGltfAssociation, resolveReferences } from "./extension_utils.js";
11
12
 
12
13
  export const debug = debugExtension
13
14
  const componentsArrayExportKey = "$___Export_Components";
@@ -15,7 +16,7 @@ const componentsArrayExportKey = "$___Export_Components";
15
16
  export const EXTENSION_NAME = "NEEDLE_components";
16
17
 
17
18
  class ExtensionData {
18
- [builtinComponentKeyName]?: Array<object | null>
19
+ [builtinComponentKeyName]?: Array<Record<string, any> | null>
19
20
  }
20
21
 
21
22
  class ExportData {
@@ -35,14 +36,7 @@ export class NEEDLE_components implements GLTFLoaderPlugin {
35
36
  get name(): string {
36
37
  return EXTENSION_NAME;
37
38
  }
38
-
39
- // import
40
- parser?: GLTFParser;
41
- nodeToObjectMap: NodeToObjectMap = {};
42
- /** The loaded gltf */
43
- gltf: GLTF | null = null;
44
-
45
- // export
39
+ // #region export
46
40
  exportContext!: { [nodeIndex: number]: ExportData };
47
41
  objectToNodeMap: ObjectToNodeMap = {};
48
42
  context!: SerializationContext;
@@ -112,7 +106,7 @@ export class NEEDLE_components implements GLTFLoaderPlugin {
112
106
 
113
107
  writeNode(node: Object3D, nodeDef) {
114
108
  const nodeIndex = this.writer.json.nodes.length;
115
- if (debug)
109
+ if (debug)
116
110
  console.log(node.name, nodeIndex, node.uuid);
117
111
  const context = new ExportData(node, nodeIndex, nodeDef);
118
112
  this.exportContext[nodeIndex] = context;
@@ -158,8 +152,13 @@ export class NEEDLE_components implements GLTFLoaderPlugin {
158
152
 
159
153
 
160
154
  // -------------------------------------
161
- // LOADING
162
- // called by GLTFLoader
155
+ // #region import
156
+ parser?: GLTFParser;
157
+ nodeToObjectMap: NodeToObjectMap = {};
158
+ /** The loaded gltf */
159
+ gltf: GLTF | null = null;
160
+
161
+
163
162
  beforeRoot() {
164
163
  if (debug)
165
164
  console.log("BEGIN LOAD");
@@ -167,7 +166,6 @@ export class NEEDLE_components implements GLTFLoaderPlugin {
167
166
  return null;
168
167
  }
169
168
 
170
- // called by GLTFLoader
171
169
  async afterRoot(result: GLTF): Promise<void> {
172
170
  this.gltf = result;
173
171
 
@@ -175,8 +173,7 @@ export class NEEDLE_components implements GLTFLoaderPlugin {
175
173
  const ext = parser?.extensions;
176
174
  if (!ext) return;
177
175
  const hasExtension = ext[this.name];
178
- if (debug)
179
- console.log("After root", result, this.parser, ext);
176
+ if (debug) console.log("After root", result, this.parser, ext);
180
177
 
181
178
  const loadComponents: Array<Promise<void>> = [];
182
179
  if (hasExtension === true) {
@@ -204,7 +201,7 @@ export class NEEDLE_components implements GLTFLoaderPlugin {
204
201
 
205
202
  apply(obj);
206
203
 
207
- loadComponents.push(this.createComponents(obj, data));
204
+ loadComponents.push(this.createComponents(result, node, obj, data));
208
205
  }
209
206
  }
210
207
  }
@@ -220,7 +217,7 @@ export class NEEDLE_components implements GLTFLoaderPlugin {
220
217
  }
221
218
  }
222
219
 
223
- private async createComponents(obj: Object3D, data: ExtensionData) {
220
+ private async createComponents(result: GLTF, node: Node, obj: Object3D, data: ExtensionData) {
224
221
  if (!data) return;
225
222
  const componentData = data[builtinComponentKeyName];
226
223
  if (componentData) {
@@ -228,20 +225,42 @@ export class NEEDLE_components implements GLTFLoaderPlugin {
228
225
  if (debug)
229
226
  console.log(obj.name, componentData);
230
227
  for (const i in componentData) {
231
- const serializedData = componentData[i];
232
- if (debug)
233
- console.log("Serialized data", JSON.parse(JSON.stringify(serializedData)));
228
+ const data = componentData[i];
229
+
230
+ if (debug) console.log("Serialized data", JSON.parse(JSON.stringify(data)));
231
+
232
+ // Fix for https://linear.app/needle/issue/NE-6779/blender-export-has-missing-sharedmaterials
233
+ if (data?.name === "MeshRenderer" || data?.name === "SkinnedMeshRenderer") {
234
+ if (!data.sharedMaterials) {
235
+ let success = false;
236
+ if ("mesh" in node) {
237
+ const meshIndex = node.mesh;
238
+ if (typeof meshIndex === "number" && result.parser) {
239
+ const meshDef = result.parser.json.meshes?.[meshIndex];
240
+ if (meshDef?.primitives) {
241
+ data.sharedMaterials = meshDef.primitives.map(prim => {
242
+ return "/materials/" + (prim.material ?? 0);
243
+ });
244
+ success = true;
245
+ }
246
+ }
247
+ }
248
+ if(!success && (debug || isDevEnvironment())) {
249
+ console.warn(`[NEEDLE_components] Component '${data.name}' on object '${obj.name}' is not added to a mesh or failed to retrieve materials from glTF.`);
250
+ }
251
+ }
252
+ }
234
253
 
235
- if (serializedData && this.parser) {
254
+ if (data && this.parser) {
236
255
  tasks.push(
237
- resolveReferences(this.parser, serializedData)
238
- .catch(e => console.error(`Error while resolving references (see console for details)\n`, e, obj, serializedData))
256
+ resolveReferences(this.parser, data)
257
+ .catch(e => console.error(`Error while resolving references (see console for details)\n`, e, obj, data))
239
258
  );
240
259
  }
241
260
 
242
261
  obj.userData = obj.userData || {};
243
262
  obj.userData[builtinComponentKeyName] = obj.userData[builtinComponentKeyName] || [];
244
- obj.userData[builtinComponentKeyName].push(serializedData);
263
+ obj.userData[builtinComponentKeyName].push(data);
245
264
  }
246
265
  await Promise.all(tasks).catch((e) => {
247
266
  console.error("Error while loading components", e);
@@ -266,4 +285,6 @@ export class NEEDLE_components implements GLTFLoaderPlugin {
266
285
  // // console.log(components);
267
286
  // return null;
268
287
  // }
269
- }
288
+ }
289
+
290
+
@@ -95,8 +95,8 @@ export class NEEDLE_lightmaps implements GLTFLoaderPlugin {
95
95
  }
96
96
  const results = await PromiseAllWithErrors(dependencies);
97
97
  if (results?.anyFailed) {
98
- if (isDevEnvironment())
99
- console.error("Failed to load lightmap extension", results);
98
+ if (isDevEnvironment() || debug)
99
+ console.error("[NEEDLE_lightmaps]Error during extension loading:", results);
100
100
  }
101
101
  resolve();
102
102
  });