@needle-tools/engine 4.15.0-next.cecd8e7 → 4.15.0-next.f391a30

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 (144) hide show
  1. package/components.needle.json +1 -1
  2. package/dist/{gltf-progressive-BttGBXw6.umd.cjs → gltf-progressive-CMwJPwEt.umd.cjs} +1 -1
  3. package/dist/{gltf-progressive-Bm_6aEi4.js → gltf-progressive-CTlvpS3A.js} +1 -1
  4. package/dist/{gltf-progressive-T5WKTux5.min.js → gltf-progressive-DYL3SLVb.min.js} +1 -1
  5. package/dist/materialx-4jJLLe9Q.js +4174 -0
  6. package/dist/materialx-Bt9FHwco.min.js +158 -0
  7. package/dist/materialx-NDD0y4JY.umd.cjs +158 -0
  8. package/dist/{needle-engine.bundle-JQGIFVRm.umd.cjs → needle-engine.bundle-C1BFRZDF.umd.cjs} +103 -101
  9. package/dist/{needle-engine.bundle-VZVrVbc3.js → needle-engine.bundle-DB4kLWO_.js} +2829 -2823
  10. package/dist/{needle-engine.bundle-CuAiLb-d.min.js → needle-engine.bundle-DsTdfmeb.min.js} +115 -113
  11. package/dist/needle-engine.d.ts +27 -46
  12. package/dist/needle-engine.js +287 -288
  13. package/dist/needle-engine.min.js +1 -1
  14. package/dist/needle-engine.umd.cjs +1 -1
  15. package/dist/{postprocessing-06AXuvdv.min.js → postprocessing-BN-f4viE.min.js} +1 -1
  16. package/dist/{postprocessing-CPDcA21P.umd.cjs → postprocessing-DYmYOVm4.umd.cjs} +1 -1
  17. package/dist/{postprocessing-CI2x8Cln.js → postprocessing-De9ZpJrk.js} +1 -1
  18. package/dist/{three-examples-BMmNgNCN.umd.cjs → three-examples-BHqRVpO_.umd.cjs} +12 -12
  19. package/dist/{three-examples-CMYCd5nH.js → three-examples-C0ZCCA_K.js} +182 -192
  20. package/dist/{three-examples-CQl1fFZp.min.js → three-examples-DmTY8tGr.min.js} +14 -14
  21. package/lib/engine/api.d.ts +0 -2
  22. package/lib/engine/api.js +0 -2
  23. package/lib/engine/api.js.map +1 -1
  24. package/lib/engine/debug/debug.js +1 -1
  25. package/lib/engine/debug/debug.js.map +1 -1
  26. package/lib/engine/debug/debug_spatial_console.js +1 -1
  27. package/lib/engine/debug/debug_spatial_console.js.map +1 -1
  28. package/lib/engine/engine_context.js +1 -1
  29. package/lib/engine/engine_context.js.map +1 -1
  30. package/lib/engine/engine_create_objects.js +1 -1
  31. package/lib/engine/engine_create_objects.js.map +1 -1
  32. package/lib/engine/engine_gizmos.js +1 -1
  33. package/lib/engine/engine_gizmos.js.map +1 -1
  34. package/lib/engine/engine_license.js +7 -2
  35. package/lib/engine/engine_license.js.map +1 -1
  36. package/lib/engine/engine_utils.js +2 -2
  37. package/lib/engine/engine_utils.js.map +1 -1
  38. package/lib/engine/export/gltf/EXT_mesh_gpu_instancing_exporter.js.map +1 -0
  39. package/lib/engine/export/gltf/index.js +1 -1
  40. package/lib/engine/export/gltf/index.js.map +1 -1
  41. package/lib/engine/webcomponents/logo-element.d.ts +6 -3
  42. package/lib/engine/webcomponents/logo-element.js +18 -0
  43. package/lib/engine/webcomponents/logo-element.js.map +1 -1
  44. package/lib/engine/webcomponents/needle menu/needle-menu-spatial.js +2 -2
  45. package/lib/engine/webcomponents/needle menu/needle-menu-spatial.js.map +1 -1
  46. package/lib/engine/webcomponents/needle menu/needle-menu.d.ts +10 -7
  47. package/lib/engine/webcomponents/needle menu/needle-menu.js +14 -4
  48. package/lib/engine/webcomponents/needle menu/needle-menu.js.map +1 -1
  49. package/lib/engine/webcomponents/needle-engine.ar-overlay.js +10 -1
  50. package/lib/engine/webcomponents/needle-engine.ar-overlay.js.map +1 -1
  51. package/lib/engine/webcomponents/needle-engine.d.ts +3 -0
  52. package/lib/engine/webcomponents/needle-engine.js +10 -0
  53. package/lib/engine/webcomponents/needle-engine.js.map +1 -1
  54. package/lib/engine-components/Component.js +0 -1
  55. package/lib/engine-components/Component.js.map +1 -1
  56. package/lib/engine-components/ReflectionProbe.d.ts +24 -2
  57. package/lib/engine-components/ReflectionProbe.js +27 -1
  58. package/lib/engine-components/ReflectionProbe.js.map +1 -1
  59. package/lib/engine-components/Skybox.js +4 -2
  60. package/lib/engine-components/Skybox.js.map +1 -1
  61. package/lib/engine-components/export/gltf/GltfExport.js +1 -1
  62. package/lib/engine-components/export/gltf/GltfExport.js.map +1 -1
  63. package/lib/engine-components/export/usdz/ThreeUSDZExporter.js +2 -2
  64. package/lib/engine-components/export/usdz/USDZExporter.js +1 -1
  65. package/lib/engine-components/export/usdz/USDZExporter.js.map +1 -1
  66. package/lib/engine-components/export/usdz/extensions/behavior/PhysicsExtension.js +2 -2
  67. package/lib/engine-components/export/usdz/extensions/behavior/PhysicsExtension.js.map +1 -1
  68. package/package.json +17 -13
  69. package/plugins/common/buildinfo.js +46 -10
  70. package/plugins/common/files.js +2 -1
  71. package/plugins/common/license.js +144 -69
  72. package/plugins/common/logger.js +172 -11
  73. package/plugins/common/needle-engine-skill.md +175 -0
  74. package/plugins/common/worker.js +5 -4
  75. package/plugins/types/userconfig.d.ts +40 -2
  76. package/plugins/vite/ai.js +71 -0
  77. package/plugins/vite/alias.js +6 -5
  78. package/plugins/vite/asap.js +6 -5
  79. package/plugins/vite/build-pipeline.js +224 -41
  80. package/plugins/vite/buildinfo.js +66 -6
  81. package/plugins/vite/copyfiles.js +41 -12
  82. package/plugins/vite/custom-element-data.js +26 -16
  83. package/plugins/vite/defines.js +8 -5
  84. package/plugins/vite/dependencies.js +16 -10
  85. package/plugins/vite/dependency-watcher.js +35 -7
  86. package/plugins/vite/drop-client.js +7 -5
  87. package/plugins/vite/drop.js +16 -14
  88. package/plugins/vite/editor-connection.js +18 -16
  89. package/plugins/vite/imports-logger.js +12 -2
  90. package/plugins/vite/index.js +8 -3
  91. package/plugins/vite/local-files-analysis.js +789 -0
  92. package/plugins/vite/local-files-core.js +992 -0
  93. package/plugins/vite/local-files-internals.js +28 -0
  94. package/plugins/vite/local-files-types.d.ts +111 -0
  95. package/plugins/vite/local-files-utils.js +359 -0
  96. package/plugins/vite/local-files.js +2 -441
  97. package/plugins/vite/logger.client.js +45 -35
  98. package/plugins/vite/logger.js +6 -3
  99. package/plugins/vite/logging.js +129 -0
  100. package/plugins/vite/meta.js +18 -4
  101. package/plugins/vite/needle-app.js +4 -3
  102. package/plugins/vite/peer.js +2 -1
  103. package/plugins/vite/pwa.js +33 -17
  104. package/plugins/vite/reload.js +24 -2
  105. package/src/engine/api.ts +0 -3
  106. package/src/engine/debug/debug.ts +1 -1
  107. package/src/engine/debug/debug_spatial_console.ts +5 -1
  108. package/src/engine/engine_context.ts +1 -1
  109. package/src/engine/engine_create_objects.ts +1 -1
  110. package/src/engine/engine_gizmos.ts +9 -5
  111. package/src/engine/engine_license.ts +7 -2
  112. package/src/engine/engine_utils.ts +2 -2
  113. package/src/engine/export/gltf/index.ts +1 -1
  114. package/src/engine/webcomponents/logo-element.ts +20 -3
  115. package/src/engine/webcomponents/needle menu/needle-menu-spatial.ts +6 -2
  116. package/src/engine/webcomponents/needle menu/needle-menu.ts +23 -11
  117. package/src/engine/webcomponents/needle-engine.ar-overlay.ts +13 -2
  118. package/src/engine/webcomponents/needle-engine.ts +13 -1
  119. package/src/engine-components/Component.ts +1 -2
  120. package/src/engine-components/ReflectionProbe.ts +32 -8
  121. package/src/engine-components/Skybox.ts +4 -2
  122. package/src/engine-components/export/gltf/GltfExport.ts +1 -1
  123. package/src/engine-components/export/usdz/ThreeUSDZExporter.ts +2 -2
  124. package/src/engine-components/export/usdz/USDZExporter.ts +1 -1
  125. package/src/engine-components/export/usdz/extensions/behavior/PhysicsExtension.ts +2 -2
  126. package/dist/materialx-CJyQZtjt.min.js +0 -90
  127. package/dist/materialx-DMs1E08Z.js +0 -4636
  128. package/dist/materialx-DaKKOoVk.umd.cjs +0 -90
  129. package/lib/engine/engine_test_utils.d.ts +0 -39
  130. package/lib/engine/engine_test_utils.js +0 -84
  131. package/lib/engine/engine_test_utils.js.map +0 -1
  132. package/lib/include/three/EXT_mesh_gpu_instancing_exporter.js.map +0 -1
  133. package/src/engine/engine_test_utils.ts +0 -109
  134. package/src/include/draco/draco_decoder.js +0 -34
  135. package/src/include/draco/draco_decoder.wasm +0 -0
  136. package/src/include/draco/draco_wasm_wrapper.js +0 -117
  137. package/src/include/ktx2/basis_transcoder.js +0 -19
  138. package/src/include/ktx2/basis_transcoder.wasm +0 -0
  139. package/src/include/needle/arial-msdf.json +0 -1472
  140. package/src/include/needle/arial.png +0 -0
  141. package/src/include/needle/poweredbyneedle.webp +0 -0
  142. /package/lib/{include/three → engine/export/gltf}/EXT_mesh_gpu_instancing_exporter.d.ts +0 -0
  143. /package/lib/{include/three → engine/export/gltf}/EXT_mesh_gpu_instancing_exporter.js +0 -0
  144. /package/src/{include/three → engine/export/gltf}/EXT_mesh_gpu_instancing_exporter.js +0 -0
