@needle-tools/engine 5.1.0-alpha.8 → 5.1.0-beta
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 +17 -0
- package/components.needle.json +1 -1
- package/dist/{materialx-CE2sUv2B.umd.cjs → materialx-B47Bz-xs.umd.cjs} +1 -1
- package/dist/{materialx-u1EqYrhu.min.js → materialx-B85WjP7E.min.js} +1 -1
- package/dist/{materialx-vyB2Zbt4.js → materialx-jkHmVPez.js} +1 -1
- package/dist/{needle-engine.bundle-3LOy9tj8.js → needle-engine.bundle-BWkDSRyF.js} +6248 -6001
- package/dist/{needle-engine.bundle-BuI2pjv1.min.js → needle-engine.bundle-BvgS8xMP.min.js} +171 -171
- package/dist/{needle-engine.bundle-DQy9vQdJ.umd.cjs → needle-engine.bundle-CdVr_iIe.umd.cjs} +142 -142
- package/dist/needle-engine.d.ts +359 -46
- package/dist/needle-engine.js +618 -606
- package/dist/needle-engine.min.js +1 -1
- package/dist/needle-engine.umd.cjs +1 -1
- package/dist/{vendor-ButPLzor.umd.cjs → vendor-1UvpPPSB.umd.cjs} +2 -2
- package/dist/{vendor-DqZC4Is7.min.js → vendor-BQ2Vuntm.min.js} +4 -4
- package/dist/{vendor-DkWSNjMV.js → vendor-BslSKZPo.js} +113 -106
- 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_audio.d.ts +30 -1
- package/lib/engine/engine_audio.js +57 -13
- package/lib/engine/engine_audio.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_math.d.ts +1 -1
- package/lib/engine/engine_math.js +10 -4
- package/lib/engine/engine_math.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_scenedata.js +7 -0
- package/lib/engine/engine_scenedata.js.map +1 -1
- package/lib/engine/engine_serialization_core.js +7 -2
- package/lib/engine/engine_serialization_core.js.map +1 -1
- package/lib/engine/engine_time_utils.d.ts +10 -6
- package/lib/engine/engine_time_utils.js +10 -6
- package/lib/engine/engine_time_utils.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/webcomponents/needle menu/needle-menu-spatial.js +2 -2
- package/lib/engine/webcomponents/needle menu/needle-menu.js +5 -5
- 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/TempXRContext.js +2 -2
- package/lib/engine/xr/TempXRContext.js.map +1 -1
- package/lib/engine-components/AudioSource.d.ts +46 -2
- package/lib/engine-components/AudioSource.js +268 -60
- package/lib/engine-components/AudioSource.js.map +1 -1
- package/lib/engine-components/Component.js +2 -1
- package/lib/engine-components/Component.js.map +1 -1
- package/lib/engine-components/DragControls.js +2 -2
- package/lib/engine-components/DragControls.js.map +1 -1
- package/lib/engine-components/DragControlsConstraints.d.ts +0 -15
- package/lib/engine-components/DragControlsConstraints.js +14 -8
- package/lib/engine-components/DragControlsConstraints.js.map +1 -1
- package/lib/engine-components/EventList.js +1 -1
- package/lib/engine-components/EventList.js.map +1 -1
- package/lib/engine-components/SceneSwitcher.d.ts +2 -0
- package/lib/engine-components/SceneSwitcher.js +43 -16
- 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/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/codegen/components.d.ts +10 -0
- package/lib/engine-components/codegen/components.js +10 -0
- package/lib/engine-components/codegen/components.js.map +1 -1
- package/lib/engine-components/export/usdz/USDZExporter.js +4 -4
- package/lib/engine-components/timeline/PlayableDirector.js +1 -1
- package/lib/engine-components/timeline/PlayableDirector.js.map +1 -1
- package/lib/engine-components/web/ScrollFollow.d.ts +33 -0
- package/lib/engine-components/web/ScrollFollow.js +164 -22
- package/lib/engine-components/web/ScrollFollow.js.map +1 -1
- package/lib/needle-app.d.ts +18 -0
- package/lib/needle-app.js +19 -0
- package/lib/needle-app.js.map +1 -0
- package/package.json +3 -2
- package/plugins/common/license.js +25 -4
- package/plugins/common/needle-engine.js +5 -2
- package/plugins/types/userconfig.d.ts +22 -0
- package/plugins/vite/dts-generator.d.ts +24 -0
- package/plugins/vite/dts-generator.js +40 -7
- package/plugins/vite/index.d.ts +2 -0
- package/plugins/vite/index.js +17 -0
- package/plugins/vite/license.js +4 -4
- package/plugins/vite/needle-app.js +78 -5
- package/plugins/vite/remote.d.ts +28 -0
- package/plugins/vite/remote.js +124 -0
- package/src/engine/api.ts +1 -1
- package/src/engine/engine_audio.ts +68 -15
- 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_math.ts +11 -4
- package/src/engine/engine_networking_blob.ts +3 -3
- package/src/engine/engine_scenedata.ts +7 -2
- package/src/engine/engine_serialization_core.ts +7 -2
- package/src/engine/engine_time_utils.ts +10 -6
- package/src/engine/engine_utils_qrcode.ts +2 -2
- package/src/engine/webcomponents/needle menu/needle-menu-spatial.ts +2 -2
- package/src/engine/webcomponents/needle menu/needle-menu.ts +5 -5
- package/src/engine/webcomponents/needle-engine.loading.ts +6 -6
- package/src/engine/webcomponents/needle-engine.ts +2 -2
- package/src/engine/xr/TempXRContext.ts +2 -2
- package/src/engine-components/AudioSource.ts +256 -55
- package/src/engine-components/Component.ts +2 -1
- package/src/engine-components/DragControls.ts +3 -2
- package/src/engine-components/DragControlsConstraints.ts +16 -8
- package/src/engine-components/EventList.ts +1 -1
- package/src/engine-components/SceneSwitcher.ts +37 -15
- package/src/engine-components/SeeThrough.ts +75 -1
- package/src/engine-components/api.ts +1 -0
- package/src/engine-components/codegen/components.ts +10 -0
- package/src/engine-components/export/usdz/USDZExporter.ts +4 -4
- package/src/engine-components/timeline/PlayableDirector.ts +1 -1
- package/src/engine-components/web/ScrollFollow.ts +168 -22
|
@@ -163,6 +163,8 @@ export class KeepScaleConstraint implements IDragConstraint {
|
|
|
163
163
|
* new ScaleLimitConstraint(0.01, 10000, true, object.worldScale.clone());
|
|
164
164
|
* ```
|
|
165
165
|
*/
|
|
166
|
+
const _scaleLimitWs = new Vector3();
|
|
167
|
+
|
|
166
168
|
export class ScaleLimitConstraint implements IDragConstraint {
|
|
167
169
|
/**
|
|
168
170
|
* @param min Lower bound. Relative mode: fraction of initial scale. Raw mode: absolute world-scale per axis.
|
|
@@ -184,27 +186,33 @@ export class ScaleLimitConstraint implements IDragConstraint {
|
|
|
184
186
|
}
|
|
185
187
|
|
|
186
188
|
apply(followObject: GameObject): void {
|
|
187
|
-
|
|
189
|
+
// Read world scale directly from the matrix columns (col 0/1/2 lengths) rather than
|
|
190
|
+
// going through the worldScale getter, which internally calls updateWorldMatrix() again.
|
|
191
|
+
// We call it once here so the matrix is fresh, then read the elements directly.
|
|
192
|
+
const obj = followObject as unknown as Object3D;
|
|
193
|
+
obj.updateWorldMatrix(true, false);
|
|
194
|
+
const e = obj.matrixWorld.elements;
|
|
195
|
+
const sx = Math.sqrt(e[0] * e[0] + e[1] * e[1] + e[2] * e[2]);
|
|
196
|
+
const sy = Math.sqrt(e[4] * e[4] + e[5] * e[5] + e[6] * e[6]);
|
|
197
|
+
const sz = Math.sqrt(e[8] * e[8] + e[9] * e[9] + e[10] * e[10]);
|
|
198
|
+
_scaleLimitWs.set(sx, sy, sz);
|
|
199
|
+
const ws = _scaleLimitWs;
|
|
200
|
+
|
|
188
201
|
if (this.relativeToInitialScale) {
|
|
189
|
-
// Derive the uniform scale ratio from the magnitude of the current world scale
|
|
190
|
-
// relative to the initial world scale magnitude.
|
|
191
202
|
const initLen = this._initialWorldScale.length();
|
|
192
203
|
if (initLen < 1e-10) return;
|
|
193
204
|
const ratio = ws.length() / initLen;
|
|
194
205
|
const clamped = Math.max(this.min, Math.min(this.max, ratio));
|
|
195
206
|
if (Math.abs(clamped - ratio) > 1e-9) {
|
|
196
|
-
ws.copy(this._initialWorldScale).multiplyScalar(clamped);
|
|
197
|
-
followObject.worldScale = ws;
|
|
207
|
+
followObject.worldScale = ws.copy(this._initialWorldScale).multiplyScalar(clamped);
|
|
198
208
|
followObject.updateMatrix();
|
|
199
209
|
}
|
|
200
210
|
} else {
|
|
201
|
-
// Absolute per-axis clamp.
|
|
202
211
|
const cx = Math.max(this.min, Math.min(this.max, ws.x));
|
|
203
212
|
const cy = Math.max(this.min, Math.min(this.max, ws.y));
|
|
204
213
|
const cz = Math.max(this.min, Math.min(this.max, ws.z));
|
|
205
214
|
if (cx !== ws.x || cy !== ws.y || cz !== ws.z) {
|
|
206
|
-
ws.set(cx, cy, cz);
|
|
207
|
-
followObject.worldScale = ws;
|
|
215
|
+
followObject.worldScale = ws.set(cx, cy, cz);
|
|
208
216
|
followObject.updateMatrix();
|
|
209
217
|
}
|
|
210
218
|
}
|
|
@@ -56,7 +56,7 @@ export class CallInfo {
|
|
|
56
56
|
if (args !== undefined && args.length > 0)
|
|
57
57
|
argumentsBuffer.push(...args);
|
|
58
58
|
argumentsBuffer.push(...this.arguments);
|
|
59
|
-
this.target(...
|
|
59
|
+
this.target(...argumentsBuffer);
|
|
60
60
|
argumentsBuffer.length = 0;
|
|
61
61
|
}
|
|
62
62
|
else {
|
|
@@ -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;
|
|
@@ -791,8 +799,20 @@ export class SceneSwitcher extends Behaviour {
|
|
|
791
799
|
for (let i = 0; i < this.scenes.length; i++) {
|
|
792
800
|
const scene = this.scenes[i];
|
|
793
801
|
if (!scene) continue;
|
|
794
|
-
|
|
795
|
-
|
|
802
|
+
// Match against the url-derived name AND, for in-scene object
|
|
803
|
+
// references (which have an empty url), the referenced object's
|
|
804
|
+
// name. Without the latter, `?scene=<name>` never resolves for
|
|
805
|
+
// non-prefab/object scenes and the switcher falls back to the
|
|
806
|
+
// default scene (scenes is AssetReference[], so the
|
|
807
|
+
// `scene instanceof Object3D` branch alone never matched them).
|
|
808
|
+
const candidateNames: string[] = [];
|
|
809
|
+
if (scene instanceof Object3D) candidateNames.push(scene.name);
|
|
810
|
+
else {
|
|
811
|
+
if (scene.url) candidateNames.push(sceneUriToName(scene.url));
|
|
812
|
+
const asset = scene.asset;
|
|
813
|
+
if (asset instanceof Object3D && asset.name) candidateNames.push(asset.name);
|
|
814
|
+
}
|
|
815
|
+
if (candidateNames.some(n => n?.toLowerCase().includes(lowerCaseValue))) {
|
|
796
816
|
return this.select(i);
|
|
797
817
|
}
|
|
798
818
|
}
|
|
@@ -814,7 +834,8 @@ export class SceneSwitcher extends Behaviour {
|
|
|
814
834
|
private _isCurrentlyLoading: boolean = false;
|
|
815
835
|
private _currentLoadingProgress: ProgressEvent | undefined = undefined;
|
|
816
836
|
|
|
817
|
-
private async onStartLoading() {
|
|
837
|
+
private async onStartLoading(loadingRequestId: number) {
|
|
838
|
+
this._activeLoadingRequestId = loadingRequestId;
|
|
818
839
|
this._isCurrentlyLoading = true;
|
|
819
840
|
if (this.loadingScene) {
|
|
820
841
|
// save the last loading scene reference so that it can be changed at runtime
|
|
@@ -827,7 +848,7 @@ export class SceneSwitcher extends Behaviour {
|
|
|
827
848
|
this._loadingScenePromise = this.loadingScene?.loadAssetAsync().then(res => res != null);
|
|
828
849
|
}
|
|
829
850
|
await this._loadingScenePromise;
|
|
830
|
-
if (this._isCurrentlyLoading && this.loadingScene?.asset) {
|
|
851
|
+
if (loadingRequestId === this._activeLoadingRequestId && this._isCurrentlyLoading && this.loadingScene?.asset) {
|
|
831
852
|
if (debug) console.log("Add loading scene", this.loadingScene.url, this.loadingScene.asset)
|
|
832
853
|
const loadingScene = this.loadingScene.asset as any as Object3D;
|
|
833
854
|
GameObject.add(loadingScene, this.gameObject);
|
|
@@ -839,7 +860,7 @@ export class SceneSwitcher extends Behaviour {
|
|
|
839
860
|
}
|
|
840
861
|
}
|
|
841
862
|
// Invoke the event on a loading listener on the same object as the scene switcher
|
|
842
|
-
if (this._isCurrentlyLoading) {
|
|
863
|
+
if (loadingRequestId === this._activeLoadingRequestId && this._isCurrentlyLoading) {
|
|
843
864
|
const listener = this.tryGetSceneEventListener(this.gameObject);
|
|
844
865
|
if (listener) {
|
|
845
866
|
if (listener.sceneOpened) {
|
|
@@ -849,7 +870,8 @@ export class SceneSwitcher extends Behaviour {
|
|
|
849
870
|
}
|
|
850
871
|
}
|
|
851
872
|
}
|
|
852
|
-
private async onEndLoading() {
|
|
873
|
+
private async onEndLoading(loadingRequestId: number) {
|
|
874
|
+
if (loadingRequestId !== this._activeLoadingRequestId) return;
|
|
853
875
|
this._isCurrentlyLoading = false;
|
|
854
876
|
if (this.loadingScene?.asset) {
|
|
855
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
|
|
|
@@ -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"
|
|
@@ -25,7 +25,17 @@ export { LogStats } from "../debug/LogStats.js";
|
|
|
25
25
|
export { DeleteBox } from "../DeleteBox.js";
|
|
26
26
|
export { Deletable } from "../DeleteBox.js";
|
|
27
27
|
export { DeviceFlag } from "../DeviceFlag.js";
|
|
28
|
+
export { DragProfile } from "../DragControls.js";
|
|
28
29
|
export { DragControls } from "../DragControls.js";
|
|
30
|
+
export { GridSnapConstraint } from "../DragControlsConstraints.js";
|
|
31
|
+
export { GrabPointPlaneConstraint } from "../DragControlsConstraints.js";
|
|
32
|
+
export { KeepRotationConstraint } from "../DragControlsConstraints.js";
|
|
33
|
+
export { KeepScaleConstraint } from "../DragControlsConstraints.js";
|
|
34
|
+
export { ScaleLimitConstraint } from "../DragControlsConstraints.js";
|
|
35
|
+
export { FixedRotationAxesConstraint } from "../DragControlsConstraints.js";
|
|
36
|
+
export { AxisRotationConstraint } from "../DragControlsConstraints.js";
|
|
37
|
+
export { PlaneHeightLockConstraint } from "../DragControlsConstraints.js";
|
|
38
|
+
export { SnapToSurfaceConstraint } from "../DragControlsConstraints.js";
|
|
29
39
|
export { DropListener } from "../DropListener.js";
|
|
30
40
|
export { Duplicatable } from "../Duplicatable.js";
|
|
31
41
|
export { CallInfo } from "../EventList.js";
|
|
@@ -3,7 +3,7 @@ import { Euler, Material, Matrix4, Mesh, Object3D, Quaternion, Vector3 } from "t
|
|
|
3
3
|
|
|
4
4
|
import { isDevEnvironment, showBalloonMessage, showBalloonWarning } from "../../../engine/debug/index.js";
|
|
5
5
|
import { findObjectOfType } from "../../../engine/engine_components.js";
|
|
6
|
-
import {
|
|
6
|
+
import { _XtEQ } from "../../../engine/engine_license.js";
|
|
7
7
|
import { serializable } from "../../../engine/engine_serialization.js";
|
|
8
8
|
import { getFormattedDate, Progress } from "../../../engine/engine_time_utils.js";
|
|
9
9
|
import { DeviceUtilities, getParam } from "../../../engine/engine_utils.js";
|
|
@@ -277,7 +277,7 @@ export class USDZExporter extends Behaviour {
|
|
|
277
277
|
let name = this.exportFileName ?? this.objectToExport?.name ?? this.name;
|
|
278
278
|
name += "-" + getFormattedDate(); // seems iOS caches the file in some cases, this ensures we always have a fresh file
|
|
279
279
|
|
|
280
|
-
if (!
|
|
280
|
+
if (!_XtEQ()) {
|
|
281
281
|
if (name !== "") name += "-";
|
|
282
282
|
name += "MadeWithNeedle";
|
|
283
283
|
}
|
|
@@ -682,7 +682,7 @@ export class USDZExporter extends Behaviour {
|
|
|
682
682
|
if (debug)
|
|
683
683
|
showBalloonMessage("Quicklook url: " + callToActionURL);
|
|
684
684
|
if (callToActionURL) {
|
|
685
|
-
if (!
|
|
685
|
+
if (!_XtEQ()) {
|
|
686
686
|
console.warn("Quicklook closed: custom redirects require a Needle Engine Pro license: https://needle.tools/pricing", callToActionURL)
|
|
687
687
|
}
|
|
688
688
|
else {
|
|
@@ -697,7 +697,7 @@ export class USDZExporter extends Behaviour {
|
|
|
697
697
|
private buildQuicklookOverlay(): CustomBranding {
|
|
698
698
|
const obj: CustomBranding = {};
|
|
699
699
|
if (this.customBranding) Object.assign(obj, this.customBranding);
|
|
700
|
-
if (!
|
|
700
|
+
if (!_XtEQ()) {
|
|
701
701
|
console.log("Custom Quicklook banner text requires pro license: https://needle.tools/pricing");
|
|
702
702
|
obj.callToAction = "Close";
|
|
703
703
|
obj.checkoutTitle = "🌵 Made with Needle";
|
|
@@ -222,7 +222,7 @@ export class PlayableDirector extends Behaviour {
|
|
|
222
222
|
|
|
223
223
|
/** @internal */
|
|
224
224
|
onEnable() {
|
|
225
|
-
if (debug) console.log("[Timeline] OnEnable", this.name, this.playOnAwake);
|
|
225
|
+
if (debug) console.log("[Timeline] OnEnable", { name: this.name, playOnAwake: this.playOnAwake });
|
|
226
226
|
|
|
227
227
|
for (const tracks of this._allTracks) {
|
|
228
228
|
for (const track of tracks) {
|