@needle-tools/engine 4.11.5-next.753a642 → 4.11.5-next.7ddcc57

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 (111) hide show
  1. package/CHANGELOG.md +7 -5
  2. package/components.needle.json +1 -1
  3. package/dist/generateMeshBVH.worker-D1Vr8UHG.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-DVXwSz-d.js → needle-engine.bundle-DdOG3e58.js} +4825 -4716
  9. package/dist/{needle-engine.bundle-OrbPGV3t.umd.cjs → needle-engine.bundle-diJ_X0v5.umd.cjs} +141 -146
  10. package/dist/{needle-engine.bundle-CAKvhOvZ.min.js → needle-engine.bundle-uqHJeR29.min.js} +147 -152
  11. package/dist/needle-engine.d.ts +15 -15
  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-6kAXU6fm.umd.cjs → vendor-DhTcel4c.umd.cjs} +2 -7
  31. package/dist/{vendor-petGQl0N.js → vendor-Dkpn1a8s.js} +1 -7
  32. package/dist/{vendor-CsyK1CFs.min.js → vendor-DtTGRuXh.min.js} +2 -7
  33. package/lib/engine/debug/debug_spector.d.ts +16 -0
  34. package/lib/engine/debug/debug_spector.js +28 -0
  35. package/lib/engine/debug/debug_spector.js.map +1 -0
  36. package/lib/engine/engine_application.d.ts +7 -0
  37. package/lib/engine/engine_application.js +8 -1
  38. package/lib/engine/engine_application.js.map +1 -1
  39. package/lib/engine/engine_context.d.ts +15 -5
  40. package/lib/engine/engine_context.js +29 -5
  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.d.ts +0 -6
  45. package/lib/engine/engine_loaders.js +5 -5
  46. package/lib/engine/engine_loaders.js.map +1 -1
  47. package/lib/engine/engine_physics.js +6 -2
  48. package/lib/engine/engine_physics.js.map +1 -1
  49. package/lib/engine/engine_physics_rapier.d.ts +11 -2
  50. package/lib/engine/engine_physics_rapier.js +9 -0
  51. package/lib/engine/engine_physics_rapier.js.map +1 -1
  52. package/lib/engine/engine_scenelighting.d.ts +12 -1
  53. package/lib/engine/engine_scenelighting.js +21 -1
  54. package/lib/engine/engine_scenelighting.js.map +1 -1
  55. package/lib/engine/engine_types.d.ts +16 -0
  56. package/lib/engine/extensions/NEEDLE_lightmaps.js +2 -2
  57. package/lib/engine/extensions/NEEDLE_lightmaps.js.map +1 -1
  58. package/lib/engine/extensions/extensions.d.ts +2 -2
  59. package/lib/engine/extensions/extensions.js +11 -5
  60. package/lib/engine/extensions/extensions.js.map +1 -1
  61. package/lib/engine/webcomponents/needle-engine.d.ts +1 -0
  62. package/lib/engine/webcomponents/needle-engine.js +3 -0
  63. package/lib/engine/webcomponents/needle-engine.js.map +1 -1
  64. package/lib/engine/xr/NeedleXRSession.js +2 -1
  65. package/lib/engine/xr/NeedleXRSession.js.map +1 -1
  66. package/lib/engine-components/ReflectionProbe.js +16 -10
  67. package/lib/engine-components/ReflectionProbe.js.map +1 -1
  68. package/lib/engine-components/Renderer.js +2 -2
  69. package/lib/engine-components/Renderer.js.map +1 -1
  70. package/lib/engine-components/RendererInstancing.d.ts +5 -3
  71. package/lib/engine-components/RendererInstancing.js +64 -31
  72. package/lib/engine-components/RendererInstancing.js.map +1 -1
  73. package/lib/engine-components/Skybox.d.ts +15 -5
  74. package/lib/engine-components/Skybox.js +37 -25
  75. package/lib/engine-components/Skybox.js.map +1 -1
  76. package/lib/engine-components/export/usdz/USDZExporter.d.ts +24 -3
  77. package/lib/engine-components/export/usdz/USDZExporter.js +36 -2
  78. package/lib/engine-components/export/usdz/USDZExporter.js.map +1 -1
  79. package/lib/engine-components/webxr/WebXR.js +4 -0
  80. package/lib/engine-components/webxr/WebXR.js.map +1 -1
  81. package/lib/engine-components/webxr/controllers/XRControllerModel.js +1 -1
  82. package/lib/engine-components/webxr/controllers/XRControllerModel.js.map +1 -1
  83. package/lib/needle-engine.js +2 -1
  84. package/lib/needle-engine.js.map +1 -1
  85. package/package.json +1 -1
  86. package/src/engine/debug/debug_spector.ts +43 -0
  87. package/src/engine/engine_application.ts +16 -1
  88. package/src/engine/engine_context.ts +41 -7
  89. package/src/engine/engine_gltf_builtin_components.ts +1 -1
  90. package/src/engine/engine_loaders.ts +6 -6
  91. package/src/engine/engine_physics.ts +6 -2
  92. package/src/engine/engine_physics_rapier.ts +11 -2
  93. package/src/engine/engine_scenelighting.ts +30 -8
  94. package/src/engine/engine_types.ts +17 -0
  95. package/src/engine/extensions/NEEDLE_lightmaps.ts +2 -2
  96. package/src/engine/extensions/extensions.ts +11 -5
  97. package/src/engine/webcomponents/needle-engine.ts +4 -0
  98. package/src/engine/xr/NeedleXRSession.ts +3 -1
  99. package/src/engine-components/ReflectionProbe.ts +18 -10
  100. package/src/engine-components/Renderer.ts +2 -2
  101. package/src/engine-components/RendererInstancing.ts +69 -33
  102. package/src/engine-components/Skybox.ts +47 -36
  103. package/src/engine-components/export/usdz/USDZExporter.ts +52 -5
  104. package/src/engine-components/webxr/WebXR.ts +4 -0
  105. package/src/engine-components/webxr/controllers/XRControllerModel.ts +1 -1
  106. package/src/needle-engine.ts +4 -2
  107. package/dist/generateMeshBVH.worker-B9bjdr6J.js +0 -25
  108. package/dist/loader.worker-CiTwpNPW.js +0 -27
  109. package/dist/rapier-BqdcSmKY.umd.cjs +0 -1
  110. package/dist/rapier-Cg3w3nFI.min.js +0 -1
  111. package/dist/rapier-sU12SWAs.js +0 -5217
