@needle-tools/engine 3.36.3-beta → 3.36.4-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 +11 -0
- package/dist/needle-engine.js +2824 -2711
- package/dist/needle-engine.light.js +1522 -1409
- package/dist/needle-engine.light.min.js +151 -146
- package/dist/needle-engine.light.umd.cjs +124 -119
- package/dist/needle-engine.min.js +110 -105
- package/dist/needle-engine.umd.cjs +124 -119
- package/lib/engine/engine_addressables.d.ts +22 -3
- package/lib/engine/engine_addressables.js +36 -4
- package/lib/engine/engine_addressables.js.map +1 -1
- package/lib/engine/engine_input.d.ts +4 -0
- package/lib/engine/engine_input.js +4 -0
- package/lib/engine/engine_input.js.map +1 -1
- package/lib/engine/engine_instancing.d.ts +3 -0
- package/lib/engine/engine_instancing.js +3 -0
- package/lib/engine/engine_instancing.js.map +1 -1
- package/lib/engine/engine_license.d.ts +4 -0
- package/lib/engine/engine_license.js +37 -13
- package/lib/engine/engine_license.js.map +1 -1
- package/lib/engine/engine_networking_instantiate.d.ts +1 -0
- package/lib/engine/engine_networking_instantiate.js +1 -0
- package/lib/engine/engine_networking_instantiate.js.map +1 -1
- package/lib/engine/engine_scenetools.d.ts +17 -0
- package/lib/engine/engine_scenetools.js +17 -0
- package/lib/engine/engine_scenetools.js.map +1 -1
- package/lib/engine/engine_shaders.d.ts +22 -0
- package/lib/engine/engine_shaders.js +23 -0
- package/lib/engine/engine_shaders.js.map +1 -1
- package/lib/engine/engine_texture.d.ts +5 -1
- package/lib/engine/engine_texture.js +5 -1
- package/lib/engine/engine_texture.js.map +1 -1
- package/lib/engine/engine_three_utils.d.ts +46 -2
- package/lib/engine/engine_three_utils.js +47 -1
- package/lib/engine/engine_three_utils.js.map +1 -1
- package/lib/engine/engine_types.d.ts +8 -0
- package/lib/engine/engine_types.js +8 -1
- package/lib/engine/engine_types.js.map +1 -1
- package/lib/engine/engine_util_decorator.d.ts +2 -1
- package/lib/engine/engine_util_decorator.js +2 -1
- package/lib/engine/engine_util_decorator.js.map +1 -1
- package/lib/engine/engine_utils.js +4 -1
- package/lib/engine/engine_utils.js.map +1 -1
- package/lib/engine/webcomponents/needle menu/needle-menu.d.ts +2 -1
- package/lib/engine/webcomponents/needle menu/needle-menu.js +13 -3
- package/lib/engine/webcomponents/needle menu/needle-menu.js.map +1 -1
- package/lib/engine-components/Component.d.ts +1 -1
- package/lib/engine-components/ContactShadows.js +5 -0
- package/lib/engine-components/ContactShadows.js.map +1 -1
- package/lib/engine-components/OrbitControls.js +8 -0
- package/lib/engine-components/OrbitControls.js.map +1 -1
- package/lib/engine-components/SceneSwitcher.d.ts +50 -0
- package/lib/engine-components/SceneSwitcher.js +66 -0
- package/lib/engine-components/SceneSwitcher.js.map +1 -1
- package/package.json +1 -1
- package/src/engine/engine_addressables.ts +36 -4
- package/src/engine/engine_input.ts +5 -1
- package/src/engine/engine_instancing.ts +3 -0
- package/src/engine/engine_license.ts +33 -12
- package/src/engine/engine_networking_instantiate.ts +2 -0
- package/src/engine/engine_scenetools.ts +17 -0
- package/src/engine/engine_shaders.ts +24 -0
- package/src/engine/engine_texture.ts +5 -1
- package/src/engine/engine_three_utils.ts +56 -7
- package/src/engine/engine_types.ts +8 -1
- package/src/engine/engine_util_decorator.ts +3 -2
- package/src/engine/engine_utils.ts +3 -1
- package/src/engine/webcomponents/needle menu/needle-menu.ts +15 -3
- package/src/engine-components/Component.ts +1 -1
- package/src/engine-components/ContactShadows.ts +9 -1
- package/src/engine-components/OrbitControls.ts +7 -0
- package/src/engine-components/SceneSwitcher.ts +70 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { AnimationAction, Euler, Mesh,Object3D, PerspectiveCamera, PlaneGeometry, Quaternion, Scene, Texture, Uniform, Vector3 } from "three";
|
|
2
|
-
import { ShaderMaterial,WebGLRenderer } from "three";
|
|
1
|
+
import { AnimationAction, Euler, Mesh, Object3D, PerspectiveCamera, PlaneGeometry, Quaternion, Scene, Texture, Uniform, Vector3 } from "three";
|
|
2
|
+
import { ShaderMaterial, WebGLRenderer } from "three";
|
|
3
3
|
|
|
4
4
|
import { Mathf } from "./engine_math.js"
|
|
5
5
|
import { CircularBuffer } from "./engine_utils.js";
|
|
@@ -13,6 +13,7 @@ export function slerp(vec: Vector3, end: Vector3, t: number) {
|
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
const flipYQuat: Quaternion = new Quaternion().setFromAxisAngle(new Vector3(0, 1, 0), Math.PI);
|
|
16
|
+
|
|
16
17
|
export function lookAtInverse(obj: Object3D, target: Vector3) {
|
|
17
18
|
|
|
18
19
|
obj.lookAt(target);
|
|
@@ -50,6 +51,14 @@ export function lookAtObject(object: Object3D, target: Object3D, keepUpDirection
|
|
|
50
51
|
|
|
51
52
|
|
|
52
53
|
const _tempVecs = new CircularBuffer(() => new Vector3(), 100);
|
|
54
|
+
|
|
55
|
+
/** Gets a temporary vector. If a vector is passed in it will be copied to the temporary vector
|
|
56
|
+
* Temporary vectors are cached and reused internally. Don't store them!
|
|
57
|
+
* @param vecOrX the vector to copy or the x value
|
|
58
|
+
* @param y the y value
|
|
59
|
+
* @param z the z value
|
|
60
|
+
* @returns a temporary vector
|
|
61
|
+
*/
|
|
53
62
|
export function getTempVector(vecOrX?: Vector3 | number | DOMPointReadOnly, y?: number, z?: number) {
|
|
54
63
|
const vec = _tempVecs.get();
|
|
55
64
|
if (vecOrX instanceof Vector3) vec.copy(vecOrX);
|
|
@@ -62,6 +71,13 @@ export function getTempVector(vecOrX?: Vector3 | number | DOMPointReadOnly, y?:
|
|
|
62
71
|
return vec;
|
|
63
72
|
}
|
|
64
73
|
const _tempQuats = new CircularBuffer(() => new Quaternion(), 100);
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Gets a temporary quaternion. If a quaternion is passed in it will be copied to the temporary quaternion
|
|
77
|
+
* Temporary quaternions are cached and reused internally. Don't store them!
|
|
78
|
+
* @param value the quaternion to copy
|
|
79
|
+
* @returns a temporary quaternion
|
|
80
|
+
*/
|
|
65
81
|
export function getTempQuaternion(value?: Quaternion | DOMPointReadOnly) {
|
|
66
82
|
const val = _tempQuats.get();
|
|
67
83
|
if (value instanceof Quaternion) val.copy(value);
|
|
@@ -73,6 +89,13 @@ export function getTempQuaternion(value?: Quaternion | DOMPointReadOnly) {
|
|
|
73
89
|
const _worldPositions = new CircularBuffer(() => new Vector3(), 100);
|
|
74
90
|
const _lastMatrixWorldUpdateKey = Symbol("lastMatrixWorldUpdateKey");
|
|
75
91
|
|
|
92
|
+
/**
|
|
93
|
+
* Get the world position of an object
|
|
94
|
+
* @param obj the object to get the world position from
|
|
95
|
+
* @param vec a vector to store the result in. If not passed in a temporary vector will be used
|
|
96
|
+
* @param updateParents if true the parents will be updated before getting the world position
|
|
97
|
+
* @returns the world position
|
|
98
|
+
*/
|
|
76
99
|
export function getWorldPosition(obj: Object3D, vec: Vector3 | null = null, updateParents: boolean = true): Vector3 {
|
|
77
100
|
const wp = vec ?? _worldPositions.get();
|
|
78
101
|
if (!obj) return wp.set(0, 0, 0);
|
|
@@ -87,20 +110,34 @@ export function getWorldPosition(obj: Object3D, vec: Vector3 | null = null, upda
|
|
|
87
110
|
return wp;
|
|
88
111
|
}
|
|
89
112
|
|
|
90
|
-
|
|
91
|
-
|
|
113
|
+
/**
|
|
114
|
+
* Set the world position of an object
|
|
115
|
+
* @param obj the object to set the world position of
|
|
116
|
+
* @param val the world position to set
|
|
117
|
+
*/
|
|
118
|
+
export function setWorldPosition(obj: Object3D, val: Vector3): Object3D {
|
|
119
|
+
if (!obj) return obj;
|
|
92
120
|
const wp = _worldPositions.get();
|
|
93
121
|
if (val !== wp)
|
|
94
122
|
wp.copy(val);
|
|
95
123
|
const obj2 = obj?.parent ?? obj;
|
|
96
124
|
obj2.worldToLocal(wp);
|
|
97
125
|
obj.position.set(wp.x, wp.y, wp.z);
|
|
126
|
+
return obj;
|
|
98
127
|
}
|
|
99
128
|
|
|
100
|
-
|
|
129
|
+
/**
|
|
130
|
+
* Set the world position of an object
|
|
131
|
+
* @param obj the object to set the world position of
|
|
132
|
+
* @param x the x position
|
|
133
|
+
* @param y the y position
|
|
134
|
+
* @param z the z position
|
|
135
|
+
*/
|
|
136
|
+
export function setWorldPositionXYZ(obj: Object3D, x: number, y: number, z: number): Object3D {
|
|
101
137
|
const wp = _worldPositions.get();
|
|
102
138
|
wp.set(x, y, z);
|
|
103
139
|
setWorldPosition(obj, wp);
|
|
140
|
+
return obj;
|
|
104
141
|
}
|
|
105
142
|
|
|
106
143
|
|
|
@@ -254,7 +291,7 @@ export function logHierarchy(root: Object3D | null | undefined, collapsible: boo
|
|
|
254
291
|
|
|
255
292
|
export function getParentHierarchyPath(obj: Object3D): string {
|
|
256
293
|
let path = obj?.name || "";
|
|
257
|
-
if(!obj) return path;
|
|
294
|
+
if (!obj) return path;
|
|
258
295
|
let parent = obj.parent;
|
|
259
296
|
while (parent) {
|
|
260
297
|
path = parent.name + "/" + path;
|
|
@@ -278,6 +315,9 @@ export function isAnimationAction(obj: object) {
|
|
|
278
315
|
|
|
279
316
|
|
|
280
317
|
|
|
318
|
+
/**
|
|
319
|
+
* Utility class to perform various graphics operations like copying textures to canvas
|
|
320
|
+
*/
|
|
281
321
|
export class Graphics {
|
|
282
322
|
private static planeGeometry = new PlaneGeometry(2, 2, 1, 1);
|
|
283
323
|
private static renderer: WebGLRenderer | undefined = undefined;
|
|
@@ -300,6 +340,9 @@ export class Graphics {
|
|
|
300
340
|
}`;
|
|
301
341
|
private static blipMaterial: ShaderMaterial | undefined = undefined;
|
|
302
342
|
|
|
343
|
+
/**
|
|
344
|
+
* Create a blit material for copying textures
|
|
345
|
+
*/
|
|
303
346
|
static createBlitMaterial(fragment: string): ShaderMaterial {
|
|
304
347
|
return new ShaderMaterial({
|
|
305
348
|
uniforms: { map: new Uniform(null) },
|
|
@@ -309,7 +352,13 @@ export class Graphics {
|
|
|
309
352
|
}
|
|
310
353
|
private static mesh: Mesh | undefined = undefined;
|
|
311
354
|
|
|
312
|
-
|
|
355
|
+
/**
|
|
356
|
+
* Copy a texture to a new texture
|
|
357
|
+
* @param texture the texture to copy
|
|
358
|
+
* @param blitMaterial the material to use for copying (optional)
|
|
359
|
+
* @returns the newly created, copied texture
|
|
360
|
+
*/
|
|
361
|
+
static copyTexture(texture: Texture, blitMaterial?: ShaderMaterial): Texture {
|
|
313
362
|
const material = blitMaterial ?? this.blipMaterial!;
|
|
314
363
|
material.uniforms.map.value = texture;
|
|
315
364
|
material.needsUpdate = true;
|
|
@@ -320,6 +320,9 @@ export type Vec4 = {
|
|
|
320
320
|
|
|
321
321
|
const contactsVectorBuffer = new CircularBuffer(() => new Vector3(), 20);
|
|
322
322
|
|
|
323
|
+
/**
|
|
324
|
+
* Holds information about physics contacts
|
|
325
|
+
*/
|
|
323
326
|
export class ContactPoint {
|
|
324
327
|
|
|
325
328
|
private readonly _point: Vec3;
|
|
@@ -350,6 +353,7 @@ export class ContactPoint {
|
|
|
350
353
|
return target.set(this._tangentVelocity.x, this._tangentVelocity.y, this._tangentVelocity.z);
|
|
351
354
|
}
|
|
352
355
|
|
|
356
|
+
/** @internal */
|
|
353
357
|
constructor(point: Vec3, dist: number, normal: Vec3, impulse: number, friction: number, tangentVelocity: Vec3) {
|
|
354
358
|
this._point = point;
|
|
355
359
|
this.distance = dist;
|
|
@@ -360,12 +364,15 @@ export class ContactPoint {
|
|
|
360
364
|
}
|
|
361
365
|
}
|
|
362
366
|
|
|
363
|
-
|
|
367
|
+
/**
|
|
368
|
+
* Holds information about a collision event. Includes a list of contact points and the colliders involved
|
|
369
|
+
*/
|
|
364
370
|
export class Collision {
|
|
365
371
|
|
|
366
372
|
/** The contact points of this collision. Contains information about positions, normals, distance, friction, impulse... */
|
|
367
373
|
readonly contacts: ContactPoint[];
|
|
368
374
|
|
|
375
|
+
/** @internal */
|
|
369
376
|
constructor(obj: IGameObject, otherCollider: ICollider, contacts: ContactPoint[]) {
|
|
370
377
|
this.me = obj;
|
|
371
378
|
this._collider = otherCollider;
|
|
@@ -96,8 +96,9 @@ function createPropertyWrapper(target: IComponent | any, _propertyKey: string |
|
|
|
96
96
|
|
|
97
97
|
|
|
98
98
|
|
|
99
|
-
/**
|
|
100
|
-
*
|
|
99
|
+
/** Experimental attribute
|
|
100
|
+
* Use to hook into another type's methods and run before the other methods run (similar to Harmony prefixes).
|
|
101
|
+
* Return false to prevent the original method from running.
|
|
101
102
|
*/
|
|
102
103
|
export const prefix = function <T>(type: Constructor<T>) {
|
|
103
104
|
return function (target: IComponent | any, _propertyKey: string | { name: string }, _PropertyDescriptor: PropertyDescriptor) {
|
|
@@ -319,10 +319,12 @@ export function resolveUrl(source: SourceIdentifier | undefined, uri: string): s
|
|
|
319
319
|
if (pathIndex >= 0) {
|
|
320
320
|
// Take the source uri as the base path
|
|
321
321
|
const basePath = source.substring(0, pathIndex + 1);
|
|
322
|
+
// make sure we don't have double slashes
|
|
323
|
+
while (basePath.endsWith("/") && uri.startsWith("/")) uri = uri.substring(1);
|
|
322
324
|
// Append the relative uri
|
|
323
325
|
const newUri = basePath + uri;
|
|
324
326
|
// newUri = new URL(newUri, globalThis.location.href).href;
|
|
325
|
-
if (debugGetPath) console.log("source:", source, "
|
|
327
|
+
if (debugGetPath) console.log("source:", source, "changed uri \nfrom", uri, "\nto ", newUri, "\nbasePath: " + basePath);
|
|
326
328
|
return newUri;
|
|
327
329
|
}
|
|
328
330
|
return uri;
|
|
@@ -34,7 +34,7 @@ export class NeedleMenu {
|
|
|
34
34
|
private readonly _spatialMenu: NeedleSpatialMenu;
|
|
35
35
|
|
|
36
36
|
constructor(context: Context) {
|
|
37
|
-
this._menu = NeedleMenuElement.getOrCreate(context.domElement);
|
|
37
|
+
this._menu = NeedleMenuElement.getOrCreate(context.domElement, context);
|
|
38
38
|
this._context = context;
|
|
39
39
|
this._spatialMenu = new NeedleSpatialMenu(context, this._menu);
|
|
40
40
|
window.addEventListener("message", this.onPostMessage);
|
|
@@ -160,7 +160,7 @@ export class NeedleMenuElement extends HTMLElement {
|
|
|
160
160
|
return document.createElement(elementName, { is: elementName });
|
|
161
161
|
}
|
|
162
162
|
|
|
163
|
-
static getOrCreate(domElement: HTMLElement) {
|
|
163
|
+
static getOrCreate(domElement: HTMLElement, context: Context) {
|
|
164
164
|
let element = domElement.querySelector(elementName) as NeedleMenuElement | null;
|
|
165
165
|
if (!element && domElement.shadowRoot) {
|
|
166
166
|
element = domElement.shadowRoot.querySelector(elementName);
|
|
@@ -168,6 +168,7 @@ export class NeedleMenuElement extends HTMLElement {
|
|
|
168
168
|
if (!element) {
|
|
169
169
|
element = NeedleMenuElement.create() as NeedleMenuElement;
|
|
170
170
|
element._domElement = domElement;
|
|
171
|
+
element._context = context;
|
|
171
172
|
if (domElement.shadowRoot)
|
|
172
173
|
domElement.shadowRoot.appendChild(element);
|
|
173
174
|
else
|
|
@@ -177,6 +178,7 @@ export class NeedleMenuElement extends HTMLElement {
|
|
|
177
178
|
}
|
|
178
179
|
|
|
179
180
|
private _domElement: HTMLElement | null = null;
|
|
181
|
+
private _context: Context | null = null;
|
|
180
182
|
|
|
181
183
|
constructor() {
|
|
182
184
|
super();
|
|
@@ -414,6 +416,11 @@ export class NeedleMenuElement extends HTMLElement {
|
|
|
414
416
|
});
|
|
415
417
|
|
|
416
418
|
|
|
419
|
+
|
|
420
|
+
let context = this._context;
|
|
421
|
+
// we need to assign it in the timeout because the reference is set *after* the constructor did run
|
|
422
|
+
setTimeout(() => context = this._context);
|
|
423
|
+
|
|
417
424
|
// watch changes
|
|
418
425
|
let showInterval = -1;
|
|
419
426
|
const rootObserver = new MutationObserver(mutations => {
|
|
@@ -425,7 +432,12 @@ export class NeedleMenuElement extends HTMLElement {
|
|
|
425
432
|
if (!hasProLicense()) {
|
|
426
433
|
clearInterval(showInterval);
|
|
427
434
|
showInterval = setInterval(() => {
|
|
428
|
-
if (
|
|
435
|
+
if (context?.isInAR && context.arOverlayElement) {
|
|
436
|
+
if (parent != context.arOverlayElement) {
|
|
437
|
+
context.arOverlayElement.appendChild(this);
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
else if (parent != this._domElement?.shadowRoot)
|
|
429
441
|
this._domElement?.shadowRoot?.appendChild(this);
|
|
430
442
|
this.style.display = "flex";
|
|
431
443
|
this.style.visibility = "visible";
|
|
@@ -447,7 +447,7 @@ export abstract class Component implements IComponent, EventTarget,
|
|
|
447
447
|
this.__destroyed = true;
|
|
448
448
|
}
|
|
449
449
|
/** called when you decorate fields with the @validate() decorator
|
|
450
|
-
* @param
|
|
450
|
+
* @param prop the name of the field that was changed
|
|
451
451
|
*/
|
|
452
452
|
onValidate?(prop?: string): void;
|
|
453
453
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CustomBlending, DoubleSide, Group, Matrix4, MaxEquation, Mesh, MeshBasicMaterial, MeshDepthMaterial, MinEquation, OrthographicCamera, PlaneGeometry, ShaderMaterial, WebGLRenderTarget } from "three";
|
|
1
|
+
import { CustomBlending, DoubleSide, FrontSide, Group, Matrix4, MaxEquation, Mesh, MeshBasicMaterial, MeshDepthMaterial, MinEquation, Object3D, OrthographicCamera, PlaneGeometry, RenderItem, ShaderMaterial, WebGLRenderTarget } from "three";
|
|
2
2
|
import { HorizontalBlurShader } from 'three/examples/jsm/shaders/HorizontalBlurShader.js';
|
|
3
3
|
import { VerticalBlurShader } from 'three/examples/jsm/shaders/VerticalBlurShader.js';
|
|
4
4
|
|
|
@@ -214,11 +214,19 @@ export class ContactShadows extends Behaviour {
|
|
|
214
214
|
|
|
215
215
|
const prevXRState = renderer.xr.enabled;
|
|
216
216
|
renderer.xr.enabled = false;
|
|
217
|
+
|
|
218
|
+
const list = renderer.renderLists.get(scene, 0);
|
|
219
|
+
const prev = list.transparent;
|
|
220
|
+
list.transparent = [];
|
|
217
221
|
|
|
218
222
|
// render to the render target to get the depths
|
|
219
223
|
renderer.setRenderTarget(this.renderTarget);
|
|
224
|
+
renderer.clear();
|
|
225
|
+
|
|
220
226
|
renderer.render(scene, this.shadowCamera);
|
|
221
227
|
|
|
228
|
+
list.transparent = prev;
|
|
229
|
+
|
|
222
230
|
// for the shearing idea
|
|
223
231
|
// this.shadowCamera.projectionMatrix.copy(mat);
|
|
224
232
|
|
|
@@ -639,10 +639,17 @@ export class OrbitControls extends Behaviour implements ICameraController {
|
|
|
639
639
|
expandByObjectRecursive(child);
|
|
640
640
|
}
|
|
641
641
|
}
|
|
642
|
+
let hasAnyObject = false;
|
|
642
643
|
for (const object of objects) {
|
|
644
|
+
if (!object) continue;
|
|
645
|
+
hasAnyObject = true;
|
|
643
646
|
object.updateMatrixWorld();
|
|
644
647
|
expandByObjectRecursive(object);
|
|
645
648
|
}
|
|
649
|
+
if (!hasAnyObject) {
|
|
650
|
+
console.warn("No objects to fit camera to...");
|
|
651
|
+
return;
|
|
652
|
+
}
|
|
646
653
|
|
|
647
654
|
camera.updateMatrixWorld();
|
|
648
655
|
camera.updateProjectionMatrix();
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Object3D } from "three";
|
|
2
2
|
|
|
3
|
-
import { AssetReference } from "../engine/engine_addressables.js";
|
|
3
|
+
import { Addressables, AssetReference } from "../engine/engine_addressables.js";
|
|
4
4
|
import { registerObservableAttribute } from "../engine/engine_element_extras.js";
|
|
5
5
|
import { InputEvents } from "../engine/engine_input.js";
|
|
6
6
|
import { isLocalNetwork } from "../engine/engine_networking_utils.js";
|
|
@@ -64,6 +64,7 @@ export interface ISceneEventListener {
|
|
|
64
64
|
* - `loadscene-start`: Called when a scene starts loading
|
|
65
65
|
* - `loadscene-finished`: Called when a scene finished loading
|
|
66
66
|
* - `progress`: Called when a scene is loading and the progress changes
|
|
67
|
+
* - `scene-opened`: Called when a scene is loaded and added to the SceneSwitcher's GameObject
|
|
67
68
|
* @example
|
|
68
69
|
* ```ts
|
|
69
70
|
* sceneSwitcher.addEventListener("loadscene-start", (e) => {
|
|
@@ -75,6 +76,9 @@ export interface ISceneEventListener {
|
|
|
75
76
|
* sceneSwitcher.addEventListener("progress", (e) => {
|
|
76
77
|
* console.log("Loading progress", e.loaded, e.total);
|
|
77
78
|
* });
|
|
79
|
+
* sceneSwitcher.addEventListener("scene-opened", (e) => {
|
|
80
|
+
* console.log("Scene opened", e.detail.scene.uri);
|
|
81
|
+
* });
|
|
78
82
|
* ```
|
|
79
83
|
*
|
|
80
84
|
*/
|
|
@@ -159,10 +163,14 @@ export class SceneSwitcher extends Behaviour {
|
|
|
159
163
|
|
|
160
164
|
private _preloadScheduler?: PreLoadScheduler;
|
|
161
165
|
|
|
166
|
+
/** @internal */
|
|
162
167
|
awake(): void {
|
|
168
|
+
if (this.scenes === undefined) this.scenes = [];
|
|
169
|
+
|
|
163
170
|
if (debug) console.log("SceneSwitcher", this);
|
|
164
171
|
}
|
|
165
172
|
|
|
173
|
+
/** @internal */
|
|
166
174
|
async onEnable() {
|
|
167
175
|
globalThis.addEventListener("popstate", this.onPopState);
|
|
168
176
|
this.context.input.addEventListener(InputEvents.KeyDown, this.onInputKeyDown);
|
|
@@ -210,6 +218,7 @@ export class SceneSwitcher extends Behaviour {
|
|
|
210
218
|
}
|
|
211
219
|
}
|
|
212
220
|
|
|
221
|
+
/** @internal */
|
|
213
222
|
onDisable(): void {
|
|
214
223
|
globalThis.removeEventListener("popstate", this.onPopState);
|
|
215
224
|
this.context.input.removeEventListener(InputEvents.KeyDown, this.onInputKeyDown);
|
|
@@ -288,14 +297,58 @@ export class SceneSwitcher extends Behaviour {
|
|
|
288
297
|
}
|
|
289
298
|
}
|
|
290
299
|
|
|
300
|
+
/**
|
|
301
|
+
* Add a scene to the SceneSwitcher.
|
|
302
|
+
* If the scene is already added it will be added again.
|
|
303
|
+
* @param urlOrAssetReference The url of the scene or an AssetReference to the scene
|
|
304
|
+
* @returns The AssetReference of the scene that was added
|
|
305
|
+
* @example
|
|
306
|
+
* ```ts
|
|
307
|
+
* // adding a scene:
|
|
308
|
+
* sceneSwitcher.addScene("scene1.glb");
|
|
309
|
+
* // add another scene and load it:
|
|
310
|
+
* const scene2 = sceneSwitcher.addScene("scene2.glb");
|
|
311
|
+
* sceneSwitcher.switchScene(scene2).then(res => { console.log("Scene loaded", res); });
|
|
312
|
+
* ```
|
|
313
|
+
*/
|
|
314
|
+
addScene(urlOrAssetReference: string | AssetReference): AssetReference {
|
|
315
|
+
if (typeof urlOrAssetReference === "string") {
|
|
316
|
+
let assetReference = this.context.addressables.findAssetReference(urlOrAssetReference);
|
|
317
|
+
if (!assetReference) {
|
|
318
|
+
assetReference = new AssetReference(urlOrAssetReference);
|
|
319
|
+
this.context.addressables.registerAssetReference(assetReference);
|
|
320
|
+
}
|
|
321
|
+
this.scenes.push(assetReference);
|
|
322
|
+
return assetReference;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
this.scenes.push(urlOrAssetReference);
|
|
326
|
+
return urlOrAssetReference;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Load the next scene in the scenes array ({@link this.currentIndex} + 1)
|
|
331
|
+
* If the current scene is the last scene in the array and {@link this.clamp} is disabled then the first scene will be loaded.
|
|
332
|
+
* @returns a promise that resolves to true if the scene was loaded successfully
|
|
333
|
+
*/
|
|
291
334
|
selectNext(): Promise<boolean> {
|
|
292
335
|
return this.select(this._currentIndex + 1);
|
|
293
336
|
}
|
|
294
337
|
|
|
338
|
+
/**
|
|
339
|
+
* Load the previous scene in the scenes array ({@link this.currentIndex} - 1)
|
|
340
|
+
* If the current scene is the first scene in the array and {@link this.clamp} is disabled then the last scene will be loaded.
|
|
341
|
+
* @returns a promise that resolves to true if the scene was loaded successfully
|
|
342
|
+
*/
|
|
295
343
|
selectPrev(): Promise<boolean> {
|
|
296
344
|
return this.select(this._currentIndex - 1);
|
|
297
345
|
}
|
|
298
346
|
|
|
347
|
+
/**
|
|
348
|
+
* Load a scene by its index in the scenes array.
|
|
349
|
+
* @param index The index of the scene or a string that represents the scene uri (if the url is not known to the SceneSwitcher it will try to load the scene by its uri but it won't be added to the current scenes array. Use {@link addScene} to add a scene to the SceneSwitcher)
|
|
350
|
+
* @returns a promise that resolves to true if the scene was loaded successfully
|
|
351
|
+
*/
|
|
299
352
|
select(index: number | string): Promise<boolean> {
|
|
300
353
|
if (debug) console.log("select", index);
|
|
301
354
|
|
|
@@ -341,6 +394,19 @@ export class SceneSwitcher extends Behaviour {
|
|
|
341
394
|
private __lastSwitchScene?: AssetReference;
|
|
342
395
|
private __lastSwitchScenePromise?: Promise<boolean>;
|
|
343
396
|
|
|
397
|
+
/**
|
|
398
|
+
* Switch to a scene by its AssetReference.
|
|
399
|
+
* If the scene is already loaded it will be unloaded and the new scene will be loaded.
|
|
400
|
+
* If the scene is already loading it will wait for the scene to be loaded.
|
|
401
|
+
* If the scene is already loaded and the same scene is requested again it will return the same promise that was returned the first time the scene was requested.
|
|
402
|
+
* @param scene The AssetReference of the scene to switch to
|
|
403
|
+
* @returns a promise that resolves to true if the scene was loaded successfully
|
|
404
|
+
* @example
|
|
405
|
+
* ```ts
|
|
406
|
+
* const myAssetReference = new AssetReference("scene1.glb");
|
|
407
|
+
* sceneSwitcher.switchScene(myAssetReference).then(res => { console.log("Scene loaded", res); });
|
|
408
|
+
* ```
|
|
409
|
+
*/
|
|
344
410
|
async switchScene(scene: AssetReference): Promise<boolean> {
|
|
345
411
|
if (!(scene instanceof AssetReference)) {
|
|
346
412
|
const type = typeof scene;
|
|
@@ -444,6 +510,9 @@ export class SceneSwitcher extends Behaviour {
|
|
|
444
510
|
const res = sceneListener.sceneOpened(this);
|
|
445
511
|
if (res instanceof Promise) await res;
|
|
446
512
|
}
|
|
513
|
+
|
|
514
|
+
const openedEvt = new CustomEvent<LoadSceneEvent>("scene-opened", { detail: { scene: scene, switcher: this, index: index } });
|
|
515
|
+
this.dispatchEvent(openedEvt);
|
|
447
516
|
return true;
|
|
448
517
|
}
|
|
449
518
|
}
|