@needle-tools/engine 4.16.0 → 4.16.1-next.ad85c25

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 +3 -0
  2. package/README.md +92 -46
  3. package/SKILL.md +25 -33
  4. package/dist/{needle-engine.bundle-mHgaFC2W.min.js → needle-engine.bundle-BH1CfP8u.min.js} +131 -131
  5. package/dist/{needle-engine.bundle-Cx8Qrpbp.js → needle-engine.bundle-CCwAJ9v_.js} +5386 -5356
  6. package/dist/{needle-engine.bundle-CXVjO2uF.umd.cjs → needle-engine.bundle-b3CRN4sk.umd.cjs} +121 -121
  7. package/dist/needle-engine.d.ts +16 -287
  8. package/dist/needle-engine.js +2 -2
  9. package/dist/needle-engine.min.js +1 -1
  10. package/dist/needle-engine.umd.cjs +1 -1
  11. package/lib/engine/engine_context.js +2 -0
  12. package/lib/engine/engine_context.js.map +1 -1
  13. package/lib/engine/webcomponents/logo-element.d.ts +6 -3
  14. package/lib/engine/webcomponents/logo-element.js +18 -0
  15. package/lib/engine/webcomponents/logo-element.js.map +1 -1
  16. package/lib/engine/webcomponents/needle menu/needle-menu.d.ts +10 -7
  17. package/lib/engine/webcomponents/needle menu/needle-menu.js +13 -2
  18. package/lib/engine/webcomponents/needle menu/needle-menu.js.map +1 -1
  19. package/lib/engine/webcomponents/needle-engine.d.ts +4 -1
  20. package/lib/engine/webcomponents/needle-engine.js +22 -15
  21. package/lib/engine/webcomponents/needle-engine.js.map +1 -1
  22. package/lib/engine/xr/NeedleXRSession.js +15 -3
  23. package/lib/engine/xr/NeedleXRSession.js.map +1 -1
  24. package/lib/engine-components/ReflectionProbe.d.ts +10 -3
  25. package/lib/engine-components/ReflectionProbe.js +34 -10
  26. package/lib/engine-components/ReflectionProbe.js.map +1 -1
  27. package/package.json +17 -9
  28. package/plugins/next/next.d.ts +8 -0
  29. package/plugins/next/next.js +7 -6
  30. package/plugins/types/userconfig.d.ts +4 -6
  31. package/plugins/vite/ai.d.ts +17 -0
  32. package/plugins/vite/ai.js +2 -2
  33. package/plugins/vite/alias.d.ts +8 -0
  34. package/plugins/vite/alias.js +5 -2
  35. package/plugins/vite/asap.d.ts +7 -0
  36. package/plugins/vite/asap.js +3 -3
  37. package/plugins/vite/build-pipeline.d.ts +15 -0
  38. package/plugins/vite/build-pipeline.js +5 -6
  39. package/plugins/vite/build.d.ts +7 -0
  40. package/plugins/vite/build.js +5 -2
  41. package/plugins/vite/buildinfo.d.ts +7 -0
  42. package/plugins/vite/buildinfo.js +4 -2
  43. package/plugins/vite/config.d.ts +13 -0
  44. package/plugins/vite/config.js +1 -1
  45. package/plugins/vite/copyfiles.d.ts +7 -0
  46. package/plugins/vite/copyfiles.js +4 -2
  47. package/plugins/vite/custom-element-data.d.ts +25 -0
  48. package/plugins/vite/custom-element-data.js +2 -1
  49. package/plugins/vite/defines.d.ts +10 -0
  50. package/plugins/vite/defines.js +4 -3
  51. package/plugins/vite/dependencies.d.ts +21 -0
  52. package/plugins/vite/dependencies.js +3 -1
  53. package/plugins/vite/dependency-watcher.d.ts +11 -0
  54. package/plugins/vite/dependency-watcher.js +2 -1
  55. package/plugins/vite/drop-client.d.ts +1 -0
  56. package/plugins/vite/drop.d.ts +7 -0
  57. package/plugins/vite/drop.js +4 -4
  58. package/plugins/vite/editor-connection.d.ts +8 -0
  59. package/plugins/vite/editor-connection.js +6 -2
  60. package/plugins/vite/facebook-instant-games.d.ts +7 -0
  61. package/plugins/vite/facebook-instant-games.js +4 -2
  62. package/plugins/vite/gzip.d.ts +6 -0
  63. package/plugins/vite/gzip.js +5 -0
  64. package/plugins/vite/imports-logger.d.ts +13 -0
  65. package/plugins/vite/imports-logger.js +2 -1
  66. package/plugins/vite/index.d.ts +54 -0
  67. package/plugins/vite/index.js +16 -15
  68. package/plugins/vite/license.d.ts +7 -0
  69. package/plugins/vite/license.js +6 -6
  70. package/plugins/vite/local-files-analysis.d.ts +95 -0
  71. package/plugins/vite/local-files-analysis.js +2 -1
  72. package/plugins/vite/local-files-core.d.ts +51 -0
  73. package/plugins/vite/local-files-core.js +2 -2
  74. package/plugins/vite/local-files-internals.d.ts +3 -0
  75. package/plugins/vite/local-files-utils.d.ts +77 -0
  76. package/plugins/vite/local-files.d.ts +2 -0
  77. package/plugins/vite/logger.d.ts +8 -0
  78. package/plugins/vite/logger.js +1 -1
  79. package/plugins/vite/logging.d.ts +31 -0
  80. package/plugins/vite/meta.d.ts +7 -0
  81. package/plugins/vite/meta.js +4 -3
  82. package/plugins/vite/needle-app.d.ts +7 -0
  83. package/plugins/vite/needle-app.js +1 -1
  84. package/plugins/vite/npm.d.ts +4 -0
  85. package/plugins/vite/npm.js +1 -1
  86. package/plugins/vite/peer.d.ts +7 -0
  87. package/plugins/vite/peer.js +3 -1
  88. package/plugins/vite/poster-client.d.ts +1 -0
  89. package/plugins/vite/poster.d.ts +8 -0
  90. package/plugins/vite/poster.js +5 -2
  91. package/plugins/vite/pwa.d.ts +7 -0
  92. package/plugins/vite/pwa.js +4 -4
  93. package/plugins/vite/reload-client.d.ts +1 -0
  94. package/plugins/vite/reload.d.ts +7 -0
  95. package/plugins/vite/reload.js +7 -6
  96. package/plugins/vite/server.d.ts +7 -0
  97. package/plugins/vite/server.js +5 -5
  98. package/plugins/vite/transform-codegen.d.ts +8 -0
  99. package/plugins/vite/transform-codegen.js +6 -4
  100. package/plugins/vite/transform.d.ts +5 -0
  101. package/plugins/vite/transform.js +1 -1
  102. package/plugins/vite/vite-4.4-hack.d.ts +8 -0
  103. package/plugins/vite/vite-4.4-hack.js +1 -1
  104. package/src/engine/engine_context.ts +1 -0
  105. package/src/engine/webcomponents/logo-element.ts +20 -3
  106. package/src/engine/webcomponents/needle menu/needle-menu.ts +22 -9
  107. package/src/engine/webcomponents/needle-engine.ts +42 -35
  108. package/src/engine/xr/NeedleXRSession.ts +16 -3
  109. package/src/engine-components/ReflectionProbe.ts +38 -11
  110. package/plugins/types/next.d.ts +0 -3
  111. package/plugins/types/vite.d.ts +0 -14