@@ -607,7 +607,7 @@ export namespace DeviceUtilities {
607
607
  /** @returns `true` if it's a phone or tablet */
608
608
  export function isMobileDevice() {
609
609
  if (_ismobile !== undefined) return _ismobile;
610
- // eslint-disable-next-line deprecation/deprecation
610
+ // eslint-disable-next-line @typescript-eslint/no-deprecated
611
611
  if ((typeof window.orientation !== "undefined") || (navigator.userAgent.indexOf('IEMobile') !== -1)) {
612
612
  return _ismobile = true;
613
613
  }
@@ -677,7 +677,7 @@ export namespace DeviceUtilities {
677
677
  /** @returns `true` for mobile Apple devices like iPad, iPhone, iPod, Vision Pro, ... */
678
678
  export function isiOS() {
679
679
  if (__isiOS !== undefined) return __isiOS;
680
- // eslint-disable-next-line deprecation/deprecation
680
+ // eslint-disable-next-line @typescript-eslint/no-deprecated
681
681
  return __isiOS = iosDevices.includes(navigator.platform)
682
682
  // iPad on iOS 13 detection
683
683
  || (navigator.userAgent.includes("Mac") && "ontouchend" in document)
@@ -1,12 +1,12 @@
1
1
  import { AnimationClip, Object3D } from "three";
2
2
  import { GLTFExporter, GLTFExporterOptions } from "three/examples/jsm/exporters/GLTFExporter.js";
3
3
 
4
- import GLTFMeshGPUInstancingExtension from "../../../include/three/EXT_mesh_gpu_instancing_exporter.js";
5
4
  import { AnimationUtils } from "../../engine_animation.js";
6
5
  import type { Context } from "../../engine_setup.js";
7
6
  import { registerExportExtensions } from "../../extensions/index.js";
8
7
  import { __isExporting } from "../state.js";
9
8
  import { shouldExport_HideFlags } from "../utils.js";
9
+ import GLTFMeshGPUInstancingExtension from "./EXT_mesh_gpu_instancing_exporter.js";
10
10
  import { GizmoWriter as GLTFGizmoWriter, RenderTextureWriter as GLTFRenderTextureWriter } from "./Writers.js";
11
11
 
12
12
  declare type ExportOptions = {
@@ -20,8 +20,13 @@ export class NeedleLogoElement extends HTMLElement {
20
20
  return document.createElement(elementName) as NeedleLogoElement;
21
21
  }
22
22
 
23
+ private _didInitialize = false;
24
+
23
25
  constructor() {
24
26
  super();
27
+ }
28
+
29
+ private initializeDom() {
25
30
  this._root = this.attachShadow({ mode: 'closed' });
26
31
  const template = document.createElement('template');
27
32
  template.innerHTML = `<style>
@@ -83,25 +88,37 @@ export class NeedleLogoElement extends HTMLElement {
83
88
  this.addEventListener("click", () => {
84
89
  globalThis.open("https://needle.tools", "_blank");
85
90
  });
91
+ }
86
92
 
93
+ ensureInitialized() {
94
+ if (!this._didInitialize) {
95
+ this._didInitialize = true;
96
+ this.initializeDom();
97
+ }
87
98
  }
88
99
 
89
100
  connectedCallback() {
101
+ this.ensureInitialized();
102
+ if (!this.wrapper) return;
90
103
  this.wrapper.setAttribute("title", "Made with Needle Engine");
91
104
  this.setAttribute("aria-label", "Needle Engine logo. Click to open the Needle Engine website.");
92
105
  }
93
106
 
94
- private readonly _root: ShadowRoot;
95
- private readonly wrapper: HTMLDivElement;
96
- private readonly logoElement: HTMLImageElement;
107
+ private _root!: ShadowRoot;
108
+ private wrapper!: HTMLDivElement;
109
+ private logoElement!: HTMLImageElement;
97
110
 
98
111
  /** Show or hide the logo element (used by the menu) */
99
112
  setLogoVisible(val: boolean) {
113
+ this.ensureInitialized();
114
+ if (!this.logoElement) return;
100
115
  this.logoElement.style.display = val ? "block" : "none";
101
116
  }
102
117
 
103
118
  /** Switch the logo between full and compact versions */
104
119
  setType(type: "full" | "compact") {
120
+ this.ensureInitialized();
121
+ if (!this.logoElement) return;
105
122
  if (type === "full") {
106
123
  this.logoElement.src = needleLogoSVG;
107
124
  this.logoElement.classList.remove("with-text");
@@ -302,7 +302,7 @@ export class NeedleSpatialMenu {
302
302
  // logoObject.position.y = 1;
303
303
  // this._context.scene.add(logoObject);
304
304
  const textureLoader = new TextureLoader();
305
- textureLoader.load("./include/needle/poweredbyneedle.webp", (texture) => {
305
+ textureLoader.load("https://cdn.needle.tools/static/branding/poweredbyneedle.webp", (texture) => {
306
306
  if (texture) {
307
307
  onClick.allowModifyUI = false;
308
308
  firstLabel.removeFromParent();
@@ -343,7 +343,11 @@ export class NeedleSpatialMenu {
343
343
 
344
344
  if (!fontFamily) {
345
345
  fontFamily = ThreeMeshUI.FontLibrary.addFontFamily(this.familyName);
346
- const normal = fontFamily.addVariant("normal", "normal", "./include/needle/arial-msdf.json", "./include/needle/arial.png") as any as ThreeMeshUI.FontVariant;
346
+ const normal = fontFamily.addVariant(
347
+ "normal",
348
+ "normal",
349
+ "https://cdn.needle.tools/static/fonts/msdf/arial/arial-msdf.json",
350
+ "https://cdn.needle.tools/static/fonts/msdf/arial/arial.png") as any as ThreeMeshUI.FontVariant;
347
351
  /** @ts-ignore */
348
352
  normal?.addEventListener('ready', () => {
349
353
  this.markDirty();
@@ -129,6 +129,7 @@ export class NeedleMenu {
129
129
 
130
130
  constructor(context: Context) {
131
131
  this._menu = NeedleMenuElement.getOrCreate(context.domElement, context);
132
+ this._menu.ensureInitialized();
132
133
  this._context = context;
133
134
  this._spatialMenu = new NeedleSpatialMenu(context, this._menu);
134
135
  window.addEventListener("message", this.onPostMessage);
@@ -309,8 +310,7 @@ export class NeedleMenu {
309
310
  export class NeedleMenuElement extends HTMLElement {
310
311
 
311
312
  static create() {
312
- // https://developer.mozilla.org/en-US/docs/Web/API/Document/createElement#is
313
- return document.createElement(elementName, { is: elementName });
313
+ return document.createElement(elementName);
314
314
  }
315
315
 
316
316
  static getOrCreate(domElement: HTMLElement, context: Context) {
@@ -337,9 +337,13 @@ export class NeedleMenuElement extends HTMLElement {
337
337
 
338
338
  private _domElement: HTMLElement | null = null;
339
339
  private _context: Context | null = null;
340
+ private _didInitialize = false;
340
341
 
341
342
  constructor() {
342
343
  super();
344
+ }
345
+
346
+ private initializeDom() {
343
347
 
344
348
  const template = document.createElement('template');
345
349
  // TODO: make host full size again and move the buttons to a wrapper so that we can later easily open e.g. foldouts/dropdowns / use the whole canvas space
@@ -795,7 +799,7 @@ export class NeedleMenuElement extends HTMLElement {
795
799
 
796
800
 
797
801
  let context = this._context;
798
- // we need to assign it in the timeout because the reference is set *after* the constructor did run
802
+ // we need to assign it in the timeout because the reference is set *after* the element is initialized
799
803
  setTimeout(() => context = this._context);
800
804
 
801
805
  // watch changes
@@ -864,13 +868,21 @@ export class NeedleMenuElement extends HTMLElement {
864
868
  }
865
869
  }
866
870
 
871
+ ensureInitialized() {
872
+ if (!this._didInitialize) {
873
+ this._didInitialize = true;
874
+ this.initializeDom();
875
+ }
876
+ }
877
+
867
878
  private _sizeChangeInterval;
868
879
 
869
880
  connectedCallback() {
881
+ this.ensureInitialized();
870
882
  window.addEventListener("resize", this.handleSizeChange);
871
883
  this.handleMenuVisible();
872
884
  this._sizeChangeInterval = setInterval(() => this.handleSizeChange(undefined, false), 5000);
873
- // the dom element is set after the constructor runs
885
+ // the dom element is set after initialization runs
874
886
  setTimeout(() => {
875
887
  this._domElement?.addEventListener("resize", this.handleSizeChange);
876
888
  this._domElement?.addEventListener("click", this.#onClick);
@@ -958,19 +970,19 @@ export class NeedleMenuElement extends HTMLElement {
958
970
 
959
971
  // private _root: ShadowRoot | null = null;
960
972
  /** @private root container element inside shadow DOM */
961
- private readonly root: HTMLDivElement;
973
+ private root!: HTMLDivElement;
962
974
  /** @private wraps the whole content (internal layout) */
963
- private readonly wrapper: HTMLDivElement;
975
+ private wrapper!: HTMLDivElement;
964
976
  /** @private contains the buttons and dynamic elements */
965
- private readonly options: HTMLDivElement;
977
+ private options!: HTMLDivElement;
966
978
  /** @private contains options visible when in compact mode */
967
- private readonly optionsCompactMode: HTMLDivElement;
979
+ private optionsCompactMode!: HTMLDivElement;
968
980
  /** @private contains the needle-logo html element */
969
- private readonly logoContainer: HTMLDivElement;
981
+ private logoContainer!: HTMLDivElement;
970
982
  /** @private compact menu button element */
971
- private readonly compactMenuButton: HTMLButtonElement;
983
+ private compactMenuButton!: HTMLButtonElement;
972
984
  /** @private foldout container used in compact mode */
973
- private readonly foldout: HTMLDivElement;
985
+ private foldout!: HTMLDivElement;
974
986
 
975
987
 
976
988
  private readonly trackedElements: WeakSet<Node> = new WeakSet();
@@ -121,7 +121,7 @@ export class AROverlayHandler {
121
121
 
122
122
  const quitARSlot = document.createElement("slot");
123
123
  quitARSlot.style.display = "contents";
124
- quitARSlot.style.padding = "10px";
124
+ quitARSlot.style.padding = "10px";
125
125
  quitARSlot.setAttribute("name", "quit-ar");
126
126
  this.appendElement(quitARSlot, element);
127
127
  this._createdAROnlyElements.push(quitARSlot);
@@ -131,7 +131,16 @@ export class AROverlayHandler {
131
131
  // No default quit button in the top right corner in app clips
132
132
  // we provide one via the native UI
133
133
  if (DeviceUtilities.isNeedleAppClip()) {
134
- quitARSlot.style.display = "none";
134
+
135
+ // quitARSlot.style.display = "none";
136
+ globalThis["NEEDLE_ENGINE_APPCLIP_DISABLE_MENU"] = true;
137
+
138
+ // respect the UI bar at the top of the screen and add some padding to the quit button container
139
+ // @TODO: this should be done in CSS in one place and not here and in debug overlay
140
+ const meta = document.querySelector('meta[name="viewport"]');
141
+ if (meta && !meta.getAttribute("content")?.includes("viewport-fit=")) {
142
+ meta.setAttribute("content", meta.getAttribute("content") + ",viewport-fit=cover");
143
+ }
135
144
  }
136
145
 
137
146
  // We want to search the document if there's a quit-ar button
@@ -155,6 +164,8 @@ export class AROverlayHandler {
155
164
  right: 0;
156
165
  z-index: 600;
157
166
  pointer-events: all;
167
+ padding-top: env(safe-area-inset-top, 0px);
168
+ padding-right: calc(env(safe-area-inset-right, 0px) + 10px);
158
169
  `;
159
170
  this.appendElement(fixedButtonContainer, quitARSlot);
160
171
 
@@ -168,19 +168,30 @@ export class NeedleEngineWebComponent extends HTMLElement implements INeedleEngi
168
168
  */
169
169
  public get context() { return this._context; }
170
170
 
171
- private _context: Context;
171
+ private _context!: Context;
172
172
  private _overlay_ar: AROverlayHandler;
173
173
  private _loadingProgress01: number = 0;
174
174
  private _loadingView?: ILoadingViewHandler;
175
175
  private _previousSrc: string | null | string[] = null;
176
176
  /** @private set to true after <needle-engine> did load completely at least once. Set to false when < to false when <needle-engine> is removed from the document removed from the document */
177
177
  private _didFullyLoad: boolean = false;
178
+ private _didInitialize = false;
178
179
 
179
180
  constructor() {
180
181
  super();
181
182
  this._overlay_ar = new AROverlayHandler();
182
183
  // TODO: do we want to rename this event?
183
184
  this.addEventListener("ready", this.onReady);
185
+ }
186
+
187
+ private ensureInitialized() {
188
+ if (!this._didInitialize) {
189
+ this._didInitialize = true;
190
+ this.initializeDom();
191
+ }
192
+ }
193
+
194
+ private initializeDom() {
184
195
 
185
196
  ensureFonts();
186
197
 
@@ -281,6 +292,7 @@ export class NeedleEngineWebComponent extends HTMLElement implements INeedleEngi
281
292
  * @internal
282
293
  */
283
294
  async connectedCallback() {
295
+ this.ensureInitialized();
284
296
  if (debug) {
285
297
  console.log("<needle-engine> connected");
286
298
  }
@@ -49,7 +49,7 @@ export abstract class GameObject extends Object3D implements Object3D, IGameObje
49
49
  abstract activeSelf: boolean;
50
50
 
51
51
  /** @deprecated Use {@link addComponent} instead */
52
- // eslint-disable-next-line deprecation/deprecation
52
+ // eslint-disable-next-line @typescript-eslint/no-deprecated
53
53
  abstract addNewComponent<T extends IComponent>(type: ConstructorConcrete<T>, init?: ComponentInit<T>): T;
54
54
 
55
55
  /**
@@ -364,7 +364,6 @@ export abstract class GameObject extends Object3D implements Object3D, IGameObje
364
364
  }
365
365
 
366
366
  /** @deprecated use `addComponent` */
367
- // eslint-disable-next-line deprecation/deprecation
368
367
  public static addNewComponent<T extends IComponent>(go: IGameObject | Object3D, type: T | ConstructorConcrete<T>, init?: ComponentInit<T>, callAwake: boolean = true): T {
369
368
  return addComponent(go, type, init, { callAwake });
370
369
  }
@@ -1,11 +1,9 @@
1
- import { Color, CubeReflectionMapping, CubeTexture, EquirectangularReflectionMapping, LinearSRGBColorSpace, Material, MeshBasicMaterial, MeshStandardMaterial, Object3D, SRGBColorSpace, Texture, Vector3 } from "three";
1
+ import { CubeTexture, EquirectangularReflectionMapping, LinearSRGBColorSpace, Material, Object3D, Texture, Vector3 } from "three";
2
2
 
3
- import { isDevEnvironment, showBalloonWarning } from "../engine/debug/index.js";
4
3
  import { MaterialPropertyBlock } from "../engine/engine_materialpropertyblock.js";
5
4
  import { loadPMREM } from "../engine/engine_pmrem.js";
6
5
  import { serializable } from "../engine/engine_serialization.js";
7
6
  import { Context } from "../engine/engine_setup.js";
8
- import type { IRenderer } from "../engine/engine_types.js";
9
7
  import { getParam, resolveUrl } from "../engine/engine_utils.js";
10
8
  import { BoxHelperComponent } from "./BoxHelperComponent.js";
11
9
  import { Behaviour } from "./Component.js";
@@ -42,7 +40,11 @@ const $reflectionProbeKey = Symbol("reflectionProbeKey");
42
40
  export class ReflectionProbe extends Behaviour {
43
41
 
44
42
  private static _probes: Map<Context, ReflectionProbe[]> = new Map();
45
-
43
+
44
+
45
+ /**
46
+ * Checks if the given material is currently using a reflection probe. This is determined by checking for an override on the material's "envMap" property, which is set by the Renderer component when applying a reflection probe.
47
+ */
46
48
  static isUsingReflectionProbe(material: Material) {
47
49
  return !!(material as any)[$reflectionProbeKey];
48
50
  }
@@ -50,8 +52,13 @@ export class ReflectionProbe extends Behaviour {
50
52
 
51
53
  /**
52
54
  * Event invoked when a reflection probe is enabled. Used internally by Renderer components to update probes when they become active. Not recommended to call this directly in most cases.
55
+ * @see {@link onDisabled} for the corresponding disable event.
53
56
  */
54
57
  static readonly onEnabled: EventList<ReflectionProbe> = new EventList();
58
+ /**
59
+ * Event invoked when a reflection probe is disabled. Used internally by Renderer components to update probes when they become inactive. Not recommended to call this directly in most cases.
60
+ * @see {@link onEnabled} for the corresponding enable event.
61
+ */
55
62
  static readonly onDisabled: EventList<ReflectionProbe> = new EventList();
56
63
 
57
64
  /**
@@ -90,11 +97,14 @@ export class ReflectionProbe extends Behaviour {
90
97
  return null;
91
98
  }
92
99
 
93
- private _texture!: Texture;
100
+ private _texture!: Texture | null;
94
101
  private _textureUrlInFlight?: string;
95
102
 
103
+ /**
104
+ * The cubemap or HDR texture used for reflections. Can be assigned directly or via a URL string. When assigning via URL, the texture will be loaded asynchronously and applied once ready.
105
+ */
96
106
  @serializable([Texture, String])
97
- set texture(tex: Texture) {
107
+ set texture(tex: Texture | null) {
98
108
  if (this._texture === tex) return;
99
109
 
100
110
  if (typeof tex === "string") {
@@ -111,7 +121,10 @@ export class ReflectionProbe extends Behaviour {
111
121
  return;
112
122
  }
113
123
 
114
- this._textureUrlInFlight = undefined;
124
+ // During deserialization / loading of the GLB the deserializer sets an empty DataTexture
125
+ // But if the texture is serialized as a string (via Blender Reflection Probes) then the async loading above
126
+ // Will abort IF textureInFlight is reset (here). That's why we do NOT reset textureInFlight during initialization (when __didAwake is false). Only after awake, when we are sure that the async loading is done, we reset the in-flight URL to allow new assignments.
127
+ if(this.__didAwake) this._textureUrlInFlight = undefined;
115
128
 
116
129
  this._texture = tex;
117
130
 
@@ -127,7 +140,7 @@ export class ReflectionProbe extends Behaviour {
127
140
  tex.needsUpdate = true;
128
141
  }
129
142
  }
130
- get texture(): Texture {
143
+ get texture(): Texture | null {
131
144
  return this._texture;
132
145
  }
133
146
 
@@ -165,6 +178,7 @@ export class ReflectionProbe extends Behaviour {
165
178
  ReflectionProbe._probes.get(this.context)?.push(this);
166
179
  }
167
180
 
181
+ /** @internal */
168
182
  awake() {
169
183
  this._boxHelper = this.gameObject.addComponent(BoxHelperComponent) as BoxHelperComponent;
170
184
  this._boxHelper.updateBox(true);
@@ -178,20 +192,24 @@ export class ReflectionProbe extends Behaviour {
178
192
  }
179
193
  }
180
194
 
195
+ /** @internal */
181
196
  onEnable(): void {
182
197
  ReflectionProbe.onEnabled?.invoke(this);
183
198
  }
184
199
 
200
+ /** @internal */
185
201
  onDisable(): void {
186
202
  ReflectionProbe.onDisabled?.invoke(this);
187
203
  }
188
204
 
205
+ /** @internal */
189
206
  start(): void {
190
207
  if (!this._texture) {
191
208
  console.warn(`[ReflectionProbe] Missing texture. Please assign a custom cubemap texture. To use reflection probes assign them to your renderer's "anchor" property.`);
192
209
  }
193
210
  }
194
211
 
212
+ /** @internal */
195
213
  onDestroy() {
196
214
  const probes = ReflectionProbe._probes.get(this.context);
197
215
  if (probes) {
@@ -202,6 +220,9 @@ export class ReflectionProbe extends Behaviour {
202
220
  }
203
221
  }
204
222
 
223
+ /**
224
+ * Applies this reflection probe to the given object by setting material property overrides for "envMap", "envMapRotation", and "envMapIntensity". This is typically called by the Renderer component when determining which reflection probe to use for a given object.
225
+ */
205
226
  apply(object: Object3D) {
206
227
  if (disable) return;
207
228
  if (!this.enabled) return;
@@ -218,6 +239,9 @@ export class ReflectionProbe extends Behaviour {
218
239
  propertyBlock.setOverride("envMapIntensity", intensity);
219
240
  }
220
241
 
242
+ /**
243
+ * Removes the reflection probe overrides from the given object. This is typically called by the Renderer component when an object is no longer influenced by this probe or when the probe is disabled.
244
+ */
221
245
  unapply(obj: Object3D) {
222
246
  const block = MaterialPropertyBlock.get(obj);
223
247
  if (block) {
@@ -479,8 +479,10 @@ function tryParseMagicSkyboxName(str: string | null | undefined, environment: bo
479
479
  return useLowRes ? value.url_low : value.url;
480
480
  }
481
481
  else if (typeof str === "string" && str?.length && (isDevEnvironment() || debug)) {
482
- const noUrlOrFile = !str.startsWith("http") && !str.startsWith("file:") && !str.startsWith("blob:") && !str.startsWith("data:") && !str.startsWith("/")
483
- if(noUrlOrFile) {
482
+ // Only warn if the string looks like it was meant to be a magic skybox name.
483
+ // Strings that contain "/" or "." are paths or URLs, not magic names.
484
+ const looksLikePath = str.includes("/") || str.includes(".");
485
+ if(!looksLikePath) {
484
486
  console.warn(`RemoteSkybox: Unknown magic skybox name "${str}". Valid names are: ${Object.keys(MagicSkyboxNames).map(n => `"${n}"`).join(", ")}`);
485
487
  }
486
488
  }
@@ -6,11 +6,11 @@ import { SerializationContext } from "../../../engine/engine_serialization_core.
6
6
  import { serializable } from "../../../engine/engine_serialization_decorator.js";
7
7
  import { getWorldPosition } from "../../../engine/engine_three_utils.js";
8
8
  import { getParam } from "../../../engine/engine_utils.js";
9
+ import GLTFMeshGPUInstancingExtension from '../../../engine/export/gltf/EXT_mesh_gpu_instancing_exporter.js';
9
10
  import { RenderTextureWriter } from "../../../engine/export/gltf/Writers.js";
10
11
  import { shouldExport_HideFlags } from "../../../engine/export/utils.js";
11
12
  import { registerExportExtensions } from "../../../engine/extensions/index.js";
12
13
  import { NEEDLE_components } from "../../../engine/extensions/NEEDLE_components.js";
13
- import GLTFMeshGPUInstancingExtension from '../../../include/three/EXT_mesh_gpu_instancing_exporter.js';
14
14
  import { BoxHelperComponent } from "../../BoxHelperComponent.js";
15
15
  import { Behaviour, GameObject } from "../../Component.js";
16
16
  import { Renderer } from "../../Renderer.js";
@@ -2457,7 +2457,7 @@ ${usedUVChannels.has(0) ? `
2457
2457
  def Shader "uvReader_st"
2458
2458
  {
2459
2459
  uniform token info:id = "UsdPrimvarReader_float2"
2460
- token inputs:varname = "st"
2460
+ string inputs:varname = "st"
2461
2461
  float2 inputs:fallback = (0.0, 0.0)
2462
2462
  float2 outputs:result
2463
2463
  }
@@ -2466,7 +2466,7 @@ ${usedUVChannels.has(1) ? `
2466
2466
  def Shader "uvReader_st1"
2467
2467
  {
2468
2468
  uniform token info:id = "UsdPrimvarReader_float2"
2469
- token inputs:varname = "st1"
2469
+ string inputs:varname = "st1"
2470
2470
  float2 inputs:fallback = (0.0, 0.0)
2471
2471
  float2 outputs:result
2472
2472
  }
@@ -740,7 +740,7 @@ export class USDZExporter extends Behaviour {
740
740
  }
741
741
  else if (sessionRoot) {
742
742
  arScale = sessionRoot.arScale;
743
- // eslint-disable-next-line deprecation/deprecation
743
+ // eslint-disable-next-line @typescript-eslint/no-deprecated
744
744
  _invertForward = sessionRoot.invertForward;
745
745
  }
746
746
 
@@ -62,9 +62,9 @@ export class PhysicsExtension implements IUSDExporterExtension {
62
62
  writer.appendLine(`double dynamicFriction = ${colliderSource.sharedMaterial?.dynamicFriction}`);
63
63
  if (mat && mat.bounciness !== undefined)
64
64
  writer.appendLine(`double restitution = ${colliderSource.sharedMaterial?.bounciness}`);
65
- // eslint-disable-next-line deprecation/deprecation
65
+ // eslint-disable-next-line @typescript-eslint/no-deprecated
66
66
  if (mat && mat.staticFriction !== undefined)
67
- // eslint-disable-next-line deprecation/deprecation
67
+ // eslint-disable-next-line @typescript-eslint/no-deprecated
68
68
  writer.appendLine(`double staticFriction = ${colliderSource.sharedMaterial?.staticFriction}`);
69
69
  writer.closeBlock( "}" );
70
70
  }