@needle-tools/engine 4.9.3 → 4.10.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -0
- package/components.needle.json +1 -1
- package/dist/{gltf-progressive-DhE1A6hX.min.js → gltf-progressive-CoZbSfPR.min.js} +1 -1
- package/dist/{gltf-progressive-egsMzRdv.js → gltf-progressive-DUR9TuAH.js} +3 -3
- package/dist/{gltf-progressive-DWiyqrwB.umd.cjs → gltf-progressive-Iy7aSAPk.umd.cjs} +1 -1
- package/dist/{needle-engine.bundle-C7LSzO5L.umd.cjs → needle-engine.bundle-6so_os_w.umd.cjs} +179 -145
- package/dist/{needle-engine.bundle-BAsxNKpA.js → needle-engine.bundle-Dj2DYdMY.js} +7699 -7235
- package/dist/needle-engine.bundle-Djy6H4lx.min.js +1650 -0
- package/dist/needle-engine.js +460 -456
- package/dist/needle-engine.min.js +1 -1
- package/dist/needle-engine.umd.cjs +1 -1
- package/dist/{postprocessing-BZOSD1ln.min.js → postprocessing-BHMVuZQ1.min.js} +1 -1
- package/dist/{postprocessing-Bb5StX0o.umd.cjs → postprocessing-BsnRNRRS.umd.cjs} +1 -1
- package/dist/{postprocessing-BzFF7i-7.js → postprocessing-DQ2pynXW.js} +2 -2
- package/dist/{three-BK56xWDs.umd.cjs → three-B-jwTHao.umd.cjs} +11 -11
- package/dist/{three-CsHK73Zc.js → three-CJSAehtG.js} +1 -0
- package/dist/{three-examples-Bph291U2.min.js → three-examples-BivkhnvN.min.js} +1 -1
- package/dist/{three-examples-C9WfZu-X.umd.cjs → three-examples-Deqc1bNw.umd.cjs} +1 -1
- package/dist/{three-examples-BvMpKSun.js → three-examples-Doq0rvFU.js} +1 -1
- package/dist/{three-mesh-ui-CN6aRT7i.js → three-mesh-ui-CktOi6oI.js} +1 -1
- package/dist/{three-mesh-ui-DnxkZWNA.umd.cjs → three-mesh-ui-CsHwj9cJ.umd.cjs} +1 -1
- package/dist/{three-mesh-ui-n_qS2BM-.min.js → three-mesh-ui-DhYXcXZe.min.js} +1 -1
- package/dist/{three-TNFQHSFa.min.js → three-qw28ZtTy.min.js} +10 -10
- package/dist/{vendor-BtJpSuCj.umd.cjs → vendor-D0Yvltn9.umd.cjs} +1 -1
- package/dist/{vendor-k9i6CeGi.js → vendor-DU8tJyl_.js} +1 -1
- package/dist/{vendor-XJ9xiwrv.min.js → vendor-JyrX4DVM.min.js} +1 -1
- package/lib/engine/api.d.ts +1 -0
- package/lib/engine/api.js +1 -0
- package/lib/engine/api.js.map +1 -1
- package/lib/engine/codegen/register_types.js +6 -0
- package/lib/engine/codegen/register_types.js.map +1 -1
- package/lib/engine/engine_animation.d.ts +21 -1
- package/lib/engine/engine_animation.js +32 -1
- package/lib/engine/engine_animation.js.map +1 -1
- package/lib/engine/engine_camera.d.ts +7 -1
- package/lib/engine/engine_camera.fit.d.ts +68 -0
- package/lib/engine/engine_camera.fit.js +166 -0
- package/lib/engine/engine_camera.fit.js.map +1 -0
- package/lib/engine/engine_camera.js +46 -6
- package/lib/engine/engine_camera.js.map +1 -1
- package/lib/engine/engine_context.d.ts +6 -0
- package/lib/engine/engine_context.js +48 -9
- package/lib/engine/engine_context.js.map +1 -1
- package/lib/engine/engine_gizmos.d.ts +2 -2
- package/lib/engine/engine_gizmos.js +2 -2
- package/lib/engine/engine_physics.js +6 -3
- package/lib/engine/engine_physics.js.map +1 -1
- package/lib/engine/webcomponents/logo-element.d.ts +1 -1
- package/lib/engine/webcomponents/logo-element.js +29 -5
- package/lib/engine/webcomponents/logo-element.js.map +1 -1
- package/lib/engine/webcomponents/needle menu/needle-menu.js +4 -3
- package/lib/engine/webcomponents/needle menu/needle-menu.js.map +1 -1
- package/lib/engine/webcomponents/needle-engine.d.ts +1 -0
- package/lib/engine/webcomponents/needle-engine.js +6 -0
- package/lib/engine/webcomponents/needle-engine.js.map +1 -1
- package/lib/engine/webcomponents/needle-engine.loading.d.ts +0 -1
- package/lib/engine/webcomponents/needle-engine.loading.js +62 -59
- package/lib/engine/webcomponents/needle-engine.loading.js.map +1 -1
- package/lib/engine-components/AnimatorController.js +16 -0
- package/lib/engine-components/AnimatorController.js.map +1 -1
- package/lib/engine-components/CameraUtils.js +8 -9
- package/lib/engine-components/CameraUtils.js.map +1 -1
- package/lib/engine-components/OrbitControls.d.ts +7 -47
- package/lib/engine-components/OrbitControls.js +25 -149
- package/lib/engine-components/OrbitControls.js.map +1 -1
- package/lib/engine-components/Renderer.d.ts +2 -2
- package/lib/engine-components/Renderer.js +10 -5
- package/lib/engine-components/Renderer.js.map +1 -1
- package/lib/engine-components/api.d.ts +0 -1
- package/lib/engine-components/api.js.map +1 -1
- package/lib/engine-components/codegen/components.d.ts +3 -0
- package/lib/engine-components/codegen/components.js +3 -0
- package/lib/engine-components/codegen/components.js.map +1 -1
- package/lib/engine-components/timeline/PlayableDirector.d.ts +35 -6
- package/lib/engine-components/timeline/PlayableDirector.js +67 -26
- package/lib/engine-components/timeline/PlayableDirector.js.map +1 -1
- package/lib/engine-components/timeline/TimelineModels.d.ts +11 -0
- package/lib/engine-components/timeline/TimelineModels.js.map +1 -1
- package/lib/engine-components/timeline/TimelineTracks.d.ts +7 -0
- package/lib/engine-components/timeline/TimelineTracks.js +23 -2
- package/lib/engine-components/timeline/TimelineTracks.js.map +1 -1
- package/lib/engine-components/utils/LookAt.js +5 -1
- package/lib/engine-components/utils/LookAt.js.map +1 -1
- package/lib/engine-components/web/Clickthrough.d.ts +3 -0
- package/lib/engine-components/web/Clickthrough.js +13 -2
- package/lib/engine-components/web/Clickthrough.js.map +1 -1
- package/lib/engine-components/web/CursorFollow.d.ts +3 -0
- package/lib/engine-components/web/CursorFollow.js +3 -0
- package/lib/engine-components/web/CursorFollow.js.map +1 -1
- package/lib/engine-components/web/HoverAnimation.d.ts +44 -0
- package/lib/engine-components/web/HoverAnimation.js +105 -0
- package/lib/engine-components/web/HoverAnimation.js.map +1 -0
- package/lib/engine-components/web/ScrollFollow.d.ts +40 -4
- package/lib/engine-components/web/ScrollFollow.js +256 -27
- package/lib/engine-components/web/ScrollFollow.js.map +1 -1
- package/lib/engine-components/web/ViewBox.d.ts +16 -0
- package/lib/engine-components/web/ViewBox.js +183 -0
- package/lib/engine-components/web/ViewBox.js.map +1 -0
- package/lib/engine-components/web/index.d.ts +2 -0
- package/lib/engine-components/web/index.js +2 -0
- package/lib/engine-components/web/index.js.map +1 -1
- package/package.json +1 -1
- package/plugins/vite/alias.js +5 -3
- package/plugins/vite/poster-client.js +22 -21
- package/src/engine/api.ts +2 -1
- package/src/engine/codegen/register_types.ts +6 -0
- package/src/engine/engine_animation.ts +69 -1
- package/src/engine/engine_camera.fit.ts +258 -0
- package/src/engine/engine_camera.ts +62 -8
- package/src/engine/engine_context.ts +50 -10
- package/src/engine/engine_gizmos.ts +2 -2
- package/src/engine/engine_physics.ts +6 -3
- package/src/engine/webcomponents/logo-element.ts +29 -4
- package/src/engine/webcomponents/needle menu/needle-menu.ts +4 -3
- package/src/engine/webcomponents/needle-engine.loading.ts +95 -56
- package/src/engine/webcomponents/needle-engine.ts +6 -1
- package/src/engine-components/AnimatorController.ts +21 -2
- package/src/engine-components/CameraUtils.ts +8 -9
- package/src/engine-components/OrbitControls.ts +36 -206
- package/src/engine-components/Renderer.ts +10 -5
- package/src/engine-components/api.ts +0 -1
- package/src/engine-components/codegen/components.ts +3 -0
- package/src/engine-components/timeline/PlayableDirector.ts +88 -34
- package/src/engine-components/timeline/TimelineModels.ts +11 -0
- package/src/engine-components/timeline/TimelineTracks.ts +26 -2
- package/src/engine-components/utils/LookAt.ts +5 -1
- package/src/engine-components/web/Clickthrough.ts +14 -2
- package/src/engine-components/web/CursorFollow.ts +3 -0
- package/src/engine-components/web/HoverAnimation.ts +99 -0
- package/src/engine-components/web/ScrollFollow.ts +316 -25
- package/src/engine-components/web/ViewBox.ts +199 -0
- package/src/engine-components/web/index.ts +3 -1
- package/dist/needle-engine.bundle-ugr1bBtk.min.js +0 -1616
|
@@ -2,18 +2,19 @@ import { Camera as Camera3, Object3D, PerspectiveCamera, Ray, Vector2, Vector3,
|
|
|
2
2
|
import { OrbitControls as ThreeOrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
|
|
3
3
|
|
|
4
4
|
import { isDevEnvironment } from "../engine/debug/index.js";
|
|
5
|
+
import { fitCamera, FitCameraOptions } from "../engine/engine_camera.fit.js";
|
|
5
6
|
import { setCameraController } from "../engine/engine_camera.js";
|
|
6
7
|
import { Gizmos } from "../engine/engine_gizmos.js";
|
|
7
8
|
import { InputEventQueue, NEPointerEvent } from "../engine/engine_input.js";
|
|
8
9
|
import { Mathf } from "../engine/engine_math.js";
|
|
9
10
|
import { IRaycastOptions, RaycastOptions } from "../engine/engine_physics.js";
|
|
10
11
|
import { serializable } from "../engine/engine_serialization_decorator.js";
|
|
11
|
-
import {
|
|
12
|
+
import { getTempVector, getWorldPosition } from "../engine/engine_three_utils.js";
|
|
12
13
|
import type { ICameraController } from "../engine/engine_types.js";
|
|
13
14
|
import { DeviceUtilities, getParam } from "../engine/engine_utils.js";
|
|
15
|
+
import { NeedleEngineWebComponent } from "../engine/webcomponents/needle-engine.js";
|
|
14
16
|
import { Camera } from "./Camera.js";
|
|
15
17
|
import { Behaviour, GameObject } from "./Component.js";
|
|
16
|
-
import { GroundProjectedEnv } from "./GroundProjection.js";
|
|
17
18
|
import { LookAtConstraint } from "./LookAtConstraint.js";
|
|
18
19
|
import { SyncedTransform } from "./SyncedTransform.js";
|
|
19
20
|
import { type AfterHandleInputEvent, EventSystem, EventSystemEvents } from "./ui/EventSystem.js";
|
|
@@ -354,6 +355,9 @@ export class OrbitControls extends Behaviour implements ICameraController {
|
|
|
354
355
|
if (debug) console.debug("OrbitControls", this);
|
|
355
356
|
this._didSetTarget = 0;
|
|
356
357
|
this._startedListeningToKeyEvents = false;
|
|
358
|
+
if ((this.context.domElement as NeedleEngineWebComponent).cameraControls === false) {
|
|
359
|
+
this.enabled = false;
|
|
360
|
+
}
|
|
357
361
|
}
|
|
358
362
|
|
|
359
363
|
/** @internal */
|
|
@@ -490,10 +494,13 @@ export class OrbitControls extends Behaviour implements ICameraController {
|
|
|
490
494
|
this._lastTimeClickOnBackground = this.context.time.time;
|
|
491
495
|
if (this.clickBackgroundToFitScene <= 1 || dt < this.clickBackgroundToFitScene * .15) {
|
|
492
496
|
this._clickOnBackgroundCount += 1;
|
|
493
|
-
if (this._clickOnBackgroundCount >= this.clickBackgroundToFitScene - 1)
|
|
494
|
-
this.
|
|
495
|
-
|
|
497
|
+
if (this._clickOnBackgroundCount >= this.clickBackgroundToFitScene - 1) {
|
|
498
|
+
this.autoRotate = false;
|
|
499
|
+
this.fitCamera({
|
|
500
|
+
objects: this.context.scene,
|
|
501
|
+
immediate: false,
|
|
496
502
|
});
|
|
503
|
+
}
|
|
497
504
|
}
|
|
498
505
|
else {
|
|
499
506
|
this._clickOnBackgroundCount = 0;
|
|
@@ -530,7 +537,7 @@ export class OrbitControls extends Behaviour implements ICameraController {
|
|
|
530
537
|
private _orbitStartAngle: number = 0;
|
|
531
538
|
private _zoomStartDistance: number = 0;
|
|
532
539
|
private onControlsChangeStarted = () => {
|
|
533
|
-
if(debug) console.debug("OrbitControls: Change started");
|
|
540
|
+
if (debug) console.debug("OrbitControls: Change started");
|
|
534
541
|
if (this._controls) {
|
|
535
542
|
this._orbitStartAngle = this._controls.getAzimuthalAngle() + this._controls.getPolarAngle();
|
|
536
543
|
this._zoomStartDistance = this._controls.getDistance();
|
|
@@ -540,7 +547,7 @@ export class OrbitControls extends Behaviour implements ICameraController {
|
|
|
540
547
|
}
|
|
541
548
|
}
|
|
542
549
|
private onControlsChangeEnded = () => {
|
|
543
|
-
if(debug) console.debug("OrbitControls: Change ended", { autoTarget: this.autoTarget });
|
|
550
|
+
if (debug) console.debug("OrbitControls: Change ended", { autoTarget: this.autoTarget });
|
|
544
551
|
if (this._controls) {
|
|
545
552
|
if (this.autoTarget) {
|
|
546
553
|
const newAngle = this._controls.getAzimuthalAngle() + this._controls.getPolarAngle();
|
|
@@ -986,18 +993,16 @@ export class OrbitControls extends Behaviour implements ICameraController {
|
|
|
986
993
|
|
|
987
994
|
// Adapted from https://discourse.threejs.org/t/camera-zoom-to-fit-object/936/24
|
|
988
995
|
// Slower but better implementation that takes bones and exact vertex positions into account: https://github.com/google/model-viewer/blob/04e900c5027de8c5306fe1fe9627707f42811b05/packages/model-viewer/src/three-components/ModelScene.ts#L321
|
|
996
|
+
|
|
989
997
|
/**
|
|
990
998
|
* Fits the camera to show the objects provided (defaults to the scene if no objects are passed in)
|
|
999
|
+
* @param options The options for fitting the camera. Use to provide objects to fit to, fit direction and size and other settings.
|
|
991
1000
|
*/
|
|
992
|
-
fitCamera(options?:
|
|
993
|
-
|
|
994
|
-
fitCamera(
|
|
1001
|
+
fitCamera(options?: OrbitFitCameraOptions);
|
|
1002
|
+
/** @deprecated Use fitCamera(options) */
|
|
1003
|
+
fitCamera(objects?: Object3D | Array<Object3D>, options?: Omit<OrbitFitCameraOptions, "objects">);
|
|
1004
|
+
fitCamera(objectsOrOptions?: Object3D | Array<Object3D> | OrbitFitCameraOptions, options?: OrbitFitCameraOptions): void {
|
|
995
1005
|
|
|
996
|
-
if (this.context.isInXR) {
|
|
997
|
-
// camera fitting in XR is not supported
|
|
998
|
-
console.warn('[OrbitControls] Can not fit camera while XR session is active');
|
|
999
|
-
return;
|
|
1000
|
-
}
|
|
1001
1006
|
|
|
1002
1007
|
let objects: Object3D | Array<Object3D> | undefined = undefined;
|
|
1003
1008
|
// If the user passed in an array as first argument
|
|
@@ -1029,202 +1034,27 @@ export class OrbitControls extends Behaviour implements ICameraController {
|
|
|
1029
1034
|
return;
|
|
1030
1035
|
}
|
|
1031
1036
|
|
|
1032
|
-
const camera = this._cameraObject as PerspectiveCamera;
|
|
1033
|
-
const controls = this._controls as ThreeOrbitControls | null;
|
|
1034
|
-
|
|
1035
|
-
if (!camera || !controls) {
|
|
1036
|
-
console.warn("No camera or controls found to fit camera to objects...");
|
|
1037
|
-
return;
|
|
1038
|
-
}
|
|
1039
|
-
if (!options) options = {}
|
|
1040
|
-
const { immediate = false, centerCamera, cameraNearFar = "auto", fitOffset = 1.1, fov = camera?.fov } = options;
|
|
1041
|
-
const size = new Vector3();
|
|
1042
|
-
const center = new Vector3();
|
|
1043
|
-
// TODO would be much better to calculate the bounds in camera space instead of world space -
|
|
1044
|
-
// we would get proper view-dependant fit.
|
|
1045
|
-
// Right now it's independent from where the camera is actually looking from,
|
|
1046
|
-
// and thus we're just getting some maximum that will work for sure.
|
|
1047
|
-
const box = getBoundingBox(objects, undefined, this._camera?.threeCamera?.layers);
|
|
1048
|
-
const boxCopy = box.clone();
|
|
1049
|
-
box.getCenter(center);
|
|
1050
|
-
|
|
1051
|
-
const box_size = new Vector3();
|
|
1052
|
-
box.getSize(box_size);
|
|
1053
|
-
|
|
1054
|
-
// project this box into camera space
|
|
1055
|
-
camera.updateMatrixWorld();
|
|
1056
|
-
box.applyMatrix4(camera.matrixWorldInverse);
|
|
1057
|
-
|
|
1058
|
-
box.getSize(size);
|
|
1059
|
-
box.setFromCenterAndSize(center, size);
|
|
1060
|
-
if (Number.isNaN(size.x) || Number.isNaN(size.y) || Number.isNaN(size.z)) {
|
|
1061
|
-
console.warn("Camera fit size resultet in NaN", camera, box, [...objects]);
|
|
1062
|
-
return;
|
|
1063
|
-
}
|
|
1064
|
-
if (size.length() <= 0.0000000001) {
|
|
1065
|
-
if (debugCameraFit) console.warn("Camera fit size is zero", box, [...objects]);
|
|
1066
|
-
return;
|
|
1067
|
-
}
|
|
1068
|
-
|
|
1069
|
-
const verticalFov = fov;
|
|
1070
|
-
const horizontalFov = 2 * Math.atan(Math.tan(verticalFov * Math.PI / 360 / 2) * camera.aspect) / Math.PI * 360;
|
|
1071
|
-
const fitHeightDistance = size.y / (2 * Math.atan(Math.PI * verticalFov / 360));
|
|
1072
|
-
const fitWidthDistance = size.x / (2 * Math.atan(Math.PI * horizontalFov / 360));
|
|
1073
|
-
|
|
1074
|
-
const distance = fitOffset * Math.max(fitHeightDistance, fitWidthDistance) + size.z / 2;
|
|
1075
|
-
|
|
1076
|
-
if (debugCameraFit) {
|
|
1077
|
-
console.log("Fit camera to objects", { fitHeightDistance, fitWidthDistance, distance, verticalFov, horizontalFov });
|
|
1078
|
-
}
|
|
1079
|
-
|
|
1080
|
-
this.maxZoom = distance * 10;
|
|
1081
|
-
this.minZoom = distance * 0.01;
|
|
1082
|
-
|
|
1083
|
-
const verticalOffset = 0.05;
|
|
1084
|
-
const lookAt = center.clone();
|
|
1085
|
-
lookAt.y -= size.y * verticalOffset;
|
|
1086
|
-
if (options.targetOffset) {
|
|
1087
|
-
if (options.targetOffset.x !== undefined) lookAt.x += options.targetOffset.x;
|
|
1088
|
-
if (options.targetOffset.y !== undefined) lookAt.y += options.targetOffset.y;
|
|
1089
|
-
if (options.targetOffset.z !== undefined) lookAt.z += options.targetOffset.z;
|
|
1090
|
-
}
|
|
1091
|
-
if (options.relativeTargetOffset) {
|
|
1092
|
-
if (options.relativeTargetOffset.x !== undefined) lookAt.x += options.relativeTargetOffset.x * size.x;
|
|
1093
|
-
if (options.relativeTargetOffset.y !== undefined) lookAt.y += options.relativeTargetOffset.y * size.y;
|
|
1094
|
-
if (options.relativeTargetOffset.z !== undefined) lookAt.z += options.relativeTargetOffset.z * size.z;
|
|
1095
|
-
}
|
|
1096
|
-
this.setLookTargetPosition(lookAt, immediate);
|
|
1097
|
-
this.setFieldOfView(options.fov, immediate);
|
|
1098
|
-
|
|
1099
|
-
if (cameraNearFar == undefined || cameraNearFar == "auto") {
|
|
1100
|
-
// Check if the scene has a GroundProjectedEnv and include the scale to the far plane so that it doesnt cut off
|
|
1101
|
-
const groundprojection = GameObject.findObjectOfType(GroundProjectedEnv);
|
|
1102
|
-
const groundProjectionRadius = groundprojection ? groundprojection.radius : 0;
|
|
1103
|
-
const boundsMax = Math.max(box_size.x, box_size.y, box_size.z, groundProjectionRadius);
|
|
1104
|
-
// TODO: this doesnt take the Camera component nearClipPlane into account
|
|
1105
|
-
camera.near = (distance / 100);
|
|
1106
|
-
camera.far = boundsMax + distance * 10;
|
|
1107
|
-
camera.updateProjectionMatrix();
|
|
1108
|
-
|
|
1109
|
-
// adjust maxZoom so that the ground projection radius is always inside
|
|
1110
|
-
if (groundprojection) {
|
|
1111
|
-
this.maxZoom = Math.max(Math.min(this.maxZoom, groundProjectionRadius * 0.5), distance);
|
|
1112
|
-
}
|
|
1113
|
-
}
|
|
1114
|
-
|
|
1115
|
-
// ensure we're not clipping out of the current zoom level just because we're fitting
|
|
1116
|
-
const currentZoom = controls.getDistance();
|
|
1117
|
-
if (currentZoom < this.minZoom) this.minZoom = currentZoom * 0.9;
|
|
1118
|
-
if (currentZoom > this.maxZoom) this.maxZoom = currentZoom * 1.1;
|
|
1119
|
-
|
|
1120
|
-
const direction = center.clone();
|
|
1121
|
-
if (options.fitDirection) {
|
|
1122
|
-
direction.sub(new Vector3().copy(options.fitDirection).multiplyScalar(1_000_000));
|
|
1123
|
-
}
|
|
1124
|
-
else {
|
|
1125
|
-
direction.sub(camera.worldPosition);
|
|
1126
|
-
}
|
|
1127
|
-
if (centerCamera === "y")
|
|
1128
|
-
direction.y = 0;
|
|
1129
|
-
direction.normalize();
|
|
1130
|
-
direction.multiplyScalar(distance);
|
|
1131
|
-
if (centerCamera === "y")
|
|
1132
|
-
direction.y += -verticalOffset * 4 * distance;
|
|
1133
|
-
|
|
1134
|
-
let cameraLocalPosition = center.clone().sub(direction);
|
|
1135
|
-
if (options.cameraOffset) {
|
|
1136
|
-
if (options.cameraOffset.x !== undefined) cameraLocalPosition.x += options.cameraOffset.x;
|
|
1137
|
-
if (options.cameraOffset.y !== undefined) cameraLocalPosition.y += options.cameraOffset.y;
|
|
1138
|
-
if (options.cameraOffset.z !== undefined) cameraLocalPosition.z += options.cameraOffset.z;
|
|
1139
|
-
}
|
|
1140
|
-
if (options.relativeCameraOffset) {
|
|
1141
|
-
if (options.relativeCameraOffset.x !== undefined) cameraLocalPosition.x += options.relativeCameraOffset.x * size.x;
|
|
1142
|
-
if (options.relativeCameraOffset.y !== undefined) cameraLocalPosition.y += options.relativeCameraOffset.y * size.y;
|
|
1143
|
-
if (options.relativeCameraOffset.z !== undefined) cameraLocalPosition.z += options.relativeCameraOffset.z * size.z;
|
|
1144
|
-
}
|
|
1145
|
-
if (camera.parent) {
|
|
1146
|
-
cameraLocalPosition = camera.parent.worldToLocal(cameraLocalPosition);
|
|
1147
|
-
}
|
|
1148
|
-
this.setCameraTargetPosition(cameraLocalPosition, immediate);
|
|
1149
|
-
|
|
1150
|
-
if (debugCameraFit || options.debug) {
|
|
1151
|
-
Gizmos.DrawWireBox3(box, 0xffff33, 10);
|
|
1152
|
-
Gizmos.DrawWireBox3(boxCopy, 0x00ff00, 10);
|
|
1153
|
-
|
|
1154
|
-
if (!this._haveAttachedKeyboardEvents && debugCameraFit) {
|
|
1155
|
-
this._haveAttachedKeyboardEvents = true;
|
|
1156
|
-
document.body.addEventListener("keydown", (e) => {
|
|
1157
|
-
if (e.code === "KeyF") {
|
|
1158
|
-
// random fov for easier debugging of fov-based fitting
|
|
1159
|
-
let fov: number | undefined = undefined;
|
|
1160
|
-
if (this._cameraObject instanceof PerspectiveCamera) fov = (Math.random() * Math.random()) * 170 + 10;
|
|
1161
|
-
this.fitCamera({ objects, fitOffset, immediate: false, fov });
|
|
1162
|
-
}
|
|
1163
|
-
if (e.code === "KeyV") {
|
|
1164
|
-
if (this._cameraObject instanceof PerspectiveCamera) this._cameraObject.fov = 60;
|
|
1165
|
-
}
|
|
1166
|
-
});
|
|
1167
|
-
}
|
|
1168
|
-
}
|
|
1169
1037
|
|
|
1038
|
+
const res = fitCamera({
|
|
1039
|
+
objects: [...objects],
|
|
1040
|
+
...options,
|
|
1041
|
+
autoApply: false,
|
|
1042
|
+
context: this.context,
|
|
1043
|
+
camera: this._cameraObject as Camera3,
|
|
1044
|
+
currentZoom: this._controls?.getDistance() || undefined,
|
|
1045
|
+
minZoom: this.minZoom,
|
|
1046
|
+
maxZoom: this.maxZoom,
|
|
1047
|
+
});
|
|
1048
|
+
if (!res) return;
|
|
1049
|
+
this.setLookTargetPosition(res.lookAt, options?.immediate || false);
|
|
1050
|
+
this.setCameraTargetPosition(res.position, options?.immediate || false);
|
|
1051
|
+
this.setFieldOfView(options?.fov, options?.immediate || false);
|
|
1170
1052
|
this.onBeforeRender();
|
|
1171
|
-
// controls.update(); // this is not enough when calling fitCamera({immediate:true}) in an interval
|
|
1172
1053
|
}
|
|
1173
1054
|
|
|
1174
1055
|
private _haveAttachedKeyboardEvents: boolean = false;
|
|
1175
1056
|
}
|
|
1176
1057
|
|
|
1177
|
-
|
|
1178
|
-
/**
|
|
1179
|
-
* Options for fitting the camera to the scene. Used in {@link OrbitControls.fitCamera}
|
|
1180
|
-
*/
|
|
1181
|
-
export type FitCameraOptions = {
|
|
1182
|
-
/** When enabled debug rendering will be shown */
|
|
1183
|
-
debug?: boolean,
|
|
1184
|
-
/**
|
|
1185
|
-
* The objects to fit the camera to. If not provided the scene children will be used
|
|
1186
|
-
*/
|
|
1187
|
-
objects?: Object3D[] | Object3D;
|
|
1188
|
-
/** If true the camera will move immediately to the new position, otherwise it will lerp
|
|
1189
|
-
* @default false
|
|
1190
|
-
*/
|
|
1058
|
+
type OrbitFitCameraOptions = FitCameraOptions & {
|
|
1191
1059
|
immediate?: boolean,
|
|
1192
|
-
|
|
1193
|
-
/** Fit offset: A factor to multiply the distance to the objects by
|
|
1194
|
-
* @default 1.1
|
|
1195
|
-
*/
|
|
1196
|
-
fitOffset?: number,
|
|
1197
|
-
|
|
1198
|
-
/** The direction from which the camera should be fitted in worldspace. If not defined the current camera's position will be used */
|
|
1199
|
-
fitDirection?: Vector3Like,
|
|
1200
|
-
|
|
1201
|
-
/** If set to "y" the camera will be centered in the y axis */
|
|
1202
|
-
centerCamera?: "none" | "y",
|
|
1203
|
-
/** Set to 'auto' to update the camera near or far plane based on the fitted-objects bounds */
|
|
1204
|
-
cameraNearFar?: "keep" | "auto",
|
|
1205
|
-
|
|
1206
|
-
/**
|
|
1207
|
-
* Offset the camera position in world space
|
|
1208
|
-
*/
|
|
1209
|
-
cameraOffset?: Partial<Vector3Like>,
|
|
1210
|
-
/**
|
|
1211
|
-
* Offset the camera position relative to the size of the objects being focused on (e.g. x: 0.5).
|
|
1212
|
-
* Value range: -1 to 1
|
|
1213
|
-
*/
|
|
1214
|
-
relativeCameraOffset?: Partial<Vector3Like>,
|
|
1215
|
-
|
|
1216
|
-
/**
|
|
1217
|
-
* Offset the camera target position in world space
|
|
1218
|
-
*/
|
|
1219
|
-
targetOffset?: Partial<Vector3Like>,
|
|
1220
|
-
/**
|
|
1221
|
-
* Offset the camera target position relative to the size of the objects being focused on.
|
|
1222
|
-
* Value range: -1 to 1
|
|
1223
|
-
*/
|
|
1224
|
-
relativeTargetOffset?: Partial<Vector3Like>,
|
|
1225
|
-
|
|
1226
|
-
/**
|
|
1227
|
-
* Field of view (FOV) for the camera
|
|
1228
|
-
*/
|
|
1229
|
-
fov?: number,
|
|
1230
|
-
}
|
|
1060
|
+
}
|
|
@@ -304,10 +304,12 @@ export class Renderer extends Behaviour implements IRenderer {
|
|
|
304
304
|
return this._sharedMeshes;
|
|
305
305
|
}
|
|
306
306
|
|
|
307
|
+
// @ts-ignore
|
|
307
308
|
get sharedMaterial(): SharedMaterial {
|
|
308
|
-
return this.sharedMaterials[0] as SharedMaterial;
|
|
309
|
+
return (this.sharedMaterials?.[0]) as SharedMaterial;
|
|
309
310
|
}
|
|
310
311
|
|
|
312
|
+
// @ts-ignore
|
|
311
313
|
set sharedMaterial(mat: SharedMaterial) {
|
|
312
314
|
const cur = this.sharedMaterials[0];
|
|
313
315
|
if (cur === mat) return;
|
|
@@ -315,12 +317,12 @@ export class Renderer extends Behaviour implements IRenderer {
|
|
|
315
317
|
this.applyLightmapping();
|
|
316
318
|
}
|
|
317
319
|
|
|
318
|
-
/**@deprecated
|
|
320
|
+
/**@deprecated Use sharedMaterial */
|
|
319
321
|
get material(): SharedMaterial {
|
|
320
|
-
return this.sharedMaterials[0] as SharedMaterial;
|
|
322
|
+
return this.sharedMaterials?.[0] as SharedMaterial;
|
|
321
323
|
}
|
|
322
324
|
|
|
323
|
-
/**@deprecated
|
|
325
|
+
/**@deprecated Use sharedMaterial */
|
|
324
326
|
set material(mat: SharedMaterial) {
|
|
325
327
|
this.sharedMaterial = mat;
|
|
326
328
|
}
|
|
@@ -356,8 +358,11 @@ export class Renderer extends Behaviour implements IRenderer {
|
|
|
356
358
|
//@ts-ignore
|
|
357
359
|
get sharedMaterials(): SharedMaterialArray {
|
|
358
360
|
|
|
361
|
+
// @ts-ignore (original materials will be set during deserialization)
|
|
362
|
+
if(this._originalMaterials === undefined) return null;
|
|
363
|
+
|
|
359
364
|
// @ts-ignore during deserialization code might access this property *before* the setter and then create an empty array
|
|
360
|
-
if (
|
|
365
|
+
if (this.__isDeserializing === true) return null;
|
|
361
366
|
|
|
362
367
|
if (!this._sharedMaterials || !this._sharedMaterials.is(this)) {
|
|
363
368
|
if (!this._originalMaterials) this._originalMaterials = [];
|
|
@@ -53,7 +53,6 @@ import "./AnimationUtilsAutoplay.js"
|
|
|
53
53
|
|
|
54
54
|
export { DragMode } from "./DragControls.js";
|
|
55
55
|
export type { DropListenerNetworkEventArguments, DropListenerOnDropArguments } from "./DropListener.js";
|
|
56
|
-
export { type FitCameraOptions } from "./OrbitControls.js";
|
|
57
56
|
export * from "./particlesystem/api.js"
|
|
58
57
|
export * from "./splines/index.js";
|
|
59
58
|
export * from "./web/index.js";
|
|
@@ -169,6 +169,7 @@ export { SignalReceiverEvent } from "../timeline/SignalAsset.js";
|
|
|
169
169
|
export { SignalReceiver } from "../timeline/SignalAsset.js";
|
|
170
170
|
export { AnimationTrackHandler } from "../timeline/TimelineTracks.js";
|
|
171
171
|
export { AudioTrackHandler } from "../timeline/TimelineTracks.js";
|
|
172
|
+
export { MarkerTrackHandler } from "../timeline/TimelineTracks.js";
|
|
172
173
|
export { SignalTrackHandler } from "../timeline/TimelineTracks.js";
|
|
173
174
|
export { ControlTrackHandler } from "../timeline/TimelineTracks.js";
|
|
174
175
|
export { TransformGizmo } from "../TransformGizmo.js";
|
|
@@ -205,7 +206,9 @@ export { VideoPlayer } from "../VideoPlayer.js";
|
|
|
205
206
|
export { Voip } from "../Voip.js";
|
|
206
207
|
export { ClickThrough } from "../web/Clickthrough.js";
|
|
207
208
|
export { CursorFollow } from "../web/CursorFollow.js";
|
|
209
|
+
export { HoverAnimation } from "../web/HoverAnimation.js";
|
|
208
210
|
export { ScrollFollow } from "../web/ScrollFollow.js";
|
|
211
|
+
export { ViewBox } from "../web/ViewBox.js";
|
|
209
212
|
export { Avatar } from "../webxr/Avatar.js";
|
|
210
213
|
export { XRControllerFollow } from "../webxr/controllers/XRControllerFollow.js";
|
|
211
214
|
export { XRControllerModel } from "../webxr/controllers/XRControllerModel.js";
|
|
@@ -66,10 +66,19 @@ export class PlayableDirector extends Behaviour {
|
|
|
66
66
|
this.createTrackFunctions[type] = fn;
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
+
/**
|
|
70
|
+
* The timeline asset that is played by this director.
|
|
71
|
+
*/
|
|
69
72
|
playableAsset?: Models.TimelineAssetModel;
|
|
73
|
+
|
|
70
74
|
/** Set to true to start playing the timeline when the scene starts */
|
|
71
75
|
@serializable()
|
|
72
76
|
playOnAwake?: boolean;
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Determines how the timeline behaves when it reaches the end of its duration.
|
|
80
|
+
* @default DirectorWrapMode.Loop
|
|
81
|
+
*/
|
|
73
82
|
@serializable()
|
|
74
83
|
extrapolationMode: DirectorWrapMode = DirectorWrapMode.Loop;
|
|
75
84
|
|
|
@@ -293,6 +302,41 @@ export class PlayableDirector extends Behaviour {
|
|
|
293
302
|
return this._audioTracks;
|
|
294
303
|
}
|
|
295
304
|
|
|
305
|
+
/**
|
|
306
|
+
* @returns all signal tracks of the timeline
|
|
307
|
+
*/
|
|
308
|
+
get signalTracks(): Tracks.SignalTrackHandler[] {
|
|
309
|
+
return this._signalTracks;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* @returns all marker tracks of the timeline
|
|
314
|
+
*/
|
|
315
|
+
get markerTracks(): Tracks.MarkerTrackHandler[] {
|
|
316
|
+
return this._markerTracks;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Iterates over all markers of the timeline, optionally filtering by type
|
|
321
|
+
*
|
|
322
|
+
* @example
|
|
323
|
+
* ```ts
|
|
324
|
+
* // Iterate over all ScrollMarkers in the timeline
|
|
325
|
+
* for (const marker of director.foreachMarker<{selector:string}>("ScrollMarker")) {
|
|
326
|
+
* console.log(marker.time, marker.selector);
|
|
327
|
+
* }
|
|
328
|
+
* ```
|
|
329
|
+
*
|
|
330
|
+
*/
|
|
331
|
+
*foreachMarker<T extends Record<string, any>>(type: string | null = null): Generator<(T & Models.MarkerModel)> {
|
|
332
|
+
for (const track of this._markerTracks) {
|
|
333
|
+
for (const marker of track.foreachMarker<T>(type)) {
|
|
334
|
+
yield marker as T & Models.MarkerModel;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
|
|
296
340
|
private _guidsMap?: GuidsMap;
|
|
297
341
|
/** @internal */
|
|
298
342
|
resolveGuids(map: GuidsMap) {
|
|
@@ -309,16 +353,18 @@ export class PlayableDirector extends Behaviour {
|
|
|
309
353
|
private _time: number = 0;
|
|
310
354
|
private _duration: number = 0;
|
|
311
355
|
private _weight: number = 1;
|
|
312
|
-
private _animationTracks: Array<Tracks.AnimationTrackHandler> = [];
|
|
313
|
-
private _audioTracks: Array<Tracks.AudioTrackHandler> = [];
|
|
314
|
-
private _signalTracks: Array<Tracks.SignalTrackHandler> = [];
|
|
315
|
-
private
|
|
316
|
-
private
|
|
317
|
-
|
|
318
|
-
|
|
356
|
+
private readonly _animationTracks: Array<Tracks.AnimationTrackHandler> = [];
|
|
357
|
+
private readonly _audioTracks: Array<Tracks.AudioTrackHandler> = [];
|
|
358
|
+
private readonly _signalTracks: Array<Tracks.SignalTrackHandler> = [];
|
|
359
|
+
private readonly _markerTracks: Array<Tracks.MarkerTrackHandler> = [];
|
|
360
|
+
private readonly _controlTracks: Array<Tracks.ControlTrackHandler> = [];
|
|
361
|
+
private readonly _customTracks: Array<Tracks.TrackHandler> = [];
|
|
362
|
+
|
|
363
|
+
private readonly _allTracks: Array<Array<Tracks.TrackHandler>> = [
|
|
319
364
|
this._animationTracks,
|
|
320
365
|
this._audioTracks,
|
|
321
366
|
this._signalTracks,
|
|
367
|
+
this._markerTracks,
|
|
322
368
|
this._controlTracks,
|
|
323
369
|
this._customTracks
|
|
324
370
|
];
|
|
@@ -335,7 +381,7 @@ export class PlayableDirector extends Behaviour {
|
|
|
335
381
|
}
|
|
336
382
|
}
|
|
337
383
|
|
|
338
|
-
private *internalUpdate() {
|
|
384
|
+
private * internalUpdate() {
|
|
339
385
|
while (this._isPlaying && this.activeAndEnabled) {
|
|
340
386
|
if (!this._isPaused && this._isPlaying) {
|
|
341
387
|
this._time += this.context.time.deltaTime * this.speed;
|
|
@@ -415,26 +461,18 @@ export class PlayableDirector extends Behaviour {
|
|
|
415
461
|
}
|
|
416
462
|
}
|
|
417
463
|
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
464
|
+
for (const track of this._allTracks) {
|
|
465
|
+
for (const handler of track) {
|
|
466
|
+
// When timeline reaches the end "stop()" is called which is evaluating with time 0
|
|
467
|
+
// We don't want to re-evaluate the animation then in case the timeline is blended with the Animator
|
|
468
|
+
// e.g then the timeline animation at time 0 is 100% applied on top of the animator animation
|
|
469
|
+
|
|
470
|
+
if (this._isStopping && handler instanceof Tracks.AnimationTrackHandler) {
|
|
471
|
+
continue;
|
|
472
|
+
}
|
|
423
473
|
handler.evaluate(time);
|
|
424
474
|
}
|
|
425
475
|
}
|
|
426
|
-
for (const handler of this._audioTracks) {
|
|
427
|
-
handler.evaluate(time);
|
|
428
|
-
}
|
|
429
|
-
for (const sig of this._signalTracks) {
|
|
430
|
-
sig.evaluate(time);
|
|
431
|
-
}
|
|
432
|
-
for (const ctrl of this._controlTracks) {
|
|
433
|
-
ctrl.evaluate(time);
|
|
434
|
-
}
|
|
435
|
-
for (const cust of this._customTracks) {
|
|
436
|
-
cust.evaluate(time);
|
|
437
|
-
}
|
|
438
476
|
}
|
|
439
477
|
|
|
440
478
|
private resolveBindings() {
|
|
@@ -630,26 +668,42 @@ export class PlayableDirector extends Behaviour {
|
|
|
630
668
|
}
|
|
631
669
|
}
|
|
632
670
|
else if (track.type === Models.TrackType.Marker) {
|
|
633
|
-
|
|
634
|
-
signalHandler.director = this;
|
|
635
|
-
signalHandler.track = track;
|
|
671
|
+
|
|
636
672
|
if (track.markers) {
|
|
673
|
+
// For the marker track we create both a signal track handler AND a markertrack handler because a marker track can have signals and markers
|
|
674
|
+
const signalHandler: Tracks.SignalTrackHandler = new Tracks.SignalTrackHandler();
|
|
675
|
+
signalHandler.director = this;
|
|
676
|
+
signalHandler.track = track;
|
|
677
|
+
|
|
678
|
+
const markerHandler: Tracks.MarkerTrackHandler = new Tracks.MarkerTrackHandler();
|
|
679
|
+
markerHandler.director = this;
|
|
680
|
+
markerHandler.track = track;
|
|
681
|
+
|
|
637
682
|
for (const marker of track.markers) {
|
|
638
683
|
switch (marker.type) {
|
|
639
684
|
case Models.MarkerType.Signal:
|
|
640
685
|
signalHandler.models.push(marker as Models.SignalMarkerModel);
|
|
641
686
|
signalHandler.didTrigger.push(false);
|
|
642
687
|
break;
|
|
688
|
+
default:
|
|
689
|
+
markerHandler.models.push(marker);
|
|
690
|
+
break;
|
|
643
691
|
}
|
|
644
692
|
}
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
693
|
+
|
|
694
|
+
if (signalHandler !== null && signalHandler.models.length > 0) {
|
|
695
|
+
const rec = GameObject.getComponent(this.gameObject, SignalReceiver);
|
|
696
|
+
if (rec) {
|
|
697
|
+
signalHandler.receivers.push(rec);
|
|
698
|
+
this._signalTracks.push(signalHandler);
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
if (markerHandler !== null && markerHandler.models.length > 0) {
|
|
703
|
+
this._markerTracks.push(markerHandler);
|
|
651
704
|
}
|
|
652
705
|
}
|
|
706
|
+
|
|
653
707
|
}
|
|
654
708
|
else if (track.type === Models.TrackType.Signal) {
|
|
655
709
|
const handler = new Tracks.SignalTrackHandler();
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { AnimationClip, Object3D, Quaternion, Vector3 } from "three";
|
|
2
|
+
import { Behavior } from "three-mesh-ui";
|
|
2
3
|
|
|
3
4
|
export declare type TimelineAssetModel = {
|
|
4
5
|
name: string;
|
|
@@ -90,3 +91,13 @@ export declare class SignalMarkerModel extends MarkerModel {
|
|
|
90
91
|
emitOnce: boolean;
|
|
91
92
|
asset: string;
|
|
92
93
|
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Marker with a name, used for scroll-driven timelines. It is used together with elements in your HTML to define what time in the timeline should be active when the element is in the scroll view.
|
|
97
|
+
*
|
|
98
|
+
* @example Mark html elements to define scroll positions
|
|
99
|
+
* ```html
|
|
100
|
+
* <div data-timeline-marker>...</div>
|
|
101
|
+
* ```
|
|
102
|
+
*/
|
|
103
|
+
export type ScrollMarkerModel = MarkerModel & { name: string };
|
|
@@ -175,8 +175,10 @@ export class AnimationTrackHandler extends TrackHandler {
|
|
|
175
175
|
// which means we want to notify the object that it's not animated anymore
|
|
176
176
|
// and the animator can then take over
|
|
177
177
|
onStateChanged() {
|
|
178
|
-
if (this._animator)
|
|
179
|
-
|
|
178
|
+
if (this._animator) {
|
|
179
|
+
// We can not check the *isPlaying* state here because the timeline might be paused and evaluated by e.g. ScrollFollow
|
|
180
|
+
setObjectAnimated(this._animator.gameObject, this, this.director.enabled && this.director.weight > 0);
|
|
181
|
+
}
|
|
180
182
|
}
|
|
181
183
|
|
|
182
184
|
createHooks(clipModel: Models.AnimationClipModel, clip) {
|
|
@@ -830,6 +832,28 @@ export class AudioTrackHandler extends TrackHandler {
|
|
|
830
832
|
}
|
|
831
833
|
}
|
|
832
834
|
|
|
835
|
+
export class MarkerTrackHandler extends TrackHandler {
|
|
836
|
+
models: Array<Models.MarkerModel & Record<string, any>> = [];
|
|
837
|
+
isDirty = true;
|
|
838
|
+
|
|
839
|
+
*foreachMarker<T>(type: string | null = null) {
|
|
840
|
+
for (const model of this.models) {
|
|
841
|
+
if (model && model.type === type) yield model as T;
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
onEnable() {
|
|
846
|
+
this.isDirty = true;
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
evaluate(_time: number) {
|
|
850
|
+
if (this.isDirty) {
|
|
851
|
+
this.isDirty = false;
|
|
852
|
+
this.models.sort((a, b) => a.time - b.time);
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
|
|
833
857
|
export class SignalTrackHandler extends TrackHandler {
|
|
834
858
|
models: Models.SignalMarkerModel[] = [];
|
|
835
859
|
didTrigger: boolean[] = [];
|
|
@@ -6,6 +6,7 @@ import { type UsdzBehaviour } from "../../engine-components/export/usdz/extensio
|
|
|
6
6
|
import { ActionBuilder, BehaviorModel, TriggerBuilder, USDVec3 } from "../../engine-components/export/usdz/extensions/behavior/BehavioursBuilder.js";
|
|
7
7
|
import { USDObject } from "../../engine-components/export/usdz/ThreeUSDZExporter.js";
|
|
8
8
|
import { Behaviour } from "../Component.js";
|
|
9
|
+
import { isDevEnvironment } from "../../engine/debug/index.js";
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* LookAt behaviour makes the object look at a target object or the camera.
|
|
@@ -42,7 +43,10 @@ export class LookAt extends Behaviour implements UsdzBehaviour {
|
|
|
42
43
|
/** @internal */
|
|
43
44
|
onBeforeRender(): void {
|
|
44
45
|
let target: Object3D | null | undefined = this.target;
|
|
45
|
-
if (!target)
|
|
46
|
+
if (!target) {
|
|
47
|
+
target = this.context.mainCamera;
|
|
48
|
+
if (isDevEnvironment()) console.warn(`[LookAt] No target set on ${this.name}, using main camera as target.`);
|
|
49
|
+
}
|
|
46
50
|
if (!target) return;
|
|
47
51
|
|
|
48
52
|
let copyTargetRotation = this.copyTargetRotation;
|