@needle-tools/engine 4.2.5 → 4.3.0-alpha.1
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 +9 -0
- package/components.needle.json +1 -1
- package/dist/needle-engine.bundle.js +4245 -2977
- package/dist/needle-engine.bundle.light.js +4236 -2968
- package/dist/needle-engine.bundle.light.min.js +73 -73
- package/dist/needle-engine.bundle.light.umd.cjs +61 -61
- package/dist/needle-engine.bundle.min.js +62 -62
- package/dist/needle-engine.bundle.umd.cjs +61 -61
- package/dist/needle-engine.light.d.ts +9 -9
- package/dist/three-examples.js +825 -794
- package/dist/three-examples.light.js +825 -794
- package/dist/three-examples.light.min.js +12 -12
- package/dist/three-examples.light.umd.cjs +10 -10
- package/dist/three-examples.min.js +12 -12
- package/dist/three-examples.umd.cjs +10 -10
- package/lib/engine/engine_addressables.d.ts +3 -0
- package/lib/engine/engine_addressables.js +18 -0
- package/lib/engine/engine_addressables.js.map +1 -1
- package/lib/engine/engine_input.d.ts +20 -1
- package/lib/engine/engine_input.js.map +1 -1
- package/lib/engine/engine_types.d.ts +162 -17
- package/lib/engine-components/Animator.d.ts +129 -21
- package/lib/engine-components/Animator.js +115 -21
- package/lib/engine-components/Animator.js.map +1 -1
- package/lib/engine-components/AnimatorController.d.ts +161 -32
- package/lib/engine-components/AnimatorController.js +176 -29
- package/lib/engine-components/AnimatorController.js.map +1 -1
- package/lib/engine-components/AudioListener.d.ts +16 -5
- package/lib/engine-components/AudioListener.js +16 -5
- package/lib/engine-components/AudioListener.js.map +1 -1
- package/lib/engine-components/AudioSource.d.ts +120 -28
- package/lib/engine-components/AudioSource.js +120 -37
- package/lib/engine-components/AudioSource.js.map +1 -1
- package/lib/engine-components/AvatarLoader.d.ts +61 -0
- package/lib/engine-components/AvatarLoader.js +61 -1
- package/lib/engine-components/AvatarLoader.js.map +1 -1
- package/lib/engine-components/AxesHelper.d.ts +19 -1
- package/lib/engine-components/AxesHelper.js +19 -1
- package/lib/engine-components/AxesHelper.js.map +1 -1
- package/lib/engine-components/BoxHelperComponent.d.ts +26 -0
- package/lib/engine-components/BoxHelperComponent.js +26 -0
- package/lib/engine-components/BoxHelperComponent.js.map +1 -1
- package/lib/engine-components/Camera.d.ts +126 -37
- package/lib/engine-components/Camera.js +139 -37
- package/lib/engine-components/Camera.js.map +1 -1
- package/lib/engine-components/CameraUtils.js +20 -0
- package/lib/engine-components/CameraUtils.js.map +1 -1
- package/lib/engine-components/Collider.d.ts +95 -21
- package/lib/engine-components/Collider.js +100 -23
- package/lib/engine-components/Collider.js.map +1 -1
- package/lib/engine-components/Component.d.ts +554 -106
- package/lib/engine-components/Component.js +352 -81
- package/lib/engine-components/Component.js.map +1 -1
- package/lib/engine-components/DragControls.d.ts +95 -21
- package/lib/engine-components/DragControls.js +126 -32
- package/lib/engine-components/DragControls.js.map +1 -1
- package/lib/engine-components/DropListener.d.ts +99 -16
- package/lib/engine-components/DropListener.js +119 -14
- package/lib/engine-components/DropListener.js.map +1 -1
- package/lib/engine-components/Light.d.ts +102 -5
- package/lib/engine-components/Light.js +102 -44
- package/lib/engine-components/Light.js.map +1 -1
- package/lib/engine-components/NeedleMenu.d.ts +28 -11
- package/lib/engine-components/NeedleMenu.js +28 -11
- package/lib/engine-components/NeedleMenu.js.map +1 -1
- package/lib/engine-components/Networking.d.ts +37 -5
- package/lib/engine-components/Networking.js +37 -5
- package/lib/engine-components/Networking.js.map +1 -1
- package/lib/engine-components/SceneSwitcher.d.ts +8 -0
- package/lib/engine-components/SceneSwitcher.js +72 -8
- package/lib/engine-components/SceneSwitcher.js.map +1 -1
- package/lib/engine-components/SpatialTrigger.d.ts +66 -1
- package/lib/engine-components/SpatialTrigger.js +74 -2
- package/lib/engine-components/SpatialTrigger.js.map +1 -1
- package/lib/engine-components/SpectatorCamera.d.ts +66 -4
- package/lib/engine-components/SpectatorCamera.js +132 -6
- package/lib/engine-components/SpectatorCamera.js.map +1 -1
- package/lib/engine-components/SyncedTransform.d.ts +45 -6
- package/lib/engine-components/SyncedTransform.js +45 -6
- package/lib/engine-components/SyncedTransform.js.map +1 -1
- package/lib/engine-components/TransformGizmo.d.ts +49 -3
- package/lib/engine-components/TransformGizmo.js +49 -3
- package/lib/engine-components/TransformGizmo.js.map +1 -1
- package/lib/engine-components/ui/EventSystem.d.ts +1 -0
- package/lib/engine-components/ui/EventSystem.js +8 -5
- package/lib/engine-components/ui/EventSystem.js.map +1 -1
- package/lib/engine-components/webxr/WebXR.d.ts +131 -22
- package/lib/engine-components/webxr/WebXR.js +132 -23
- package/lib/engine-components/webxr/WebXR.js.map +1 -1
- package/lib/engine-components-experimental/networking/PlayerSync.d.ts +82 -9
- package/lib/engine-components-experimental/networking/PlayerSync.js +76 -11
- package/lib/engine-components-experimental/networking/PlayerSync.js.map +1 -1
- package/package.json +1 -1
- package/plugins/vite/alias.js +6 -3
- package/src/engine/engine_addressables.ts +21 -0
- package/src/engine/engine_input.ts +20 -1
- package/src/engine/engine_types.ts +179 -18
- package/src/engine-components/Animator.ts +142 -22
- package/src/engine-components/AnimatorController.ts +184 -34
- package/src/engine-components/AudioListener.ts +16 -5
- package/src/engine-components/AudioSource.ts +126 -37
- package/src/engine-components/AvatarLoader.ts +61 -2
- package/src/engine-components/AxesHelper.ts +21 -1
- package/src/engine-components/BoxHelperComponent.ts +26 -0
- package/src/engine-components/Camera.ts +147 -41
- package/src/engine-components/CameraUtils.ts +20 -0
- package/src/engine-components/Collider.ts +102 -27
- package/src/engine-components/Component.ts +605 -129
- package/src/engine-components/DragControls.ts +134 -38
- package/src/engine-components/DropListener.ts +143 -23
- package/src/engine-components/Light.ts +105 -44
- package/src/engine-components/NeedleMenu.ts +29 -11
- package/src/engine-components/Networking.ts +37 -6
- package/src/engine-components/SceneSwitcher.ts +78 -9
- package/src/engine-components/SpatialTrigger.ts +80 -3
- package/src/engine-components/SpectatorCamera.ts +136 -18
- package/src/engine-components/SyncedTransform.ts +50 -7
- package/src/engine-components/TransformGizmo.ts +49 -4
- package/src/engine-components/ui/EventSystem.ts +9 -7
- package/src/engine-components/webxr/WebXR.ts +144 -27
- package/src/engine-components-experimental/networking/PlayerSync.ts +85 -13
|
@@ -4,50 +4,68 @@ import { DeviceUtilities } from '../engine/engine_utils.js';
|
|
|
4
4
|
import { Behaviour } from './Component.js';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
7
|
+
* Provides configuration options for the built-in Needle Menu.
|
|
8
|
+
* Needle Menu uses HTML in 2D mode, and automatically switches to a 3D menu in VR/AR mode.
|
|
9
|
+
*
|
|
10
|
+
* Controls display options, button visibility, and menu positioning.
|
|
8
11
|
* From code, you can access the menu via {@link Context.menu}.
|
|
9
12
|
* @category User Interface
|
|
10
13
|
* @group Components
|
|
11
14
|
**/
|
|
12
15
|
export class NeedleMenu extends Behaviour {
|
|
13
16
|
|
|
17
|
+
/**
|
|
18
|
+
* Determines the vertical positioning of the menu on the screen
|
|
19
|
+
*/
|
|
14
20
|
@serializable()
|
|
15
21
|
position: "top" | "bottom" = "bottom";
|
|
16
22
|
|
|
17
|
-
/**
|
|
23
|
+
/**
|
|
24
|
+
* Controls the visibility of the Needle logo in the menu (requires PRO license)
|
|
25
|
+
*/
|
|
18
26
|
@serializable()
|
|
19
27
|
showNeedleLogo: boolean = true;
|
|
20
28
|
|
|
21
|
-
/**
|
|
29
|
+
/**
|
|
30
|
+
* When enabled, displays the menu in VR/AR mode when the user looks up
|
|
22
31
|
* @default undefined
|
|
23
|
-
|
|
32
|
+
*/
|
|
24
33
|
@serializable()
|
|
25
34
|
showSpatialMenu?: boolean;
|
|
26
35
|
|
|
27
|
-
/**
|
|
36
|
+
/**
|
|
37
|
+
* When enabled, adds a fullscreen toggle button to the menu
|
|
28
38
|
* @default undefined
|
|
29
|
-
|
|
39
|
+
*/
|
|
30
40
|
@serializable()
|
|
31
41
|
createFullscreenButton?: boolean;
|
|
32
|
-
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* When enabled, adds an audio mute/unmute button to the menu
|
|
33
45
|
* @default undefined
|
|
34
|
-
|
|
46
|
+
*/
|
|
35
47
|
@serializable()
|
|
36
48
|
createMuteButton?: boolean;
|
|
37
49
|
|
|
38
50
|
/**
|
|
39
|
-
* When enabled a button to
|
|
51
|
+
* When enabled, adds a button to display a QR code for sharing the application.
|
|
52
|
+
* The QR code is only displayed on desktop devices.
|
|
40
53
|
* @default undefined
|
|
41
54
|
*/
|
|
42
55
|
@serializable()
|
|
43
56
|
createQRCodeButton?: boolean;
|
|
44
57
|
|
|
45
|
-
/**
|
|
58
|
+
/**
|
|
59
|
+
* Applies the configured menu options when the component is enabled
|
|
60
|
+
* @hidden
|
|
61
|
+
*/
|
|
46
62
|
onEnable() {
|
|
47
63
|
this.applyOptions();
|
|
48
64
|
}
|
|
49
65
|
|
|
50
|
-
/**
|
|
66
|
+
/**
|
|
67
|
+
* Applies all configured options to the active {@link Context.menu}.
|
|
68
|
+
*/
|
|
51
69
|
applyOptions() {
|
|
52
70
|
this.context.menu.setPosition(this.position);
|
|
53
71
|
this.context.menu.showNeedleLogo(this.showNeedleLogo);
|
|
@@ -7,21 +7,33 @@ import { Behaviour } from "./Component.js";
|
|
|
7
7
|
const debug = getParam("debugnet");
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
-
*
|
|
10
|
+
* Provides configuration to the built-in networking system.
|
|
11
|
+
* This component supplies websocket URLs for establishing connections.
|
|
12
|
+
* It implements the {@link INetworkingWebsocketUrlProvider} interface.
|
|
13
|
+
*
|
|
11
14
|
* @category Networking
|
|
12
15
|
* @group Components
|
|
13
16
|
*/
|
|
14
17
|
export class Networking extends Behaviour implements INetworkingWebsocketUrlProvider {
|
|
15
18
|
|
|
16
|
-
/**
|
|
19
|
+
/**
|
|
20
|
+
* The websocket URL to connect to for networking functionality.
|
|
21
|
+
* Can be a complete URL or a relative path that will be resolved against the current origin.
|
|
22
|
+
*/
|
|
17
23
|
@serializable()
|
|
18
24
|
url: string | null = null;
|
|
19
25
|
|
|
20
|
-
/**
|
|
26
|
+
/**
|
|
27
|
+
* Name of the URL parameter that can override the websocket connection URL.
|
|
28
|
+
* When set, the URL will be overridden by the parameter value from the browser URL.
|
|
29
|
+
* For example, with `urlParameterName="ws"`, adding `?ws=ws://localhost:8080` to the browser URL will override the connection URL.
|
|
30
|
+
*/
|
|
21
31
|
@serializable()
|
|
22
32
|
urlParameterName: string | null = null;
|
|
23
33
|
|
|
24
|
-
/**
|
|
34
|
+
/**
|
|
35
|
+
* Alternative URL to use when running on a local network.
|
|
36
|
+
* This is particularly useful for development, when the server is running on the same machine as the client.
|
|
25
37
|
*/
|
|
26
38
|
@serializable()
|
|
27
39
|
localhost: string | null = null;
|
|
@@ -33,7 +45,13 @@ export class Networking extends Behaviour implements INetworkingWebsocketUrlProv
|
|
|
33
45
|
this.context.connection.registerProvider(this);
|
|
34
46
|
}
|
|
35
47
|
|
|
36
|
-
/**
|
|
48
|
+
/**
|
|
49
|
+
* Determines the websocket URL to use for networking connections.
|
|
50
|
+
* Processes the configured URL, applying localhost fallbacks when appropriate and
|
|
51
|
+
* handling URL parameter overrides if specified.
|
|
52
|
+
* @returns The formatted websocket URL string or null if no valid URL could be determined
|
|
53
|
+
* @internal
|
|
54
|
+
*/
|
|
37
55
|
getWebsocketUrl(): string | null {
|
|
38
56
|
|
|
39
57
|
let socketurl = this.url ? Networking.GetUrl(this.url, this.localhost) : null;
|
|
@@ -58,7 +76,13 @@ export class Networking extends Behaviour implements INetworkingWebsocketUrlProv
|
|
|
58
76
|
return "wss://" + match?.groups["url"];
|
|
59
77
|
}
|
|
60
78
|
|
|
61
|
-
|
|
79
|
+
/**
|
|
80
|
+
* Processes a URL string applying various transformations based on network environment.
|
|
81
|
+
* Handles relative paths and localhost fallbacks for local network environments.
|
|
82
|
+
* @param url The original URL to process
|
|
83
|
+
* @param localhostFallback Alternative URL to use when on a local network
|
|
84
|
+
* @returns The processed URL string or null/undefined if input was invalid
|
|
85
|
+
*/
|
|
62
86
|
public static GetUrl(url: string | null | undefined, localhostFallback?: string | null): string | null | undefined {
|
|
63
87
|
|
|
64
88
|
let result = url;
|
|
@@ -78,6 +102,13 @@ export class Networking extends Behaviour implements INetworkingWebsocketUrlProv
|
|
|
78
102
|
return result;
|
|
79
103
|
}
|
|
80
104
|
|
|
105
|
+
/**
|
|
106
|
+
* Determines if the current connection is on a local network.
|
|
107
|
+
* Useful for applying different networking configurations in local development environments.
|
|
108
|
+
* This is the same as calling {@link isLocalNetwork}.
|
|
109
|
+
* @param hostname Optional hostname to check instead of the current window location
|
|
110
|
+
* @returns True if the connection is on a local network, false otherwise
|
|
111
|
+
*/
|
|
81
112
|
public static IsLocalNetwork(hostname = window.location.hostname) {
|
|
82
113
|
return isLocalNetwork(hostname);
|
|
83
114
|
}
|
|
@@ -46,6 +46,10 @@ export type LoadSceneEvent = {
|
|
|
46
46
|
index: number;
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
+
export type LoadSceneProgressEvent = LoadSceneEvent & {
|
|
50
|
+
progress: number,
|
|
51
|
+
}
|
|
52
|
+
|
|
49
53
|
/**
|
|
50
54
|
* The ISceneEventListener is called by the {@link SceneSwitcher} when a scene is loaded or unloaded.
|
|
51
55
|
* It must be added to the root object of your scene (that is being loaded) or on the same object as the SceneSwitcher
|
|
@@ -225,6 +229,15 @@ export class SceneSwitcher extends Behaviour {
|
|
|
225
229
|
*/
|
|
226
230
|
get currentlyLoadedScene() { return this._currentScene; }
|
|
227
231
|
|
|
232
|
+
/**
|
|
233
|
+
* Called when a scene starts loading
|
|
234
|
+
*/
|
|
235
|
+
@serializable(EventList)
|
|
236
|
+
sceneLoadingStart: EventList<LoadSceneEvent> = new EventList();
|
|
237
|
+
|
|
238
|
+
@serializable(EventList)
|
|
239
|
+
sceneLoadingProgress: EventList<ProgressEvent> = new EventList();
|
|
240
|
+
|
|
228
241
|
/**
|
|
229
242
|
* The sceneLoaded event is called when a scene/glTF is loaded and added to the scene
|
|
230
243
|
*/
|
|
@@ -281,7 +294,7 @@ export class SceneSwitcher extends Behaviour {
|
|
|
281
294
|
this._preloadScheduler.maxLoadAhead = this.preloadNext;
|
|
282
295
|
this._preloadScheduler.maxLoadBehind = this.preloadPrevious;
|
|
283
296
|
this._preloadScheduler.maxConcurrent = this.preloadConcurrent;
|
|
284
|
-
this._preloadScheduler.begin();
|
|
297
|
+
this._preloadScheduler.begin(2000);
|
|
285
298
|
|
|
286
299
|
// Begin loading the loading scene
|
|
287
300
|
if (this.autoLoadFirstScene && this._currentIndex === -1 && !await this.tryLoadFromQueryParam()) {
|
|
@@ -602,11 +615,13 @@ export class SceneSwitcher extends Behaviour {
|
|
|
602
615
|
|
|
603
616
|
const loadStartEvt = new CustomEvent<LoadSceneEvent>("loadscene-start", { detail: { scene: scene, switcher: this, index: index } })
|
|
604
617
|
this.dispatchEvent(loadStartEvt);
|
|
618
|
+
this.sceneLoadingStart?.invoke(loadStartEvt.detail);
|
|
605
619
|
await this.onStartLoading();
|
|
606
620
|
// start loading and wait for the scene to be loaded
|
|
607
621
|
await scene.loadAssetAsync((_, prog) => {
|
|
608
622
|
this._currentLoadingProgress = prog;
|
|
609
623
|
this.dispatchEvent(prog);
|
|
624
|
+
this.sceneLoadingProgress?.invoke(prog);
|
|
610
625
|
}).catch(console.error);
|
|
611
626
|
await this.onEndLoading();
|
|
612
627
|
const finishedEvt = new CustomEvent<LoadSceneEvent>("loadscene-finished", { detail: { scene: scene, switcher: this, index: index } });
|
|
@@ -842,9 +857,18 @@ function sceneUriToName(uri: string): string {
|
|
|
842
857
|
|
|
843
858
|
|
|
844
859
|
|
|
860
|
+
/**
|
|
861
|
+
* PreLoadScheduler is responsible for managing preloading of scenes.
|
|
862
|
+
* It handles scheduling and limiting concurrent downloads of scenes based on specified parameters.
|
|
863
|
+
*/
|
|
845
864
|
class PreLoadScheduler {
|
|
865
|
+
/** Maximum number of scenes to preload ahead of the current scene */
|
|
846
866
|
maxLoadAhead: number;
|
|
867
|
+
|
|
868
|
+
/** Maximum number of scenes to preload behind the current scene */
|
|
847
869
|
maxLoadBehind: number;
|
|
870
|
+
|
|
871
|
+
/** Maximum number of scenes that can be preloaded concurrently */
|
|
848
872
|
maxConcurrent: number;
|
|
849
873
|
|
|
850
874
|
private _isRunning: boolean = false;
|
|
@@ -852,6 +876,13 @@ class PreLoadScheduler {
|
|
|
852
876
|
private _loadTasks: LoadTask[] = [];
|
|
853
877
|
private _maxConcurrentLoads: number = 1;
|
|
854
878
|
|
|
879
|
+
/**
|
|
880
|
+
* Creates a new PreLoadScheduler instance
|
|
881
|
+
* @param rooms The SceneSwitcher that this scheduler belongs to
|
|
882
|
+
* @param ahead Number of scenes to preload ahead of current scene
|
|
883
|
+
* @param behind Number of scenes to preload behind current scene
|
|
884
|
+
* @param maxConcurrent Maximum number of concurrent preloads allowed
|
|
885
|
+
*/
|
|
855
886
|
constructor(rooms: SceneSwitcher, ahead: number = 1, behind: number = 1, maxConcurrent: number = 2) {
|
|
856
887
|
this._switcher = rooms;
|
|
857
888
|
this.maxLoadAhead = ahead;
|
|
@@ -859,26 +890,34 @@ class PreLoadScheduler {
|
|
|
859
890
|
this.maxConcurrent = maxConcurrent;
|
|
860
891
|
}
|
|
861
892
|
|
|
862
|
-
|
|
893
|
+
/**
|
|
894
|
+
* Starts the preloading process after a specified delay
|
|
895
|
+
* @param delay Time in milliseconds to wait before starting preload
|
|
896
|
+
*/
|
|
897
|
+
begin(delay: number) {
|
|
863
898
|
if (this._isRunning) return;
|
|
864
|
-
if (debug) console.log("Preload begin")
|
|
899
|
+
if (debug) console.log("Preload begin", { delay })
|
|
865
900
|
this._isRunning = true;
|
|
866
|
-
let lastRoom: number = -
|
|
901
|
+
let lastRoom: number = -10;
|
|
867
902
|
let searchDistance: number;
|
|
868
903
|
let searchCall: number;
|
|
869
904
|
const array = this._switcher.scenes;
|
|
905
|
+
const startTime = Date.now() + delay;
|
|
870
906
|
const interval = setInterval(() => {
|
|
871
907
|
if (this.allLoaded()) {
|
|
872
908
|
if (debug)
|
|
873
|
-
console.log("All scenes loaded");
|
|
909
|
+
console.log("All scenes (pre-)loaded");
|
|
874
910
|
this.stop();
|
|
875
911
|
}
|
|
876
912
|
if (!this._isRunning) {
|
|
877
913
|
clearInterval(interval);
|
|
878
914
|
return;
|
|
879
915
|
}
|
|
916
|
+
|
|
917
|
+
if (Date.now() < startTime) return;
|
|
918
|
+
|
|
880
919
|
if (this.canLoadNewScene() === false) return;
|
|
881
|
-
if (lastRoom !== this._switcher.currentIndex) {
|
|
920
|
+
if (lastRoom === -10 || lastRoom !== this._switcher.currentIndex) {
|
|
882
921
|
lastRoom = this._switcher.currentIndex;
|
|
883
922
|
searchCall = 0;
|
|
884
923
|
searchDistance = 0;
|
|
@@ -892,19 +931,33 @@ class PreLoadScheduler {
|
|
|
892
931
|
if (roomIndex < 0) return;
|
|
893
932
|
// if (roomIndex < 0) roomIndex = array.length + roomIndex;
|
|
894
933
|
if (roomIndex < 0 || roomIndex >= array.length) return;
|
|
895
|
-
|
|
896
|
-
|
|
934
|
+
if (!this._loadTasks.some(t => t.index === roomIndex)) {
|
|
935
|
+
const scene = array[roomIndex];
|
|
936
|
+
if (debug) console.log("Preload scene", { roomIndex, searchForward, lastRoom, currentIndex: this._switcher.currentIndex, tasks: this._loadTasks.length }, scene?.url);
|
|
937
|
+
new LoadTask(roomIndex, scene, this._loadTasks);
|
|
938
|
+
}
|
|
897
939
|
}, 200);
|
|
898
940
|
}
|
|
899
941
|
|
|
942
|
+
/**
|
|
943
|
+
* Stops the preloading process
|
|
944
|
+
*/
|
|
900
945
|
stop() {
|
|
901
946
|
this._isRunning = false;
|
|
902
947
|
}
|
|
903
948
|
|
|
949
|
+
/**
|
|
950
|
+
* Checks if a new scene can be loaded based on current load limits
|
|
951
|
+
* @returns True if a new scene can be loaded, false otherwise
|
|
952
|
+
*/
|
|
904
953
|
canLoadNewScene(): boolean {
|
|
905
954
|
return this._loadTasks.length < this._maxConcurrentLoads;
|
|
906
955
|
}
|
|
907
956
|
|
|
957
|
+
/**
|
|
958
|
+
* Checks if all scenes in the SceneSwitcher have been loaded
|
|
959
|
+
* @returns True if all scenes are loaded, false otherwise
|
|
960
|
+
*/
|
|
908
961
|
allLoaded(): boolean {
|
|
909
962
|
if (this._switcher.scenes) {
|
|
910
963
|
for (const scene of this._switcher.scenes) {
|
|
@@ -915,12 +968,25 @@ class PreLoadScheduler {
|
|
|
915
968
|
}
|
|
916
969
|
}
|
|
917
970
|
|
|
971
|
+
/**
|
|
972
|
+
* Represents a single preloading task for a scene
|
|
973
|
+
*/
|
|
918
974
|
class LoadTask {
|
|
919
|
-
|
|
975
|
+
/** The index of the scene in the scenes array */
|
|
920
976
|
index: number;
|
|
977
|
+
|
|
978
|
+
/** The AssetReference to be loaded */
|
|
921
979
|
asset: AssetReference;
|
|
980
|
+
|
|
981
|
+
/** The collection of active load tasks this task belongs to */
|
|
922
982
|
tasks: LoadTask[];
|
|
923
983
|
|
|
984
|
+
/**
|
|
985
|
+
* Creates a new LoadTask and begins loading immediately
|
|
986
|
+
* @param index The index of the scene in the scenes array
|
|
987
|
+
* @param asset The AssetReference to preload
|
|
988
|
+
* @param tasks The collection of active load tasks
|
|
989
|
+
*/
|
|
924
990
|
constructor(index: number, asset: AssetReference, tasks: LoadTask[]) {
|
|
925
991
|
this.index = index;
|
|
926
992
|
this.asset = asset;
|
|
@@ -929,6 +995,9 @@ class LoadTask {
|
|
|
929
995
|
this.awaitLoading();
|
|
930
996
|
}
|
|
931
997
|
|
|
998
|
+
/**
|
|
999
|
+
* Asynchronously loads the asset and removes this task from the active tasks list when complete
|
|
1000
|
+
*/
|
|
932
1001
|
private async awaitLoading() {
|
|
933
1002
|
if (this.asset && !this.asset.isLoaded()) {
|
|
934
1003
|
if (debug)
|
|
@@ -8,8 +8,15 @@ import { EventList } from "./EventList.js";
|
|
|
8
8
|
|
|
9
9
|
const debug = getParam("debugspatialtrigger");
|
|
10
10
|
|
|
11
|
+
/** Layer instances used for mask comparison */
|
|
11
12
|
const layer1 = new Layers();
|
|
12
13
|
const layer2 = new Layers();
|
|
14
|
+
/**
|
|
15
|
+
* Tests if two layer masks intersect
|
|
16
|
+
* @param mask1 First layer mask
|
|
17
|
+
* @param mask2 Second layer mask
|
|
18
|
+
* @returns True if the layers intersect
|
|
19
|
+
*/
|
|
13
20
|
function testMask(mask1, mask2) {
|
|
14
21
|
layer1.mask = mask1;
|
|
15
22
|
layer2.mask = mask2;
|
|
@@ -17,25 +24,45 @@ function testMask(mask1, mask2) {
|
|
|
17
24
|
}
|
|
18
25
|
|
|
19
26
|
/**
|
|
27
|
+
* Component that receives and responds to spatial events, like entering or exiting a trigger zone.
|
|
28
|
+
* Used in conjunction with {@link SpatialTrigger} to create interactive spatial events.
|
|
20
29
|
* @category Interactivity
|
|
21
30
|
* @group Components
|
|
22
31
|
*/
|
|
23
32
|
export class SpatialTriggerReceiver extends Behaviour {
|
|
24
33
|
|
|
25
|
-
|
|
34
|
+
/**
|
|
35
|
+
* Bitmask determining which triggers this receiver responds to
|
|
36
|
+
* Only triggers with matching masks will interact with this receiver
|
|
37
|
+
*/
|
|
26
38
|
@serializable()
|
|
27
39
|
triggerMask: number = 0;
|
|
40
|
+
|
|
41
|
+
/** Event invoked when this object enters a trigger zone */
|
|
28
42
|
@serializable(EventList)
|
|
29
43
|
onEnter?: EventList<any>;
|
|
44
|
+
|
|
45
|
+
/** Event invoked continuously while this object is inside a trigger zone */
|
|
30
46
|
@serializable(EventList)
|
|
31
47
|
onStay?: EventList<any>;
|
|
48
|
+
|
|
49
|
+
/** Event invoked when this object exits a trigger zone */
|
|
32
50
|
@serializable(EventList)
|
|
33
51
|
onExit?: EventList<any>;
|
|
34
52
|
|
|
53
|
+
/**
|
|
54
|
+
* Initializes the receiver and logs debug info if enabled
|
|
55
|
+
* @internal
|
|
56
|
+
*/
|
|
35
57
|
start() {
|
|
36
58
|
if (debug) console.log(this.name, this.triggerMask, this);
|
|
37
59
|
}
|
|
38
60
|
|
|
61
|
+
/**
|
|
62
|
+
* Checks for intersections with spatial triggers and fires appropriate events
|
|
63
|
+
* Handles enter, stay, and exit events for all relevant triggers
|
|
64
|
+
* @internal
|
|
65
|
+
*/
|
|
39
66
|
update(): void {
|
|
40
67
|
this.currentIntersected.length = 0;
|
|
41
68
|
for (const trigger of SpatialTrigger.triggers) {
|
|
@@ -59,23 +86,38 @@ export class SpatialTriggerReceiver extends Behaviour {
|
|
|
59
86
|
}
|
|
60
87
|
this.lastIntersected.length = 0;
|
|
61
88
|
this.lastIntersected.push(...this.currentIntersected);
|
|
62
|
-
|
|
63
89
|
}
|
|
64
90
|
|
|
91
|
+
/** Array of triggers currently intersecting with this receiver */
|
|
65
92
|
readonly currentIntersected: SpatialTrigger[] = [];
|
|
93
|
+
|
|
94
|
+
/** Array of triggers that intersected with this receiver in the previous frame */
|
|
66
95
|
readonly lastIntersected: SpatialTrigger[] = [];
|
|
67
96
|
|
|
97
|
+
/**
|
|
98
|
+
* Handles trigger enter events.
|
|
99
|
+
* @param trigger The spatial trigger that was entered
|
|
100
|
+
*/
|
|
68
101
|
onEnterTrigger(trigger: SpatialTrigger): void {
|
|
69
102
|
if(debug) console.log("ENTER TRIGGER", this.name, trigger.name, this, trigger);
|
|
70
103
|
trigger.raiseOnEnterEvent(this);
|
|
71
104
|
this.onEnter?.invoke();
|
|
72
105
|
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Handles trigger exit events.
|
|
109
|
+
* @param trigger The spatial trigger that was exited
|
|
110
|
+
*/
|
|
73
111
|
onExitTrigger(trigger: SpatialTrigger): void {
|
|
74
112
|
if(debug) console.log("EXIT TRIGGER", this.name, trigger.name, );
|
|
75
113
|
trigger.raiseOnExitEvent(this);
|
|
76
114
|
this.onExit?.invoke();
|
|
77
115
|
}
|
|
78
116
|
|
|
117
|
+
/**
|
|
118
|
+
* Handles trigger stay events.
|
|
119
|
+
* @param trigger The spatial trigger that the receiver is staying in
|
|
120
|
+
*/
|
|
79
121
|
onStayTrigger(trigger: SpatialTrigger): void {
|
|
80
122
|
trigger.raiseOnStayEvent(this);
|
|
81
123
|
this.onStay?.invoke();
|
|
@@ -83,24 +125,38 @@ export class SpatialTriggerReceiver extends Behaviour {
|
|
|
83
125
|
}
|
|
84
126
|
|
|
85
127
|
/**
|
|
86
|
-
* A trigger that
|
|
128
|
+
* A spatial trigger component that detects objects within a box-shaped area.
|
|
129
|
+
* Used to trigger events when objects enter, stay in, or exit the defined area
|
|
87
130
|
* @category Interactivity
|
|
88
131
|
* @group Components
|
|
89
132
|
*/
|
|
90
133
|
export class SpatialTrigger extends Behaviour {
|
|
91
134
|
|
|
135
|
+
/** Global registry of all active spatial triggers in the scene */
|
|
92
136
|
static triggers: SpatialTrigger[] = [];
|
|
93
137
|
|
|
138
|
+
/**
|
|
139
|
+
* Bitmask determining which receivers this trigger affects.
|
|
140
|
+
* Only receivers with matching masks will be triggered.
|
|
141
|
+
*/
|
|
142
|
+
// currently Layers in unity but maybe this should be a string or plane number? Or should it be a bitmask to allow receivers use multiple triggers?
|
|
94
143
|
@serializable()
|
|
95
144
|
triggerMask?: number;
|
|
96
145
|
|
|
146
|
+
/** Box helper component used to visualize and calculate the trigger area */
|
|
97
147
|
private boxHelper?: BoxHelperComponent;
|
|
98
148
|
|
|
149
|
+
/**
|
|
150
|
+
* Initializes the trigger and logs debug info if enabled
|
|
151
|
+
*/
|
|
99
152
|
start() {
|
|
100
153
|
if (debug)
|
|
101
154
|
console.log(this.name, this.triggerMask, this);
|
|
102
155
|
}
|
|
103
156
|
|
|
157
|
+
/**
|
|
158
|
+
* Registers this trigger in the global registry and sets up debug visualization if enabled
|
|
159
|
+
*/
|
|
104
160
|
onEnable(): void {
|
|
105
161
|
SpatialTrigger.triggers.push(this);
|
|
106
162
|
if (!this.boxHelper) {
|
|
@@ -108,10 +164,19 @@ export class SpatialTrigger extends Behaviour {
|
|
|
108
164
|
this.boxHelper?.showHelper(null, debug as boolean);
|
|
109
165
|
}
|
|
110
166
|
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Removes this trigger from the global registry when disabled
|
|
170
|
+
*/
|
|
111
171
|
onDisable(): void {
|
|
112
172
|
SpatialTrigger.triggers.splice(SpatialTrigger.triggers.indexOf(this), 1);
|
|
113
173
|
}
|
|
114
174
|
|
|
175
|
+
/**
|
|
176
|
+
* Tests if an object is inside this trigger's box
|
|
177
|
+
* @param obj The object to test against this trigger
|
|
178
|
+
* @returns True if the object is inside the trigger box
|
|
179
|
+
*/
|
|
115
180
|
test(obj: Object3D): boolean {
|
|
116
181
|
if (!this.boxHelper) return false;
|
|
117
182
|
return this.boxHelper.isInBox(obj) ?? false;
|
|
@@ -119,6 +184,10 @@ export class SpatialTrigger extends Behaviour {
|
|
|
119
184
|
|
|
120
185
|
// private args: SpatialTriggerEventArgs = new SpatialTriggerEventArgs();
|
|
121
186
|
|
|
187
|
+
/**
|
|
188
|
+
* Raises the onEnter event on any SpatialTriggerReceiver components attached to this trigger's GameObject
|
|
189
|
+
* @param rec The receiver that entered this trigger
|
|
190
|
+
*/
|
|
122
191
|
raiseOnEnterEvent(rec: SpatialTriggerReceiver) {
|
|
123
192
|
// this.args.trigger = this;
|
|
124
193
|
// this.args.source = rec;
|
|
@@ -130,6 +199,10 @@ export class SpatialTrigger extends Behaviour {
|
|
|
130
199
|
}, false);
|
|
131
200
|
}
|
|
132
201
|
|
|
202
|
+
/**
|
|
203
|
+
* Raises the onStay event on any SpatialTriggerReceiver components attached to this trigger's GameObject
|
|
204
|
+
* @param rec The receiver that is staying in this trigger
|
|
205
|
+
*/
|
|
133
206
|
raiseOnStayEvent(rec: SpatialTriggerReceiver) {
|
|
134
207
|
// this.args.trigger = this;
|
|
135
208
|
// this.args.source = rec;
|
|
@@ -141,6 +214,10 @@ export class SpatialTrigger extends Behaviour {
|
|
|
141
214
|
}, false);
|
|
142
215
|
}
|
|
143
216
|
|
|
217
|
+
/**
|
|
218
|
+
* Raises the onExit event on any SpatialTriggerReceiver components attached to this trigger's GameObject
|
|
219
|
+
* @param rec The receiver that exited this trigger
|
|
220
|
+
*/
|
|
144
221
|
raiseOnExitEvent(rec: SpatialTriggerReceiver) {
|
|
145
222
|
GameObject.foreachComponent(this.gameObject, c => {
|
|
146
223
|
if (c === rec) return;
|