@needle-tools/engine 5.1.0-next.1780579670.dramatic-pierogi.b8fa941 → 5.1.0
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.
- package/CHANGELOG.md +119 -0
- package/components.needle.json +1 -1
- package/dist/gltf-progressive-BGQ-cjly.umd.cjs +10 -0
- package/dist/gltf-progressive-BvQHrERj.min.js +10 -0
- package/dist/{gltf-progressive-CH7aqHHr.js → gltf-progressive-CtYnVfLc.js} +420 -351
- package/dist/{materialx-B85WjP7E.min.js → materialx-B5gKeM65.min.js} +1 -1
- package/dist/{materialx-B47Bz-xs.umd.cjs → materialx-CFRcCYWy.umd.cjs} +1 -1
- package/dist/{materialx-jkHmVPez.js → materialx-DqGmteNO.js} +1 -1
- package/dist/{needle-engine.bundle-WQeWxmZD.js → needle-engine.bundle-5oIL49YP.js} +6826 -6685
- package/dist/{needle-engine.bundle-BSPk6RkD.min.js → needle-engine.bundle-DAAfj8Cs.min.js} +161 -149
- package/dist/{needle-engine.bundle-DCPHDPAd.umd.cjs → needle-engine.bundle-DvOrfXws.umd.cjs} +159 -147
- package/dist/needle-engine.d.ts +106 -44
- package/dist/needle-engine.js +610 -608
- package/dist/needle-engine.min.js +1 -1
- package/dist/needle-engine.umd.cjs +1 -1
- package/dist/three.js +3 -0
- package/dist/three.min.js +22 -22
- package/dist/three.umd.cjs +24 -24
- package/dist/{vendor-BQ2Vuntm.min.js → vendor-2--bD1c9.min.js} +19 -19
- package/dist/{vendor-BslSKZPo.js → vendor-BNtcIZkv.js} +1141 -1138
- package/dist/{vendor-1UvpPPSB.umd.cjs → vendor-vbe9sv1t.umd.cjs} +20 -20
- package/lib/engine/api.d.ts +1 -1
- package/lib/engine/api.js +1 -1
- package/lib/engine/api.js.map +1 -1
- package/lib/engine/engine_context.js +16 -3
- package/lib/engine/engine_context.js.map +1 -1
- package/lib/engine/engine_init.js +2 -2
- package/lib/engine/engine_init.js.map +1 -1
- package/lib/engine/engine_license.d.ts +7 -7
- package/lib/engine/engine_license.js +72 -72
- package/lib/engine/engine_license.js.map +1 -1
- package/lib/engine/engine_networking_blob.js +3 -3
- package/lib/engine/engine_networking_blob.js.map +1 -1
- package/lib/engine/engine_utils_qrcode.js +2 -2
- package/lib/engine/engine_utils_qrcode.js.map +1 -1
- package/lib/engine/postprocessing/postprocessing.d.ts +2 -1
- package/lib/engine/postprocessing/postprocessing.js +3 -2
- package/lib/engine/postprocessing/postprocessing.js.map +1 -1
- package/lib/engine/webcomponents/needle menu/needle-menu-spatial.js +2 -2
- package/lib/engine/webcomponents/needle menu/needle-menu.d.ts +2 -2
- package/lib/engine/webcomponents/needle menu/needle-menu.js +9 -9
- package/lib/engine/webcomponents/needle menu/needle-menu.js.map +1 -1
- package/lib/engine/webcomponents/needle-engine.js +2 -2
- package/lib/engine/webcomponents/needle-engine.js.map +1 -1
- package/lib/engine/webcomponents/needle-engine.loading.js +2 -2
- package/lib/engine/webcomponents/needle-engine.loading.js.map +1 -1
- package/lib/engine/xr/NeedleXRSession.d.ts +24 -0
- package/lib/engine/xr/NeedleXRSession.js +52 -7
- package/lib/engine/xr/NeedleXRSession.js.map +1 -1
- package/lib/engine/xr/TempXRContext.js +2 -2
- package/lib/engine/xr/TempXRContext.js.map +1 -1
- package/lib/engine/xr/init.js +2 -3
- package/lib/engine/xr/init.js.map +1 -1
- package/lib/engine-components/Component.js +14 -1
- package/lib/engine-components/Component.js.map +1 -1
- package/lib/engine-components/ContactShadows.d.ts +7 -2
- package/lib/engine-components/ContactShadows.js +65 -45
- package/lib/engine-components/ContactShadows.js.map +1 -1
- package/lib/engine-components/GroundProjection.d.ts +6 -0
- package/lib/engine-components/GroundProjection.js +69 -7
- package/lib/engine-components/GroundProjection.js.map +1 -1
- package/lib/engine-components/NeedleMenu.d.ts +1 -1
- package/lib/engine-components/NeedleMenu.js +5 -2
- package/lib/engine-components/NeedleMenu.js.map +1 -1
- package/lib/engine-components/SceneSwitcher.d.ts +2 -0
- package/lib/engine-components/SceneSwitcher.js +26 -14
- package/lib/engine-components/SceneSwitcher.js.map +1 -1
- package/lib/engine-components/SeeThrough.d.ts +39 -0
- package/lib/engine-components/SeeThrough.js +69 -1
- package/lib/engine-components/SeeThrough.js.map +1 -1
- package/lib/engine-components/Skybox.js +9 -0
- package/lib/engine-components/Skybox.js.map +1 -1
- package/lib/engine-components/api.d.ts +1 -0
- package/lib/engine-components/api.js +1 -0
- package/lib/engine-components/api.js.map +1 -1
- package/lib/engine-components/export/usdz/USDZExporter.js +4 -4
- package/lib/engine-components/export/usdz/USDZExporter.js.map +1 -1
- package/lib/engine-components/export/usdz/extensions/behavior/BehaviourComponents.js +9 -5
- package/lib/engine-components/export/usdz/extensions/behavior/BehaviourComponents.js.map +1 -1
- package/lib/engine-components/particlesystem/ParticleSystem.js +4 -0
- package/lib/engine-components/particlesystem/ParticleSystem.js.map +1 -1
- package/lib/engine-components/postprocessing/Volume.d.ts +1 -9
- package/lib/engine-components/postprocessing/Volume.js +2 -14
- package/lib/engine-components/postprocessing/Volume.js.map +1 -1
- package/lib/engine-components/webxr/WebXRImageTracking.js +3 -0
- package/lib/engine-components/webxr/WebXRImageTracking.js.map +1 -1
- package/package.json +4 -4
- package/plugins/common/license.js +4 -4
- package/plugins/types/userconfig.d.ts +10 -0
- package/plugins/vite/build-pipeline.d.ts +19 -0
- package/plugins/vite/build-pipeline.js +67 -5
- package/plugins/vite/license.js +4 -4
- package/src/engine/api.ts +1 -1
- package/src/engine/engine_context.ts +15 -2
- package/src/engine/engine_gameobject.ts +1 -1
- package/src/engine/engine_init.ts +2 -2
- package/src/engine/engine_license.ts +69 -69
- package/src/engine/engine_networking_blob.ts +3 -3
- package/src/engine/engine_utils_qrcode.ts +2 -2
- package/src/engine/postprocessing/postprocessing.ts +3 -2
- package/src/engine/webcomponents/needle menu/needle-menu-spatial.ts +2 -2
- package/src/engine/webcomponents/needle menu/needle-menu.ts +10 -10
- package/src/engine/webcomponents/needle-engine.loading.ts +6 -6
- package/src/engine/webcomponents/needle-engine.ts +2 -2
- package/src/engine/xr/NeedleXRSession.ts +53 -7
- package/src/engine/xr/TempXRContext.ts +2 -2
- package/src/engine/xr/init.ts +2 -3
- package/src/engine-components/Component.ts +13 -1
- package/src/engine-components/ContactShadows.ts +75 -49
- package/src/engine-components/GroundProjection.ts +71 -7
- package/src/engine-components/NeedleMenu.ts +6 -3
- package/src/engine-components/SceneSwitcher.ts +23 -13
- package/src/engine-components/SeeThrough.ts +75 -1
- package/src/engine-components/Skybox.ts +8 -0
- package/src/engine-components/api.ts +1 -0
- package/src/engine-components/export/usdz/USDZExporter.ts +4 -4
- package/src/engine-components/export/usdz/extensions/behavior/BehaviourComponents.ts +12 -5
- package/src/engine-components/particlesystem/ParticleSystem.ts +4 -0
- package/src/engine-components/postprocessing/Volume.ts +2 -13
- package/src/engine-components/webxr/WebXRImageTracking.ts +1 -0
- package/dist/gltf-progressive-9tlS5iCx.umd.cjs +0 -10
- package/dist/gltf-progressive-CVg80COO.min.js +0 -10
package/src/engine/xr/init.ts
CHANGED
|
@@ -12,9 +12,8 @@ export function initXR() {
|
|
|
12
12
|
|
|
13
13
|
if (DeviceUtilities.isiOS()) {
|
|
14
14
|
|
|
15
|
-
// Prefetch
|
|
16
|
-
const url =
|
|
17
|
-
url.searchParams.set("url", location.href);
|
|
15
|
+
// Prefetch (uses the custom branded AppClip experience if NeedleXRSession.appClipUrl is set, otherwise the default Needle AppClip)
|
|
16
|
+
const url = NeedleXRSession.getAppClipLaunchUrl();
|
|
18
17
|
const urlStr = url.toString();
|
|
19
18
|
fetch(urlStr, { method: "HEAD", mode: "no-cors" }).catch(() => {
|
|
20
19
|
// appclip prefetch - to get metadata faster on iOS devices, this seems to fix the double tap issue when opening the appclip for AR sessions.
|
|
@@ -1107,7 +1107,8 @@ export abstract class Component implements IComponent, EventTarget,
|
|
|
1107
1107
|
this.__didCompleteStart = false;
|
|
1108
1108
|
this.__inEnableOrDisableCallback = false;
|
|
1109
1109
|
this.__didEnable = false;
|
|
1110
|
-
|
|
1110
|
+
// Preserve copied/constructor-assigned enabled state while resetting lifecycle flags.
|
|
1111
|
+
// Cloned disabled components must not fall back to the default enabled=true state.
|
|
1111
1112
|
this.__destroyed = false;
|
|
1112
1113
|
this._internalInit(init);
|
|
1113
1114
|
return this;
|
|
@@ -1165,6 +1166,17 @@ export abstract class Component implements IComponent, EventTarget,
|
|
|
1165
1166
|
}
|
|
1166
1167
|
this.__didEnable = true;
|
|
1167
1168
|
this.__isEnabled = true;
|
|
1169
|
+
// Enforce "enabled + not-yet-started ⇒ queued for start". A component can be
|
|
1170
|
+
// re-enabled before it ever started — e.g. removed from the scene (which
|
|
1171
|
+
// strips it from new_script_start via processRemoveFromScene) and then
|
|
1172
|
+
// re-activated through a path other than GameObject.add (raw scene re-add +
|
|
1173
|
+
// setActive, a parent activeInHierarchy flip, …). Those paths re-run onEnable
|
|
1174
|
+
// but never re-enqueue start, leaving the component enabled+active but with
|
|
1175
|
+
// start() never called. Re-queue it here so processStart picks it up.
|
|
1176
|
+
if (!this.__didStart) {
|
|
1177
|
+
const startQueue = this.context?.new_script_start;
|
|
1178
|
+
if (startQueue && !startQueue.includes(this)) startQueue.push(this);
|
|
1179
|
+
}
|
|
1168
1180
|
this.__inEnableOrDisableCallback = true;
|
|
1169
1181
|
this.onEnable();
|
|
1170
1182
|
this.__inEnableOrDisableCallback = false;
|
|
@@ -83,7 +83,7 @@ type FitParameters = {
|
|
|
83
83
|
*/
|
|
84
84
|
export class ContactShadows extends Behaviour {
|
|
85
85
|
|
|
86
|
-
private static readonly _instances:
|
|
86
|
+
private static readonly _instances: WeakMap<Context, ContactShadows> = new WeakMap();
|
|
87
87
|
/**
|
|
88
88
|
* Create contact shadows for the scene. Automatically fits the shadows to the scene.
|
|
89
89
|
* The instance of contact shadows will be created only once.
|
|
@@ -179,8 +179,9 @@ export class ContactShadows extends Behaviour {
|
|
|
179
179
|
* in which case ContactShadows scale would affect the projection
|
|
180
180
|
**/
|
|
181
181
|
private readonly shadowsRoot: IGameObject = new Object3D() as IGameObject;
|
|
182
|
-
private shadowCamera?: OrthographicCamera;
|
|
183
182
|
private readonly shadowGroup: Group = new Group();
|
|
183
|
+
|
|
184
|
+
private shadowCamera?: OrthographicCamera;
|
|
184
185
|
|
|
185
186
|
private renderTarget?: WebGLRenderTarget;
|
|
186
187
|
private renderTargetBlur?: WebGLRenderTarget;
|
|
@@ -240,19 +241,86 @@ export class ContactShadows extends Behaviour {
|
|
|
240
241
|
if (debug) console.log("Fitted shadows to scene", this.shadowsRoot.scale.clone());
|
|
241
242
|
}
|
|
242
243
|
|
|
244
|
+
/**
|
|
245
|
+
* Call to update the shadows immediately. Only needed if `manualUpdate` is enabled, usually the shadows will update automatically when the scene changes or when the component is enabled.
|
|
246
|
+
*/
|
|
247
|
+
renderShadowsNow() {
|
|
248
|
+
this._renderShadows();
|
|
249
|
+
}
|
|
243
250
|
|
|
244
251
|
/** @internal */
|
|
245
252
|
awake() {
|
|
246
253
|
ContactShadows._instances.set(this.context, this);
|
|
247
|
-
|
|
254
|
+
}
|
|
248
255
|
|
|
256
|
+
/** @internal */
|
|
257
|
+
onEnable(): void {
|
|
258
|
+
this.shadowsRoot.hideFlags = HideFlags.DontExport;
|
|
249
259
|
// ignore self for autofitting
|
|
250
260
|
setAutoFitEnabled(this.shadowsRoot, false);
|
|
261
|
+
|
|
262
|
+
this._needsUpdate = true;
|
|
263
|
+
this.autoCleanup(this.context.events.on("scene-content-changed", () => {
|
|
264
|
+
if (!this.autoFit) return;
|
|
265
|
+
this._needsFit = true;
|
|
266
|
+
this._needsUpdate = true;
|
|
267
|
+
}));
|
|
268
|
+
|
|
269
|
+
if (this.autoFit) this.fitShadows();
|
|
270
|
+
else this.applyMinSize();
|
|
251
271
|
}
|
|
252
272
|
|
|
273
|
+
/** @internal */
|
|
274
|
+
onDestroy(): void {
|
|
275
|
+
if (debug) console.warn("Destroying ContactShadows on " + this.gameObject.name);
|
|
276
|
+
|
|
277
|
+
const instance = ContactShadows._instances.get(this.context);
|
|
278
|
+
if (instance === this) {
|
|
279
|
+
ContactShadows._instances.delete(this.context);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// dispose the render targets
|
|
283
|
+
this.renderTarget?.dispose();
|
|
284
|
+
this.renderTargetBlur?.dispose();
|
|
285
|
+
|
|
286
|
+
// dispose the materials
|
|
287
|
+
this.depthMaterial?.dispose();
|
|
288
|
+
this.horizontalBlurMaterial?.dispose();
|
|
289
|
+
this.verticalBlurMaterial?.dispose();
|
|
290
|
+
|
|
291
|
+
// dispose the geometries
|
|
292
|
+
this.blurPlane?.geometry.dispose();
|
|
293
|
+
this.plane?.geometry.dispose();
|
|
294
|
+
this.occluderMesh?.geometry.dispose();
|
|
295
|
+
}
|
|
253
296
|
|
|
254
297
|
/** @internal */
|
|
255
|
-
|
|
298
|
+
onBeforeRender(_frame: XRFrame | null): void {
|
|
299
|
+
|
|
300
|
+
if (this._needsFit && this.autoFit) {
|
|
301
|
+
this._needsFit = false;
|
|
302
|
+
this.fitShadows();
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
if (this.manualUpdate) {
|
|
306
|
+
if (!this._needsUpdate) return;
|
|
307
|
+
}
|
|
308
|
+
this._needsUpdate = false;
|
|
309
|
+
this._renderShadows();
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
private _ensureSetup() {
|
|
313
|
+
|
|
314
|
+
// Make sure the shadows root is parented to the game object. This is needed for the shadows to be in the correct position and scale relative to the game object. We need to do this before checking if the materials are set up because some properties (e.g. darkness) can be changed in the editor before the component is enabled, so we want to avoid re-creating everything in that case.
|
|
315
|
+
if (this.shadowsRoot.parent !== this.gameObject) {
|
|
316
|
+
this.gameObject.add(this.shadowsRoot);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// Check if everything is already set up. We need to do this because some properties (e.g. darkness) can be changed in the editor before the component is enabled, so we want to avoid re-creating everything in that case.
|
|
320
|
+
if (this.depthMaterial && this.horizontalBlurMaterial && this.verticalBlurMaterial && this.planeMaterial) {
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
|
|
256
324
|
if (debug) console.log("Create ContactShadows on " + this.gameObject.name, this)
|
|
257
325
|
|
|
258
326
|
this.gameObject.add(this.shadowsRoot);
|
|
@@ -363,54 +431,11 @@ export class ContactShadows extends Behaviour {
|
|
|
363
431
|
this.verticalBlurMaterial.depthTest = false;
|
|
364
432
|
|
|
365
433
|
this.shadowGroup.visible = false;
|
|
366
|
-
|
|
367
|
-
if (this.autoFit) this.fitShadows();
|
|
368
|
-
else this.applyMinSize();
|
|
369
434
|
}
|
|
370
435
|
|
|
371
|
-
|
|
372
|
-
this._needsUpdate = true;
|
|
373
|
-
this.autoCleanup(this.context.events.on("scene-content-changed", () => {
|
|
374
|
-
if (!this.autoFit) return;
|
|
375
|
-
this._needsFit = true;
|
|
376
|
-
this._needsUpdate = true;
|
|
377
|
-
}));
|
|
378
|
-
}
|
|
436
|
+
private _renderShadows() {
|
|
379
437
|
|
|
380
|
-
|
|
381
|
-
onDestroy(): void {
|
|
382
|
-
const instance = ContactShadows._instances.get(this.context);
|
|
383
|
-
if (instance === this) {
|
|
384
|
-
ContactShadows._instances.delete(this.context);
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
// dispose the render targets
|
|
388
|
-
this.renderTarget?.dispose();
|
|
389
|
-
this.renderTargetBlur?.dispose();
|
|
390
|
-
|
|
391
|
-
// dispose the materials
|
|
392
|
-
this.depthMaterial?.dispose();
|
|
393
|
-
this.horizontalBlurMaterial?.dispose();
|
|
394
|
-
this.verticalBlurMaterial?.dispose();
|
|
395
|
-
|
|
396
|
-
// dispose the geometries
|
|
397
|
-
this.blurPlane?.geometry.dispose();
|
|
398
|
-
this.plane?.geometry.dispose();
|
|
399
|
-
this.occluderMesh?.geometry.dispose();
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
/** @internal */
|
|
403
|
-
onBeforeRender(_frame: XRFrame | null): void {
|
|
404
|
-
|
|
405
|
-
if (this._needsFit && this.autoFit) {
|
|
406
|
-
this._needsFit = false;
|
|
407
|
-
this.fitShadows();
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
if (this.manualUpdate) {
|
|
411
|
-
if (!this._needsUpdate) return;
|
|
412
|
-
}
|
|
413
|
-
this._needsUpdate = false;
|
|
438
|
+
this._ensureSetup();
|
|
414
439
|
|
|
415
440
|
if (!this.renderTarget || !this.renderTargetBlur ||
|
|
416
441
|
!this.depthMaterial || !this.shadowCamera ||
|
|
@@ -536,6 +561,7 @@ export class ContactShadows extends Behaviour {
|
|
|
536
561
|
scene.background = initialBackground;
|
|
537
562
|
renderer.xr.enabled = prevXRState;
|
|
538
563
|
this.context.scene.matrixWorldAutoUpdate = prevSceneMatrixAutoUpdate;
|
|
564
|
+
|
|
539
565
|
}
|
|
540
566
|
|
|
541
567
|
// renderTarget --> blurPlane (horizontalBlur) --> renderTargetBlur --> blurPlane (verticalBlur) --> renderTarget
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CubeUVReflectionMapping, ShaderMaterial, Texture } from "three";
|
|
1
|
+
import { CubeUVReflectionMapping, Euler, Matrix3, Matrix4, ShaderMaterial, Texture } from "three";
|
|
2
2
|
import { GroundedSkybox as GroundProjection } from 'three/examples/jsm/objects/GroundedSkybox.js';
|
|
3
3
|
|
|
4
4
|
import { Gizmos } from "../engine/engine_gizmos.js";
|
|
@@ -21,8 +21,13 @@ type GroundProjectionShaderUniforms = {
|
|
|
21
21
|
needleGroundProjectionBlending: { value: number };
|
|
22
22
|
needleGroundProjectionAlphaFactor: { value: number };
|
|
23
23
|
needleGroundProjectionBackgroundIntensity: { value: number };
|
|
24
|
+
needleGroundProjectionBackgroundRotation: { value: Matrix3 };
|
|
24
25
|
};
|
|
25
26
|
|
|
27
|
+
// Reused per-frame to build the background-rotation matrix without allocating in onBeforeRender.
|
|
28
|
+
const _backgroundRotationEuler = new Euler();
|
|
29
|
+
const _backgroundRotationMatrix = new Matrix4();
|
|
30
|
+
|
|
26
31
|
const needleCubeUvMapVarying = /* glsl */`
|
|
27
32
|
#ifdef NEEDLE_USE_CUBE_UV_MAP
|
|
28
33
|
varying vec3 vNeedleGroundProjectionWorldDirection;
|
|
@@ -79,7 +84,10 @@ const needleCubeUvMapFragment = /* glsl */`
|
|
|
79
84
|
|
|
80
85
|
#endif
|
|
81
86
|
|
|
82
|
-
|
|
87
|
+
// Apply backgroundIntensity uniformly across the whole projection (center included), matching how the
|
|
88
|
+
// skybox applies it (backgroundCube.glsl: texColor.rgb *= backgroundIntensity). Only the blurriness
|
|
89
|
+
// ramps toward the horizon - the intensity must not.
|
|
90
|
+
sampledDiffuseColor.rgb *= needleGroundProjectionBackgroundIntensity;
|
|
83
91
|
diffuseColor *= sampledDiffuseColor;
|
|
84
92
|
|
|
85
93
|
#endif
|
|
@@ -122,7 +130,8 @@ function getGroundProjectionShaderUniforms(material: GroundProjectionMaterial):
|
|
|
122
130
|
needleGroundProjectionBlurriness: { value: 0 },
|
|
123
131
|
needleGroundProjectionBlending: { value: 0 },
|
|
124
132
|
needleGroundProjectionAlphaFactor: { value: 1 },
|
|
125
|
-
needleGroundProjectionBackgroundIntensity: { value: 1 }
|
|
133
|
+
needleGroundProjectionBackgroundIntensity: { value: 1 },
|
|
134
|
+
needleGroundProjectionBackgroundRotation: { value: new Matrix3() }
|
|
126
135
|
};
|
|
127
136
|
}
|
|
128
137
|
|
|
@@ -133,15 +142,25 @@ function configureGroundProjectionMaterial(material: GroundProjectionMaterial, t
|
|
|
133
142
|
shader.uniforms.needleGroundProjectionBlending = projectionUniforms.needleGroundProjectionBlending;
|
|
134
143
|
shader.uniforms.needleGroundProjectionAlphaFactor = projectionUniforms.needleGroundProjectionAlphaFactor;
|
|
135
144
|
shader.uniforms.needleGroundProjectionBackgroundIntensity = projectionUniforms.needleGroundProjectionBackgroundIntensity;
|
|
145
|
+
shader.uniforms.needleGroundProjectionBackgroundRotation = projectionUniforms.needleGroundProjectionBackgroundRotation;
|
|
136
146
|
|
|
137
147
|
shader.vertexShader = shader.vertexShader
|
|
138
|
-
.replace("#include <uv_pars_vertex>", `#include <uv_pars_vertex>\n${needleCubeUvMapVarying}
|
|
148
|
+
.replace("#include <uv_pars_vertex>", `#include <uv_pars_vertex>\n${needleCubeUvMapVarying}
|
|
149
|
+
#ifdef NEEDLE_USE_CUBE_UV_MAP
|
|
150
|
+
uniform mat3 needleGroundProjectionBackgroundRotation;
|
|
151
|
+
#endif`)
|
|
139
152
|
.replace(
|
|
140
153
|
"#include <worldpos_vertex>",
|
|
141
154
|
`#include <worldpos_vertex>
|
|
142
155
|
#ifdef NEEDLE_USE_CUBE_UV_MAP
|
|
143
|
-
//
|
|
144
|
-
|
|
156
|
+
// Sample the environment in the fragment's actual world direction. GroundedSkybox mirrors the geometry
|
|
157
|
+
// on Z (geometry.scale(1,1,-1)) and that mirrored vertex IS the on-screen direction; the skybox and
|
|
158
|
+
// environment lighting are likewise sampled in true world directions. So we must NOT undo the mirror
|
|
159
|
+
// here - doing so reflects the sampling azimuth and flips the panorama left-right vs. the skybox.
|
|
160
|
+
// backgroundRotation is applied to the sampling direction (exactly like the skybox does in
|
|
161
|
+
// backgroundCube.glsl), NOT by rotating the mesh - that keeps the floor flat and matches the skybox at
|
|
162
|
+
// the horizon for any background rotation.
|
|
163
|
+
vNeedleGroundProjectionWorldDirection = needleGroundProjectionBackgroundRotation * transformDirection( position, modelMatrix );
|
|
145
164
|
#endif`
|
|
146
165
|
);
|
|
147
166
|
|
|
@@ -271,6 +290,25 @@ export class GroundProjectedEnv extends Behaviour {
|
|
|
271
290
|
private _projection?: GroundProjection;
|
|
272
291
|
private _watcher?: Watch;
|
|
273
292
|
|
|
293
|
+
/** The scene's real backgroundBlurriness, stored while we temporarily override it for the render. */
|
|
294
|
+
private _savedBackgroundBlur: number | null = null;
|
|
295
|
+
|
|
296
|
+
/** pre_render_callback: force the skybox blur just before the render (after all component onBeforeRender). */
|
|
297
|
+
private readonly _forceBackgroundBlur = (_frame?: XRFrame | null) => {
|
|
298
|
+
if (this._savedBackgroundBlur !== null) return; // already overriding (safety)
|
|
299
|
+
if (!(this.scene.background instanceof Texture)) return; // no skybox to blur
|
|
300
|
+
const current = this.scene.backgroundBlurriness ?? 0;
|
|
301
|
+
this._savedBackgroundBlur = current;
|
|
302
|
+
this.scene.backgroundBlurriness = 1;
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
/** post_render_callback: restore the scene's real blur right after the render. */
|
|
306
|
+
private readonly _restoreBackgroundBlur = () => {
|
|
307
|
+
if (this._savedBackgroundBlur === null) return;
|
|
308
|
+
this.scene.backgroundBlurriness = this._savedBackgroundBlur;
|
|
309
|
+
this._savedBackgroundBlur = null;
|
|
310
|
+
};
|
|
311
|
+
|
|
274
312
|
|
|
275
313
|
/** @internal */
|
|
276
314
|
awake() {
|
|
@@ -291,11 +329,23 @@ export class GroundProjectedEnv extends Behaviour {
|
|
|
291
329
|
this._needsTextureUpdate = true;
|
|
292
330
|
});
|
|
293
331
|
}
|
|
332
|
+
// Force the skybox blur via the render-bracketing callbacks (these run after every component's
|
|
333
|
+
// onBeforeRender, so the override can't be clobbered by e.g. Camera.applyClearFlags).
|
|
334
|
+
if (this.context.pre_render_callbacks.indexOf(this._forceBackgroundBlur) < 0)
|
|
335
|
+
this.context.pre_render_callbacks.push(this._forceBackgroundBlur);
|
|
336
|
+
if (this.context.post_render_callbacks.indexOf(this._restoreBackgroundBlur) < 0)
|
|
337
|
+
this.context.post_render_callbacks.push(this._restoreBackgroundBlur);
|
|
294
338
|
}
|
|
295
339
|
/** @internal */
|
|
296
340
|
onDisable() {
|
|
297
341
|
this._watcher?.revoke();
|
|
298
342
|
this._projection?.removeFromParent();
|
|
343
|
+
const pre = this.context.pre_render_callbacks.indexOf(this._forceBackgroundBlur);
|
|
344
|
+
if (pre >= 0) this.context.pre_render_callbacks.splice(pre, 1);
|
|
345
|
+
const post = this.context.post_render_callbacks.indexOf(this._restoreBackgroundBlur);
|
|
346
|
+
if (post >= 0) this.context.post_render_callbacks.splice(post, 1);
|
|
347
|
+
// If we were disabled mid-frame (after force, before restore), put the real value back.
|
|
348
|
+
this._restoreBackgroundBlur();
|
|
299
349
|
}
|
|
300
350
|
/** @internal */
|
|
301
351
|
onEnterXR(): void {
|
|
@@ -312,7 +362,21 @@ export class GroundProjectedEnv extends Behaviour {
|
|
|
312
362
|
/** @internal */
|
|
313
363
|
onBeforeRender(): void {
|
|
314
364
|
if (this._projection && this.scene.backgroundRotation) {
|
|
315
|
-
|
|
365
|
+
// Mirror three's WebGLBackground: build the same mat3 it applies to the skybox sampling
|
|
366
|
+
// direction and feed it to our shader. This rotates the sampled environment exactly like the
|
|
367
|
+
// skybox (backgroundCube.glsl) instead of rotating the mesh - keeping the floor flat and the
|
|
368
|
+
// horizon aligned with the skybox for any background rotation.
|
|
369
|
+
_backgroundRotationEuler.copy(this.scene.backgroundRotation);
|
|
370
|
+
// accommodate left-handed frame (same as WebGLBackground.js)
|
|
371
|
+
_backgroundRotationEuler.x *= -1;
|
|
372
|
+
_backgroundRotationEuler.y *= -1;
|
|
373
|
+
_backgroundRotationEuler.z *= -1;
|
|
374
|
+
// Note: the extra cube-texture flip in WebGLBackground only applies to raw CubeTextures, not the
|
|
375
|
+
// PMREM render-target env this shader path samples - so it is intentionally omitted here.
|
|
376
|
+
const uniforms = getGroundProjectionShaderUniforms(this._projection.material);
|
|
377
|
+
uniforms.needleGroundProjectionBackgroundRotation.value.setFromMatrix4(
|
|
378
|
+
_backgroundRotationMatrix.makeRotationFromEuler(_backgroundRotationEuler)
|
|
379
|
+
);
|
|
316
380
|
}
|
|
317
381
|
|
|
318
382
|
if (this._projection && this.context.scene.background instanceof Texture) {
|
|
@@ -49,8 +49,8 @@ export class NeedleMenu extends Behaviour {
|
|
|
49
49
|
@serializable()
|
|
50
50
|
position: "top" | "bottom" = "bottom";
|
|
51
51
|
|
|
52
|
-
/**
|
|
53
|
-
* Controls the visibility of the Needle logo in the menu (requires
|
|
52
|
+
/**
|
|
53
|
+
* Controls the visibility of the Needle logo in the menu (requires a commercial license)
|
|
54
54
|
*/
|
|
55
55
|
@serializable()
|
|
56
56
|
showNeedleLogo: boolean = false;
|
|
@@ -102,7 +102,10 @@ export class NeedleMenu extends Behaviour {
|
|
|
102
102
|
this.context.menu.showFullscreenOption(true);
|
|
103
103
|
if (this.createMuteButton === true)
|
|
104
104
|
this.context.menu.showAudioPlaybackOption(true);
|
|
105
|
-
|
|
105
|
+
// Apply the value in BOTH directions: the spatial menu is enabled by
|
|
106
|
+
// default, so unchecking the option must explicitly disable it. Only skip
|
|
107
|
+
// when the field is unset (undefined) to keep the engine default.
|
|
108
|
+
if (this.showSpatialMenu !== undefined)
|
|
106
109
|
this.context.menu.showSpatialMenu(this.showSpatialMenu);
|
|
107
110
|
if (this.createQRCodeButton === true) {
|
|
108
111
|
if (!DeviceUtilities.isMobileDevice()) {
|
|
@@ -614,7 +614,8 @@ export class SceneSwitcher extends Behaviour {
|
|
|
614
614
|
this._currentScene = undefined;
|
|
615
615
|
if (current) {
|
|
616
616
|
if (debug) console.log("[SceneSwitcher] UNLOAD", current.url, "HasURL?: " + current.hasUrl)
|
|
617
|
-
const
|
|
617
|
+
const activeSceneInstance = this._currentSceneAsset;
|
|
618
|
+
const sceneListener = this.tryGetSceneEventListener(activeSceneInstance as any as Object3D);
|
|
618
619
|
if (sceneListener?.sceneClosing) {
|
|
619
620
|
const res = sceneListener.sceneClosing();
|
|
620
621
|
if (res instanceof Promise) await res;
|
|
@@ -624,20 +625,24 @@ export class SceneSwitcher extends Behaviour {
|
|
|
624
625
|
// // then we unload it
|
|
625
626
|
if (current.hasUrl) {
|
|
626
627
|
current.unload();
|
|
627
|
-
if (
|
|
628
|
+
if (activeSceneInstance) destroy(activeSceneInstance, true, false);
|
|
628
629
|
}
|
|
629
630
|
else {
|
|
630
|
-
GameObject.remove(
|
|
631
|
+
GameObject.remove(activeSceneInstance);
|
|
631
632
|
}
|
|
633
|
+
this._currentSceneAsset = undefined;
|
|
632
634
|
}
|
|
633
635
|
}
|
|
634
636
|
|
|
635
637
|
private _currentlyLoadingScene?: AssetReference;
|
|
638
|
+
private _loadingRequestId: number = 0;
|
|
639
|
+
private _activeLoadingRequestId: number = 0;
|
|
636
640
|
/** @internal */
|
|
637
641
|
private async __internalSwitchScene(scene: AssetReference): Promise<boolean> {
|
|
638
642
|
|
|
639
643
|
await this.__unloadCurrentScene();
|
|
640
644
|
const index = this._currentIndex = this.scenes?.indexOf(scene) ?? -1;
|
|
645
|
+
const loadingRequestId = ++this._loadingRequestId;
|
|
641
646
|
|
|
642
647
|
try {
|
|
643
648
|
if (debug) console.debug(`${Date.now()} [SceneSwitcher] Loading scene start: ${scene.url} (index: ${index})`);
|
|
@@ -647,9 +652,10 @@ export class SceneSwitcher extends Behaviour {
|
|
|
647
652
|
const loadStartEvt = new CustomEvent<LoadSceneEvent>("loadscene-start", { detail: { scene: scene, switcher: this, index: index } })
|
|
648
653
|
this.dispatchEvent(loadStartEvt);
|
|
649
654
|
this.sceneLoadingStart?.invoke(loadStartEvt.detail);
|
|
650
|
-
await this.onStartLoading();
|
|
655
|
+
await this.onStartLoading(loadingRequestId);
|
|
651
656
|
// start loading and wait for the scene to be loaded
|
|
652
657
|
await scene.loadAssetAsync((_, prog) => {
|
|
658
|
+
if (loadingRequestId !== this._activeLoadingRequestId) return;
|
|
653
659
|
if (debug) {
|
|
654
660
|
const t01 = prog.loaded / prog.total;
|
|
655
661
|
const progressBarString = "[" + "=".repeat(Math.floor(t01 * 20)) + "-".repeat(20 - Math.floor(t01 * 20)) + "]";
|
|
@@ -659,12 +665,14 @@ export class SceneSwitcher extends Behaviour {
|
|
|
659
665
|
this.dispatchEvent(prog);
|
|
660
666
|
this.sceneLoadingProgress?.invoke(prog);
|
|
661
667
|
}).catch(console.error);
|
|
662
|
-
await this.onEndLoading();
|
|
668
|
+
await this.onEndLoading(loadingRequestId);
|
|
663
669
|
const finishedEvt = new CustomEvent<LoadSceneEvent>("loadscene-finished", { detail: { scene: scene, switcher: this, index: index } });
|
|
664
670
|
this.dispatchEvent(finishedEvt);
|
|
665
671
|
|
|
666
|
-
this.
|
|
667
|
-
|
|
672
|
+
if (loadingRequestId === this._activeLoadingRequestId) {
|
|
673
|
+
this._currentLoadingProgress = undefined;
|
|
674
|
+
this._currentlyLoadingScene = undefined;
|
|
675
|
+
}
|
|
668
676
|
|
|
669
677
|
if (finishedEvt.defaultPrevented) {
|
|
670
678
|
if (debug) console.warn("[SceneSwitcher] Adding loaded scene prevented:", scene, finishedEvt);
|
|
@@ -737,8 +745,8 @@ export class SceneSwitcher extends Behaviour {
|
|
|
737
745
|
history.pushState(stateName, "unused", location.href);
|
|
738
746
|
}
|
|
739
747
|
}
|
|
740
|
-
// Call SceneListener opened callback
|
|
741
|
-
const sceneListener = this.tryGetSceneEventListener(
|
|
748
|
+
// Call SceneListener opened callback on the scene instance that was actually added.
|
|
749
|
+
const sceneListener = this.tryGetSceneEventListener(this._currentSceneAsset as any as Object3D);
|
|
742
750
|
if (sceneListener?.sceneOpened) {
|
|
743
751
|
const res = sceneListener.sceneOpened(this);
|
|
744
752
|
if (res instanceof Promise) await res;
|
|
@@ -826,7 +834,8 @@ export class SceneSwitcher extends Behaviour {
|
|
|
826
834
|
private _isCurrentlyLoading: boolean = false;
|
|
827
835
|
private _currentLoadingProgress: ProgressEvent | undefined = undefined;
|
|
828
836
|
|
|
829
|
-
private async onStartLoading() {
|
|
837
|
+
private async onStartLoading(loadingRequestId: number) {
|
|
838
|
+
this._activeLoadingRequestId = loadingRequestId;
|
|
830
839
|
this._isCurrentlyLoading = true;
|
|
831
840
|
if (this.loadingScene) {
|
|
832
841
|
// save the last loading scene reference so that it can be changed at runtime
|
|
@@ -839,7 +848,7 @@ export class SceneSwitcher extends Behaviour {
|
|
|
839
848
|
this._loadingScenePromise = this.loadingScene?.loadAssetAsync().then(res => res != null);
|
|
840
849
|
}
|
|
841
850
|
await this._loadingScenePromise;
|
|
842
|
-
if (this._isCurrentlyLoading && this.loadingScene?.asset) {
|
|
851
|
+
if (loadingRequestId === this._activeLoadingRequestId && this._isCurrentlyLoading && this.loadingScene?.asset) {
|
|
843
852
|
if (debug) console.log("Add loading scene", this.loadingScene.url, this.loadingScene.asset)
|
|
844
853
|
const loadingScene = this.loadingScene.asset as any as Object3D;
|
|
845
854
|
GameObject.add(loadingScene, this.gameObject);
|
|
@@ -851,7 +860,7 @@ export class SceneSwitcher extends Behaviour {
|
|
|
851
860
|
}
|
|
852
861
|
}
|
|
853
862
|
// Invoke the event on a loading listener on the same object as the scene switcher
|
|
854
|
-
if (this._isCurrentlyLoading) {
|
|
863
|
+
if (loadingRequestId === this._activeLoadingRequestId && this._isCurrentlyLoading) {
|
|
855
864
|
const listener = this.tryGetSceneEventListener(this.gameObject);
|
|
856
865
|
if (listener) {
|
|
857
866
|
if (listener.sceneOpened) {
|
|
@@ -861,7 +870,8 @@ export class SceneSwitcher extends Behaviour {
|
|
|
861
870
|
}
|
|
862
871
|
}
|
|
863
872
|
}
|
|
864
|
-
private async onEndLoading() {
|
|
873
|
+
private async onEndLoading(loadingRequestId: number) {
|
|
874
|
+
if (loadingRequestId !== this._activeLoadingRequestId) return;
|
|
865
875
|
this._isCurrentlyLoading = false;
|
|
866
876
|
if (this.loadingScene?.asset) {
|
|
867
877
|
if (debug) console.log("Remove loading scene", this.loadingScene.url);
|
|
@@ -11,7 +11,7 @@ import { Behaviour } from "./Component.js";
|
|
|
11
11
|
import { IUSDExporterExtension } from "./export/usdz/Extension.js";
|
|
12
12
|
import { USDZExporter } from "./export/usdz/USDZExporter.js";
|
|
13
13
|
import type { OrbitControls } from "./OrbitControls.js";
|
|
14
|
-
import { Renderer } from "./Renderer.js";
|
|
14
|
+
import { Renderer, ShadowCastingMode } from "./Renderer.js";
|
|
15
15
|
|
|
16
16
|
const debugSeeThrough = getParam("debugseethrough");
|
|
17
17
|
|
|
@@ -164,6 +164,35 @@ export class SeeThrough extends Behaviour {
|
|
|
164
164
|
@serializable()
|
|
165
165
|
autoUpdate: boolean = true;
|
|
166
166
|
|
|
167
|
+
/**
|
|
168
|
+
* The alpha (opacity) the object is currently faded to, in the `[0, 1]` range.
|
|
169
|
+
* `1` means fully visible, `0` means fully hidden. This is the live, animated value
|
|
170
|
+
* (it lerps toward the target over {@link fadeDuration}), updated every frame the fade runs.
|
|
171
|
+
*
|
|
172
|
+
* External components can read this to react to the see-through state — e.g. toggling a
|
|
173
|
+
* collider, muting audio, or driving a UI prompt as the object fades in or out.
|
|
174
|
+
*
|
|
175
|
+
* @example React to the object becoming (almost) hidden
|
|
176
|
+
* ```ts
|
|
177
|
+
* if (seeThrough.currentAlpha < 0.1) {
|
|
178
|
+
* // object is essentially see-through now
|
|
179
|
+
* }
|
|
180
|
+
* ```
|
|
181
|
+
* @default 1
|
|
182
|
+
*/
|
|
183
|
+
get currentAlpha(): number {
|
|
184
|
+
return this._currentAlpha;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Below this alpha the object stops casting a shadow. The shadow map only stores depth, so a
|
|
190
|
+
* faded object would otherwise keep casting a fully solid shadow (three.js does not fade shadows
|
|
191
|
+
* by opacity). Toggling shadow casting off near-invisible is the cleanest fix; we drive it via
|
|
192
|
+
* the {@link Renderer.shadowCastingMode} so it cooperates with the per-frame shadow settings the
|
|
193
|
+
* Renderer re-applies. Note this is a hard on/off, not a fade.
|
|
194
|
+
*/
|
|
195
|
+
public static readonly shadowHideThreshold = 0.05;
|
|
167
196
|
|
|
168
197
|
private readonly _referencePointVector: Vector3 = new Vector3();
|
|
169
198
|
private readonly _referencePointDir: Vector3 = new Vector3();
|
|
@@ -171,6 +200,9 @@ export class SeeThrough extends Behaviour {
|
|
|
171
200
|
private _renderer: Renderer[] | null = null;
|
|
172
201
|
private _needsUpdate = true;
|
|
173
202
|
private _id = i++;
|
|
203
|
+
private _currentAlpha: number = 1;
|
|
204
|
+
/** Renderers whose shadow casting we turned off while hidden, mapped to their original mode for restore. */
|
|
205
|
+
private readonly _suppressedShadowCasting = new Map<Renderer, ShadowCastingMode>();
|
|
174
206
|
|
|
175
207
|
/** * @internal */
|
|
176
208
|
onEnable() {
|
|
@@ -178,6 +210,12 @@ export class SeeThrough extends Behaviour {
|
|
|
178
210
|
this._renderer = null;
|
|
179
211
|
}
|
|
180
212
|
|
|
213
|
+
/** @internal */
|
|
214
|
+
onDestroy() {
|
|
215
|
+
// Don't leave renderers permanently non-casting if this component is removed.
|
|
216
|
+
this.restoreSuppressedShadowCasting();
|
|
217
|
+
}
|
|
218
|
+
|
|
181
219
|
/**
|
|
182
220
|
* @internal
|
|
183
221
|
*/
|
|
@@ -258,13 +296,49 @@ export class SeeThrough extends Behaviour {
|
|
|
258
296
|
if (newAlpha >= 0.99) newAlpha = 1;
|
|
259
297
|
else if (newAlpha <= 0.01) newAlpha = 0;
|
|
260
298
|
|
|
299
|
+
// Expose the live fade value so external components can react to it (see `currentAlpha`).
|
|
300
|
+
// Renderers are driven by the same target/duration, so the last applied value is representative.
|
|
301
|
+
this._currentAlpha = newAlpha;
|
|
302
|
+
|
|
261
303
|
// const currentTransparent = (block.getOverride("transparent")?.value ?? materials[0].transparent ?? false) as boolean;
|
|
262
304
|
|
|
263
305
|
block.setOverride("alphaHash", this.useAlphaHash);
|
|
264
306
|
block.setOverride("opacity", newAlpha);
|
|
265
307
|
block.setOverride("transparent", newAlpha >= 0.99999 ? false : !this.useAlphaHash);
|
|
308
|
+
|
|
309
|
+
// Stop casting a shadow once the object is essentially invisible (and restore it when it
|
|
310
|
+
// comes back). A faded object otherwise keeps a fully solid shadow — see shadowHideThreshold.
|
|
311
|
+
this.updateShadowCasting(renderer, newAlpha);
|
|
266
312
|
});
|
|
267
313
|
}
|
|
268
314
|
|
|
315
|
+
/**
|
|
316
|
+
* Suppress or restore a renderer's shadow casting based on its current alpha.
|
|
317
|
+
* Driving {@link Renderer.shadowCastingMode} (rather than the raw `mesh.castShadow`) is required
|
|
318
|
+
* because the Renderer re-applies that setting every frame; we remember the original mode so it
|
|
319
|
+
* is restored exactly when the object fades back in.
|
|
320
|
+
*/
|
|
321
|
+
private updateShadowCasting(renderer: Renderer, alpha: number) {
|
|
322
|
+
const shouldHideShadow = alpha < SeeThrough.shadowHideThreshold;
|
|
323
|
+
if (shouldHideShadow) {
|
|
324
|
+
if (renderer.shadowCastingMode !== ShadowCastingMode.Off) {
|
|
325
|
+
this._suppressedShadowCasting.set(renderer, renderer.shadowCastingMode);
|
|
326
|
+
renderer.shadowCastingMode = ShadowCastingMode.Off;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
else if (this._suppressedShadowCasting.has(renderer)) {
|
|
330
|
+
renderer.shadowCastingMode = this._suppressedShadowCasting.get(renderer)!;
|
|
331
|
+
this._suppressedShadowCasting.delete(renderer);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/** Restore every renderer we suppressed shadow casting on, then clear the bookkeeping. */
|
|
336
|
+
private restoreSuppressedShadowCasting() {
|
|
337
|
+
for (const [renderer, mode] of this._suppressedShadowCasting) {
|
|
338
|
+
if (!renderer.destroyed) renderer.shadowCastingMode = mode;
|
|
339
|
+
}
|
|
340
|
+
this._suppressedShadowCasting.clear();
|
|
341
|
+
}
|
|
342
|
+
|
|
269
343
|
}
|
|
270
344
|
|
|
@@ -56,6 +56,14 @@ function applyRotationAttribute(attr: string, target: { set(x: number, y: number
|
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
function applyTexture(context: IContext, texture: Texture, isBackground: boolean, isEnvironment: boolean) {
|
|
59
|
+
// Textures load asynchronously. The context can be disposed/torn down while a
|
|
60
|
+
// load is in flight (element removed, context disposed, scene switched) — at
|
|
61
|
+
// which point context.scene is set to null on dispose. Bail instead of
|
|
62
|
+
// dereferencing it (the rest of this function also touches context.scene).
|
|
63
|
+
if (!context.scene) {
|
|
64
|
+
if (debug) console.warn("Skybox: skipping applyTexture — context was torn down before the texture finished loading");
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
59
67
|
if (!(texture instanceof CubeTexture || texture instanceof CompressedCubeTexture)
|
|
60
68
|
&& texture.mapping !== CubeUVReflectionMapping) {
|
|
61
69
|
texture.mapping = EquirectangularRefractionMapping;
|
|
@@ -44,6 +44,7 @@ export { Behaviour, Component, GameObject } from "./Component.js";
|
|
|
44
44
|
export { AudioRolloffMode } from "./AudioSource.js";
|
|
45
45
|
export { ClearFlags } from "./Camera.js"
|
|
46
46
|
export { DragMode } from "./DragControls.js";
|
|
47
|
+
export { ShadowCastingMode, ReflectionProbeUsage } from "./Renderer.js";
|
|
47
48
|
export type { DropListenerNetworkEventArguments, DropListenerOnDropArguments } from "./DropListener.js";
|
|
48
49
|
export * from "./export/index.js"
|
|
49
50
|
export * from "./particlesystem/api.js"
|