@needle-tools/engine 4.8.4-next.5c03fe1 → 4.8.4-next.b63dc2f

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 (54) hide show
  1. package/README.md +5 -0
  2. package/dist/{gltf-progressive-PB_58h1b.umd.cjs → gltf-progressive-C-U_onhf.umd.cjs} +1 -1
  3. package/dist/{gltf-progressive-DorC035H.min.js → gltf-progressive-DViD_J_l.min.js} +1 -1
  4. package/dist/{gltf-progressive-B3JW4cAu.js → gltf-progressive-DXRy9EQz.js} +35 -35
  5. package/dist/loader.worker-CiTwpNPW.js +27 -0
  6. package/dist/{needle-engine.bundle-BecMzBfA.js → needle-engine.bundle-BR8P4m1O.js} +6363 -6394
  7. package/dist/{needle-engine.bundle-BDQm33td.umd.cjs → needle-engine.bundle-CuQtpPTN.umd.cjs} +134 -135
  8. package/dist/{needle-engine.bundle-C3oFZgvW.min.js → needle-engine.bundle-DA6dZylq.min.js} +138 -139
  9. package/dist/needle-engine.js +4 -4
  10. package/dist/needle-engine.min.js +1 -1
  11. package/dist/needle-engine.umd.cjs +1 -1
  12. package/dist/{postprocessing-BgC7XZwK.umd.cjs → postprocessing-61aXdqNz.umd.cjs} +1 -1
  13. package/dist/{postprocessing-ChsrvDkI.min.js → postprocessing-Be9Ds4NK.min.js} +1 -1
  14. package/dist/{postprocessing-DddlM3CK.js → postprocessing-D9jDHD0U.js} +2 -2
  15. package/dist/{three-DrqIzZTH.js → three-BRSLmpyi.js} +2966 -2958
  16. package/dist/{three-DuDKwKB8.min.js → three-CsmWHVn7.min.js} +172 -172
  17. package/dist/{three-B_hneGZr.umd.cjs → three-Dceyffus.umd.cjs} +176 -176
  18. package/dist/{three-examples-BIuXQPSf.js → three-examples-BihZ_R96.js} +2761 -2623
  19. package/dist/{three-examples-CNRuT27G.umd.cjs → three-examples-Ce6Th3bv.umd.cjs} +17 -17
  20. package/dist/three-examples-DKY9Nfge.min.js +501 -0
  21. package/dist/{three-mesh-ui-DWcMuyQ_.min.js → three-mesh-ui-Bwy12Qvg.min.js} +1 -1
  22. package/dist/{three-mesh-ui-tt0buEDC.umd.cjs → three-mesh-ui-Cdh2iW8b.umd.cjs} +1 -1
  23. package/dist/{three-mesh-ui-D6Mz5Yl7.js → three-mesh-ui-gqAXlGNB.js} +1 -1
  24. package/dist/{vendor-C31T0mYm.min.js → vendor-BRpzuoJE.min.js} +1 -1
  25. package/dist/{vendor-D51IT5ns.umd.cjs → vendor-Ja-vKV-a.umd.cjs} +1 -1
  26. package/dist/{vendor-B_ytQUuR.js → vendor-p_xp9KuJ.js} +1 -1
  27. package/lib/engine/debug/debug_console.js +3 -33
  28. package/lib/engine/debug/debug_console.js.map +1 -1
  29. package/lib/engine/engine_pmrem.d.ts +2 -0
  30. package/lib/engine/engine_pmrem.js +100 -0
  31. package/lib/engine/engine_pmrem.js.map +1 -0
  32. package/lib/engine-components/Camera.js +1 -1
  33. package/lib/engine-components/Camera.js.map +1 -1
  34. package/lib/engine-components/DragControls.js +0 -1
  35. package/lib/engine-components/DragControls.js.map +1 -1
  36. package/lib/engine-components/Duplicatable.js +5 -2
  37. package/lib/engine-components/Duplicatable.js.map +1 -1
  38. package/lib/engine-components/Skybox.d.ts +0 -2
  39. package/lib/engine-components/Skybox.js +26 -110
  40. package/lib/engine-components/Skybox.js.map +1 -1
  41. package/lib/engine-components/TransformGizmo.js +10 -2
  42. package/lib/engine-components/TransformGizmo.js.map +1 -1
  43. package/package.json +3 -3
  44. package/src/engine/debug/debug_console.ts +3 -33
  45. package/src/engine/engine_pmrem.ts +118 -0
  46. package/src/engine-components/Camera.ts +1 -1
  47. package/src/engine-components/DragControls.ts +0 -1
  48. package/src/engine-components/Duplicatable.ts +3 -2
  49. package/src/engine-components/Skybox.ts +30 -102
  50. package/src/engine-components/TransformGizmo.ts +11 -2
  51. package/src/include/ktx2/basis_transcoder.js +8 -10
  52. package/src/include/ktx2/basis_transcoder.wasm +0 -0
  53. package/dist/loader.worker-CrU5fNbR.js +0 -27
  54. package/dist/three-examples-D2zemuAM.min.js +0 -501
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@needle-tools/engine",
3
- "version": "4.8.4-next.5c03fe1",
3
+ "version": "4.8.4-next.b63dc2f",
4
4
  "description": "Needle Engine is a web-based runtime for 3D apps. It runs on your machine for development with great integrations into editors like Unity or Blender - and can be deployed onto any device! It is flexible, extensible and networking and XR are built-in.",