@@ -0,0 +1,43 @@
1
+ import type { Context } from "../engine_setup.js";
2
+ import { isDevEnvironment } from "./debug.js";
3
+
4
+ declare global {
5
+ interface Window {
6
+ SPECTOR?: {
7
+ Spector: new () => Spector;
8
+ }
9
+ }
10
+ interface Spector {
11
+ displayUI: () => void;
12
+ setCanvas: (canvas: HTMLCanvasElement) => void;
13
+ spyCanvases: boolean;
14
+ startCaptureOnNextFrame: () => void;
15
+ captureCanvas: (canvas: HTMLCanvasElement) => any;
16
+ }
17
+ }
18
+
19
+
20
+ export function initSpectorIfAvailable(_context: Context, canvas: HTMLCanvasElement): void {
21
+ if (typeof window !== undefined && window.SPECTOR) {
22
+ console.log(window.SPECTOR);
23
+ const params = new URLSearchParams(window.location.search);
24
+ if (params.has("spector")) {
25
+ const frame = Number.parseInt(params.get("spector") || "0") || 0;
26
+ console.log("Scheduled Spector capture at frame #" + frame);
27
+ const spector = new window.SPECTOR.Spector();
28
+ spector.spyCanvases = true;
29
+ waitForFrameAndCapture();
30
+ return;
31
+
32
+ function waitForFrameAndCapture() {
33
+ if (frame > _context.time.frame) return window.requestAnimationFrame(() => waitForFrameAndCapture());
34
+ const res = spector.captureCanvas(canvas);
35
+ if (res && res instanceof Promise) res.then(() => spector.displayUI());
36
+ else spector.displayUI();
37
+ }
38
+ }
39
+ else if (isDevEnvironment()) {
40
+ console.debug("Spector available: Add '?spector=<frame>' to the URL to enable it and capture a frame.");
41
+ }
42
+ }
43
+ }
@@ -15,7 +15,7 @@ const userInteractionCallbacks: Function[] = [];
15
15
  */
