@needle-tools/engine 3.36.3-beta.1 → 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 +6 -0
- package/dist/needle-engine.js +2785 -2684
- package/dist/needle-engine.light.js +2561 -2460
- package/dist/needle-engine.light.min.js +137 -137
- package/dist/needle-engine.light.umd.cjs +109 -109
- package/dist/needle-engine.min.js +96 -96
- package/dist/needle-engine.umd.cjs +109 -109
- 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 +4 -0
- 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 +3 -0
- package/lib/engine/engine_texture.js +3 -0
- 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-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 +4 -0
- 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 +3 -0
- 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-components/OrbitControls.ts +7 -0
- package/src/engine-components/SceneSwitcher.ts +70 -1
|
@@ -45,11 +45,17 @@ export class Addressables {
|
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
/**
|
|
49
|
+
* Find a registered AssetReference by its URL
|
|
50
|
+
*/
|
|
51
|
+
findAssetReference(url: string): AssetReference | null {
|
|
52
|
+
return this._assetReferences[url] || null;
|
|
50
53
|
}
|
|
51
54
|
|
|
52
|
-
/**
|
|
55
|
+
/**
|
|
56
|
+
* Register an asset reference
|
|
57
|
+
* @internal
|
|
58
|
+
*/
|
|
53
59
|
registerAssetReference(ref: AssetReference): AssetReference {
|
|
54
60
|
if (!ref.uri) return ref;
|
|
55
61
|
if (!this._assetReferences[ref.uri]) {
|
|
@@ -83,8 +89,32 @@ const $assetReference = Symbol("assetReference");
|
|
|
83
89
|
*/
|
|
84
90
|
export class AssetReference {
|
|
85
91
|
|
|
92
|
+
/**
|
|
93
|
+
* Experimental!
|
|
94
|
+
* @internal
|
|
95
|
+
* Get an AssetReference for a URL to be easily loaded.
|
|
96
|
+
* AssetReferences are cached so calling this method multiple times with the same arguments will always return the same AssetReference.
|
|
97
|
+
* @param url The URL of the asset to load. The url can be relative or absolute.
|
|
98
|
+
* @param context The context to use for loading the asset
|
|
99
|
+
* @returns the AssetReference for the URL
|
|
100
|
+
*/
|
|
101
|
+
static getOrCreateFromUrl(url: string, context?: Context): AssetReference {
|
|
102
|
+
if (!context) {
|
|
103
|
+
context = Context.Current;
|
|
104
|
+
if (!context)
|
|
105
|
+
throw new Error("Context is required when sourceId is a string. When you call this method from a component you can call it with \"getOrCreate(this, url)\" where \"this\" is the component.");
|
|
106
|
+
}
|
|
107
|
+
const addressables = context.addressables;
|
|
108
|
+
const existing = addressables.findAssetReference(url);
|
|
109
|
+
if (existing) return existing;
|
|
110
|
+
const ref = new AssetReference(url, context.hash);
|
|
111
|
+
addressables.registerAssetReference(ref);
|
|
112
|
+
return ref;
|
|
113
|
+
}
|
|
114
|
+
|
|
86
115
|
/**
|
|
87
|
-
* Get an AssetReference for a URL to be easily loaded.
|
|
116
|
+
* Get an AssetReference for a URL to be easily loaded.
|
|
117
|
+
* AssetReferences are cached so calling this method multiple times with the same arguments will always return the same AssetReference.
|
|
88
118
|
*/
|
|
89
119
|
static getOrCreate(sourceId: SourceIdentifier | IComponent, url: string, context?: Context): AssetReference {
|
|
90
120
|
|
|
@@ -515,6 +545,7 @@ export class ImageReference {
|
|
|
515
545
|
}
|
|
516
546
|
|
|
517
547
|
|
|
548
|
+
/** @internal */
|
|
518
549
|
export class ImageReferenceSerializer extends TypeSerializer {
|
|
519
550
|
constructor() {
|
|
520
551
|
super([ImageReference], "ImageReferenceSerializer");
|
|
@@ -578,6 +609,7 @@ export class FileReference {
|
|
|
578
609
|
}
|
|
579
610
|
|
|
580
611
|
|
|
612
|
+
/** @internal */
|
|
581
613
|
export class FileReferenceSerializer extends TypeSerializer {
|
|
582
614
|
constructor() {
|
|
583
615
|
super([FileReference], "FileReferenceSerializer");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Intersection,Matrix4, Object3D, Ray, Vector2, Vector3 } from 'three';
|
|
1
|
+
import { Intersection, Matrix4, Object3D, Ray, Vector2, Vector3 } from 'three';
|
|
2
2
|
|
|
3
3
|
import { showBalloonMessage, showBalloonWarning } from './debug/debug.js';
|
|
4
4
|
import { Context } from './engine_setup.js';
|
|
@@ -207,6 +207,9 @@ declare type EventListenerOptions = {
|
|
|
207
207
|
signal?: AbortSignal;
|
|
208
208
|
}
|
|
209
209
|
|
|
210
|
+
/**
|
|
211
|
+
* The input system is responsible for handling all input events like pointer events (mouse, touch, xr controllers) and keyboard events.
|
|
212
|
+
*/
|
|
210
213
|
export class Input implements IInput {
|
|
211
214
|
|
|
212
215
|
/** This is a list of event listeners per event type (e.g. pointerdown, pointerup, keydown...). Each entry contains a priority and list of listeners.
|
|
@@ -649,6 +652,7 @@ export class Input implements IInput {
|
|
|
649
652
|
vec2.y = -((vec2.y - this.context.domY) / this.context.domHeight) * 2 + 1;
|
|
650
653
|
}
|
|
651
654
|
|
|
655
|
+
/** @internal */
|
|
652
656
|
constructor(context: Context) {
|
|
653
657
|
this.context = context;
|
|
654
658
|
this.context.post_render_callbacks.push(this.onEndOfFrame);
|
|
@@ -6,6 +6,9 @@ export const $isUsingInstancing = Symbol("isUsingInstancing");
|
|
|
6
6
|
export const $instancingRenderer = Symbol("instancingRenderer");
|
|
7
7
|
export const $instancingAutoUpdateBounds = Symbol("instancingAutoUpdateBounds");
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* Utility class for accessing instancing related properties
|
|
11
|
+
*/
|
|
9
12
|
export class InstancingUtil {
|
|
10
13
|
|
|
11
14
|
/** Is this object rendered using a InstancedMesh */
|
|
@@ -10,6 +10,7 @@ const debug = getParam("debuglicense");
|
|
|
10
10
|
let NEEDLE_ENGINE_LICENSE_TYPE: string = "basic";
|
|
11
11
|
if (debug) console.log("License Type: " + NEEDLE_ENGINE_LICENSE_TYPE)
|
|
12
12
|
|
|
13
|
+
/** @internal */
|
|
13
14
|
export function hasProLicense() {
|
|
14
15
|
switch (NEEDLE_ENGINE_LICENSE_TYPE) {
|
|
15
16
|
case "pro":
|
|
@@ -19,6 +20,7 @@ export function hasProLicense() {
|
|
|
19
20
|
return false;
|
|
20
21
|
}
|
|
21
22
|
|
|
23
|
+
/** @internal */
|
|
22
24
|
export function hasIndieLicense() {
|
|
23
25
|
switch (NEEDLE_ENGINE_LICENSE_TYPE) {
|
|
24
26
|
case "indie":
|
|
@@ -27,11 +29,13 @@ export function hasIndieLicense() {
|
|
|
27
29
|
return false;
|
|
28
30
|
}
|
|
29
31
|
|
|
32
|
+
/** @internal */
|
|
30
33
|
export function hasCommercialLicense() {
|
|
31
34
|
return hasProLicense() || hasIndieLicense();
|
|
32
35
|
}
|
|
33
36
|
|
|
34
37
|
const _licenseCheckResultChangedCallbacks: ((result: boolean) => void)[] = [];
|
|
38
|
+
/** @internal */
|
|
35
39
|
export function onLicenseCheckResultChanged(cb: (result: boolean) => void) {
|
|
36
40
|
if (hasProLicense() || hasIndieLicense())
|
|
37
41
|
return cb(true);
|
|
@@ -151,6 +151,8 @@ export function beginListenDestroy(context: Context) {
|
|
|
151
151
|
|
|
152
152
|
// when a file is instantiated via some server (e.g. via file drop) we also want to send the info where the file can be downloaded
|
|
153
153
|
// doing it this route will ensure we have
|
|
154
|
+
|
|
155
|
+
/** @internal */
|
|
154
156
|
export class HostData {
|
|
155
157
|
filename: string;
|
|
156
158
|
hash: string;
|
|
@@ -15,6 +15,7 @@ import { registerComponentExtension, registerExtensions } from "./extensions/ext
|
|
|
15
15
|
import { NEEDLE_components } from "./extensions/NEEDLE_components.js";
|
|
16
16
|
|
|
17
17
|
|
|
18
|
+
/** @internal */
|
|
18
19
|
export class NeedleGltfLoader implements INeedleGltfLoader {
|
|
19
20
|
createBuiltinComponents(context: Context, gltfId: string, gltf: any, seed: number | UIDProvider | null, extension?: NEEDLE_components | undefined) {
|
|
20
21
|
return createBuiltinComponents(context, gltfId, gltf, seed, extension);
|
|
@@ -103,6 +104,13 @@ export async function createGLTFLoader(url: string, context: Context) {
|
|
|
103
104
|
return loader;
|
|
104
105
|
}
|
|
105
106
|
|
|
107
|
+
/** Load a gltf file from a url. This is the core method used by Needle Engine to load gltf files. All known extensions are registered here.
|
|
108
|
+
* @param context The current context
|
|
109
|
+
* @param data The gltf data as string or ArrayBuffer
|
|
110
|
+
* @param path The path to the gltf file
|
|
111
|
+
* @param seed The seed for generating unique ids
|
|
112
|
+
* @returns The loaded gltf object
|
|
113
|
+
*/
|
|
106
114
|
export async function parseSync(context: Context, data: string | ArrayBuffer, path: string, seed: number | UIDProvider | null): Promise<GLTF | undefined> {
|
|
107
115
|
if (typeof path !== "string") {
|
|
108
116
|
console.warn("Parse gltf binary without path, this might lead to errors in resolving extensions. Please provide the source path of the gltf/glb file", path, typeof path);
|
|
@@ -153,6 +161,15 @@ export async function parseSync(context: Context, data: string | ArrayBuffer, pa
|
|
|
153
161
|
});
|
|
154
162
|
}
|
|
155
163
|
|
|
164
|
+
/**
|
|
165
|
+
* Load a gltf file from a url. This is the core method used by Needle Engine to load gltf files. All known extensions are registered here.
|
|
166
|
+
* @param context The current context
|
|
167
|
+
* @param url The url to the gltf file
|
|
168
|
+
* @param sourceId The source id of the gltf file - this is usually the url
|
|
169
|
+
* @param seed The seed for generating unique ids
|
|
170
|
+
* @param prog A progress callback
|
|
171
|
+
* @returns The loaded gltf object
|
|
172
|
+
*/
|
|
156
173
|
export async function loadSync(context: Context, url: string, sourceId: string, seed: number | UIDProvider | null, prog?: (ProgressEvent) => void): Promise<GLTF | undefined> {
|
|
157
174
|
// better to create new loaders every time
|
|
158
175
|
// (maybe we can cache them...)
|
|
@@ -11,6 +11,12 @@ const white = new Uint8Array(4);
|
|
|
11
11
|
white[0] = 255; white[1] = 255; white[2] = 255; white[3] = 255;
|
|
12
12
|
export const whiteDefaultTexture = new DataTexture(white, 1, 1, RGBAFormat);
|
|
13
13
|
|
|
14
|
+
/**
|
|
15
|
+
* Creates a new texture with a single color
|
|
16
|
+
* @param col Color to use
|
|
17
|
+
* @param size Size of the texture
|
|
18
|
+
* @returns A texture with the specified color
|
|
19
|
+
*/
|
|
14
20
|
export function createFlatTexture(col: RGBAColor | Color, size: number = 1) {
|
|
15
21
|
const hasAlpha = "alpha" in col;
|
|
16
22
|
const length = size * size;
|
|
@@ -31,6 +37,15 @@ export function createFlatTexture(col: RGBAColor | Color, size: number = 1) {
|
|
|
31
37
|
return tex;
|
|
32
38
|
}
|
|
33
39
|
|
|
40
|
+
/**
|
|
41
|
+
* Creates a new texture with three colors
|
|
42
|
+
* @param col0 First color
|
|
43
|
+
* @param col1 Second color
|
|
44
|
+
* @param col2 Third color
|
|
45
|
+
* @param width Width of the texture
|
|
46
|
+
* @param height Height of the texture
|
|
47
|
+
* @returns A texture with the specified colors
|
|
48
|
+
*/
|
|
34
49
|
export function createTrilightTexture<T extends Color>(col0: T, col1: T, col2: T, width: number = 1, height: number = 3) {
|
|
35
50
|
const hasAlpha = false;// "alpha" in col0;
|
|
36
51
|
const channels = 4;
|
|
@@ -62,11 +77,13 @@ export function createTrilightTexture<T extends Color>(col0: T, col1: T, col2: T
|
|
|
62
77
|
return tex;
|
|
63
78
|
}
|
|
64
79
|
|
|
80
|
+
/** @internal */
|
|
65
81
|
export enum Stage {
|
|
66
82
|
Vertex,
|
|
67
83
|
Fragment,
|
|
68
84
|
}
|
|
69
85
|
|
|
86
|
+
/** @internal */
|
|
70
87
|
export class UnityShaderStage {
|
|
71
88
|
stage: Stage;
|
|
72
89
|
code: string;
|
|
@@ -105,9 +122,11 @@ class ShaderLib {
|
|
|
105
122
|
}
|
|
106
123
|
}
|
|
107
124
|
|
|
125
|
+
/** @internal */
|
|
108
126
|
export const lib = new ShaderLib();
|
|
109
127
|
|
|
110
128
|
|
|
129
|
+
/** @internal */
|
|
111
130
|
export function ToUnityMatrixArray(mat: Matrix4, buffer?: Array<Vector4>): Array<Vector4> {
|
|
112
131
|
const arr = mat.elements;
|
|
113
132
|
if (!buffer)
|
|
@@ -126,6 +145,8 @@ export function ToUnityMatrixArray(mat: Matrix4, buffer?: Array<Vector4>): Array
|
|
|
126
145
|
|
|
127
146
|
const noAmbientLight: Array<number> = [];
|
|
128
147
|
const copyBuffer: Array<number> = [];
|
|
148
|
+
|
|
149
|
+
/** @internal */
|
|
129
150
|
export function SetUnitySphericalHarmonics(obj: object, array?: number[]) {
|
|
130
151
|
|
|
131
152
|
if (noAmbientLight.length === 0) {
|
|
@@ -147,6 +168,7 @@ export function SetUnitySphericalHarmonics(obj: object, array?: number[]) {
|
|
|
147
168
|
obj["unity_SHC"] = { value: new Vector4(array[24], array[25], array[26], 1) };
|
|
148
169
|
}
|
|
149
170
|
|
|
171
|
+
/** @internal */
|
|
150
172
|
export class ShaderBundle {
|
|
151
173
|
readonly vertexShader: string;
|
|
152
174
|
readonly fragmentShader: string;
|
|
@@ -159,6 +181,7 @@ export class ShaderBundle {
|
|
|
159
181
|
}
|
|
160
182
|
}
|
|
161
183
|
|
|
184
|
+
/** @internal */
|
|
162
185
|
export async function FindShaderTechniques(shaderData: SHADERDATA.ShaderData, id: number): Promise<ShaderBundle | null> {
|
|
163
186
|
// console.log(shaderData);
|
|
164
187
|
if (!shaderData) {
|
|
@@ -191,6 +214,7 @@ export async function FindShaderTechniques(shaderData: SHADERDATA.ShaderData, id
|
|
|
191
214
|
return null;
|
|
192
215
|
}
|
|
193
216
|
|
|
217
|
+
/** @internal */
|
|
194
218
|
async function loadShaderCode(shader: SHADERDATA.Shader) {
|
|
195
219
|
const uri = shader.uri;
|
|
196
220
|
if (!uri) return;
|
|
@@ -21,6 +21,9 @@ const _prevVisible = Symbol("previous-visibility");
|
|
|
21
21
|
*/
|
|
22
22
|
export class RenderTexture extends WebGLRenderTarget {
|
|
23
23
|
|
|
24
|
+
/**
|
|
25
|
+
* Render the scene to the texture
|
|
26
|
+
*/
|
|
24
27
|
render(scene: Object3D, camera: Camera, renderer: WebGLRenderer | EffectComposer) {
|
|
25
28
|
if (renderer instanceof EffectComposer) {
|
|
26
29
|
if (!this["_unsupported_effectcomposer_warning"]) {
|
|
@@ -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;
|
|
@@ -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
|
}
|