@needle-tools/engine 4.8.7-next.e134730 → 4.8.8-next.12b5946
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 +13 -0
- package/README.md +55 -42
- package/components.needle.json +1 -1
- package/dist/{needle-engine.bundle-DrGsgE4t.min.js → needle-engine.bundle-CS7vqRb3.min.js} +144 -144
- package/dist/{needle-engine.bundle-X9nxhICu.umd.cjs → needle-engine.bundle-kYzccQZF.umd.cjs} +147 -147
- package/dist/{needle-engine.bundle-CvRpjtJj.js → needle-engine.bundle-lC7eSFno.js} +7090 -7010
- package/dist/needle-engine.d.ts +7 -0
- package/dist/needle-engine.js +2 -2
- package/dist/needle-engine.min.js +1 -1
- package/dist/needle-engine.umd.cjs +1 -1
- package/lib/engine/engine_addressables.d.ts +12 -12
- package/lib/engine/engine_addressables.js +32 -23
- package/lib/engine/engine_addressables.js.map +1 -1
- package/lib/engine/engine_animation.d.ts +1 -3
- package/lib/engine/engine_animation.js +18 -13
- package/lib/engine/engine_animation.js.map +1 -1
- package/lib/engine/engine_assetdatabase.js +6 -6
- package/lib/engine/engine_assetdatabase.js.map +1 -1
- package/lib/engine/engine_camera.d.ts +23 -3
- package/lib/engine/engine_camera.js +34 -2
- package/lib/engine/engine_camera.js.map +1 -1
- package/lib/engine/engine_context.d.ts +15 -0
- package/lib/engine/engine_context.js +33 -0
- package/lib/engine/engine_context.js.map +1 -1
- package/lib/engine/engine_create_objects.d.ts +3 -3
- package/lib/engine/engine_create_objects.js +5 -4
- package/lib/engine/engine_create_objects.js.map +1 -1
- package/lib/engine/engine_gameobject.js +2 -2
- package/lib/engine/engine_gameobject.js.map +1 -1
- package/lib/engine/engine_gltf_builtin_components.js +11 -11
- package/lib/engine/engine_gltf_builtin_components.js.map +1 -1
- package/lib/engine/engine_loaders.callbacks.d.ts +1 -0
- package/lib/engine/engine_loaders.callbacks.js +1 -0
- package/lib/engine/engine_loaders.callbacks.js.map +1 -1
- package/lib/engine/engine_loaders.js +15 -11
- package/lib/engine/engine_loaders.js.map +1 -1
- package/lib/engine/extensions/NEEDLE_lighting_settings.js +5 -2
- package/lib/engine/extensions/NEEDLE_lighting_settings.js.map +1 -1
- package/lib/engine/extensions/NEEDLE_lightmaps.js +1 -1
- package/lib/engine/extensions/NEEDLE_lightmaps.js.map +1 -1
- package/lib/engine/js-extensions/Object3D.d.ts +1 -1
- package/lib/engine/js-extensions/Vector.d.ts +5 -0
- package/lib/engine/js-extensions/Vector.js.map +1 -1
- package/lib/engine/webcomponents/needle-engine.attributes.d.ts +1 -0
- package/lib/engine/webcomponents/needle-engine.d.ts +2 -2
- package/lib/engine/webcomponents/needle-engine.js +19 -21
- package/lib/engine/webcomponents/needle-engine.js.map +1 -1
- package/lib/engine-components/Animation.js +2 -1
- package/lib/engine-components/Animation.js.map +1 -1
- package/lib/engine-components/AnimationUtilsAutoplay.js +1 -6
- package/lib/engine-components/AnimationUtilsAutoplay.js.map +1 -1
- package/lib/engine-components/Camera.d.ts +2 -0
- package/lib/engine-components/Camera.js +5 -1
- package/lib/engine-components/Camera.js.map +1 -1
- package/lib/engine-components/ContactShadows.d.ts +12 -2
- package/lib/engine-components/ContactShadows.js +24 -4
- package/lib/engine-components/ContactShadows.js.map +1 -1
- package/lib/engine-components/DropListener.d.ts +21 -15
- package/lib/engine-components/DropListener.js +38 -34
- package/lib/engine-components/DropListener.js.map +1 -1
- package/lib/engine-components/LookAtConstraint.d.ts +5 -1
- package/lib/engine-components/LookAtConstraint.js +8 -0
- package/lib/engine-components/LookAtConstraint.js.map +1 -1
- package/lib/engine-components/NestedGltf.d.ts +9 -4
- package/lib/engine-components/NestedGltf.js +32 -26
- package/lib/engine-components/NestedGltf.js.map +1 -1
- package/lib/engine-components/OrbitControls.d.ts +30 -9
- package/lib/engine-components/OrbitControls.js +56 -19
- package/lib/engine-components/OrbitControls.js.map +1 -1
- package/lib/engine-components/Renderer.js +2 -1
- package/lib/engine-components/Renderer.js.map +1 -1
- package/lib/engine-components/Skybox.js +8 -9
- package/lib/engine-components/Skybox.js.map +1 -1
- package/lib/engine-components/api.d.ts +1 -0
- package/lib/engine-components/api.js.map +1 -1
- package/lib/engine-components/export/usdz/Extension.d.ts +1 -1
- package/lib/engine-components/export/usdz/ThreeUSDZExporter.js +1 -1
- package/lib/engine-components/export/usdz/ThreeUSDZExporter.js.map +1 -1
- package/lib/engine-components/export/usdz/USDZExporter.d.ts +7 -0
- package/lib/engine-components/export/usdz/USDZExporter.js +8 -1
- package/lib/engine-components/export/usdz/USDZExporter.js.map +1 -1
- package/lib/engine-components/webxr/WebXRImageTracking.d.ts +4 -2
- package/lib/engine-components/webxr/WebXRImageTracking.js +117 -81
- package/lib/engine-components/webxr/WebXRImageTracking.js.map +1 -1
- package/package.json +2 -2
- package/plugins/common/files.js +6 -3
- package/plugins/vite/alias.js +26 -17
- package/plugins/vite/editor-connection.js +4 -4
- package/src/engine/engine_addressables.ts +46 -33
- package/src/engine/engine_animation.ts +20 -13
- package/src/engine/engine_assetdatabase.ts +7 -7
- package/src/engine/engine_camera.ts +54 -3
- package/src/engine/engine_context.ts +39 -1
- package/src/engine/engine_create_objects.ts +8 -7
- package/src/engine/engine_gameobject.ts +2 -2
- package/src/engine/engine_gltf_builtin_components.ts +12 -11
- package/src/engine/engine_loaders.callbacks.ts +1 -0
- package/src/engine/engine_loaders.ts +18 -13
- package/src/engine/extensions/NEEDLE_lighting_settings.ts +5 -2
- package/src/engine/extensions/NEEDLE_lightmaps.ts +1 -1
- package/src/engine/js-extensions/Vector.ts +6 -0
- package/src/engine/webcomponents/needle-engine.attributes.ts +2 -0
- package/src/engine/webcomponents/needle-engine.ts +21 -21
- package/src/engine-components/Animation.ts +1 -1
- package/src/engine-components/AnimationUtilsAutoplay.ts +1 -6
- package/src/engine-components/Camera.ts +7 -1
- package/src/engine-components/ContactShadows.ts +27 -6
- package/src/engine-components/DropListener.ts +44 -34
- package/src/engine-components/LookAtConstraint.ts +9 -1
- package/src/engine-components/NestedGltf.ts +33 -24
- package/src/engine-components/OrbitControls.ts +81 -32
- package/src/engine-components/Renderer.ts +2 -1
- package/src/engine-components/Skybox.ts +9 -10
- package/src/engine-components/api.ts +2 -1
- package/src/engine-components/export/usdz/Extension.ts +1 -1
- package/src/engine-components/export/usdz/ThreeUSDZExporter.ts +1 -1
- package/src/engine-components/export/usdz/USDZExporter.ts +21 -12
- package/src/engine-components/webxr/WebXRImageTracking.ts +138 -90
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BackSide, CustomBlending, DoubleSide, FrontSide, Group, Material, Matrix4, MaxEquation, Mesh, MeshBasicMaterial, MeshDepthMaterial, MeshStandardMaterial, MinEquation, Object3D, OrthographicCamera, PlaneGeometry, RenderItem, ShaderMaterial, Vector3, WebGLRenderTarget } from "three";
|
|
1
|
+
import { BackSide, CustomBlending, DoubleSide, FrontSide, Group, Material, Matrix4, MaxEquation, Mesh, MeshBasicMaterial, MeshDepthMaterial, MeshStandardMaterial, MinEquation, Object3D, OrthographicCamera, PlaneGeometry, RenderItem, ShaderMaterial, Vector3, Vector3Like, 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
|
|
|
@@ -27,7 +27,14 @@ onStart(ctx => {
|
|
|
27
27
|
shadows.darkness = intensity;
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
|
-
})
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
type FitParameters = {
|
|
34
|
+
object?: Object3D | Object3D[];
|
|
35
|
+
positionOffset?: Partial<Vector3Like>;
|
|
36
|
+
scaleFactor?: Partial<Vector3Like>;
|
|
37
|
+
}
|
|
31
38
|
|
|
32
39
|
// Adapted from https://github.com/mrdoob/three.js/blob/master/examples/webgl_shadow_contact.html.
|
|
33
40
|
|
|
@@ -50,7 +57,7 @@ export class ContactShadows extends Behaviour {
|
|
|
50
57
|
* @param context The context to create the contact shadows in.
|
|
51
58
|
* @returns The instance of the contact shadows.
|
|
52
59
|
*/
|
|
53
|
-
static auto(context?: Context): ContactShadows {
|
|
60
|
+
static auto(context?: Context, params?: FitParameters): ContactShadows {
|
|
54
61
|
if (!context) context = Context.Current;
|
|
55
62
|
if (!context) {
|
|
56
63
|
throw new Error("No context provided and no current context set.");
|
|
@@ -65,12 +72,13 @@ export class ContactShadows extends Behaviour {
|
|
|
65
72
|
this._instances.set(context, instance);
|
|
66
73
|
}
|
|
67
74
|
context.scene.add(instance.gameObject);
|
|
68
|
-
instance.fitShadows();
|
|
75
|
+
instance.fitShadows(params);
|
|
69
76
|
return instance;
|
|
70
77
|
}
|
|
71
78
|
|
|
72
79
|
/**
|
|
73
80
|
* When enabled the contact shadows component will be created to fit the whole scene.
|
|
81
|
+
* @default false
|
|
74
82
|
*/
|
|
75
83
|
@serializable()
|
|
76
84
|
autoFit: boolean = false;
|
|
@@ -107,12 +115,14 @@ export class ContactShadows extends Behaviour {
|
|
|
107
115
|
|
|
108
116
|
/**
|
|
109
117
|
* The minimum size of the shadows box
|
|
118
|
+
* @default undefined
|
|
110
119
|
*/
|
|
111
120
|
minSize?: Partial<Vec3>;
|
|
112
121
|
|
|
113
122
|
/**
|
|
114
123
|
* When enabled the shadows will not be updated automatically. Use `needsUpdate()` to update the shadows manually.
|
|
115
124
|
* This is useful when you want to update the shadows only when the scene changes.
|
|
125
|
+
* @default false
|
|
116
126
|
*/
|
|
117
127
|
manualUpdate: boolean = false;
|
|
118
128
|
/**
|
|
@@ -150,10 +160,11 @@ export class ContactShadows extends Behaviour {
|
|
|
150
160
|
/**
|
|
151
161
|
* Call to fit the shadows to the scene.
|
|
152
162
|
*/
|
|
153
|
-
fitShadows() {
|
|
163
|
+
fitShadows(params: FitParameters = {}) {
|
|
154
164
|
if (debug) console.warn("Fitting shadows to scene");
|
|
155
165
|
setAutoFitEnabled(this.shadowsRoot, false);
|
|
156
|
-
const
|
|
166
|
+
const objectToFit = params.object || this.context.scene;
|
|
167
|
+
const box = getBoundingBox(objectToFit, [this.shadowsRoot]);
|
|
157
168
|
// expand box in all directions (except below ground)
|
|
158
169
|
// 0.75 expands by 75% in each direction
|
|
159
170
|
// The "32" is pretty much heuristically determined – adjusting the value until we don't get a visible border anymore.
|
|
@@ -175,6 +186,16 @@ export class ContactShadows extends Behaviour {
|
|
|
175
186
|
// We can't move GroundProjection down because of immersive-ar mesh/plane tracking where occlusion would otherwise hide GroundProjection
|
|
176
187
|
this.shadowsRoot.position.set((min.x + box.max.x) / 2, min.y - offset, (min.z + box.max.z) / 2);
|
|
177
188
|
this.shadowsRoot.scale.set(box.max.x - min.x, box.max.y - min.y, box.max.z - min.z);
|
|
189
|
+
if (params.positionOffset) {
|
|
190
|
+
if (params.positionOffset.x !== undefined) this.shadowsRoot.position.x += params.positionOffset.x;
|
|
191
|
+
if (params.positionOffset.y !== undefined) this.shadowsRoot.position.y += params.positionOffset.y;
|
|
192
|
+
if (params.positionOffset.z !== undefined) this.shadowsRoot.position.z += params.positionOffset.z;
|
|
193
|
+
}
|
|
194
|
+
if (params.scaleFactor) {
|
|
195
|
+
if (params.scaleFactor.x !== undefined) this.shadowsRoot.scale.x *= params.scaleFactor.x;
|
|
196
|
+
if (params.scaleFactor.y !== undefined) this.shadowsRoot.scale.y *= params.scaleFactor.y;
|
|
197
|
+
if (params.scaleFactor.z !== undefined) this.shadowsRoot.scale.z *= params.scaleFactor.z;
|
|
198
|
+
}
|
|
178
199
|
this.applyMinSize();
|
|
179
200
|
this.shadowsRoot.matrixWorldNeedsUpdate = true;
|
|
180
201
|
if (debug) console.log("Fitted shadows to scene", this.shadowsRoot.scale.clone());
|
|
@@ -143,13 +143,6 @@ const blobKeyName = "blob";
|
|
|
143
143
|
*/
|
|
144
144
|
export class DropListener extends Behaviour {
|
|
145
145
|
|
|
146
|
-
/**
|
|
147
|
-
* When enabled, the DropListener will automatically synchronize dropped files to other connected clients.
|
|
148
|
-
* When a file is dropped locally, it will be uploaded to blob storage and the URL will be shared with other clients.
|
|
149
|
-
*/
|
|
150
|
-
@serializable()
|
|
151
|
-
useNetworking: boolean = true;
|
|
152
|
-
|
|
153
146
|
/**
|
|
154
147
|
* When assigned, the DropListener will only accept files that are dropped on this specific object.
|
|
155
148
|
* This allows creating designated drop zones in your scene.
|
|
@@ -159,7 +152,10 @@ export class DropListener extends Behaviour {
|
|
|
159
152
|
|
|
160
153
|
/**
|
|
161
154
|
* When enabled, dropped objects will be automatically scaled to fit within the volume defined by fitVolumeSize.
|
|
162
|
-
* Useful for ensuring dropped models appear at an appropriate scale.
|
|
155
|
+
* Useful for ensuring dropped models appear at an appropriate scale.
|
|
156
|
+
*
|
|
157
|
+
* **Tip**: Use the handy `fitObjectIntoVolume` function (`import { fitObjectIntoVolume } from "@needle-tools/engine"`) for custom fitting needs.
|
|
158
|
+
*
|
|
163
159
|
* @default false
|
|
164
160
|
*/
|
|
165
161
|
@serializable()
|
|
@@ -180,6 +176,14 @@ export class DropListener extends Behaviour {
|
|
|
180
176
|
@serializable()
|
|
181
177
|
placeAtHitPosition: boolean = true;
|
|
182
178
|
|
|
179
|
+
/**
|
|
180
|
+
* When enabled, the DropListener will automatically synchronize dropped files to other connected clients.
|
|
181
|
+
* When a file is dropped locally, it will be uploaded to blob storage and the URL will be shared with other clients.
|
|
182
|
+
* @default false
|
|
183
|
+
*/
|
|
184
|
+
@serializable()
|
|
185
|
+
useNetworking: boolean = false;
|
|
186
|
+
|
|
183
187
|
/**
|
|
184
188
|
* Event list that gets invoked after a file has been successfully added to the scene.
|
|
185
189
|
* Receives {@link DropListenerOnDropArguments} containing the added object and related information.
|
|
@@ -193,6 +197,27 @@ export class DropListener extends Behaviour {
|
|
|
193
197
|
@serializable(EventList)
|
|
194
198
|
onDropped: EventList<DropListenerOnDropArguments> = new EventList();
|
|
195
199
|
|
|
200
|
+
/**
|
|
201
|
+
* Loads a file from the given URL and adds it to the scene.
|
|
202
|
+
* @returns A promise that resolves to the loaded object or null if loading failed.
|
|
203
|
+
*/
|
|
204
|
+
loadFromURL(url: string, data?: { point?: Vec3, size?: Vec3 }): Promise<Object3D | null> {
|
|
205
|
+
return this.addFromUrl(url, { screenposition: new Vector2(), point: data?.point, size: data?.size, }, false);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Forgets all previously added objects.
|
|
210
|
+
* The droplistener will then not be able to remove previously added objects.
|
|
211
|
+
*/
|
|
212
|
+
forgetObjects() {
|
|
213
|
+
this.removePreviouslyAddedObjects(false);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
// #region internals
|
|
220
|
+
|
|
196
221
|
/** @internal */
|
|
197
222
|
onEnable(): void {
|
|
198
223
|
this.context.renderer.domElement.addEventListener("dragover", this.onDrag);
|
|
@@ -208,21 +233,6 @@ export class DropListener extends Behaviour {
|
|
|
208
233
|
this.context.connection.stopListen("droplistener", this.onNetworkEvent);
|
|
209
234
|
}
|
|
210
235
|
|
|
211
|
-
/**
|
|
212
|
-
* Loads a file from the given URL and adds it to the scene.
|
|
213
|
-
*/
|
|
214
|
-
loadFromURL(url: string, data?: { point?: Vec3, size?: Vec3 }) {
|
|
215
|
-
this.addFromUrl(url, { screenposition: new Vector2(), point: data?.point, size: data?.size, }, true);
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
/**
|
|
219
|
-
* Forgets all previously added objects.
|
|
220
|
-
* The droplistener will then not be able to remove previously added objects.
|
|
221
|
-
*/
|
|
222
|
-
forgetObjects() {
|
|
223
|
-
this.removePreviouslyAddedObjects(false);
|
|
224
|
-
}
|
|
225
|
-
|
|
226
236
|
/**
|
|
227
237
|
* Handles network events received from other clients containing information about dropped objects
|
|
228
238
|
* @param evt Network event data containing object information, position, and content URL
|
|
@@ -326,7 +336,7 @@ export class DropListener extends Behaviour {
|
|
|
326
336
|
}
|
|
327
337
|
}
|
|
328
338
|
if (files.length > 0) {
|
|
329
|
-
await this.
|
|
339
|
+
await this.addFromFiles(files, ctx);
|
|
330
340
|
}
|
|
331
341
|
}
|
|
332
342
|
|
|
@@ -359,6 +369,7 @@ export class DropListener extends Behaviour {
|
|
|
359
369
|
// Ignore dropped images
|
|
360
370
|
const lowercaseUrl = url.toLowerCase();
|
|
361
371
|
if (lowercaseUrl.endsWith(".hdr") || lowercaseUrl.endsWith(".hdri") || lowercaseUrl.endsWith(".exr") || lowercaseUrl.endsWith(".png") || lowercaseUrl.endsWith(".jpg") || lowercaseUrl.endsWith(".jpeg")) {
|
|
372
|
+
console.warn(`Fileformat is not supported: ${lowercaseUrl}`);
|
|
362
373
|
return null;
|
|
363
374
|
}
|
|
364
375
|
|
|
@@ -374,7 +385,7 @@ export class DropListener extends Behaviour {
|
|
|
374
385
|
});
|
|
375
386
|
if (res && this._addedObjects.length <= 0) {
|
|
376
387
|
ctx.url = url;
|
|
377
|
-
const obj = this.
|
|
388
|
+
const obj = this.onObjectLoaded(res, ctx, isRemote);
|
|
378
389
|
return obj;
|
|
379
390
|
}
|
|
380
391
|
}
|
|
@@ -388,12 +399,13 @@ export class DropListener extends Behaviour {
|
|
|
388
399
|
private _abort: AbortController | null = null;
|
|
389
400
|
|
|
390
401
|
/**
|
|
391
|
-
* Processes dropped files
|
|
392
|
-
*
|
|
402
|
+
* Processes dropped files and loads them as 3D models.
|
|
403
|
+
* When enabled, it also handles network drops (sending files between clients).
|
|
404
|
+
* Automatically handles cancelling previous uploads if new files are dropped.
|
|
393
405
|
* @param fileList Array of dropped files
|
|
394
|
-
* @param ctx Context information about where the drop occurred
|
|
406
|
+
* @param ctx Context information about where on the screen or in 3D space the drop occurred
|
|
395
407
|
*/
|
|
396
|
-
private async
|
|
408
|
+
private async addFromFiles(fileList: Array<File>, ctx: DropContext) {
|
|
397
409
|
if (debug) console.log("Add files", fileList)
|
|
398
410
|
if (!Array.isArray(fileList)) return;
|
|
399
411
|
if (!fileList.length) return;
|
|
@@ -427,7 +439,7 @@ export class DropListener extends Behaviour {
|
|
|
427
439
|
if (res) {
|
|
428
440
|
this.dispatchEvent(new CustomEvent(DropListenerEvents.FileDropped, { detail: file }));
|
|
429
441
|
ctx.file = file;
|
|
430
|
-
const obj = this.
|
|
442
|
+
const obj = this.onObjectLoaded(res, ctx, false);
|
|
431
443
|
|
|
432
444
|
// handle uploading the dropped object and networking the event
|
|
433
445
|
if (obj && this.context.connection.isConnected && this.useNetworking) {
|
|
@@ -478,7 +490,7 @@ export class DropListener extends Behaviour {
|
|
|
478
490
|
* @param isRemote Whether this object was shared from a remote client
|
|
479
491
|
* @returns The added object or null if adding failed
|
|
480
492
|
*/
|
|
481
|
-
private
|
|
493
|
+
private onObjectLoaded(data: { model: Model, contentMD5: string }, ctx: DropContext, isRemote: boolean): Object3D | null {
|
|
482
494
|
|
|
483
495
|
const { model, contentMD5 } = data;
|
|
484
496
|
|
|
@@ -522,9 +534,7 @@ export class DropListener extends Behaviour {
|
|
|
522
534
|
}
|
|
523
535
|
}
|
|
524
536
|
|
|
525
|
-
AnimationUtils.
|
|
526
|
-
createAnimationComponent: obj => addComponent(obj, Animation)
|
|
527
|
-
});
|
|
537
|
+
AnimationUtils.autoplayAnimations(model);
|
|
528
538
|
|
|
529
539
|
const evt = new DropListenerAddedEvent({
|
|
530
540
|
sender: this,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Object3D } from "three";
|
|
1
|
+
import { Object3D, Vector3 } from "three";
|
|
2
2
|
|
|
3
3
|
import { serializable } from "../engine/engine_serialization_decorator.js";
|
|
4
4
|
import { Behaviour } from "./Component.js";
|
|
@@ -23,4 +23,12 @@ export class LookAtConstraint extends Behaviour {
|
|
|
23
23
|
*/
|
|
24
24
|
@serializable(Object3D)
|
|
25
25
|
sources: Object3D[] = [];
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Set the position of the constraint.
|
|
29
|
+
*/
|
|
30
|
+
setConstraintPosition(worldPosition: Vector3) {
|
|
31
|
+
const source = this.sources[0];
|
|
32
|
+
if (source) source.worldPosition = worldPosition;
|
|
33
|
+
}
|
|
26
34
|
}
|
|
@@ -4,6 +4,7 @@ import { InstantiateIdProvider } from "../engine/engine_networking_instantiate.j
|
|
|
4
4
|
import { serializable } from "../engine/engine_serialization_decorator.js";
|
|
5
5
|
import { getParam } from "../engine/engine_utils.js";
|
|
6
6
|
import { Behaviour } from "../engine-components/Component.js";
|
|
7
|
+
import { EventList } from "./EventList.js";
|
|
7
8
|
|
|
8
9
|
const debug = getParam("debugnestedgltf");
|
|
9
10
|
|
|
@@ -11,17 +12,21 @@ const debug = getParam("debugnestedgltf");
|
|
|
11
12
|
* It will load the gltf file and instantiate it as a child of the parent of the GameObject that has this component
|
|
12
13
|
*/
|
|
13
14
|
export class NestedGltf extends Behaviour {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
*/
|
|
15
|
+
|
|
16
|
+
/** A reference to the gltf file that should be loaded */
|
|
17
17
|
@serializable(AssetReference)
|
|
18
18
|
filePath?: AssetReference;
|
|
19
19
|
|
|
20
|
+
/** Invoked when the nested glTF file has been instantiated */
|
|
21
|
+
@serializable(EventList)
|
|
22
|
+
loaded: EventList<{ component: NestedGltf, instance: any, asset: AssetReference }> = new EventList();
|
|
23
|
+
|
|
20
24
|
/**
|
|
21
25
|
* EXPERIMENTAL for cloud asset loading
|
|
22
26
|
*/
|
|
23
27
|
loadAssetInParent = true;
|
|
24
28
|
|
|
29
|
+
|
|
25
30
|
private _isLoadingOrDoneLoading: boolean = false;
|
|
26
31
|
|
|
27
32
|
/** Register a callback that will be called when the progress of the loading changes */
|
|
@@ -31,7 +36,7 @@ export class NestedGltf extends Behaviour {
|
|
|
31
36
|
|
|
32
37
|
/** Begin loading the referenced gltf file in filePath */
|
|
33
38
|
preload() {
|
|
34
|
-
this.filePath?.preload();
|
|
39
|
+
return this.filePath?.preload() || null;
|
|
35
40
|
}
|
|
36
41
|
|
|
37
42
|
/** @internal */
|
|
@@ -41,27 +46,31 @@ export class NestedGltf extends Behaviour {
|
|
|
41
46
|
|
|
42
47
|
const parent = this.gameObject.parent;
|
|
43
48
|
if (parent) {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
res
|
|
56
|
-
|
|
57
|
-
res.
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
49
|
+
|
|
50
|
+
if (this.filePath) {
|
|
51
|
+
|
|
52
|
+
this._isLoadingOrDoneLoading = true;
|
|
53
|
+
const opts = new InstantiateOptions();
|
|
54
|
+
// we need to provide stable guids for creating nested gltfs
|
|
55
|
+
opts.idProvider = new InstantiateIdProvider(this.hash(this.guid));
|
|
56
|
+
opts.parent = this.loadAssetInParent !== false ? parent : this.gameObject;
|
|
57
|
+
this.gameObject.updateMatrix();
|
|
58
|
+
const matrix = this.gameObject.matrix;
|
|
59
|
+
if (debug) console.log("Load nested:", this.filePath?.url ?? this.filePath, this.gameObject.position);
|
|
60
|
+
const res = await this.filePath?.instantiate?.call(this.filePath, opts);
|
|
61
|
+
if (debug) console.log("Nested loaded:", this.filePath?.url ?? this.filePath, res);
|
|
62
|
+
if (res && this.loadAssetInParent !== false) {
|
|
63
|
+
res.matrixAutoUpdate = false;
|
|
64
|
+
res.matrix.identity();
|
|
65
|
+
res.applyMatrix4(matrix);
|
|
66
|
+
res.matrixAutoUpdate = true;
|
|
67
|
+
res.layers.disableAll();
|
|
68
|
+
res.layers.set(this.layer);
|
|
69
|
+
this.loaded.invoke({ component: this, instance: res, asset: this.filePath });
|
|
70
|
+
}
|
|
71
|
+
if (debug) console.log("Nested loading done:", this.filePath?.url ?? this.filePath, res);
|
|
63
72
|
}
|
|
64
|
-
|
|
73
|
+
|
|
65
74
|
}
|
|
66
75
|
}
|
|
67
76
|
|
|
@@ -649,12 +649,12 @@ export class OrbitControls extends Behaviour implements ICameraController {
|
|
|
649
649
|
if (this._lookTargetLerpActive) {
|
|
650
650
|
this._lookTargetLerp01 += this.context.time.deltaTime / this._lookTargetLerpDuration;
|
|
651
651
|
if (this._lookTargetLerp01 >= 1) {
|
|
652
|
-
this.
|
|
652
|
+
this.lerpLookTarget(this._lookTargetEndPosition, this._lookTargetEndPosition, 1);
|
|
653
653
|
this._lookTargetLerpActive = false;
|
|
654
654
|
this.dispatchEvent(new CameraTargetReachedEvent(this, "lookat"));
|
|
655
655
|
} else {
|
|
656
656
|
const t = Mathf.easeInOutCubic(this._lookTargetLerp01);
|
|
657
|
-
this.
|
|
657
|
+
this.lerpLookTarget(this._lookTargetStartPosition, this._lookTargetEndPosition, t);
|
|
658
658
|
}
|
|
659
659
|
}
|
|
660
660
|
|
|
@@ -729,7 +729,9 @@ export class OrbitControls extends Behaviour implements ICameraController {
|
|
|
729
729
|
|
|
730
730
|
// this._controls.zoomToCursor = this.zoomToCursor;
|
|
731
731
|
if (!this.context.isInXR) {
|
|
732
|
-
if (!freeCam && this.lookAtConstraint?.locked
|
|
732
|
+
if (!freeCam && this.lookAtConstraint?.locked && !this._lookTargetLerpActive) {
|
|
733
|
+
this.setLookTargetFromConstraint(0, this.lookAtConstraint01);
|
|
734
|
+
}
|
|
733
735
|
this._controls.update(this.context.time.deltaTime);
|
|
734
736
|
|
|
735
737
|
if (debug) {
|
|
@@ -887,6 +889,8 @@ export class OrbitControls extends Behaviour implements ICameraController {
|
|
|
887
889
|
}
|
|
888
890
|
else this._fovLerpDuration = this.targetLerpDuration;
|
|
889
891
|
}
|
|
892
|
+
|
|
893
|
+
// if (this.context.mainCameraComponent) this.context.mainCameraComponent.fieldOfView = fov;
|
|
890
894
|
}
|
|
891
895
|
|
|
892
896
|
/** Moves the camera look-at target to a position smoothly.
|
|
@@ -910,7 +914,7 @@ export class OrbitControls extends Behaviour implements ICameraController {
|
|
|
910
914
|
}
|
|
911
915
|
|
|
912
916
|
if (immediateOrDuration === true) {
|
|
913
|
-
this.
|
|
917
|
+
this.lerpLookTarget(this._lookTargetEndPosition, this._lookTargetEndPosition, 1);
|
|
914
918
|
}
|
|
915
919
|
else {
|
|
916
920
|
this._lookTargetLerpActive = true;
|
|
@@ -941,20 +945,18 @@ export class OrbitControls extends Behaviour implements ICameraController {
|
|
|
941
945
|
const target = sources[index];
|
|
942
946
|
if (target) {
|
|
943
947
|
target.getWorldPosition(this._lookTargetEndPosition);
|
|
944
|
-
this.lerpLookTarget(this._lookTargetEndPosition, t);
|
|
948
|
+
this.lerpLookTarget(this._controls.target, this._lookTargetEndPosition, t);
|
|
945
949
|
return true;
|
|
946
950
|
}
|
|
947
951
|
}
|
|
948
952
|
return false;
|
|
949
953
|
}
|
|
950
954
|
|
|
951
|
-
|
|
952
|
-
public lerpTarget(position: Vector3, delta: number) { return this.lerpLookTarget(position, delta); }
|
|
953
|
-
|
|
954
|
-
private lerpLookTarget(position: Vector3, delta: number) {
|
|
955
|
+
private lerpLookTarget(start: Vector3, position: Vector3, t: number) {
|
|
955
956
|
if (!this._controls) return;
|
|
956
|
-
if (
|
|
957
|
-
else this._controls.target.
|
|
957
|
+
if (t >= 1) this._controls.target.copy(position);
|
|
958
|
+
else this._controls.target.lerpVectors(start, position, t);
|
|
959
|
+
if (this.lookAtConstraint) this.lookAtConstraint.setConstraintPosition(this._controls.target);
|
|
958
960
|
}
|
|
959
961
|
|
|
960
962
|
private setTargetFromRaycast(ray?: Ray, immediateOrDuration: number | boolean = false): boolean {
|
|
@@ -984,22 +986,22 @@ export class OrbitControls extends Behaviour implements ICameraController {
|
|
|
984
986
|
*/
|
|
985
987
|
fitCamera(options?: FitCameraOptions);
|
|
986
988
|
fitCamera(objects?: Object3D | Array<Object3D>, options?: Omit<FitCameraOptions, "objects">);
|
|
987
|
-
fitCamera(objectsOrOptions?: Object3D | Array<Object3D> | FitCameraOptions, options?: FitCameraOptions) {
|
|
989
|
+
fitCamera(objectsOrOptions?: Object3D | Array<Object3D> | FitCameraOptions, options?: FitCameraOptions): void {
|
|
988
990
|
|
|
989
991
|
if (this.context.isInXR) {
|
|
990
992
|
// camera fitting in XR is not supported
|
|
993
|
+
console.warn('[OrbitControls] Can not fit camera while XR session is active');
|
|
991
994
|
return;
|
|
992
995
|
}
|
|
993
996
|
|
|
994
997
|
let objects: Object3D | Array<Object3D> | undefined = undefined;
|
|
995
|
-
|
|
996
998
|
// If the user passed in an array as first argument
|
|
997
999
|
if (Array.isArray(objectsOrOptions)) {
|
|
998
1000
|
objects = objectsOrOptions;
|
|
999
1001
|
}
|
|
1000
1002
|
// If the user passed in an object as first argument
|
|
1001
1003
|
else if (objectsOrOptions && "type" in objectsOrOptions) {
|
|
1002
|
-
objects = objectsOrOptions
|
|
1004
|
+
objects = objectsOrOptions;
|
|
1003
1005
|
}
|
|
1004
1006
|
// If the user passed in an object as first argument and options as second argument
|
|
1005
1007
|
else if (objectsOrOptions && typeof objectsOrOptions === "object") {
|
|
@@ -1008,12 +1010,10 @@ export class OrbitControls extends Behaviour implements ICameraController {
|
|
|
1008
1010
|
objects = options.objects;
|
|
1009
1011
|
}
|
|
1010
1012
|
}
|
|
1011
|
-
|
|
1012
1013
|
// Ensure objects are setup correctly
|
|
1013
1014
|
if (objects && !Array.isArray(objects)) {
|
|
1014
1015
|
objects = objects.children;
|
|
1015
1016
|
}
|
|
1016
|
-
|
|
1017
1017
|
if (!Array.isArray(objects) || objects && objects.length <= 0) {
|
|
1018
1018
|
objects = this.context.scene.children;
|
|
1019
1019
|
}
|
|
@@ -1032,7 +1032,7 @@ export class OrbitControls extends Behaviour implements ICameraController {
|
|
|
1032
1032
|
return;
|
|
1033
1033
|
}
|
|
1034
1034
|
if (!options) options = {}
|
|
1035
|
-
const { immediate = false, centerCamera
|
|
1035
|
+
const { immediate = false, centerCamera, cameraNearFar = "auto", fitOffset = 1.1, fov = camera?.fov } = options;
|
|
1036
1036
|
const size = new Vector3();
|
|
1037
1037
|
const center = new Vector3();
|
|
1038
1038
|
// TODO would be much better to calculate the bounds in camera space instead of world space -
|
|
@@ -1041,15 +1041,13 @@ export class OrbitControls extends Behaviour implements ICameraController {
|
|
|
1041
1041
|
// and thus we're just getting some maximum that will work for sure.
|
|
1042
1042
|
const box = getBoundingBox(objects, undefined, this._camera?.threeCamera?.layers);
|
|
1043
1043
|
const boxCopy = box.clone();
|
|
1044
|
-
|
|
1045
|
-
camera.updateMatrixWorld();
|
|
1046
|
-
camera.updateProjectionMatrix();
|
|
1047
1044
|
box.getCenter(center);
|
|
1048
1045
|
|
|
1049
1046
|
const box_size = new Vector3();
|
|
1050
1047
|
box.getSize(box_size);
|
|
1051
1048
|
|
|
1052
1049
|
// project this box into camera space
|
|
1050
|
+
camera.updateMatrixWorld();
|
|
1053
1051
|
box.applyMatrix4(camera.matrixWorldInverse);
|
|
1054
1052
|
|
|
1055
1053
|
box.getSize(size);
|
|
@@ -1063,7 +1061,7 @@ export class OrbitControls extends Behaviour implements ICameraController {
|
|
|
1063
1061
|
return;
|
|
1064
1062
|
}
|
|
1065
1063
|
|
|
1066
|
-
const verticalFov =
|
|
1064
|
+
const verticalFov = fov;
|
|
1067
1065
|
const horizontalFov = 2 * Math.atan(Math.tan(verticalFov * Math.PI / 360 / 2) * camera.aspect) / Math.PI * 360;
|
|
1068
1066
|
const fitHeightDistance = size.y / (2 * Math.atan(Math.PI * verticalFov / 360));
|
|
1069
1067
|
const fitWidthDistance = size.x / (2 * Math.atan(Math.PI * horizontalFov / 360));
|
|
@@ -1078,9 +1076,18 @@ export class OrbitControls extends Behaviour implements ICameraController {
|
|
|
1078
1076
|
this.minZoom = distance * 0.01;
|
|
1079
1077
|
|
|
1080
1078
|
const verticalOffset = 0.05;
|
|
1081
|
-
|
|
1082
1079
|
const lookAt = center.clone();
|
|
1083
1080
|
lookAt.y -= size.y * verticalOffset;
|
|
1081
|
+
if (options.targetOffset) {
|
|
1082
|
+
if (options.targetOffset.x !== undefined) lookAt.x += options.targetOffset.x;
|
|
1083
|
+
if (options.targetOffset.y !== undefined) lookAt.y += options.targetOffset.y;
|
|
1084
|
+
if (options.targetOffset.z !== undefined) lookAt.z += options.targetOffset.z;
|
|
1085
|
+
}
|
|
1086
|
+
if (options.relativeTargetOffset) {
|
|
1087
|
+
if (options.relativeTargetOffset.x !== undefined) lookAt.x += options.relativeTargetOffset.x * size.x;
|
|
1088
|
+
if (options.relativeTargetOffset.y !== undefined) lookAt.y += options.relativeTargetOffset.y * size.y;
|
|
1089
|
+
if (options.relativeTargetOffset.z !== undefined) lookAt.z += options.relativeTargetOffset.z * size.z;
|
|
1090
|
+
}
|
|
1084
1091
|
this.setLookTargetPosition(lookAt, immediate);
|
|
1085
1092
|
this.setFieldOfView(options.fov, immediate);
|
|
1086
1093
|
|
|
@@ -1092,6 +1099,7 @@ export class OrbitControls extends Behaviour implements ICameraController {
|
|
|
1092
1099
|
// TODO: this doesnt take the Camera component nearClipPlane into account
|
|
1093
1100
|
camera.near = (distance / 100);
|
|
1094
1101
|
camera.far = boundsMax + distance * 10;
|
|
1102
|
+
camera.updateProjectionMatrix();
|
|
1095
1103
|
|
|
1096
1104
|
// adjust maxZoom so that the ground projection radius is always inside
|
|
1097
1105
|
if (groundprojection) {
|
|
@@ -1104,12 +1112,13 @@ export class OrbitControls extends Behaviour implements ICameraController {
|
|
|
1104
1112
|
if (currentZoom < this.minZoom) this.minZoom = currentZoom * 0.9;
|
|
1105
1113
|
if (currentZoom > this.maxZoom) this.maxZoom = currentZoom * 1.1;
|
|
1106
1114
|
|
|
1107
|
-
camera.updateMatrixWorld();
|
|
1108
|
-
camera.updateProjectionMatrix();
|
|
1109
|
-
|
|
1110
|
-
const cameraWp = getWorldPosition(camera);
|
|
1111
1115
|
const direction = center.clone();
|
|
1112
|
-
|
|
1116
|
+
if (options.fitDirection) {
|
|
1117
|
+
direction.sub(new Vector3().copy(options.fitDirection).multiplyScalar(1_000_000));
|
|
1118
|
+
}
|
|
1119
|
+
else {
|
|
1120
|
+
direction.sub(camera.worldPosition);
|
|
1121
|
+
}
|
|
1113
1122
|
if (centerCamera === "y")
|
|
1114
1123
|
direction.y = 0;
|
|
1115
1124
|
direction.normalize();
|
|
@@ -1118,6 +1127,16 @@ export class OrbitControls extends Behaviour implements ICameraController {
|
|
|
1118
1127
|
direction.y += -verticalOffset * 4 * distance;
|
|
1119
1128
|
|
|
1120
1129
|
let cameraLocalPosition = center.clone().sub(direction);
|
|
1130
|
+
if (options.cameraOffset) {
|
|
1131
|
+
if (options.cameraOffset.x !== undefined) cameraLocalPosition.x += options.cameraOffset.x;
|
|
1132
|
+
if (options.cameraOffset.y !== undefined) cameraLocalPosition.y += options.cameraOffset.y;
|
|
1133
|
+
if (options.cameraOffset.z !== undefined) cameraLocalPosition.z += options.cameraOffset.z;
|
|
1134
|
+
}
|
|
1135
|
+
if (options.relativeCameraOffset) {
|
|
1136
|
+
if (options.relativeCameraOffset.x !== undefined) cameraLocalPosition.x += options.relativeCameraOffset.x * size.x;
|
|
1137
|
+
if (options.relativeCameraOffset.y !== undefined) cameraLocalPosition.y += options.relativeCameraOffset.y * size.y;
|
|
1138
|
+
if (options.relativeCameraOffset.z !== undefined) cameraLocalPosition.z += options.relativeCameraOffset.z * size.z;
|
|
1139
|
+
}
|
|
1121
1140
|
if (camera.parent) {
|
|
1122
1141
|
cameraLocalPosition = camera.parent.worldToLocal(cameraLocalPosition);
|
|
1123
1142
|
}
|
|
@@ -1154,23 +1173,53 @@ export class OrbitControls extends Behaviour implements ICameraController {
|
|
|
1154
1173
|
/**
|
|
1155
1174
|
* Options for fitting the camera to the scene. Used in {@link OrbitControls.fitCamera}
|
|
1156
1175
|
*/
|
|
1157
|
-
|
|
1176
|
+
export type FitCameraOptions = {
|
|
1158
1177
|
/** When enabled debug rendering will be shown */
|
|
1159
1178
|
debug?: boolean,
|
|
1160
1179
|
/**
|
|
1161
1180
|
* The objects to fit the camera to. If not provided the scene children will be used
|
|
1162
1181
|
*/
|
|
1163
1182
|
objects?: Object3D[] | Object3D;
|
|
1164
|
-
/** Fit offset: A factor to multiply the distance to the objects by
|
|
1165
|
-
* @default 1.1
|
|
1166
|
-
*/
|
|
1167
|
-
fitOffset?: number,
|
|
1168
1183
|
/** If true the camera will move immediately to the new position, otherwise it will lerp
|
|
1169
1184
|
* @default false
|
|
1170
1185
|
*/
|
|
1171
1186
|
immediate?: boolean,
|
|
1187
|
+
|
|
1188
|
+
/** Fit offset: A factor to multiply the distance to the objects by
|
|
1189
|
+
* @default 1.1
|
|
1190
|
+
*/
|
|
1191
|
+
fitOffset?: number,
|
|
1192
|
+
|
|
1193
|
+
/** The direction from which the camera should be fitted in worldspace. If not defined the current camera's position will be used */
|
|
1194
|
+
fitDirection?: Vector3Like,
|
|
1195
|
+
|
|
1172
1196
|
/** If set to "y" the camera will be centered in the y axis */
|
|
1173
1197
|
centerCamera?: "none" | "y",
|
|
1198
|
+
/** Set to 'auto' to update the camera near or far plane based on the fitted-objects bounds */
|
|
1174
1199
|
cameraNearFar?: "keep" | "auto",
|
|
1200
|
+
|
|
1201
|
+
/**
|
|
1202
|
+
* Offset the camera position in world space
|
|
1203
|
+
*/
|
|
1204
|
+
cameraOffset?: Partial<Vector3Like>,
|
|
1205
|
+
/**
|
|
1206
|
+
* Offset the camera position relative to the size of the objects being focused on (e.g. x: 0.5).
|
|
1207
|
+
* Value range: -1 to 1
|
|
1208
|
+
*/
|
|
1209
|
+
relativeCameraOffset?: Partial<Vector3Like>,
|
|
1210
|
+
|
|
1211
|
+
/**
|
|
1212
|
+
* Offset the camera target position in world space
|
|
1213
|
+
*/
|
|
1214
|
+
targetOffset?: Partial<Vector3Like>,
|
|
1215
|
+
/**
|
|
1216
|
+
* Offset the camera target position relative to the size of the objects being focused on.
|
|
1217
|
+
* Value range: -1 to 1
|
|
1218
|
+
*/
|
|
1219
|
+
relativeTargetOffset?: Partial<Vector3Like>,
|
|
1220
|
+
|
|
1221
|
+
/**
|
|
1222
|
+
* Field of view (FOV) for the camera
|
|
1223
|
+
*/
|
|
1175
1224
|
fov?: number,
|
|
1176
1225
|
}
|
|
@@ -706,7 +706,8 @@ export class Renderer extends Behaviour implements IRenderer {
|
|
|
706
706
|
}
|
|
707
707
|
|
|
708
708
|
if (this.reflectionProbeUsage !== ReflectionProbeUsage.Off && this._reflectionProbe) {
|
|
709
|
-
this.
|
|
709
|
+
if (!this._lightmaps?.length) // Currently reflectionprobes cant be used with lightmaps
|
|
710
|
+
this._reflectionProbe.onSet(this);
|
|
710
711
|
}
|
|
711
712
|
// since three 163 we need to set the envMap to the scene envMap if it is not set
|
|
712
713
|
// otherwise the envmapIntensity has no effect: https://github.com/mrdoob/three.js/pull/27903
|