@needle-tools/engine 4.9.3-next.0facab6 → 4.9.3
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/components.needle.json +1 -1
- package/dist/{gltf-progressive-Iy7aSAPk.umd.cjs → gltf-progressive-DWiyqrwB.umd.cjs} +1 -1
- package/dist/{gltf-progressive-CoZbSfPR.min.js → gltf-progressive-DhE1A6hX.min.js} +1 -1
- package/dist/{gltf-progressive-DUR9TuAH.js → gltf-progressive-egsMzRdv.js} +3 -3
- package/dist/{needle-engine.bundle-TvT7wv7z.js → needle-engine.bundle-BAsxNKpA.js} +7343 -7525
- package/dist/{needle-engine.bundle-DAo7BPxQ.umd.cjs → needle-engine.bundle-C7LSzO5L.umd.cjs} +122 -144
- package/dist/needle-engine.bundle-ugr1bBtk.min.js +1616 -0
- package/dist/needle-engine.js +444 -446
- package/dist/needle-engine.min.js +1 -1
- package/dist/needle-engine.umd.cjs +1 -1
- package/dist/{postprocessing-BHMVuZQ1.min.js → postprocessing-BZOSD1ln.min.js} +1 -1
- package/dist/{postprocessing-BsnRNRRS.umd.cjs → postprocessing-Bb5StX0o.umd.cjs} +1 -1
- package/dist/{postprocessing-DQ2pynXW.js → postprocessing-BzFF7i-7.js} +2 -2
- package/dist/{three-B-jwTHao.umd.cjs → three-BK56xWDs.umd.cjs} +11 -11
- package/dist/{three-CJSAehtG.js → three-CsHK73Zc.js} +0 -1
- package/dist/{three-qw28ZtTy.min.js → three-TNFQHSFa.min.js} +10 -10
- package/dist/{three-examples-BivkhnvN.min.js → three-examples-Bph291U2.min.js} +1 -1
- package/dist/{three-examples-Doq0rvFU.js → three-examples-BvMpKSun.js} +1 -1
- package/dist/{three-examples-Deqc1bNw.umd.cjs → three-examples-C9WfZu-X.umd.cjs} +1 -1
- package/dist/{three-mesh-ui-CktOi6oI.js → three-mesh-ui-CN6aRT7i.js} +1 -1
- package/dist/{three-mesh-ui-CsHwj9cJ.umd.cjs → three-mesh-ui-DnxkZWNA.umd.cjs} +1 -1
- package/dist/{three-mesh-ui-DhYXcXZe.min.js → three-mesh-ui-n_qS2BM-.min.js} +1 -1
- package/dist/{vendor-D0Yvltn9.umd.cjs → vendor-BtJpSuCj.umd.cjs} +1 -1
- package/dist/{vendor-JyrX4DVM.min.js → vendor-XJ9xiwrv.min.js} +1 -1
- package/dist/{vendor-DU8tJyl_.js → vendor-k9i6CeGi.js} +1 -1
- package/lib/engine/api.d.ts +0 -1
- package/lib/engine/api.js +0 -1
- package/lib/engine/api.js.map +1 -1
- package/lib/engine/codegen/register_types.js +0 -2
- package/lib/engine/codegen/register_types.js.map +1 -1
- package/lib/engine/engine_animation.d.ts +1 -21
- package/lib/engine/engine_animation.js +1 -32
- package/lib/engine/engine_animation.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 +3 -6
- package/lib/engine/engine_physics.js.map +1 -1
- package/lib/engine/webcomponents/needle-engine.d.ts +0 -1
- package/lib/engine/webcomponents/needle-engine.js +0 -6
- package/lib/engine/webcomponents/needle-engine.js.map +1 -1
- package/lib/engine/webcomponents/needle-engine.loading.js +23 -59
- package/lib/engine/webcomponents/needle-engine.loading.js.map +1 -1
- package/lib/engine-components/AnimatorController.js +0 -16
- package/lib/engine-components/AnimatorController.js.map +1 -1
- package/lib/engine-components/CameraUtils.js +9 -8
- package/lib/engine-components/CameraUtils.js.map +1 -1
- package/lib/engine-components/OrbitControls.d.ts +47 -4
- package/lib/engine-components/OrbitControls.js +178 -30
- package/lib/engine-components/OrbitControls.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/codegen/components.d.ts +0 -1
- package/lib/engine-components/codegen/components.js +0 -1
- package/lib/engine-components/codegen/components.js.map +1 -1
- package/lib/engine-components/web/Clickthrough.d.ts +0 -3
- package/lib/engine-components/web/Clickthrough.js +0 -3
- package/lib/engine-components/web/Clickthrough.js.map +1 -1
- package/lib/engine-components/web/CursorFollow.d.ts +0 -3
- package/lib/engine-components/web/CursorFollow.js +0 -3
- package/lib/engine-components/web/CursorFollow.js.map +1 -1
- package/lib/engine-components/web/ScrollFollow.d.ts +0 -4
- package/lib/engine-components/web/ScrollFollow.js +0 -4
- package/lib/engine-components/web/ScrollFollow.js.map +1 -1
- package/lib/engine-components/web/index.d.ts +0 -1
- package/lib/engine-components/web/index.js +0 -1
- package/lib/engine-components/web/index.js.map +1 -1
- package/package.json +2 -2
- package/plugins/vite/alias.js +3 -5
- package/plugins/vite/poster-client.js +21 -22
- package/src/engine/api.ts +1 -2
- package/src/engine/codegen/register_types.ts +0 -2
- package/src/engine/engine_animation.ts +1 -69
- package/src/engine/engine_gizmos.ts +2 -2
- package/src/engine/engine_physics.ts +3 -6
- package/src/engine/webcomponents/needle-engine.loading.ts +24 -63
- package/src/engine/webcomponents/needle-engine.ts +1 -6
- package/src/engine-components/AnimatorController.ts +2 -21
- package/src/engine-components/CameraUtils.ts +9 -8
- package/src/engine-components/OrbitControls.ts +239 -30
- package/src/engine-components/api.ts +1 -0
- package/src/engine-components/codegen/components.ts +0 -1
- package/src/engine-components/web/Clickthrough.ts +0 -3
- package/src/engine-components/web/CursorFollow.ts +0 -3
- package/src/engine-components/web/ScrollFollow.ts +0 -4
- package/src/engine-components/web/index.ts +0 -1
- package/dist/needle-engine.bundle-DP2gYtOQ.min.js +0 -1638
- package/lib/engine/engine_camera.fit.d.ts +0 -68
- package/lib/engine/engine_camera.fit.js +0 -193
- package/lib/engine/engine_camera.fit.js.map +0 -1
- package/lib/engine-components/web/HoverAnimation.d.ts +0 -44
- package/lib/engine-components/web/HoverAnimation.js +0 -105
- package/lib/engine-components/web/HoverAnimation.js.map +0 -1
- package/src/engine/engine_camera.fit.ts +0 -288
- package/src/engine-components/web/HoverAnimation.ts +0 -99
|
@@ -158,18 +158,8 @@ 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
|
-
|
|
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
|
-
});
|
|
161
|
+
this._loadingElement.style.display = "none";
|
|
162
|
+
this._loadingElement.remove();
|
|
173
163
|
}
|
|
174
164
|
if (this._progressLoop)
|
|
175
165
|
clearInterval(this._progressLoop);
|
|
@@ -200,7 +190,6 @@ export class EngineLoadingView implements ILoadingViewHandler {
|
|
|
200
190
|
loadingStyle = "light";
|
|
201
191
|
}
|
|
202
192
|
|
|
203
|
-
|
|
204
193
|
const hasLicense = hasProLicense();
|
|
205
194
|
if (!existing) {
|
|
206
195
|
this._loadingElement.style.position = "absolute";
|
|
@@ -208,7 +197,6 @@ export class EngineLoadingView implements ILoadingViewHandler {
|
|
|
208
197
|
this._loadingElement.style.height = "100%";
|
|
209
198
|
this._loadingElement.style.left = "0";
|
|
210
199
|
this._loadingElement.style.top = "0";
|
|
211
|
-
this._loadingElement.style.overflow = "hidden";
|
|
212
200
|
const loadingBackgroundColor = this._element.getAttribute("loading-background");
|
|
213
201
|
if (loadingBackgroundColor) {
|
|
214
202
|
this._loadingElement.style.background = loadingBackgroundColor;
|
|
@@ -239,48 +227,24 @@ export class EngineLoadingView implements ILoadingViewHandler {
|
|
|
239
227
|
}
|
|
240
228
|
|
|
241
229
|
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
|
-
`
|
|
252
230
|
this._loadingElement.appendChild(content);
|
|
253
231
|
|
|
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
|
-
|
|
275
232
|
const logo = document.createElement("img");
|
|
276
|
-
const
|
|
277
|
-
|
|
278
|
-
|
|
233
|
+
const logoSize = 120;
|
|
234
|
+
logo.style.width = `${logoSize}px`;
|
|
235
|
+
logo.style.height = `${logoSize}px`;
|
|
236
|
+
logo.style.paddingTop = "20px";
|
|
237
|
+
logo.style.paddingBottom = "10px";
|
|
238
|
+
logo.style.margin = "0px";
|
|
279
239
|
logo.style.userSelect = "none";
|
|
280
240
|
logo.style.objectFit = "contain";
|
|
241
|
+
logo.style.transition = "transform 1.5s ease-out, opacity .3s ease-in-out";
|
|
281
242
|
logo.style.transform = "translateY(30px)";
|
|
282
|
-
logo.style.opacity = "0.
|
|
283
|
-
|
|
243
|
+
logo.style.opacity = "0.05";
|
|
244
|
+
setTimeout(() => {
|
|
245
|
+
logo.style.opacity = "1";
|
|
246
|
+
logo.style.transform = "translateY(0px)";
|
|
247
|
+
}, 1);
|
|
284
248
|
logo.src = needleLogoOnlySVG;
|
|
285
249
|
let isUsingCustomLogo = false;
|
|
286
250
|
if (hasLicense && this._element) {
|
|
@@ -288,15 +252,8 @@ export class EngineLoadingView implements ILoadingViewHandler {
|
|
|
288
252
|
if (customLogo) {
|
|
289
253
|
isUsingCustomLogo = true;
|
|
290
254
|
logo.src = customLogo;
|
|
291
|
-
setTimeout(() => {
|
|
292
|
-
logo.style.opacity = "1";
|
|
293
|
-
logo.style.transform = "translateY(0px)";
|
|
294
|
-
}, 1);
|
|
295
255
|
}
|
|
296
256
|
}
|
|
297
|
-
|
|
298
|
-
logo.style.width = `${logoWidth}`;
|
|
299
|
-
logo.style.height = `min(1000px, max(${logoHeight}, 50px))`;
|
|
300
257
|
content.appendChild(logo);
|
|
301
258
|
|
|
302
259
|
const details = document.createElement("div");
|
|
@@ -316,14 +273,18 @@ export class EngineLoadingView implements ILoadingViewHandler {
|
|
|
316
273
|
const maxWidth = 100;
|
|
317
274
|
loadingBarContainer.style.display = "flex";
|
|
318
275
|
loadingBarContainer.style.width = maxWidth + "%";
|
|
319
|
-
loadingBarContainer.style.height = "
|
|
276
|
+
loadingBarContainer.style.height = "3px";
|
|
320
277
|
loadingBarContainer.style.position = "absolute";
|
|
321
278
|
loadingBarContainer.style.left = "0";
|
|
322
|
-
loadingBarContainer.style.
|
|
279
|
+
loadingBarContainer.style.bottom = "0px";
|
|
323
280
|
loadingBarContainer.style.opacity = "0";
|
|
324
|
-
loadingBarContainer.style.transition = "opacity 1s ease-in-out";
|
|
325
|
-
loadingBarContainer.style.backgroundColor = "rgba(240,240,240,.5)"
|
|
281
|
+
loadingBarContainer.style.transition = "opacity 1s ease-in-out 2s";
|
|
326
282
|
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";
|
|
327
288
|
|
|
328
289
|
this._loadingElement.appendChild(loadingBarContainer);
|
|
329
290
|
|
|
@@ -333,9 +294,9 @@ export class EngineLoadingView implements ILoadingViewHandler {
|
|
|
333
294
|
const getGradientPos = function (t: number): string {
|
|
334
295
|
return Mathf.lerp(0, maxWidth, t) + "%";
|
|
335
296
|
}
|
|
336
|
-
|
|
297
|
+
this._loadingBar.style.background = "#66A22F";
|
|
298
|
+
// `linear-gradient(90deg, #204f49 ${getGradientPos(0)}, #0BA398 ${getGradientPos(.3)}, #66A22F ${getGradientPos(.6)}, #D7DB0A ${getGradientPos(1)})`;
|
|
337
299
|
this._loadingBar.style.backgroundAttachment = "fixed";
|
|
338
|
-
this._loadingBar.style.background = "#c4c4c4ab";
|
|
339
300
|
this._loadingBar.style.width = "0%";
|
|
340
301
|
this._loadingBar.style.height = "100%";
|
|
341
302
|
if (hasLicense && this._element) {
|
|
@@ -85,17 +85,12 @@ 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
|
-
get cameraControls(): boolean | null {
|
|
88
|
+
public 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
|
-
|
|
99
94
|
|
|
100
95
|
/**
|
|
101
96
|
* Get the current context for this web component instance. The context is created when the src attribute is set and the loading has finished.
|
|
@@ -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,25 +321,6 @@ 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
|
-
|
|
343
324
|
/**
|
|
344
325
|
* The normalized time (0-1) to start playing the first state at.
|
|
345
326
|
* This affects the initial state when the animator is first enabled.
|
|
@@ -350,7 +331,7 @@ export class AnimatorController {
|
|
|
350
331
|
* The Animator component this controller is bound to.
|
|
351
332
|
*/
|
|
352
333
|
animator?: Animator;
|
|
353
|
-
|
|
334
|
+
|
|
354
335
|
/**
|
|
355
336
|
* The data model describing the animation states and transitions.
|
|
356
337
|
*/
|
|
@@ -117,15 +117,16 @@ 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
|
|
121
120
|
const autoRotate = context.domElement.getAttribute("auto-rotate");
|
|
122
|
-
orbit.autoRotate = autoRotate != "0" && autoRotate?.toLowerCase() != "false";
|
|
123
|
-
|
|
124
|
-
orbit.
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
121
|
+
orbit.autoRotate = autoRotate !== undefined && autoRotate !== null && (autoRotate != "0" && autoRotate?.toLowerCase() != "false");
|
|
122
|
+
orbit.autoRotateSpeed = 0.5;
|
|
123
|
+
orbit.autoFit = true;
|
|
124
|
+
if (orbit.autoRotate && autoRotate) {
|
|
125
|
+
const autoRotateValue = parseFloat(autoRotate);
|
|
126
|
+
if (!isNaN(autoRotateValue)) {
|
|
127
|
+
orbit.autoRotateSpeed = autoRotateValue;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
129
130
|
}
|
|
130
131
|
else {
|
|
131
132
|
console.warn("Missing camera object, can not add orbit controls")
|
|
@@ -2,19 +2,18 @@ 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";
|
|
6
5
|
import { setCameraController } from "../engine/engine_camera.js";
|
|
7
6
|
import { Gizmos } from "../engine/engine_gizmos.js";
|
|
8
7
|
import { InputEventQueue, NEPointerEvent } from "../engine/engine_input.js";
|
|
9
8
|
import { Mathf } from "../engine/engine_math.js";
|
|
10
9
|
import { IRaycastOptions, RaycastOptions } from "../engine/engine_physics.js";
|
|
11
10
|
import { serializable } from "../engine/engine_serialization_decorator.js";
|
|
12
|
-
import { getTempVector, getWorldPosition } from "../engine/engine_three_utils.js";
|
|
11
|
+
import { getBoundingBox, getTempVector, getWorldPosition } from "../engine/engine_three_utils.js";
|
|
13
12
|
import type { ICameraController } from "../engine/engine_types.js";
|
|
14
13
|
import { DeviceUtilities, getParam } from "../engine/engine_utils.js";
|
|
15
|
-
import { NeedleEngineWebComponent } from "../engine/webcomponents/needle-engine.js";
|
|
16
14
|
import { Camera } from "./Camera.js";
|
|
17
15
|
import { Behaviour, GameObject } from "./Component.js";
|
|
16
|
+
import { GroundProjectedEnv } from "./GroundProjection.js";
|
|
18
17
|
import { LookAtConstraint } from "./LookAtConstraint.js";
|
|
19
18
|
import { SyncedTransform } from "./SyncedTransform.js";
|
|
20
19
|
import { type AfterHandleInputEvent, EventSystem, EventSystemEvents } from "./ui/EventSystem.js";
|
|
@@ -355,9 +354,6 @@ export class OrbitControls extends Behaviour implements ICameraController {
|
|
|
355
354
|
if (debug) console.debug("OrbitControls", this);
|
|
356
355
|
this._didSetTarget = 0;
|
|
357
356
|
this._startedListeningToKeyEvents = false;
|
|
358
|
-
if ((this.context.domElement as NeedleEngineWebComponent).cameraControls === false) {
|
|
359
|
-
this.enabled = false;
|
|
360
|
-
}
|
|
361
357
|
}
|
|
362
358
|
|
|
363
359
|
/** @internal */
|
|
@@ -494,13 +490,10 @@ export class OrbitControls extends Behaviour implements ICameraController {
|
|
|
494
490
|
this._lastTimeClickOnBackground = this.context.time.time;
|
|
495
491
|
if (this.clickBackgroundToFitScene <= 1 || dt < this.clickBackgroundToFitScene * .15) {
|
|
496
492
|
this._clickOnBackgroundCount += 1;
|
|
497
|
-
if (this._clickOnBackgroundCount >= this.clickBackgroundToFitScene - 1)
|
|
498
|
-
this.
|
|
499
|
-
|
|
500
|
-
objects: this.context.scene,
|
|
501
|
-
immediate: false,
|
|
493
|
+
if (this._clickOnBackgroundCount >= this.clickBackgroundToFitScene - 1)
|
|
494
|
+
this.fitCamera(this.context.scene.children, {
|
|
495
|
+
immediate: false
|
|
502
496
|
});
|
|
503
|
-
}
|
|
504
497
|
}
|
|
505
498
|
else {
|
|
506
499
|
this._clickOnBackgroundCount = 0;
|
|
@@ -537,7 +530,7 @@ export class OrbitControls extends Behaviour implements ICameraController {
|
|
|
537
530
|
private _orbitStartAngle: number = 0;
|
|
538
531
|
private _zoomStartDistance: number = 0;
|
|
539
532
|
private onControlsChangeStarted = () => {
|
|
540
|
-
if
|
|
533
|
+
if(debug) console.debug("OrbitControls: Change started");
|
|
541
534
|
if (this._controls) {
|
|
542
535
|
this._orbitStartAngle = this._controls.getAzimuthalAngle() + this._controls.getPolarAngle();
|
|
543
536
|
this._zoomStartDistance = this._controls.getDistance();
|
|
@@ -547,7 +540,7 @@ export class OrbitControls extends Behaviour implements ICameraController {
|
|
|
547
540
|
}
|
|
548
541
|
}
|
|
549
542
|
private onControlsChangeEnded = () => {
|
|
550
|
-
if
|
|
543
|
+
if(debug) console.debug("OrbitControls: Change ended", { autoTarget: this.autoTarget });
|
|
551
544
|
if (this._controls) {
|
|
552
545
|
if (this.autoTarget) {
|
|
553
546
|
const newAngle = this._controls.getAzimuthalAngle() + this._controls.getPolarAngle();
|
|
@@ -996,26 +989,242 @@ export class OrbitControls extends Behaviour implements ICameraController {
|
|
|
996
989
|
/**
|
|
997
990
|
* Fits the camera to show the objects provided (defaults to the scene if no objects are passed in)
|
|
998
991
|
*/
|
|
999
|
-
fitCamera(options?:
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
camera
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
992
|
+
fitCamera(options?: FitCameraOptions);
|
|
993
|
+
fitCamera(objects?: Object3D | Array<Object3D>, options?: Omit<FitCameraOptions, "objects">);
|
|
994
|
+
fitCamera(objectsOrOptions?: Object3D | Array<Object3D> | FitCameraOptions, options?: FitCameraOptions): void {
|
|
995
|
+
|
|
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
|
+
|
|
1002
|
+
let objects: Object3D | Array<Object3D> | undefined = undefined;
|
|
1003
|
+
// If the user passed in an array as first argument
|
|
1004
|
+
if (Array.isArray(objectsOrOptions)) {
|
|
1005
|
+
objects = objectsOrOptions;
|
|
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
|
+
|
|
1013
1170
|
this.onBeforeRender();
|
|
1171
|
+
// controls.update(); // this is not enough when calling fitCamera({immediate:true}) in an interval
|
|
1014
1172
|
}
|
|
1015
1173
|
|
|
1016
1174
|
private _haveAttachedKeyboardEvents: boolean = false;
|
|
1017
1175
|
}
|
|
1018
1176
|
|
|
1019
|
-
|
|
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
|
+
*/
|
|
1020
1191
|
immediate?: boolean,
|
|
1021
|
-
|
|
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
|
+
}
|
|
@@ -53,6 +53,7 @@ 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";
|
|
56
57
|
export * from "./particlesystem/api.js"
|
|
57
58
|
export * from "./splines/index.js";
|
|
58
59
|
export * from "./web/index.js";
|
|
@@ -205,7 +205,6 @@ export { VideoPlayer } from "../VideoPlayer.js";
|
|
|
205
205
|
export { Voip } from "../Voip.js";
|
|
206
206
|
export { ClickThrough } from "../web/Clickthrough.js";
|
|
207
207
|
export { CursorFollow } from "../web/CursorFollow.js";
|
|
208
|
-
export { HoverAnimation } from "../web/HoverAnimation.js";
|
|
209
208
|
export { ScrollFollow } from "../web/ScrollFollow.js";
|
|
210
209
|
export { Avatar } from "../webxr/Avatar.js";
|
|
211
210
|
export { XRControllerFollow } from "../webxr/controllers/XRControllerFollow.js";
|
|
@@ -21,9 +21,6 @@ onStart(ctx => {
|
|
|
21
21
|
* - Alternatively, add the `clickthrough` attribute to the `<needle-engine>` HTML element (e.g. `<needle-engine clickthrough></needle-engine>`).
|
|
22
22
|
*
|
|
23
23
|
* @link Example https://stackblitz.com/~/github.com/needle-engine/sample-3d-over-html
|
|
24
|
-
* @category Web
|
|
25
|
-
* @group Components
|
|
26
|
-
* @component
|
|
27
24
|
*/
|
|
28
25
|
export class ClickThrough extends Behaviour {
|
|
29
26
|
|
|
@@ -44,10 +44,6 @@ type ScrollFollowEvent = {
|
|
|
44
44
|
* 2. Add a ScrollFollow component to the same GameObject or another GameObject in the scene.
|
|
45
45
|
* 3. Assign the PlayableDirector component to the ScrollFollow's target property.
|
|
46
46
|
* 4. The timeline will now scrub based on the scroll position of the page.
|
|
47
|
-
*
|
|
48
|
-
* @category Web
|
|
49
|
-
* @group Components
|
|
50
|
-
* @component
|
|
51
47
|
*/
|
|
52
48
|
export class ScrollFollow extends Behaviour {
|
|
53
49
|
|