@@ -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
+ private 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");
@@ -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);
@@ -337,9 +338,13 @@ export class NeedleMenuElement extends HTMLElement {
337
338
 
338
339
  private _domElement: HTMLElement | null = null;
339
340
  private _context: Context | null = null;
341
+ private _didInitialize = false;
340
342
 
341
343
  constructor() {
342
344
  super();
345
+ }
346
+
347
+ private initializeDom() {
343
348
 
344
349
  const template = document.createElement('template');
345
350
  // 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
@@ -793,7 +798,7 @@ export class NeedleMenuElement extends HTMLElement {
793
798
 
794
799
 
795
800
  let context = this._context;
796
- // we need to assign it in the timeout because the reference is set *after* the constructor did run
801
+ // we need to assign it in the timeout because the reference is set *after* the element is initialized
797
802
  setTimeout(() => context = this._context);
798
803
 
799
804
  // watch changes
@@ -862,13 +867,21 @@ export class NeedleMenuElement extends HTMLElement {
862
867
  }
863
868
  }
864
869
 
870
+ ensureInitialized() {
871
+ if (!this._didInitialize) {
872
+ this._didInitialize = true;
873
+ this.initializeDom();
874
+ }
875
+ }
876
+
865
877
  private _sizeChangeInterval;
866
878
 
867
879
  connectedCallback() {
880
+ this.ensureInitialized();
868
881
  window.addEventListener("resize", this.handleSizeChange);
869
882
  this.handleMenuVisible();
870
883
  this._sizeChangeInterval = setInterval(() => this.handleSizeChange(undefined, false), 5000);
871
- // the dom element is set after the constructor runs
884
+ // the dom element is set after initialization runs
872
885
  setTimeout(() => {
873
886
  this._domElement?.addEventListener("resize", this.handleSizeChange);
874
887
  this._domElement?.addEventListener("click", this.#onClick);
@@ -956,19 +969,19 @@ export class NeedleMenuElement extends HTMLElement {
956
969
 
957
970
  // private _root: ShadowRoot | null = null;
958
971
  /** @private root container element inside shadow DOM */
959
- private readonly root: HTMLDivElement;
972
+ private root!: HTMLDivElement;
960
973
  /** @private wraps the whole content (internal layout) */
961
- private readonly wrapper: HTMLDivElement;
974
+ private wrapper!: HTMLDivElement;
962
975
  /** @private contains the buttons and dynamic elements */
963
- private readonly options: HTMLDivElement;
976
+ private options!: HTMLDivElement;
964
977
  /** @private contains options visible when in compact mode */
965
- private readonly optionsCompactMode: HTMLDivElement;
978
+ private optionsCompactMode!: HTMLDivElement;
966
979
  /** @private contains the needle-logo html element */
967
- private readonly logoContainer: HTMLDivElement;
980
+ private logoContainer!: HTMLDivElement;
968
981
  /** @private compact menu button element */
969
- private readonly compactMenuButton: HTMLButtonElement;
982
+ private compactMenuButton!: HTMLButtonElement;
970
983
  /** @private foldout container used in compact mode */
971
- private readonly foldout: HTMLDivElement;
984
+ private foldout!: HTMLDivElement;
972
985
 
973
986
 
974
987
  private readonly trackedElements: WeakSet<Node> = new WeakSet();
@@ -14,7 +14,7 @@ import { getParam } from "../engine_utils.js";
14
14
  import { RGBAColor } from "../js-extensions/RGBAColor.js";
15
15
  import { ensureFonts } from "./fonts.js";
16
16
  import { arContainerClassName, AROverlayHandler } from "./needle-engine.ar-overlay.js";
17
- import type { registerObservableAttribute} from "./needle-engine.extras.js";
17
+ import type { registerObservableAttribute } from "./needle-engine.extras.js";
18
18
  import { calculateProgress01, EngineLoadingView, type ILoadingViewHandler } from "./needle-engine.loading.js";
19
19
 
20
20
  declare global {
@@ -168,27 +168,19 @@ export class NeedleEngineWebComponent extends HTMLElement implements INeedleEngi
168
168
  */
169
169
  public get context() { return this._context; }
170
170
 
171
- private _context: Context;
172
- private _overlay_ar: AROverlayHandler;
171
+ private _context!: Context;
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
- this._overlay_ar = new AROverlayHandler();
182
- // TODO: do we want to rename this event?
183
- this.addEventListener("ready", this.onReady);
184
-
185
- ensureFonts();
186
-
187
182
  this.attachShadow({ mode: 'open', delegatesFocus: true });
188
- this.setAttribute("role", "application");
189
- this.setAttribute("aria-label", "Needle Engine 3D scene");
190
183
  const template = document.createElement('template');
191
- // #region CSS
192
184
  template.innerHTML = `<style>
193
185
  @import url('https://fonts.googleapis.com/css2?family=Roboto+Flex:opsz,wght@8..144,100..1000&display=swap');
194
186
 
@@ -254,7 +246,7 @@ export class NeedleEngineWebComponent extends HTMLElement implements INeedleEngi
254
246
  }
255
247
  :host .quit-ar-button {
256
248
  position: absolute;
257
- // top: env(titlebar-area-y); /** this doesnt work **/
249
+ /** top: env(titlebar-area-y); this doesnt work **/
258
250
  top: 60px; /** camera access needs a bit more space **/
259
251
  right: 20px;
260
252
  z-index: 9999;
@@ -267,24 +259,36 @@ export class NeedleEngineWebComponent extends HTMLElement implements INeedleEngi
267
259
  <slot class="overlay-content" style="display: contents;"></slot>
268
260
  </div>
269
261
  `;
270
- // #endregion
271
262
 
272
- if (this.shadowRoot)
273
- this.shadowRoot.appendChild(template.content.cloneNode(true));
263
+ this.shadowRoot!.appendChild(template.content.cloneNode(true));
274
264
 
275
- this._context = new Context({ domElement: this });
265
+ // TODO: do we want to rename this event?
266
+ this.addEventListener("ready", this.onReady);
276
267
  this.addEventListener("error", this.onError);
277
268
  }
278
269
 
270
+ private ensureInitialized() {
271
+ if (!this._didInitialize) {
272
+ this._didInitialize = true;
273
+ this.initializeDom();
274
+ }
275
+ }
276
+
277
+ // #region Init DOM
278
+ private initializeDom() {
279
+ ensureFonts();
280
+ this.setAttribute("role", "application");
281
+ this.setAttribute("aria-label", "Needle Engine 3D scene");
282
+ }
283
+
279
284
 
280
285
  /**
281
286
  * @internal
282
287
  */
283
- async connectedCallback() {
284
- if (debug) {
285
- console.log("<needle-engine> connected");
286
- }
288
+ connectedCallback() {
289
+ if (debug) console.log("<needle-engine> connected");
287
290
 
291
+ this.ensureInitialized();
288
292
  this.setPublicKey();
289
293
  this.setVersion();
290
294
 
@@ -293,9 +297,13 @@ export class NeedleEngineWebComponent extends HTMLElement implements INeedleEngi
293
297
  this.setAttribute("tabindex", "0");
294
298
 
295
299
 
300
+ this._overlay_ar = new AROverlayHandler();
301
+ this._context ??= new Context({ domElement: this }); // we might have one already if onLoad is running
296
302
  this.addEventListener("xr-session-started", this.onXRSessionStarted);
297
303
  this.onSetupDesktop();
298
304
 
305
+
306
+
299
307
  if (!this.getAttribute("src")) {
300
308
  const global = (globalThis as any)["needle:codegen_files"] as unknown as string;
301
309
  if (debug) console.log("src is null, trying to load from globalThis[\"needle:codegen_files\"]", global);
@@ -442,6 +450,7 @@ export class NeedleEngineWebComponent extends HTMLElement implements INeedleEngi
442
450
  private async onLoad() {
443
451
 
444
452
  if (!this.isConnected) return;
453
+
445
454
  if (!this._context) {
446
455
  if (debug) console.warn("Create new context");
447
456
  this._context = new Context({ domElement: this });
@@ -476,7 +485,6 @@ export class NeedleEngineWebComponent extends HTMLElement implements INeedleEngi
476
485
  this.classList.add("loading");
477
486
 
478
487
 
479
-
480
488
  // Loading start events
481
489
  const allowOverridingDefaultLoading = hasCommercialLicense();
482
490
  // default loading can be overriden by calling preventDefault in the onload start event
@@ -901,7 +909,7 @@ export class NeedleEngineWebComponent extends HTMLElement implements INeedleEngi
901
909
  setAttribute(name: 'ktx2DecoderPath', value: string): void;
902
910
  /** Prevent Needle Engine context from being disposed when the element is removed from the DOM */
903
911
  setAttribute(name: 'keep-alive', value: 'true' | 'false'): void;
904
-
912
+
905
913
  /** @private Public key used for licensing and feature gating. */
906
914
  setAttribute(name: 'public-key', value: string): void;
907
915
  /** @private Engine version string — usually set by the build/runtime. */
@@ -909,7 +917,7 @@ export class NeedleEngineWebComponent extends HTMLElement implements INeedleEngi
909
917
 
910
918
  // LoadingAttributes
911
919
  // ...
912
-
920
+
913
921
  // SkyboxAttributes
914
922
  /** URL to .exr, .hdr, .png, .jpg to be used as skybox */
915
923
  setAttribute(name: 'background-image', value: string): void;
@@ -930,7 +938,7 @@ export class NeedleEngineWebComponent extends HTMLElement implements INeedleEngi
930
938
  * @example "background-color='#ff0000'" will set the background color to red.
931
939
  */
932
940
  setAttribute(name: 'background-color', value: string): void;
933
-
941
+
934
942
  // RenderingAttributes
935
943
  /** Enable/disable renderer canvas transparency. */
936
944
  setAttribute(name: 'transparent', value: 'true' | 'false'): void;
@@ -953,9 +961,9 @@ export class NeedleEngineWebComponent extends HTMLElement implements INeedleEngi
953
961
  setAttribute(name: "autoplay", value: 'true' | 'false'): void;
954
962
  /** @private Used for switching the scene in SceneSwitcher */
955
963
  setAttribute(name: 'scene', value: string): void;
956
-
964
+
957
965
  // setAttribute(name: 'loadstart', value: string): void;
958
-
966
+
959
967
  /** @private Experimental.*/
960
968
  setAttribute(name: 'loading-blur', value: 'true' | 'false'): void;
961
969
  /** @private */
@@ -968,9 +976,9 @@ export class NeedleEngineWebComponent extends HTMLElement implements INeedleEngi
968
976
  /** Generic typed setter for known Needle Engine attributes */
969
977
  // Comment out to see errors inside NE for undocumented attributes
970
978
  // setAttribute<T extends keyof NeedleEngineAttributes>(qualifiedName: T, value: NeedleEngineAttributes[T]): void;
971
-
979
+
972
980
  setAttribute(qualifiedName: ({} & string), value: string): void;
973
-
981
+
974
982
  // The ones we're using internally:
975
983
  /*
976
984
  setAttribute(name: "tabindex", value: string): void;
@@ -999,7 +1007,7 @@ export class NeedleEngineWebComponent extends HTMLElement implements INeedleEngi
999
1007
  getAttribute(name: 'ktx2DecoderPath'): string | null;
1000
1008
  /** Prevent Needle Engine context from being disposed when the element is removed from the DOM */
1001
1009
  getAttribute(name: 'keep-alive'): string | null;
1002
-
1010
+
1003
1011
  /** @private Public key used for licensing and feature gating. */
1004
1012
  getAttribute(name: 'public-key'): string | null;
1005
1013
  /** @private Engine version string — usually set by the build/runtime. */
@@ -1070,13 +1078,13 @@ export class NeedleEngineWebComponent extends HTMLElement implements INeedleEngi
1070
1078
  /** Typed getter for known NeedleEngine attribute names; returns the typed shape declared in NeedleEngineAttributes or null. */
1071
1079
  // getAttribute<T extends keyof NeedleEngineAttributes>(qualifiedName: T): NeedleEngineAttributes[T] | null;
1072
1080
  getAttribute(qualifiedName: ({} & string)): string | null;
1073
-
1081
+
1074
1082
  // The ones we're using interally:
1075
1083
  /*
1076
1084
  getAttribute(name: "autostart"): string | null;
1077
1085
  getAttribute(name: "tabindex"): string | null;
1078
1086
  */
1079
-
1087
+
1080
1088
  getAttribute(qualifiedName: string): string | null {
1081
1089
  return super.getAttribute(qualifiedName);
1082
1090
  }
@@ -1114,7 +1122,7 @@ export class NeedleEngineWebComponent extends HTMLElement implements INeedleEngi
1114
1122
  addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
1115
1123
  // This would be better but doesn't completely solve it
1116
1124
  // addEventListener(type: ({} & string), listener: any, options?: boolean | AddEventListenerOptions): void;
1117
-
1125
+
1118
1126
  // The ones we're using interally:
1119
1127
  /*
1120
1128
  addEventListener(type: "error", listener: (ev: ErrorEvent) => void, options?: boolean | AddEventListenerOptions): void;
@@ -1123,8 +1131,7 @@ export class NeedleEngineWebComponent extends HTMLElement implements INeedleEngi
1123
1131
  addEventListener(type: "drop", listener: (ev: DragEvent) => void, options?: boolean | AddEventListenerOptions): void;
1124
1132
  addEventListener(type: "dragover", listener: (ev: DragEvent) => void, options?: boolean | AddEventListenerOptions): void;
1125
1133
  */
1126
- addEventListener(type: string, listener: any, options?: boolean | AddEventListenerOptions): void
1127
- {
1134
+ addEventListener(type: string, listener: any, options?: boolean | AddEventListenerOptions): void {
1128
1135
  return super.addEventListener(type, listener as EventListenerOrEventListenerObject, options);
1129
1136
  }
1130
1137
  }
@@ -255,6 +255,7 @@ function handleAutoStart(value: string | null) {
255
255
  // }
256
256
 
257
257
  const $initialFov = Symbol("initial-fov");
258
+ const $initialNear = Symbol("initial-near");
258
259
 
259
260
  /**
260
261
  * This class manages an XRSession to provide helper methods and events. It provides easy access to the XRInputSources (controllers and hands)
@@ -1070,6 +1071,12 @@ export class NeedleXRSession implements INeedleXRSession {
1070
1071
  this._originalCameraParent = this.context.mainCamera.parent;
1071
1072
  if (this.context.mainCamera instanceof PerspectiveCamera) {
1072
1073
  this.context.mainCamera[$initialFov] = this.context.mainCamera.fov;
1074
+
1075
+ // if (this.mode === "immersive-ar" && this.context.mainCamera.near > .1) {
1076
+ // console.log("[WebXR] Setting near plane to 0.1 for better AR experience (was " + this.context.mainCamera.near + "). The initial near plane will be restored when the session ends.");
1077
+ // this.context.mainCamera.near = 0.1;
1078
+ // this.context.mainCamera[$initialNear] = this.context.mainCamera.near;
1079
+ // }
1073
1080
  }
1074
1081
  }
1075
1082
 
@@ -1308,9 +1315,15 @@ export class NeedleXRSession implements INeedleXRSession {
1308
1315
  setWorldScale(this.context.mainCamera, this._originalCameraWorldScale);
1309
1316
  }
1310
1317
 
1311
- if (this.context.mainCamera instanceof PerspectiveCamera && this.context.mainCamera[$initialFov]) {
1312
- this.context.mainCamera.fov = this.context.mainCamera[$initialFov]!;
1313
- this.context.mainCamera[$initialFov] = 0;
1318
+ if (this.context.mainCamera instanceof PerspectiveCamera) {
1319
+ if (this.context.mainCamera[$initialFov]) {
1320
+ this.context.mainCamera.fov = this.context.mainCamera[$initialFov]!;
1321
+ this.context.mainCamera[$initialFov] = 0;
1322
+ }
1323
+ if (this.context.mainCamera[$initialNear]) {
1324
+ this.context.mainCamera.near = this.context.mainCamera[$initialNear]!;
1325
+ this.context.mainCamera[$initialNear] = 0;
1326
+ }
1314
1327
  }
1315
1328
  }
1316
1329
 
@@ -1,4 +1,4 @@
1
- import { CubeTexture, EquirectangularReflectionMapping, LinearSRGBColorSpace, Material, Object3D, Texture, Vector3 } from "three";
1
+ import { Box3, Color, CubeTexture, EquirectangularReflectionMapping, LinearSRGBColorSpace, LineSegments, Material, Object3D, Quaternion, Texture, Vector3 } from "three";
2
2
 
3
3
  import { MaterialPropertyBlock } from "../engine/engine_materialpropertyblock.js";
4
4
  import { loadPMREM } from "../engine/engine_pmrem.js";
@@ -8,6 +8,8 @@ import { getParam, resolveUrl } from "../engine/engine_utils.js";
8
8
  import { BoxHelperComponent } from "./BoxHelperComponent.js";
9
9
  import { Behaviour } from "./Component.js";
10
10
  import { EventList } from "./EventList.js";
11
+ import { getBoundingBox, getWorldPosition } from "../engine/engine_three_utils.js";
12
+ import { CreateWireCube } from "../engine/engine_gizmos.js";
11
13
 
12
14
  export const debug = getParam("debugreflectionprobe");
13
15
  const disable = getParam("noreflectionprobe");
@@ -40,8 +42,11 @@ const $reflectionProbeKey = Symbol("reflectionProbeKey");
40
42
  export class ReflectionProbe extends Behaviour {
41
43
 
42
44
  private static _probes: Map<Context, ReflectionProbe[]> = new Map();
43
-
44
-
45
+ /** The bounding box for this component */
46
+ private box: Box3 | null = null;
47
+ private static testBox: Box3 = new Box3();
48
+ private static _identityRotation: Quaternion = new Quaternion();
49
+
45
50
  /**
46
51
  * 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
52
  */
@@ -151,23 +156,33 @@ export class ReflectionProbe extends Behaviour {
151
156
  * Defines the center and size of the reflection probe's influence area.
152
157
  */
153
158
  @serializable(Vector3)
154
- center?: Vector3;
159
+ center: Vector3 = new Vector3();
155
160
 
156
161
  /**
157
162
  * Defines the size of the reflection probe's influence area. Objects within this box will be affected by the probe's reflections.
158
163
  */
159
164
  @serializable(Vector3)
160
- size?: Vector3;
165
+ size: Vector3 = new Vector3(1, 1, 1);
161
166
 
162
167
  /**
163
168
  * Workaround for lightmap. Compensates for the fact that lightmaps are pre-multiplied by intensity, while reflection probes are not. This means that if you use both lightmaps and reflection probes, you may need to adjust this value to get the correct balance between them. The default value of `Math.PI` is a good starting point for most cases, but you may need to tweak it based on your specific lighting setup and artistic needs.
164
169
  */
165
170
  __lightmapIntensityScale: boolean = true;
166
171
 
167
- private _boxHelper?: BoxHelperComponent;
168
172
 
169
173
  private isInBox(obj: Object3D) {
170
- return this._boxHelper?.isInBox(obj);
174
+
175
+ this.box ??= new Box3();
176
+ this.box!.setFromCenterAndSize(this.gameObject.worldPosition.add(this.center), this.gameObject.worldScale.multiply(this.size));
177
+
178
+ getBoundingBox([obj], undefined, undefined, ReflectionProbe.testBox);
179
+
180
+ if (ReflectionProbe.testBox.isEmpty()) {
181
+ return this.box.containsPoint(obj.worldPosition);
182
+ }
183
+ else {
184
+ return this.box?.intersectsBox(ReflectionProbe.testBox);
185
+ }
171
186
  }
172
187
 
173
188
  constructor() {
@@ -178,12 +193,15 @@ export class ReflectionProbe extends Behaviour {
178
193
  ReflectionProbe._probes.get(this.context)?.push(this);
179
194
  }
180
195
 
196
+ private _gizmo: LineSegments | null = null;
197
+ private _color: Color | null = null;
198
+
181
199
  /** @internal */
182
200
  awake() {
183
- this._boxHelper = this.gameObject.addComponent(BoxHelperComponent) as BoxHelperComponent;
184
- this._boxHelper.updateBox(true);
185
- if (debug)
186
- this._boxHelper.showHelper(0x555500, true);
201
+ if (debug) {
202
+ this._gizmo = CreateWireCube(0x555500);
203
+ this.gameObject.add(this._gizmo);
204
+ }
187
205
 
188
206
  if (this._texture) {
189
207
  this._texture.mapping = EquirectangularReflectionMapping;
@@ -192,6 +210,15 @@ export class ReflectionProbe extends Behaviour {
192
210
  }
193
211
  }
194
212
 
213
+ /** @internal */
214
+ update(): void {
215
+ if (this._gizmo) {
216
+ if (this.center) this._gizmo.position.copy(this.center);
217
+ if (this.size) this._gizmo.scale.copy(this.size);
218
+ this._gizmo.worldQuaternion = ReflectionProbe._identityRotation;
219
+ }
220
+ }
221
+
195
222
  /** @internal */
196
223
  onEnable(): void {
197
224
  ReflectionProbe.onEnabled?.invoke(this);
@@ -1,3 +0,0 @@
1
- import { userSettings } from './userconfig.js';
2
-
3
- export declare function needleNext(nextConfig?: import('next').NextConfig, userSettings?: userSettings): Promise<import('next').NextConfig>;
@@ -1,14 +0,0 @@
1
- import { needleConfig, needleMeta } from "./needleConfig.d.ts";
2
- import { userSettings } from "./userconfig.d.ts";
3
-
4
- export declare function needlePlugins(command: import("vite").ConfigEnv["command"], meta?: needleMeta | null, userSettings?: userSettings): Promise<any>;
5
-
6
- /**
7
- * Loads the generated needle meta file
8
- */
9
- export declare function loadConfig(path: string | null = null): Promise<needleMeta | null>;
10
-
11
- /**
12
- * Returns true if the project is configured to use gzip compression
13
- */
14
- export declare function useGzip(config?: needleConfig): boolean;