5
5
  "main": "dist/needle-engine.min.js",
6
6
  "exports": {
@@ -93,7 +93,7 @@
93
93
  ],
94
94
  "dependencies": {
95
95
  "@dimforge/rapier3d-compat": "^0.14.0",
96
- "@needle-tools/gltf-progressive": "^3.3.1",
96
+ "@needle-tools/gltf-progressive": "3.3.2",
97
97
  "@webxr-input-profiles/motion-controllers": "^1.0.0",
98
98
  "flatbuffers": "2.0.4",
99
99
  "md5": "^2.3.0",
@@ -102,7 +102,7 @@
102
102
  "postprocessing": "^6.36.6",
103
103
  "simplex-noise": "^4.0.1",
104
104
  "stats.js": "^0.17.0",
105
- "three": "npm:@needle-tools/three@0.169.7",
105
+ "three": "npm:@needle-tools/three@0.169.8",
106
106
  "three-mesh-bvh": "^0.9.1",
107
107
  "three-mesh-ui": "npm:@needle-tools/three-mesh-ui@^7.1.5-alpha.5",
108
108
  "three.quarks": "0.15.6",
@@ -1,7 +1,5 @@
1
- import { isLocalNetwork } from "../engine_networking_utils.js";
2
- import { DeviceUtilities,getParam } from "../engine_utils.js";
3
- import { isDevEnvironment } from "./debug.js";
4
- import { getErrorCount, makeErrorsVisibleForDevelopment } from "./debug_overlay.js";
1
+ import { getParam } from "../engine_utils.js";
2
+ import { getErrorCount } from "./debug_overlay.js";
5
3
 
6
4
  let consoleInstance: VConsole | null | undefined = undefined;
7
5
  let consoleHtmlElement: HTMLElement | null = null;
@@ -12,38 +10,10 @@ let watchInterval: any = null;
12
10
  const defaultButtonIcon = "terminal";
13
11
 
14
12
  const showConsole = getParam("console");
15
- const suppressConsole = getParam("noerrors") || getParam("noconsole") || window.crossOriginIsolated;
16
13
  if (showConsole) {
17
14
  showDebugConsole();
18
15
  }
19
16
 
20
- if (!suppressConsole && (showConsole || isLocalNetwork())) {
21
- if (isLocalNetwork() && !showConsole) {
22
- const consoleUrl = new URL(window.location.href);
23
- consoleUrl.searchParams.set("console", "1");
24
- console.log("🌵 Tip: You can add the \"?console\" query parameter to the url to show the debug console (on mobile it will automatically open in the bottom right corner when your get errors during development. In VR a spatial console will appear.)", "\nOpen this page to get the console: " + consoleUrl.toString());
25
- }
26
- const enableConsole = DeviceUtilities.isMobileDevice() || (DeviceUtilities.isQuest() && isDevEnvironment());
27
- if (enableConsole || showConsole) {
28
- // we need to invoke this here - otherwise we will miss errors that happen after the console is loaded
29
- // and calling the method from the root needle-engine.ts import is evaluated later (if we import the method from the toplevel file and then invoke it)
30
- makeErrorsVisibleForDevelopment();
31
- beginWatchingLogs();
32
- createConsole(true);
33
- if (enableConsole) {
34
- const engineElement = document.querySelector("needle-engine");
35
- engineElement?.addEventListener("enter-ar", () => {
36
- if (showConsole || consoleInstance || getErrorCount() > 0) {
37
- if (getParam("noerrors")) return;
38
- }
39
- });
40
- engineElement?.addEventListener("exit-ar", () => {
41
- onResetConsoleElementToDefaultParent();
42
- });
43
- }
44
- }
45
- }
46
-
47
17
  const $defaultConsoleParent = Symbol("consoleParent");
48
18
 
49
19
  export function showDebugConsole() {
@@ -247,7 +217,7 @@ function createConsole(startHidden: boolean = false) {
247
217
  isLoading = false;
248
218
  consoleInstance = null;
249
219
  };
250
- script.src = "https://cdn.jsdelivr.net/npm/vconsole@3.9.1/dist/vconsole.min.js";
220
+ script.src = "https://cdn.jsdelivr.net/npm/vconsole@3.15.1/dist/vconsole.min.js";
251
221
  document.body.appendChild(script);
252
222
  }
253
223
 
@@ -0,0 +1,118 @@
1
+ import { createLoaders } from "@needle-tools/gltf-progressive";
2
+ import { CubeUVReflectionMapping, SRGBColorSpace, Texture, TextureLoader, WebGLRenderer } from "three";
3
+ import { EXRLoader } from "three/examples/jsm/loaders/EXRLoader";
4
+ import { KTX2Loader } from "three/examples/jsm/loaders/KTX2Loader";
5
+ import { RGBELoader } from "three/examples/jsm/loaders/RGBELoader";
6
+ import { disposeObjectResources, setDisposable } from "./engine_assetdatabase.js";
7
+
8
+ const running: Map<string, Promise<Texture | null>> = new Map();
9
+
10
+ // #region api
11
+
12
+ export function loadPMREM(url: string, renderer: WebGLRenderer): Promise<Texture | null> {
13
+ if (running.has(url)) {
14
+ return running.get(url)!;
15
+ }
16
+ const actualUrl = new URL(url, window.location.href);
17
+ const promise = internalLoadPMREM(actualUrl, renderer);
18
+ running.set(url, promise);
19
+ return promise;
20
+ }
21
+
22
+
23
+
24
+ // #region Cache
25
+
26
+ declare type SkyboxCacheEntry = { src: string, texture: Promise<Texture | null> };
27
+ function ensureGlobalCache() {
28
+ if (!globalThis["NEEDLE_ENGINE_SKYBOX_TEXTURES"])
29
+ globalThis["NEEDLE_ENGINE_SKYBOX_TEXTURES"] = new Array<SkyboxCacheEntry>();
30
+ return globalThis["NEEDLE_ENGINE_SKYBOX_TEXTURES"] as Array<SkyboxCacheEntry>;
31
+ }
32
+
33
+ function tryGetPreviouslyLoadedTexture(src: string) {
34
+ const cache = ensureGlobalCache();
35
+ const found = cache.find(x => x.src === src);
36
+ if (found) {
37
+ return found.texture;
38
+ }
39
+ return null;
40
+ }
41
+ async function disposeCachedTexture(tex: Promise<Texture | null>) {
42
+ const texture = await tex;
43
+ if (!texture) return;
44
+ setDisposable(texture, true);
45
+ disposeObjectResources(texture);
46
+ }
47
+ function registerPromise(src: string, texture: Promise<Texture | null>) {
48
+ const cache = ensureGlobalCache();
49
+ // Make sure the cache doesnt get too big
50
+ while (cache.length > 5) {
51
+ const entry = cache.shift();
52
+ if (entry) { disposeCachedTexture(entry.texture); }
53
+ }
54
+ texture.then(t => { return setDisposable(t, false) });
55
+ cache.push({ src, texture });
56
+ }
57
+
58
+
59
+
60
+ // #region loading
61
+
62
+
63
+ async function internalLoadPMREM(url: URL, renderer: WebGLRenderer) {
64
+ if (!url) return Promise.resolve(null);
65
+
66
+ const pathname = url.pathname;
67
+ const isPMREM_URL: boolean = url.toString().toLowerCase().includes("pmrem") || url.searchParams.get("pmrem") != null;
68
+
69
+ const cached = tryGetPreviouslyLoadedTexture(pathname);
70
+ if (cached) {
71
+ const res = await cached;
72
+ if (res?.source?.data?.length > 0 || res?.source?.data?.data?.length) return res;
73
+ }
74
+ const isEXR = pathname.endsWith(".exr");
75
+ const isHdr = pathname.endsWith(".hdr");
76
+ const isKtx2 = pathname.endsWith(".ktx2");
77
+
78
+ let loader: RGBELoader | EXRLoader | TextureLoader | KTX2Loader;
79
+
80
+ if (isEXR) {
81
+ loader = new EXRLoader();
82
+ }
83
+ else if (isHdr) {
84
+ loader = new RGBELoader();
85
+ }
86
+ else if (isKtx2) {
87
+ const { ktx2Loader } = createLoaders(renderer);
88
+ loader = ktx2Loader;
89
+ }
90
+ else {
91
+ loader = new TextureLoader();
92
+ }
93
+
94
+ const str = url.toString();
95
+ const promise = loader.loadAsync(str)
96
+ .then(tex => {
97
+ if (tex) {
98
+ const nameIndex = pathname.lastIndexOf("/");
99
+ tex.name = pathname.substring(nameIndex >= 0 ? nameIndex + 1 : 0);
100
+
101
+ if (isPMREM_URL) {
102
+ tex.mapping = CubeUVReflectionMapping;
103
+ }
104
+
105
+ if (loader instanceof TextureLoader) {
106
+ tex.colorSpace = SRGBColorSpace;
107
+ }
108
+ }
109
+ return tex;
110
+ });
111
+
112
+ registerPromise(str, promise);
113
+ const texture = await promise.catch(_err => {
114
+ console.warn("Failed to load texture from url:", url);
115
+ return null;
116
+ });
117
+ return texture;
118
+ }
@@ -694,7 +694,7 @@ class CameraSkybox {
694
694
  }
695
695
  else if (this.context.scene.background !== this._skybox) {
696
696
 
697
- const hasBackgroundAttribute = this.context.domElement.getAttribute("background-image") || this.context.domElement.getAttribute("background-color") || this.context.domElement.getAttribute("skybox-image");
697
+ const hasBackgroundAttribute = this.context.domElement.getAttribute("background-image") || this.context.domElement.getAttribute("background-color");
698
698
 
699
699
  if (debug) console.debug(`[Camera] Apply Skybox ${this._skybox?.name} ${hasBackgroundAttribute} - \"${this._camera.name}\"`);
700
700
  if (!hasBackgroundAttribute?.length) {
@@ -454,7 +454,6 @@ export class DragControls extends Behaviour implements IPointerEventHandler {
454
454
  const sync = GameObject.getComponentInChildren(evt.object, SyncedTransform);
455
455
  if (sync) {
456
456
  sync.fastMode = false;
457
- // sync?.requestOwnership();
458
457
  }
459
458
  }
460
459
  if (this._marker)
@@ -119,8 +119,9 @@ export class Duplicatable extends Behaviour implements IPointerEventHandler {
119
119
  const res = this.handleDuplication();
120
120
  if (res) {
121
121
  const dragControls = GameObject.getComponent(res, DragControls);
122
- if (!dragControls) console.warn("Duplicated object does not have DragControls", res);
123
- else {
122
+ if (!dragControls) {
123
+ if (isDevEnvironment()) console.warn(`Duplicated object (${res.name}) does not have DragControls`);
124
+ } else {
124
125
  dragControls.onPointerDown(args);
125
126
  this._forwardPointerEvents.set(args.event.space, dragControls);
126
127
  }
@@ -1,12 +1,11 @@
1
- import { createLoaders } from "@needle-tools/gltf-progressive";
2
- import { CompressedCubeTexture, CubeRefractionMapping, CubeTexture, EquirectangularRefractionMapping, SRGBColorSpace, Texture, TextureLoader } from "three"
1
+ import { CompressedCubeTexture, CubeRefractionMapping, CubeTexture, CubeUVReflectionMapping, EquirectangularRefractionMapping, SRGBColorSpace, Texture, TextureLoader } from "three"
3
2
  import { EXRLoader } from "three/examples/jsm/loaders/EXRLoader.js";
4
3
  import { KTX2Loader } from "three/examples/jsm/loaders/KTX2Loader.js";
5
4
  import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader.js';
6
5
 
7
- import { disposeObjectResources, setDisposable } from "../engine/engine_assetdatabase.js";
8
6
  import { ContextEvent, ContextRegistry } from "../engine/engine_context_registry.js";
9
7
  import { syncField } from "../engine/engine_networking_auto.js";
8
+ import { loadPMREM } from "../engine/engine_pmrem.js";
10
9
  import { serializable } from "../engine/engine_serialization_decorator.js";
11
10
  import { type IContext } from "../engine/engine_types.js";
12
11
  import { addAttributeChangeCallback, getParam, PromiseAllWithErrors, removeAttributeChangeCallback } from "../engine/engine_utils.js";
@@ -51,7 +50,9 @@ ContextRegistry.registerCallback(ContextEvent.ContextCreationStart, (args) => {
51
50
  const context = args.context;
52
51
  const skyboxImage = context.domElement.getAttribute("background-image");
53
52
  const environmentImage = context.domElement.getAttribute("environment-image");
54
- if (skyboxImage) {
53
+ const envAndSkyboxAreSame = skyboxImage === environmentImage;
54
+
55
+ if (skyboxImage && !envAndSkyboxAreSame) {
55
56
  if (debug) console.log("Creating remote skybox to load " + skyboxImage);
56
57
  // if the user is loading a GLB without a camera then the CameraUtils (which creates the default camera)
57
58
  // checks if we have this attribute set and then sets the skybox clearflags accordingly
@@ -61,7 +62,7 @@ ContextRegistry.registerCallback(ContextEvent.ContextCreationStart, (args) => {
61
62
  }
62
63
  if (environmentImage) {
63
64
  if (debug) console.log("Creating remote environment to load " + environmentImage);
64
- const promise = createRemoteSkyboxComponent(context, environmentImage, false, true, "environment-image");
65
+ const promise = createRemoteSkyboxComponent(context, environmentImage, envAndSkyboxAreSame, true, "environment-image");
65
66
  if (promise) promises.push(promise);
66
67
  }
67
68
  });
@@ -71,39 +72,6 @@ ContextRegistry.registerCallback(ContextEvent.ContextCreationStart, () => {
71
72
  })
72
73
  });
73
74
 
74
- declare type SkyboxCacheEntry = { src: string, texture: Promise<Texture | null> };
75
- function ensureGlobalCache() {
76
- if (!globalThis["NEEDLE_ENGINE_SKYBOX_TEXTURES"])
77
- globalThis["NEEDLE_ENGINE_SKYBOX_TEXTURES"] = new Array<SkyboxCacheEntry>();
78
- return globalThis["NEEDLE_ENGINE_SKYBOX_TEXTURES"] as Array<SkyboxCacheEntry>;
79
- }
80
-
81
- function tryGetPreviouslyLoadedTexture(src: string) {
82
- const cache = ensureGlobalCache();
83
- const found = cache.find(x => x.src === src);
84
- if (found) {
85
- if (debug) console.log("Skybox: Found previously loaded texture for " + src);
86
- return found.texture;
87
- }
88
- return null;
89
- }
90
- async function disposeCachedTexture(tex: Promise<Texture | null>) {
91
- const texture = await tex;
92
- if (!texture) return;
93
- setDisposable(texture, true);
94
- disposeObjectResources(texture);
95
- }
96
- function registerLoadedTexture(src: string, texture: Promise<Texture | null>) {
97
- const cache = ensureGlobalCache();
98
- // Make sure the cache doesnt get too big
99
- while (cache.length > 5) {
100
- const entry = cache.shift();
101
- if (entry) { disposeCachedTexture(entry.texture); }
102
- }
103
- texture.then(t => { return setDisposable(t, false) });
104
- cache.push({ src, texture });
105
- }
106
-
107
75
 
108
76
  /**
109
77
  * RemoteSkybox is a component that allows you to set the skybox of a scene from a URL or a local file.
@@ -177,7 +145,6 @@ export class RemoteSkybox extends Behaviour {
177
145
  @serializable()
178
146
  allowNetworking: boolean = true;
179
147
 
180
- private _loader?: RGBELoader | EXRLoader | TextureLoader | KTX2Loader;
181
148
  private _prevUrl?: string;
182
149
  private _prevLoadedEnvironment?: Texture;
183
150
  private _prevEnvironment: Texture | null = null;
@@ -245,7 +212,7 @@ export class RemoteSkybox extends Behaviour {
245
212
  }
246
213
  this._prevUrl = url;
247
214
 
248
- const texture = await this.loadTexture(url, name);
215
+ const texture = await loadPMREM(url, this.context.renderer);
249
216
  if (!texture) {
250
217
  if (debug) console.warn("RemoteSkybox: Failed to load texture from url", url);
251
218
  return false;
@@ -262,62 +229,18 @@ export class RemoteSkybox extends Behaviour {
262
229
  }
263
230
  // Update the current url
264
231
  this.url = url;
265
- const nameIndex = url.lastIndexOf("/");
266
- texture.name = url.substring(nameIndex >= 0 ? nameIndex + 1 : 0);
267
- if (this._loader instanceof TextureLoader) {
268
- texture.colorSpace = SRGBColorSpace;
269
- }
270
232
  this._prevLoadedEnvironment = texture;
271
233
  this.apply();
272
234
  return true;
273
235
  }
274
236
 
275
- private async loadTexture(url: string, name?: string) {
276
- if (!url) return Promise.resolve(null);
277
- name ??= url;
278
- const cached = tryGetPreviouslyLoadedTexture(name);
279
- if (cached) {
280
- const res = await cached;
281
- if (res?.source?.data?.length > 0 || res?.source?.data?.data?.length) return res;
282
- }
283
- const isEXR = name.endsWith(".exr");
284
- const isHdr = name.endsWith(".hdr");
285
- const isKtx2 = name.endsWith(".ktx2");
286
- if (isEXR) {
287
- if (!(this._loader instanceof EXRLoader))
288
- this._loader = new EXRLoader();
289
- }
290
- else if (isHdr) {
291
- if (!(this._loader instanceof RGBELoader))
292
- this._loader = new RGBELoader();
293
- }
294
- else if (isKtx2) {
295
- if (!(this._loader instanceof KTX2Loader)) {
296
- const { ktx2Loader } = createLoaders(this.context.renderer);
297
- this._loader = ktx2Loader;
298
- }
299
- }
300
- else {
301
- if (!(this._loader instanceof TextureLoader))
302
- this._loader = new TextureLoader();
303
- }
304
-
305
- if (debug) console.log("Loading skybox: " + url);
306
- const loadingTask = this._loader.loadAsync(url).catch(_err => {
307
- console.warn("RemoteSkybox: Failed to load texture from url:", url);
308
- return null;
309
- });
310
- registerLoadedTexture(name, loadingTask);
311
- const envMap = await loadingTask;
312
- return envMap;
313
- }
314
237
 
315
238
  private apply() {
316
239
  const envMap = this._prevLoadedEnvironment;
317
240
  if (!envMap) return;
318
241
 
319
242
 
320
- if ((envMap instanceof CubeTexture || envMap instanceof CompressedCubeTexture)) {
243
+ if ((envMap instanceof CubeTexture || envMap instanceof CompressedCubeTexture) || envMap.mapping == CubeUVReflectionMapping) {
321
244
  // Nothing to do
322
245
  }
323
246
  else {
@@ -437,13 +360,14 @@ export class RemoteSkybox extends Behaviour {
437
360
  console.warn(`[RemoteSkybox]: File \"${file.name}\" is not supported. Supported files are ${this.validTextureTypes.join(", ")}`);
438
361
  return;
439
362
  }
440
- if (tryGetPreviouslyLoadedTexture(file.name) === null) {
441
- const blob = new Blob([file]);
442
- const url = URL.createObjectURL(blob);
443
- e.preventDefault();
444
- this.setSkybox(url, file.name);
445
- }
446
- else {
363
+ // if (tryGetPreviouslyLoadedTexture(file.name) === null) {
364
+ // const blob = new Blob([file]);
365
+ // const url = URL.createObjectURL(blob);
366
+ // e.preventDefault();
367
+ // this.setSkybox(url, file.name);
368
+ // }
369
+ // else
370
+ {
447
371
  e.preventDefault();
448
372
  this.setSkybox(file.name);
449
373
  }
@@ -458,31 +382,35 @@ export class RemoteSkybox extends Behaviour {
458
382
 
459
383
  function tryParseMagicSkyboxName(str: string | null | undefined, environment: boolean, background: boolean): string | null {
460
384
 
385
+ if (str === null || str === undefined) return null;
386
+
461
387
  const useLowRes = environment && !background;
462
388
 
463
- switch (str?.toLowerCase()) {
389
+ switch (str.toLowerCase()) {
464
390
  case "studio":
465
391
  if (useLowRes) {
466
- return "https://cdn.needle.tools/static/skybox/modelviewer-Neutral-small.hdr";
392
+ return "https://cdn.needle.tools/static/skybox/modelviewer-Neutral-small.pmrem4x4.ktx2?pmrem";
467
393
  }
468
- else return "https://cdn.needle.tools/static/skybox/modelviewer-Neutral.hdr";
394
+ return "https://cdn.needle.tools/static/skybox/modelviewer-Neutral.pmrem4x4.ktx2?pmrem";
469
395
 
470
396
  case "blurred-skybox":
471
397
  if (useLowRes) {
472
- return "https://cdn.needle.tools/static/skybox/blurred-skybox-small.exr";
398
+ return "https://cdn.needle.tools/static/skybox/blurred-skybox-small.pmrem4x4.ktx2?pmrem";
473
399
  }
474
- return "https://cdn.needle.tools/static/skybox/blurred-skybox.exr";
400
+ return "https://cdn.needle.tools/static/skybox/blurred-skybox.pmrem4x4.ktx2?pmrem";
475
401
  case "quicklook-ar":
476
402
  if (useLowRes) {
477
- return "https://cdn.needle.tools/static/skybox/QuickLook-ARMode-small.exr";
403
+ return "https://cdn.needle.tools/static/skybox/QuickLook-ARMode-small.pmrem4x4.ktx2?pmrem";
478
404
  }
479
- return "https://cdn.needle.tools/static/skybox/QuickLook-ARMode.exr";
405
+ return "https://cdn.needle.tools/static/skybox/QuickLook-ARMode.pmrem4x4.ktx2?pmrem";
480
406
  case "quicklook":
481
407
  if (useLowRes) {
482
- return "https://cdn.needle.tools/static/skybox/QuickLook-ObjectMode-small.exr";
408
+ return "https://cdn.needle.tools/static/skybox/QuickLook-ObjectMode-small.pmrem4x4.ktx2?pmrem";
483
409
  }
484
- return "https://cdn.needle.tools/static/skybox/QuickLook-ObjectMode.exr";
410
+ return "https://cdn.needle.tools/static/skybox/QuickLook-ObjectMode.pmrem4x4.ktx2?pmrem";
485
411
  }
486
- if (str === undefined) return null;
412
+
413
+
414
+
487
415
  return str;
488
416
  }
@@ -129,13 +129,22 @@ export class TransformGizmo extends Behaviour {
129
129
  private onControlChangedEvent = (event) => {
130
130
  const orbit = this.orbit;
131
131
  if (orbit) orbit.enabled = !event.value;
132
+
132
133
  if (event.value) {
133
- // request ownership on drag start
134
- const sync = GameObject.getComponentInParent(this.gameObject, SyncedTransform);
134
+ // Start of interaction
135
+ const sync = this.gameObject.getComponentInParent(SyncedTransform);
135
136
  if (sync) {
137
+ sync.fastMode = true;
136
138
  sync.requestOwnership();
137
139
  }
138
140
  }
141
+ else {
142
+ // End of interaction
143
+ const sync = this.gameObject.getComponentInParent(SyncedTransform);
144
+ if (sync) {
145
+ sync.fastMode = false;
146
+ }
147
+ }
139
148
  }
140
149
 
141
150
  /**