16
16
  export function internalOnUserInputRegistered() {
17
17
  if (userInteractionRegistered) return;
18
- if (isDevEnvironment()) console.debug("User interaction registered: audio can now be played");
18
+ if (isDevEnvironment()) console.debug("[Needle Engine] User input registered: Media playback is now allowed.");
19
19
  userInteractionRegistered = true;
20
20
  const copy = [...userInteractionCallbacks];
21
21
  userInteractionCallbacks.length = 0;
@@ -28,6 +28,21 @@ document.addEventListener('dragstart', internalOnUserInputRegistered);
28
28
  document.addEventListener('touchend', internalOnUserInputRegistered);
29
29
  document.addEventListener('keydown', internalOnUserInputRegistered);
30
30
 
31
+
32
+ declare global {
33
+ interface Navigator {
34
+ userActivation?: {
35
+ isActive: boolean;
36
+ };
37
+ }
38
+ }
39
+ // User Activation should be available across browsers since November 2023 https://developer.mozilla.org/en-US/docs/Web/API/UserActivation
40
+ if (typeof window !== "undefined" && "userActivation" in navigator && navigator.userActivation?.isActive) {
41
+ if (isDevEnvironment()) console.debug("[Needle Engine] User input already active: Media playback is now allowed.");
42
+ userInteractionCallbacks.length = 0;
43
+ userInteractionRegistered = true;
44
+ }
45
+
31
46
  /**
32
47
  * The Application class can be used to mute audio globally, and to check if the application (canvas) is currently visible (it's tab is active and not minimized).
33
48
  */
@@ -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';
@@ -372,6 +373,7 @@ export class Context implements IContext {
372
373
  */
373
374
  composer: EffectComposer | ThreeEffectComposer | null = null;
374
375
 
376
+ // #region internal script lists
375
377
  /**
376
378
  * @internal All known components. Don't use directly
377
379
  */
@@ -439,6 +441,9 @@ export class Context implements IContext {
439
441
  /** @internal */
440
442
  readonly new_scripts_xr: INeedleXRSessionEventReceiver[] = [];
441
443
 
444
+ // #endregion
445
+ // #region Properties
446
+
442
447
  /**
443
448
  * The **main camera component** of the scene - this camera is used for rendering.
444
449
  * Use `setCurrentCamera` for updating the main camera.
@@ -483,25 +488,36 @@ export class Context implements IContext {
483
488
  physics: Physics;
484
489
  /** access networking methods (use it to send or listen to messages or join a networking backend) */
485
490
  connection: NetworkConnection;
486
- /**
487
- * @deprecated AssetDataBase is deprecated
488
- */
491
+ /** @deprecated AssetDatabase is deprecated */
489
492
  assets: AssetDatabase;
490
493
  /** The main light in the scene */
491
494
  mainLight: ILight | null = null;
492
495
  /** @deprecated Use sceneLighting */
493
496
  get rendererData() { return this.sceneLighting }
497
+ /** Access the scene lighting manager to control lighting settings in the context */
494
498
  sceneLighting: SceneLighting;
495
499
  addressables: Addressables;
496
500
  lightmaps: ILightDataRegistry;
497
501
  players: PlayerViewManager;
502
+
503
+ /** Access the LODs manager to control LOD behavior in the context */
498
504
  readonly lodsManager: LODsManager;
499
505
  /** Access the needle menu to add or remove buttons to the menu element */
500
506
  readonly menu: NeedleMenu;
501
507
 
502
- /** @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
+ */
503
512
  get isCreated() { return this._isCreated; }
504
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
+
505
521
  private _needsUpdateSize: boolean = false;
506
522
  private _isCreated: boolean = false;
507
523
  private _isCreating: boolean = false;
@@ -556,6 +572,7 @@ export class Context implements IContext {
556
572
  ContextRegistry.register(this);
557
573
  }
558
574
 
575
+ // #region Renderer
559
576
  /**
560
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.
561
578
  * **Note**: Instead you can also modify the static `Context.DefaultWebGlRendererParameters` before the context is created.
@@ -606,6 +623,8 @@ export class Context implements IContext {
606
623
 
607
624
  this.input.bindEvents();
608
625
 
626
+ initSpectorIfAvailable(this, this.renderer.domElement);
627
+
609
628
  return this.renderer;
610
629
  }
611
630
 
@@ -909,6 +928,7 @@ export class Context implements IContext {
909
928
  }
910
929
  }
911
930
 
931
+ // #region onBeforeRender / onAfterRender listeners
912
932
  private readonly _onBeforeRenderListeners = new Map<string, OnRenderCallback[]>();
913
933
  private readonly _onAfterRenderListeners = new Map<string, OnRenderCallback[]>();
914
934
 
@@ -1006,6 +1026,8 @@ export class Context implements IContext {
1006
1026
  private _lastStyleComputedResult: boolean | undefined = undefined;
1007
1027
 
1008
1028
  private _createId: number = 0;
1029
+
1030
+ // #region internal create
1009
1031
  private async internalOnCreate(opts?: ContextCreateArgs): Promise<boolean> {
1010
1032
  const createId = ++this._createId;
1011
1033
 
@@ -1215,6 +1237,11 @@ export class Context implements IContext {
1215
1237
  if (opts?.abortSignal?.aborted) {
1216
1238
  return false;
1217
1239
  }
1240
+
1241
+
1242
+ const mainIdentifier = this.rootSourceId;
1243
+ if (mainIdentifier) this.sceneLighting.enable(mainIdentifier);
1244
+
1218
1245
  invokeLifecycleFunctions(this, ContextEvent.ContextCreated);
1219
1246
  if (debug) console.log("Context Created...", this.renderer, this.renderer.domElement)
1220
1247
 
@@ -1224,13 +1251,18 @@ export class Context implements IContext {
1224
1251
  return true;
1225
1252
  }
1226
1253
 
1254
+
1255
+ private readonly rootSceneSourceIdentifiers: SourceIdentifier[] = [];
1227
1256
  private async internalLoadInitialContent(createId: number, args: ContextCreateArgs): Promise<Array<LoadedModel>> {
1257
+ this.rootSceneSourceIdentifiers.length = 0;
1258
+
1228
1259
  const results = new Array<LoadedModel>();
1229
1260
  // early out if we dont have any files to load
1230
1261
  if (args.files.length === 0) return results;
1231
1262
 
1232
-
1233
1263
  const files = [...args.files];
1264
+ this.rootSceneSourceIdentifiers.push(...files);
1265
+
1234
1266
  const progressArg: LoadingProgressArgs = {
1235
1267
  name: "",
1236
1268
  progress: null!,
@@ -1238,6 +1270,7 @@ export class Context implements IContext {
1238
1270
  count: files.length
1239
1271
  }
1240
1272
 
1273
+
1241
1274
  const loader = getLoader();
1242
1275
  // this hash should be constant since it is used to initialize the UIDProvider per initially loaded scene
1243
1276
  const loadingHash = 0;
@@ -1676,7 +1709,8 @@ export class Context implements IContext {
1676
1709
 
1677
1710
  if (this._stats) {
1678
1711
  this._stats.end();
1679
- if (this.time.frameCount % 150 === 0)
1712
+ const dt = this.time.fps < 20 ? 50 : 150;
1713
+ if (this.time.frameCount % dt === 0)
1680
1714
  console.log(this.renderer.info.render.calls + " DrawCalls", "\nRender:", { ...this.renderer.info.render }, "\nMemory:", { ...this.renderer.info.memory }, "\nTarget Framerate: " + this.targetFrameRate);
1681
1715
  }
1682
1716
 
@@ -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
@@ -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)
@@ -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
@@ -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
  });
@@ -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))
@@ -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 });
@@ -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
  }
@@ -702,7 +702,6 @@ export class Renderer extends Behaviour implements IRenderer {
702
702
  // If the material has a envMap and is NOT using a reflection probe we set the envMap to the scene environment
703
703
  if (mat && "envMap" in mat && "envMapIntensity" in mat && !ReflectionProbe.isUsingReflectionProbe(mat)) {
704
704
  mat.envMap = this.context.scene.environment;
705
- mat.envMapIntensity = this.context.scene.environmentIntensity;
706
705
  mat.envMapRotation = this.context.scene.environmentRotation;
707
706
  }
708
707
  }
@@ -713,8 +712,9 @@ export class Renderer extends Behaviour implements IRenderer {
713
712
  private onBeforeRenderThree = (_renderer, _scene, _camera, _geometry, material, _group) => {
714
713
  if (material.envMapIntensity !== undefined) {
715
714
  const factor = this.hasLightmap ? Math.PI : 1;
716
- const environmentIntensity = this.context.mainCameraComponent?.environmentIntensity ?? 1;
715
+ const environmentIntensity = this.context.scene.environmentIntensity;
717
716
  material.envMapIntensity = Math.max(0, environmentIntensity * this.context.sceneLighting.environmentIntensity / factor);
717
+ // console.log(this.context.sceneLighting.environmentIntensity);
718
718
  }
719
719
  if (this._lightmaps) {
720
720
  for (const lm of this._lightmaps) {