@needle-tools/engine 4.9.2 → 4.9.3-next.0facab6
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 +4 -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-WLYWw5OF.umd.cjs → needle-engine.bundle-DAo7BPxQ.umd.cjs} +149 -127
- package/dist/needle-engine.bundle-DP2gYtOQ.min.js +1638 -0
- package/dist/{needle-engine.bundle-XBRfMDwo.js → needle-engine.bundle-TvT7wv7z.js} +7567 -7380
- package/dist/needle-engine.js +446 -444
- package/dist/needle-engine.min.js +1 -1
- package/dist/needle-engine.umd.cjs +1 -1
- package/dist/{postprocessing-B_FzkwDz.min.js → postprocessing-BHMVuZQ1.min.js} +53 -53
- package/dist/{postprocessing-DTX5VXrj.umd.cjs → postprocessing-BsnRNRRS.umd.cjs} +76 -76
- package/dist/{postprocessing-DP1U_BpT.js → postprocessing-DQ2pynXW.js} +213 -191
- 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 +2 -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.fit.d.ts +68 -0
- package/lib/engine/engine_camera.fit.js +193 -0
- package/lib/engine/engine_camera.fit.js.map +1 -0
- 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/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.js +59 -23
- package/lib/engine/webcomponents/needle-engine.loading.js.map +1 -1
- package/lib/engine/xr/NeedleXRSession.d.ts +1 -0
- package/lib/engine/xr/NeedleXRSession.js +25 -11
- package/lib/engine/xr/NeedleXRSession.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 +4 -47
- package/lib/engine-components/OrbitControls.js +30 -178
- package/lib/engine-components/OrbitControls.js.map +1 -1
- package/lib/engine-components/Renderer.js +9 -5
- package/lib/engine-components/Renderer.js.map +1 -1
- package/lib/engine-components/SpriteRenderer.js +4 -1
- package/lib/engine-components/SpriteRenderer.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 +1 -0
- package/lib/engine-components/codegen/components.js +1 -0
- package/lib/engine-components/codegen/components.js.map +1 -1
- package/lib/engine-components/web/Clickthrough.d.ts +3 -0
- package/lib/engine-components/web/Clickthrough.js +3 -0
- 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 +4 -0
- package/lib/engine-components/web/ScrollFollow.js +4 -0
- package/lib/engine-components/web/ScrollFollow.js.map +1 -1
- package/lib/engine-components/web/index.d.ts +1 -0
- package/lib/engine-components/web/index.js +1 -0
- package/lib/engine-components/web/index.js.map +1 -1
- package/package.json +2 -2
- 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 +2 -0
- package/src/engine/engine_animation.ts +69 -1
- package/src/engine/engine_camera.fit.ts +288 -0
- package/src/engine/engine_gizmos.ts +2 -2
- package/src/engine/engine_physics.ts +6 -3
- package/src/engine/webcomponents/needle-engine.loading.ts +63 -24
- package/src/engine/webcomponents/needle-engine.ts +6 -1
- package/src/engine/xr/NeedleXRSession.ts +25 -10
- package/src/engine-components/AnimatorController.ts +21 -2
- package/src/engine-components/CameraUtils.ts +8 -9
- package/src/engine-components/OrbitControls.ts +30 -239
- package/src/engine-components/Renderer.ts +9 -6
- package/src/engine-components/SpriteRenderer.ts +3 -1
- package/src/engine-components/api.ts +0 -1
- package/src/engine-components/codegen/components.ts +1 -0
- package/src/engine-components/web/Clickthrough.ts +3 -0
- 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 +4 -0
- package/src/engine-components/web/index.ts +1 -0
- package/dist/needle-engine.bundle-DwspSk2O.min.js +0 -1616
|
@@ -230,8 +230,8 @@ export class Gizmos {
|
|
|
230
230
|
* Draw a 3D wiremesh box gizmo in the scene
|
|
231
231
|
* @param box the box in world space
|
|
232
232
|
* @param color the color of the box
|
|
233
|
-
* @param duration the duration in seconds the box will be rendered. If 0 it will be rendered for one frame
|
|
234
|
-
* @param depthTest if true the box will be rendered with depth test
|
|
233
|
+
* @param duration the duration in seconds the box will be rendered. If 0 it will be rendered for one frame. Default: 0
|
|
234
|
+
* @param depthTest if true the box will be rendered with depth test. Default: true
|
|
235
235
|
*/
|
|
236
236
|
static DrawWireBox3(box: Box3, color: ColorRepresentation = defaultColor, duration: number = 0, depthTest: boolean = true) {
|
|
237
237
|
if (!Gizmos.enabled) return;
|
|
@@ -592,7 +592,7 @@ namespace NEMeshBVH {
|
|
|
592
592
|
const timeSinceLastUpdate = now - skinnedMesh.staticGeometryLastUpdate!;
|
|
593
593
|
const interval = skinnedMesh.autoUpdateMeshBvhInterval ?? 100;
|
|
594
594
|
if (skinnedMeshBVHNeedsUpdate || timeSinceLastUpdate > interval) {
|
|
595
|
-
if(debugPhysics) console.warn(`Physics: updating skinned mesh bvh for ${mesh.name} after ${timeSinceLastUpdate.toFixed(2)}ms`);
|
|
595
|
+
if (debugPhysics) console.warn(`Physics: updating skinned mesh bvh for ${mesh.name} after ${timeSinceLastUpdate.toFixed(2)}ms`);
|
|
596
596
|
skinnedMesh.bvhNeedsUpdate = false;
|
|
597
597
|
skinnedMesh.staticGeometryLastUpdate = now;
|
|
598
598
|
skinnedMesh.staticGenerator?.generate(skinnedMesh.staticGeometry);
|
|
@@ -709,8 +709,11 @@ namespace NEMeshBVH {
|
|
|
709
709
|
else {
|
|
710
710
|
if (debugPhysics) console.warn("No bounds tree found for mesh", mesh.name, { workerTask: geom[workerTaskSymbol], hasAcceleratedRaycast: _acceleratedRaycast != null });
|
|
711
711
|
if (options.allowSlowRaycastFallback === false) {
|
|
712
|
-
|
|
713
|
-
|
|
712
|
+
const vertices = geom.getAttribute("position")?.array?.length ?? 0;
|
|
713
|
+
if (vertices > 2000) {
|
|
714
|
+
if (debugPhysics) console.warn("Skipping raycast because no bounds tree is available and allowSlowRaycastFallback is false");
|
|
715
|
+
return false;
|
|
716
|
+
}
|
|
714
717
|
}
|
|
715
718
|
}
|
|
716
719
|
const prevFirstHitOnly = raycaster.firstHitOnly;
|
|
@@ -158,8 +158,18 @@ export class EngineLoadingView implements ILoadingViewHandler {
|
|
|
158
158
|
private onDoneLoading() {
|
|
159
159
|
if (this._loadingElement) {
|
|
160
160
|
if (debug) console.log("Hiding loading element");
|
|
161
|
-
|
|
162
|
-
this._loadingElement
|
|
161
|
+
// animate alpha to 0
|
|
162
|
+
const element = this._loadingElement;
|
|
163
|
+
element.animate([
|
|
164
|
+
{ opacity: 1 },
|
|
165
|
+
{ opacity: 0 }
|
|
166
|
+
], {
|
|
167
|
+
duration: 200,
|
|
168
|
+
easing: 'ease-in-out',
|
|
169
|
+
}).addEventListener('finish', () => {
|
|
170
|
+
element.style.display = "none";
|
|
171
|
+
element.remove();
|
|
172
|
+
});
|
|
163
173
|
}
|
|
164
174
|
if (this._progressLoop)
|
|
165
175
|
clearInterval(this._progressLoop);
|
|
@@ -190,6 +200,7 @@ export class EngineLoadingView implements ILoadingViewHandler {
|
|
|
190
200
|
loadingStyle = "light";
|
|
191
201
|
}
|
|
192
202
|
|
|
203
|
+
|
|
193
204
|
const hasLicense = hasProLicense();
|
|
194
205
|
if (!existing) {
|
|
195
206
|
this._loadingElement.style.position = "absolute";
|
|
@@ -197,6 +208,7 @@ export class EngineLoadingView implements ILoadingViewHandler {
|
|
|
197
208
|
this._loadingElement.style.height = "100%";
|
|
198
209
|
this._loadingElement.style.left = "0";
|
|
199
210
|
this._loadingElement.style.top = "0";
|
|
211
|
+
this._loadingElement.style.overflow = "hidden";
|
|
200
212
|
const loadingBackgroundColor = this._element.getAttribute("loading-background");
|
|
201
213
|
if (loadingBackgroundColor) {
|
|
202
214
|
this._loadingElement.style.background = loadingBackgroundColor;
|
|
@@ -227,24 +239,48 @@ export class EngineLoadingView implements ILoadingViewHandler {
|
|
|
227
239
|
}
|
|
228
240
|
|
|
229
241
|
const content = document.createElement("div");
|
|
242
|
+
content.style.cssText = `
|
|
243
|
+
position: relative;
|
|
244
|
+
display: flex;
|
|
245
|
+
flex-direction: column;
|
|
246
|
+
align-items: center;
|
|
247
|
+
justify-content: center;
|
|
248
|
+
width: 100%;
|
|
249
|
+
height: 100%;
|
|
250
|
+
pointer-events: none;
|
|
251
|
+
`
|
|
230
252
|
this._loadingElement.appendChild(content);
|
|
231
253
|
|
|
254
|
+
const poster = this._element.getAttribute("poster");
|
|
255
|
+
if (poster !== null && poster !== "0") {
|
|
256
|
+
const backgroundImage = document.createElement("div");
|
|
257
|
+
const backgroundBlur = poster?.length ? "0px" : "50px";
|
|
258
|
+
backgroundImage.style.cssText = `
|
|
259
|
+
position: absolute;
|
|
260
|
+
left: 0;
|
|
261
|
+
top: 0;
|
|
262
|
+
bottom: 0;
|
|
263
|
+
right: 0;
|
|
264
|
+
z-index: -1;
|
|
265
|
+
overflow: hidden;
|
|
266
|
+
|
|
267
|
+
margin: -${backgroundBlur};
|
|
268
|
+
background: url('${poster?.length ? poster : "/include/poster.webp"}') center center no-repeat;
|
|
269
|
+
background-size: cover;
|
|
270
|
+
filter: blur(${backgroundBlur});
|
|
271
|
+
`;
|
|
272
|
+
this._loadingElement.appendChild(backgroundImage);
|
|
273
|
+
}
|
|
274
|
+
|
|
232
275
|
const logo = document.createElement("img");
|
|
233
|
-
const
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
logo.style.paddingTop = "20px";
|
|
237
|
-
logo.style.paddingBottom = "10px";
|
|
238
|
-
logo.style.margin = "0px";
|
|
276
|
+
const logoWidth = "80%";
|
|
277
|
+
const logoHeight = "15%";
|
|
278
|
+
const logoDelay = ".2s";
|
|
239
279
|
logo.style.userSelect = "none";
|
|
240
280
|
logo.style.objectFit = "contain";
|
|
241
|
-
logo.style.transition = "transform 1.5s ease-out, opacity .3s ease-in-out";
|
|
242
281
|
logo.style.transform = "translateY(30px)";
|
|
243
|
-
logo.style.opacity = "0.
|
|
244
|
-
|
|
245
|
-
logo.style.opacity = "1";
|
|
246
|
-
logo.style.transform = "translateY(0px)";
|
|
247
|
-
}, 1);
|
|
282
|
+
logo.style.opacity = "0.0000001";
|
|
283
|
+
logo.style.transition = `transform 1s ease-out ${logoDelay}, opacity .3s ease-in-out ${logoDelay}`;
|
|
248
284
|
logo.src = needleLogoOnlySVG;
|
|
249
285
|
let isUsingCustomLogo = false;
|
|
250
286
|
if (hasLicense && this._element) {
|
|
@@ -252,8 +288,15 @@ export class EngineLoadingView implements ILoadingViewHandler {
|
|
|
252
288
|
if (customLogo) {
|
|
253
289
|
isUsingCustomLogo = true;
|
|
254
290
|
logo.src = customLogo;
|
|
291
|
+
setTimeout(() => {
|
|
292
|
+
logo.style.opacity = "1";
|
|
293
|
+
logo.style.transform = "translateY(0px)";
|
|
294
|
+
}, 1);
|
|
255
295
|
}
|
|
256
296
|
}
|
|
297
|
+
|
|
298
|
+
logo.style.width = `${logoWidth}`;
|
|
299
|
+
logo.style.height = `min(1000px, max(${logoHeight}, 50px))`;
|
|
257
300
|
content.appendChild(logo);
|
|
258
301
|
|
|
259
302
|
const details = document.createElement("div");
|
|
@@ -273,18 +316,14 @@ export class EngineLoadingView implements ILoadingViewHandler {
|
|
|
273
316
|
const maxWidth = 100;
|
|
274
317
|
loadingBarContainer.style.display = "flex";
|
|
275
318
|
loadingBarContainer.style.width = maxWidth + "%";
|
|
276
|
-
loadingBarContainer.style.height = "
|
|
319
|
+
loadingBarContainer.style.height = "5px";
|
|
277
320
|
loadingBarContainer.style.position = "absolute";
|
|
278
321
|
loadingBarContainer.style.left = "0";
|
|
279
|
-
loadingBarContainer.style.
|
|
322
|
+
loadingBarContainer.style.top = "0px";
|
|
280
323
|
loadingBarContainer.style.opacity = "0";
|
|
281
|
-
loadingBarContainer.style.transition = "opacity 1s ease-in-out
|
|
324
|
+
loadingBarContainer.style.transition = "opacity 1s ease-in-out";
|
|
325
|
+
loadingBarContainer.style.backgroundColor = "rgba(240,240,240,.5)"
|
|
282
326
|
setTimeout(() => { loadingBarContainer.style.opacity = "1"; }, 1);
|
|
283
|
-
if (loadingStyle === "light")
|
|
284
|
-
loadingBarContainer.style.backgroundColor = "rgba(0,0,0,.2)"
|
|
285
|
-
else
|
|
286
|
-
loadingBarContainer.style.backgroundColor = "rgba(255,255,255,.2)"
|
|
287
|
-
// loadingBarContainer.style.alignItems = "center";
|
|
288
327
|
|
|
289
328
|
this._loadingElement.appendChild(loadingBarContainer);
|
|
290
329
|
|
|
@@ -294,9 +333,9 @@ export class EngineLoadingView implements ILoadingViewHandler {
|
|
|
294
333
|
const getGradientPos = function (t: number): string {
|
|
295
334
|
return Mathf.lerp(0, maxWidth, t) + "%";
|
|
296
335
|
}
|
|
297
|
-
|
|
298
|
-
// `linear-gradient(90deg, #204f49 ${getGradientPos(0)}, #0BA398 ${getGradientPos(.3)}, #66A22F ${getGradientPos(.6)}, #D7DB0A ${getGradientPos(1)})`;
|
|
336
|
+
// `linear-gradient(90deg, #204f49 ${getGradientPos(0)}, #0BA398 ${getGradientPos(.3)}, #66A22F ${getGradientPos(.6)}, #D7DB0A ${getGradientPos(1)})`;
|
|
299
337
|
this._loadingBar.style.backgroundAttachment = "fixed";
|
|
338
|
+
this._loadingBar.style.background = "#c4c4c4ab";
|
|
300
339
|
this._loadingBar.style.width = "0%";
|
|
301
340
|
this._loadingBar.style.height = "100%";
|
|
302
341
|
if (hasLicense && this._element) {
|
|
@@ -85,12 +85,17 @@ export class NeedleEngineWebComponent extends HTMLElement implements INeedleEngi
|
|
|
85
85
|
* <needle-engine></needle-engine>
|
|
86
86
|
* @returns {boolean | null} if the attribute is not set it returns null
|
|
87
87
|
*/
|
|
88
|
-
|
|
88
|
+
get cameraControls(): boolean | null {
|
|
89
89
|
const attr = this.getAttribute("camera-controls") as NeedleEngineAttributes["camera-controls"] | ({} & string)
|
|
90
90
|
if (attr == null) return null;
|
|
91
91
|
if (attr === null || attr === "False" || attr === "false" || attr === "0" || attr === "none") return false;
|
|
92
92
|
return true;
|
|
93
93
|
}
|
|
94
|
+
set cameraControls(value: boolean | null) {
|
|
95
|
+
if (value === null) this.removeAttribute("camera-controls");
|
|
96
|
+
else this.setAttribute("camera-controls", value ? "true" : "false");
|
|
97
|
+
}
|
|
98
|
+
|
|
94
99
|
|
|
95
100
|
/**
|
|
96
101
|
* Get the current context for this web component instance. The context is created when the src attribute is set and the loading has finished.
|
|
@@ -1114,6 +1114,7 @@ export class NeedleXRSession implements INeedleXRSession {
|
|
|
1114
1114
|
this.revertCustomForward();
|
|
1115
1115
|
this._didStart = false;
|
|
1116
1116
|
this._previousCameraParent = null;
|
|
1117
|
+
this.requestedCameraNearPlane = null;
|
|
1117
1118
|
|
|
1118
1119
|
unregisterFrameEventCallback(this.onBefore, FrameEvent.LateUpdate);
|
|
1119
1120
|
const index = this.context.pre_render_callbacks.indexOf(this.onBeforeRender);
|
|
@@ -1412,8 +1413,14 @@ export class NeedleXRSession implements INeedleXRSession {
|
|
|
1412
1413
|
}
|
|
1413
1414
|
|
|
1414
1415
|
private onBeforeRender = () => {
|
|
1415
|
-
if (this.context.mainCamera)
|
|
1416
|
+
if (this.context.mainCamera) {
|
|
1416
1417
|
this.updateFade(this.context.mainCamera);
|
|
1418
|
+
|
|
1419
|
+
if (this.requestedCameraNearPlane !== null && this.context.mainCamera instanceof PerspectiveCamera) {
|
|
1420
|
+
this.context.mainCamera.near = this.requestedCameraNearPlane;
|
|
1421
|
+
this.requestedCameraNearPlane = null;
|
|
1422
|
+
}
|
|
1423
|
+
}
|
|
1417
1424
|
}
|
|
1418
1425
|
|
|
1419
1426
|
private onAfterRender = () => {
|
|
@@ -1556,6 +1563,7 @@ export class NeedleXRSession implements INeedleXRSession {
|
|
|
1556
1563
|
private _previousCameraParent!: Object3D | null;
|
|
1557
1564
|
private readonly _customforward: boolean = true;
|
|
1558
1565
|
private originalCameraNearPlane?: number;
|
|
1566
|
+
private requestedCameraNearPlane: number | null = null;
|
|
1559
1567
|
/** This is used to have the XR system camera look into threejs Z forward direction (instead of -z) */
|
|
1560
1568
|
private applyCustomForward() {
|
|
1561
1569
|
if (this.context.mainCamera && this._customforward) {
|
|
@@ -1567,15 +1575,22 @@ export class NeedleXRSession implements INeedleXRSession {
|
|
|
1567
1575
|
this._cameraRenderParent.name = "XR Camera Render Parent";
|
|
1568
1576
|
this._cameraRenderParent.add(this._camera);
|
|
1569
1577
|
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
const
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
this._camera.near
|
|
1578
|
-
|
|
1578
|
+
{
|
|
1579
|
+
let minNearPlane = .02;
|
|
1580
|
+
const threshold = 0.001;
|
|
1581
|
+
if (this.rig) {
|
|
1582
|
+
const rigWorldScale = getWorldScale(this.rig.gameObject);
|
|
1583
|
+
minNearPlane *= rigWorldScale.x;
|
|
1584
|
+
}
|
|
1585
|
+
if (this._camera instanceof PerspectiveCamera && Math.abs(this._camera.near - minNearPlane) > threshold) {
|
|
1586
|
+
// TODO: when we're in AR and update the near plane we do get glitchy rendering/sheered matrix transforms when scaling the scene (WebXRSessionRoot). As a workaround we defer AR near plane changes to before rendering but change VR near planes immediately.
|
|
1587
|
+
if (this.isAR)
|
|
1588
|
+
this.originalCameraNearPlane = this._camera.near;
|
|
1589
|
+
else {
|
|
1590
|
+
this._camera.near = minNearPlane;
|
|
1591
|
+
}
|
|
1592
|
+
if (debug) console.debug(`Setting camera near plane to ${minNearPlane} (was ${this.originalCameraNearPlane}) to account for XR rendering scale`);
|
|
1593
|
+
}
|
|
1579
1594
|
}
|
|
1580
1595
|
}
|
|
1581
1596
|
}
|
|
@@ -276,7 +276,7 @@ export class AnimatorController {
|
|
|
276
276
|
* @returns The found state or null if not found
|
|
277
277
|
*/
|
|
278
278
|
FindState(name: string | number | undefined | null): State | null { return this.findState(name); }
|
|
279
|
-
|
|
279
|
+
|
|
280
280
|
/**
|
|
281
281
|
* Finds an animation state by name or hash.
|
|
282
282
|
*
|
|
@@ -321,6 +321,25 @@ export class AnimatorController {
|
|
|
321
321
|
return action;
|
|
322
322
|
}
|
|
323
323
|
|
|
324
|
+
// addState(state: State, layerIndex: number = 0) {
|
|
325
|
+
// if (!this.model) throw new Error("AnimatorController model is missing");
|
|
326
|
+
// if (layerIndex < 0 || layerIndex >= this.model.layers.length) {
|
|
327
|
+
// throw new Error(`Invalid layer index: ${layerIndex}`);
|
|
328
|
+
// }
|
|
329
|
+
// const layer = this.model.layers[layerIndex];
|
|
330
|
+
// if (!layer.stateMachine) {
|
|
331
|
+
// layer.stateMachine = { states: [], defaultState: 0 };
|
|
332
|
+
// }
|
|
333
|
+
// if (!layer.stateMachine.states) {
|
|
334
|
+
// layer.stateMachine.states = [];
|
|
335
|
+
// }
|
|
336
|
+
// if (state.hash === undefined) {
|
|
337
|
+
// state.hash = stringToHash(state.name || "state" + layer.stateMachine.states.length);
|
|
338
|
+
// }
|
|
339
|
+
|
|
340
|
+
// }
|
|
341
|
+
|
|
342
|
+
|
|
324
343
|
/**
|
|
325
344
|
* The normalized time (0-1) to start playing the first state at.
|
|
326
345
|
* This affects the initial state when the animator is first enabled.
|
|
@@ -331,7 +350,7 @@ export class AnimatorController {
|
|
|
331
350
|
* The Animator component this controller is bound to.
|
|
332
351
|
*/
|
|
333
352
|
animator?: Animator;
|
|
334
|
-
|
|
353
|
+
|
|
335
354
|
/**
|
|
336
355
|
* The data model describing the animation states and transitions.
|
|
337
356
|
*/
|
|
@@ -117,16 +117,15 @@ function createDefaultCameraControls(context: IContext, cam?: ICamera) {
|
|
|
117
117
|
if (cameraObject) {
|
|
118
118
|
const orbit = getOrAddComponent(cameraObject, OrbitControls) as OrbitControls;
|
|
119
119
|
orbit.sourceId = cam?.sourceId ?? "unknown";
|
|
120
|
+
// /enable auto-rotate if the auto-rotate attribute is provided
|
|
120
121
|
const autoRotate = context.domElement.getAttribute("auto-rotate");
|
|
121
|
-
orbit.autoRotate = autoRotate
|
|
122
|
-
|
|
123
|
-
orbit.
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
}
|
|
129
|
-
}
|
|
122
|
+
orbit.autoRotate = autoRotate != "0" && autoRotate?.toLowerCase() != "false";
|
|
123
|
+
const autoRotateSpeed = Number.parseFloat(autoRotate || ".5");
|
|
124
|
+
orbit.autoRotateSpeed = !isNaN(autoRotateSpeed) ? autoRotateSpeed : .5;
|
|
125
|
+
console.log("Auto-rotate", orbit.autoRotate, "speed:", orbit.autoRotateSpeed);
|
|
126
|
+
const autoFit = context.domElement.getAttribute("auto-fit");
|
|
127
|
+
orbit.autoFit = autoFit !== "0" && autoFit?.toLowerCase() != "false";
|
|
128
|
+
orbit.autoTarget = true;
|
|
130
129
|
}
|
|
131
130
|
else {
|
|
132
131
|
console.warn("Missing camera object, can not add orbit controls")
|
|
@@ -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();
|
|
@@ -989,242 +996,26 @@ export class OrbitControls extends Behaviour implements ICameraController {
|
|
|
989
996
|
/**
|
|
990
997
|
* Fits the camera to show the objects provided (defaults to the scene if no objects are passed in)
|
|
991
998
|
*/
|
|
992
|
-
fitCamera(options?:
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
}
|
|
1007
|
-
// If the user passed in an object as first argument
|
|
1008
|
-
else if (objectsOrOptions && "type" in objectsOrOptions) {
|
|
1009
|
-
objects = objectsOrOptions;
|
|
1010
|
-
}
|
|
1011
|
-
// If the user passed in an object as first argument and options as second argument
|
|
1012
|
-
else if (objectsOrOptions && typeof objectsOrOptions === "object") {
|
|
1013
|
-
if (!(objectsOrOptions instanceof Object3D) && !Array.isArray(objectsOrOptions)) {
|
|
1014
|
-
options = objectsOrOptions;
|
|
1015
|
-
objects = options.objects;
|
|
1016
|
-
}
|
|
1017
|
-
}
|
|
1018
|
-
// Ensure objects are setup correctly
|
|
1019
|
-
if (objects && !Array.isArray(objects)) {
|
|
1020
|
-
objects = [objects];
|
|
1021
|
-
}
|
|
1022
|
-
if (!Array.isArray(objects) || objects && objects.length <= 0) {
|
|
1023
|
-
objects = this.context.scene.children;
|
|
1024
|
-
}
|
|
1025
|
-
|
|
1026
|
-
// Make sure there's anything to fit to
|
|
1027
|
-
if (!Array.isArray(objects) || objects.length <= 0) {
|
|
1028
|
-
console.warn("No objects to fit camera to...");
|
|
1029
|
-
return;
|
|
1030
|
-
}
|
|
1031
|
-
|
|
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
|
-
|
|
999
|
+
fitCamera(options?: OrbitFitCameraOptions): void {
|
|
1000
|
+
const res = fitCamera({
|
|
1001
|
+
...options,
|
|
1002
|
+
autoApply: false,
|
|
1003
|
+
context: this.context,
|
|
1004
|
+
camera: this._cameraObject as Camera3,
|
|
1005
|
+
currentZoom: this._controls?.getDistance() || undefined,
|
|
1006
|
+
minZoom: this.minZoom,
|
|
1007
|
+
maxZoom: this.maxZoom,
|
|
1008
|
+
});
|
|
1009
|
+
if (!res) return;
|
|
1010
|
+
this.setLookTargetPosition(res.lookAt, options?.immediate || false);
|
|
1011
|
+
this.setCameraTargetPosition(res.position, options?.immediate || false);
|
|
1012
|
+
this.setFieldOfView(options?.fov, options?.immediate || false);
|
|
1170
1013
|
this.onBeforeRender();
|
|
1171
|
-
// controls.update(); // this is not enough when calling fitCamera({immediate:true}) in an interval
|
|
1172
1014
|
}
|
|
1173
1015
|
|
|
1174
1016
|
private _haveAttachedKeyboardEvents: boolean = false;
|
|
1175
1017
|
}
|
|
1176
1018
|
|
|
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
|
-
*/
|
|
1019
|
+
type OrbitFitCameraOptions = FitCameraOptions & {
|
|
1191
1020
|
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
|
-
}
|
|
1021
|
+
}
|