@needle-tools/engine 4.10.0-next.f5ce0ae → 4.10.1-next.72b915b

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.
Files changed (47) hide show
  1. package/CHANGELOG.md +12 -3
  2. package/README.md +2 -1
  3. package/components.needle.json +1 -1
  4. package/dist/{needle-engine.bundle-pI8o_eru.min.js → needle-engine.bundle-BMYuDB60.min.js} +118 -118
  5. package/dist/{needle-engine.bundle-VqrrECGF.umd.cjs → needle-engine.bundle-DTlp4wcy.umd.cjs} +119 -119
  6. package/dist/{needle-engine.bundle-DSzlnhCs.js → needle-engine.bundle-oFhUhV7o.js} +4731 -4689
  7. package/dist/needle-engine.js +2 -2
  8. package/dist/needle-engine.min.js +1 -1
  9. package/dist/needle-engine.umd.cjs +1 -1
  10. package/lib/engine/engine_context.js +7 -7
  11. package/lib/engine/engine_context.js.map +1 -1
  12. package/lib/engine/webcomponents/needle-engine.loading.d.ts +1 -0
  13. package/lib/engine/webcomponents/needle-engine.loading.js +5 -13
  14. package/lib/engine/webcomponents/needle-engine.loading.js.map +1 -1
  15. package/lib/engine-components/Camera.d.ts +1 -1
  16. package/lib/engine-components/Camera.js +1 -1
  17. package/lib/engine-components/CharacterController.d.ts +2 -2
  18. package/lib/engine-components/CharacterController.js +2 -2
  19. package/lib/engine-components/OrbitControls.d.ts +1 -1
  20. package/lib/engine-components/OrbitControls.js +1 -1
  21. package/lib/engine-components/SceneSwitcher.d.ts +1 -1
  22. package/lib/engine-components/SceneSwitcher.js +1 -1
  23. package/lib/engine-components/timeline/TimelineModels.d.ts +37 -2
  24. package/lib/engine-components/timeline/TimelineModels.js +6 -0
  25. package/lib/engine-components/timeline/TimelineModels.js.map +1 -1
  26. package/lib/engine-components/web/HoverAnimation.js +1 -1
  27. package/lib/engine-components/web/HoverAnimation.js.map +1 -1
  28. package/lib/engine-components/web/ScrollFollow.d.ts +1 -0
  29. package/lib/engine-components/web/ScrollFollow.js +1 -0
  30. package/lib/engine-components/web/ScrollFollow.js.map +1 -1
  31. package/lib/engine-components/web/ViewBox.d.ts +38 -9
  32. package/lib/engine-components/web/ViewBox.js +185 -61
  33. package/lib/engine-components/web/ViewBox.js.map +1 -1
  34. package/lib/engine-components/webxr/WebARSessionRoot.js +1 -0
  35. package/lib/engine-components/webxr/WebARSessionRoot.js.map +1 -1
  36. package/package.json +1 -1
  37. package/src/engine/engine_context.ts +8 -8
  38. package/src/engine/webcomponents/needle-engine.loading.ts +5 -13
  39. package/src/engine-components/Camera.ts +1 -1
  40. package/src/engine-components/CharacterController.ts +2 -2
  41. package/src/engine-components/OrbitControls.ts +1 -1
  42. package/src/engine-components/SceneSwitcher.ts +1 -1
  43. package/src/engine-components/timeline/TimelineModels.ts +37 -3
  44. package/src/engine-components/web/HoverAnimation.ts +1 -1
  45. package/src/engine-components/web/ScrollFollow.ts +1 -0
  46. package/src/engine-components/web/ViewBox.ts +197 -55
  47. package/src/engine-components/webxr/WebARSessionRoot.ts +1 -0
@@ -66,6 +66,7 @@ export class EngineLoadingView implements ILoadingViewHandler {
66
66
  private _loadingElement?: HTMLElement;
67
67
  private _loadingTextContainer: HTMLElement | null = null;
68
68
  private _loadingBar: HTMLElement | null = null;
69
+ private _loadingBarFinishedColor: string | null = null;
69
70
  private _messageContainer: HTMLElement | null = null;
70
71
  private _loadingElementOptions?: LoadingElementOptions;
71
72
 
@@ -181,6 +182,9 @@ export class EngineLoadingView implements ILoadingViewHandler {
181
182
  const percent = (t * 100).toFixed(0) + "%";
182
183
  if (this._loadingBar) {
183
184
  this._loadingBar.style.width = t * 100 + "%";
185
+ if(t >= 1 && this._loadingBarFinishedColor) {
186
+ this._loadingBar.style.background = this._loadingBarFinishedColor;
187
+ }
184
188
  }
185
189
 
186
190
  if (this._loadingTextContainer)
@@ -336,21 +340,9 @@ export class EngineLoadingView implements ILoadingViewHandler {
336
340
  // `linear-gradient(90deg, #204f49 ${getGradientPos(0)}, #0BA398 ${getGradientPos(.3)}, #66A22F ${getGradientPos(.6)}, #D7DB0A ${getGradientPos(1)})`;
337
341
  this._loadingBar.style.backgroundAttachment = "fixed";
338
342
  this._loadingBar.style.background = "#c4c4c4ab";
343
+ this._loadingBarFinishedColor = "#ddddddab";
339
344
  this._loadingBar.style.width = "0%";
340
345
  this._loadingBar.style.height = "100%";
341
- if (hasLicense && this._element) {
342
- const primaryColor = this._element.getAttribute("primary-color");
343
- const secondaryColor = this._element.getAttribute("secondary-color");
344
- if (primaryColor && secondaryColor) {
345
- this._loadingBar.style.background = `linear-gradient(90deg, ${primaryColor} ${getGradientPos(0)}, ${secondaryColor} ${getGradientPos(1)})`;
346
- }
347
- else if (primaryColor) {
348
- this._loadingBar.style.background = primaryColor;
349
- }
350
- else if (secondaryColor) {
351
- this._loadingBar.style.background = secondaryColor;
352
- }
353
- }
354
346
 
355
347
  // this._loadingTextContainer = document.createElement("div");
356
348
  // this._loadingTextContainer.style.display = "flex";
@@ -36,7 +36,7 @@ const debugscreenpointtoray = getParam("debugscreenpointtoray");
36
36
  * Supports both perspective and orthographic cameras with various rendering options.
37
37
  * Internally, this component uses {@link PerspectiveCamera} and {@link OrthographicCamera} three.js objects.
38
38
  *
39
- * @category Camera Controls
39
+ * @category Camera
40
40
  * @group Components
41
41
  */
42
42
  export class Camera extends Behaviour implements ICamera {
@@ -14,7 +14,7 @@ import { Rigidbody } from "./RigidBody.js";
14
14
  const debug = getParam("debugcharactercontroller");
15
15
 
16
16
  /**
17
- * @category Camera Controls
17
+ * @category Camera
18
18
  * @group Components
19
19
  */
20
20
  export class CharacterController extends Behaviour {
@@ -107,7 +107,7 @@ export class CharacterController extends Behaviour {
107
107
  }
108
108
 
109
109
  /**
110
- * @category Camera Controls
110
+ * @category Camera
111
111
  * @category Interactivity
112
112
  * @group Components
113
113
  */
@@ -63,7 +63,7 @@ declare module 'three/examples/jsm/controls/OrbitControls.js' {
63
63
  /** The OrbitControls component is used to control a camera using the [OrbitControls from three.js](https://threejs.org/docs/#examples/en/controls/OrbitControls) library.
64
64
  * The three OrbitControls object can be accessed via the `controls` property.
65
65
  * The object being controlled by the OrbitControls (usually the camera) can be accessed via the `controllerObject` property.
66
- * @category Camera Controls
66
+ * @category Camera
67
67
  * @group Components
68
68
  */
69
69
  export class OrbitControls extends Behaviour implements ICameraController {
@@ -80,7 +80,7 @@ export interface ISceneEventListener {
80
80
  * Available scenes are defined in the `scenes` array.
81
81
  * Loaded scenes will be added to the SceneSwitcher's GameObject as a child and removed when another scene is loaded by the same SceneSwitcher.
82
82
  * Live Examples
83
- * - [Multi Scenes Sample](https://engine.needle.tools/samples/multi-scenes-dynamic-loading) (source code available)
83
+ * - [Multi Scenes Sample](https://engine.needle.tools/samples/multi-scene-example) (source code available)
84
84
  * - [Needle Website](https://needle.tools)
85
85
  * - [Songs Of Cultures](https://app.songsofcultures.com)
86
86
  *
@@ -1,11 +1,16 @@
1
1
  import { AnimationClip, Object3D, Quaternion, Vector3 } from "three";
2
- import { Behavior } from "three-mesh-ui";
3
2
 
3
+ /**
4
+ * @category Animation and Sequencing
5
+ */
4
6
  export declare type TimelineAssetModel = {
5
7
  name: string;
6
8
  tracks: TrackModel[];
7
9
  }
8
10
 
11
+ /**
12
+ * @category Animation and Sequencing
13
+ */
9
14
  export enum TrackType {
10
15
  Activation = "ActivationTrack",
11
16
  Animation = "AnimationTrack",
@@ -15,6 +20,9 @@ export enum TrackType {
15
20
  Signal = "SignalTrack",
16
21
  }
17
22
 
23
+ /**
24
+ * @category Animation and Sequencing
25
+ */
18
26
  export enum ClipExtrapolation {
19
27
  None = 0,
20
28
  Hold = 1,
@@ -23,6 +31,9 @@ export enum ClipExtrapolation {
23
31
  Continue = 4
24
32
  };
25
33
 
34
+ /**
35
+ * @category Animation and Sequencing
36
+ */
26
37
  export declare type TrackModel = {
27
38
  name: string;
28
39
  type: TrackType;
@@ -37,11 +48,17 @@ export declare type TrackModel = {
37
48
  declare type Vec3 = { x: number, y: number, z: number };
38
49
  declare type Quat = { x: number, y: number, z: number, w: number };
39
50
 
51
+ /**
52
+ * @category Animation and Sequencing
53
+ */
40
54
  export declare type TrackOffset = {
41
55
  position: Vec3 | Vector3;
42
56
  rotation: Quat | Quaternion;
43
57
  }
44
58
 
59
+ /**
60
+ * @category Animation and Sequencing
61
+ */
45
62
  export declare type ClipModel = {
46
63
  start: number;
47
64
  end: number;
@@ -56,6 +73,9 @@ export declare type ClipModel = {
56
73
  reversed?: boolean;
57
74
  }
58
75
 
76
+ /**
77
+ * @category Animation and Sequencing
78
+ */
59
79
  export declare type AnimationClipModel = {
60
80
  clip: string | number | AnimationClip;
61
81
  loop: boolean;
@@ -65,12 +85,18 @@ export declare type AnimationClipModel = {
65
85
  rotation?: Quat | Quaternion;
66
86
  }
67
87
 
88
+ /**
89
+ * @category Animation and Sequencing
90
+ */
68
91
  export declare type AudioClipModel = {
69
92
  clip: string;
70
93
  loop: boolean;
71
94
  volume: number;
72
95
  }
73
96
 
97
+ /**
98
+ * @category Animation and Sequencing
99
+ */
74
100
  export declare type ControlClipModel = {
75
101
  sourceObject: string | Object3D;
76
102
  controlActivation: boolean;
@@ -81,11 +107,16 @@ export enum MarkerType {
81
107
  Signal = "SignalEmitter",
82
108
  }
83
109
 
84
- export declare class MarkerModel {
110
+ /**
111
+ * @category Animation and Sequencing
112
+ */export declare class MarkerModel {
85
113
  type: MarkerType;
86
114
  time: number;
87
115
  }
88
116
 
117
+ /**
118
+ * @category Animation and Sequencing
119
+ */
89
120
  export declare class SignalMarkerModel extends MarkerModel {
90
121
  retroActive: boolean;
91
122
  emitOnce: boolean;
@@ -99,5 +130,8 @@ export declare class SignalMarkerModel extends MarkerModel {
99
130
  * ```html
100
131
  * <div data-timeline-marker>...</div>
101
132
  * ```
102
- */
133
+ *
134
+ * @link [Example Project using ScrollMarker](https://scrollytelling-bike-z23hmxb2gnu5a.needle.run/)
135
+ * @category Animation and Sequencing
136
+ */
103
137
  export type ScrollMarkerModel = MarkerModel & { name: string };
@@ -58,7 +58,7 @@ export class HoverAnimation extends Behaviour {
58
58
  start() {
59
59
  if (!this.idle) this.idle = AnimationUtils.emptyClip();
60
60
 
61
- if (!this.hovered) {
61
+ if (!this.hovered || !(this.hovered instanceof AnimationClip)) {
62
62
  this.hovered = AnimationUtils.createScaleClip({
63
63
  type: "linear",
64
64
  duration: this.duration || 0.1,
@@ -40,6 +40,7 @@ type ScrollFollowEvent = {
40
40
  *
41
41
  * @link Example at https://scrollytelling-2-z23hmxby7c6x-u30ld.needle.run/
42
42
  * @link Template at https://github.com/needle-engine/scrollytelling-template
43
+ * @link [Scrollytelling Bike Demo](https://scrollytelling-bike-z23hmxb2gnu5a.needle.run/)
43
44
  *
44
45
  * ## How to use with an Animator
45
46
  * 1. Create an Animator component and set up a float parameter named "scroll".
@@ -1,9 +1,11 @@
1
- import { Camera, PerspectiveCamera, Quaternion, Scene, Vector2, Vector3 } from "three";
1
+ import { Camera, Matrix4, PerspectiveCamera, Quaternion, Scene, Vector2, Vector3 } from "three";
2
2
 
3
3
  import { isDevEnvironment } from "../../engine/debug/debug.js";
4
+ import type { Context } from "../../engine/engine_context.js";
4
5
  import { Gizmos } from "../../engine/engine_gizmos.js";
6
+ import { Mathf } from "../../engine/engine_math.js";
5
7
  import { serializable } from "../../engine/engine_serialization_decorator.js";
6
- import { getTempVector } from "../../engine/engine_three_utils.js";
8
+ import { getTempQuaternion, getTempVector } from "../../engine/engine_three_utils.js";
7
9
  import { registerType } from "../../engine/engine_typestore.js";
8
10
  import { getParam } from "../../engine/engine_utils.js";
9
11
  import { RGBAColor } from "../../engine/js-extensions/RGBAColor.js";
@@ -11,24 +13,57 @@ import { Behaviour } from "../Component.js";
11
13
 
12
14
 
13
15
  const debugParam = getParam("debugviewbox");
14
- const disabledGizmoColor = new RGBAColor(.5, .5, .5, .5);
16
+ const disabledGizmoColor = new RGBAColor(.5, .5, .5, .3);
17
+ const enabledGizmoColor = new RGBAColor(.5, .5, 0, 1);
15
18
 
16
19
  /**
17
20
  * This component can be used to automatically fit a certain box area into the camera view - no matter your screen size or aspect ratio.
18
21
  *
19
22
  * Add the ViewBox to an object into your scene
23
+ *
24
+ * @link [Example on needle.run](https://viewbox-demo-z23hmxbz2gkayo-z1nyzm6.needle.run/)
25
+ * @link [Scrollytelling Demo using animated Viewbox](https://scrollytelling-bike-z23hmxb2gnu5a.needle.run/)
26
+ * @link [Example on Stackblitz](https://stackblitz.com/edit/needle-engine-view-box-example)
27
+ *
28
+ * @example Add a Viewbox component to an object in your scene
29
+ * ```ts
30
+ const viewBox = new Object3D();
31
+ viewBox.scale.set(0, 0, 0);
32
+ viewBox.addComponent(ViewBox, { debug: true });
33
+ scene.add(viewBox);
34
+ * ```
35
+
36
+ * @category Camera
37
+ * @group Components
38
+ * @component
20
39
  */
21
40
  @registerType
22
41
  export class ViewBox extends Behaviour {
23
42
 
43
+ /** All known viewbox instances */
24
44
  static readonly instances: ViewBox[] = [];
25
45
 
46
+ /**
47
+ * The currently active viewbox (the last one that was set active). If you have multiple viewboxes in your scene, only the active one will be used.
48
+ * Note that the last viewbox may be inactive if its component is disabled or disabled in the hierarchy.
49
+ * @returns The active viewbox or null if none is active
50
+ */
51
+ static get activeInstance(): ViewBox | null {
52
+ if (ViewBox.instances.length === 0) return null;
53
+ return ViewBox.instances[ViewBox.instances.length - 1];
54
+ }
55
+
56
+ /**
57
+ * A runner instance is used per context to update the viewbox
58
+ */
59
+ private static readonly runners: WeakMap<Context, Runner> = new WeakMap();
60
+
26
61
  /**
27
62
  * The reference field of view is used to calculate the box size. This should usually be the same as your camera's fov.
28
- * @default undefined (meaning it will use the camera fov on the first frame)
63
+ * @default -1 (meaning it will use the camera fov on the first frame)
29
64
  */
30
65
  @serializable()
31
- referenceFieldOfView: number | undefined = undefined;
66
+ referenceFieldOfView: number = -1;
32
67
 
33
68
  /**
34
69
  * Enable debug logs and rendering for this component instance
@@ -36,23 +71,47 @@ export class ViewBox extends Behaviour {
36
71
  @serializable()
37
72
  debug: boolean = false;
38
73
 
74
+
75
+ /**
76
+ * Set this ViewBox as the active one (if you have multiple in your scene). The last active one will be used.
77
+ * @returns self for chaining
78
+ */
79
+ setActive(): this {
80
+ this.enabled = true;
81
+ const idx = ViewBox.instances.indexOf(this);
82
+ if (idx !== -1) ViewBox.instances.splice(idx, 1);
83
+ ViewBox.instances.push(this);
84
+ return this;
85
+ }
86
+
87
+ /** @internal */
39
88
  onEnable(): void {
40
89
  if (debugParam || this.debug || isDevEnvironment()) console.debug("[ViewBox] Using camera fov:", this.referenceFieldOfView);
41
90
  // register instance
42
91
  ViewBox.instances.push(this);
92
+
93
+ this.removeUpdateCallback();
94
+ this.context.pre_render_callbacks.push(this.internalUpdate);
43
95
  }
44
96
 
97
+ /** @internal */
45
98
  onDisable(): void {
46
99
  if (debugParam || this.debug) console.debug("[ViewBox] Disabled");
47
100
  // unregister instance
48
101
  const idx = ViewBox.instances.indexOf(this);
49
102
  if (idx !== -1) ViewBox.instances.splice(idx, 1);
50
- this._projectedBoxElement?.remove();
103
+ this.removeUpdateCallback();
104
+ }
105
+
106
+ private removeUpdateCallback() {
107
+ // remove prerender callback
108
+ const cbIdx = this.context.pre_render_callbacks.indexOf(this.internalUpdate);
109
+ if (cbIdx !== -1) this.context.pre_render_callbacks.splice(cbIdx, 1);
51
110
  }
52
111
 
53
- onBeforeRender(): void {
112
+ private internalUpdate = () => {
54
113
  if (this.context.isInXR) return;
55
- if (this.destroyed) return;
114
+ if (this.destroyed || !this.activeAndEnabled) return;
56
115
  const isActive = ViewBox.instances[ViewBox.instances.length - 1] === this;
57
116
  if (!isActive) {
58
117
  if (debugParam || this.debug) {
@@ -60,40 +119,87 @@ export class ViewBox extends Behaviour {
60
119
  }
61
120
  return;
62
121
  }
63
- if (debugParam || this.debug) Gizmos.DrawWireBox(this.gameObject.worldPosition, this.gameObject.worldScale, 0xdddd00, 0, true, this.gameObject.worldQuaternion);
122
+ if (debugParam || this.debug) Gizmos.DrawWireBox(this.gameObject.worldPosition, this.gameObject.worldScale, enabledGizmoColor, 0, true, this.gameObject.worldQuaternion);
123
+
64
124
 
65
125
  // calculate box size to fit the camera frustrum size at the current position (just scale)
66
126
  const camera = this.context.mainCamera;
67
127
  if (!camera) return;
68
128
  if (!(camera instanceof PerspectiveCamera)) {
69
129
  // TODO: support orthographic camera
130
+ if (!this["__warnedOrthographic"]) {
131
+ console.warn("[ViewBox] Only perspective cameras are supported.");
132
+ this["__warnedOrthographic"] = true;
133
+ }
70
134
  return;
71
135
  }
72
136
 
73
- if (this.referenceFieldOfView === undefined) {
137
+ if (this.referenceFieldOfView === undefined || this.referenceFieldOfView === -1) {
74
138
  this.referenceFieldOfView = camera.fov;
139
+ console.debug("[ViewBox] No referenceFieldOfView set, using camera fov:", this.referenceFieldOfView);
75
140
  }
76
-
77
141
  if (this.referenceFieldOfView === undefined || this.referenceFieldOfView <= 0) {
78
142
  if (debugParam || this.debug) console.warn("[ViewBox] No valid referenceFieldOfView set, cannot adjust box size:", this.referenceFieldOfView);
79
143
  return;
80
144
  }
81
145
 
82
- const domWidth = this.context.domWidth;
83
- const domHeight = this.context.domHeight;
146
+ let runner = ViewBox.runners.get(this.context);
147
+ if (!runner) {
148
+ runner = new Runner();
149
+ ViewBox.runners.set(this.context, runner);
150
+ }
151
+ runner.update(this);
152
+
153
+ // BACKLOG: some code for box scale of an object (different component)
154
+ // this.gameObject.worldScale = getTempVector(width, height, worldscale.z);
155
+ // this.gameObject.scale.multiplyScalar(.98)
156
+ // const minscale = Math.min(width, height);
157
+ // console.log(width, height);
158
+ // this.gameObject.worldScale = getTempVector(scale, scale, scale);
159
+ }
160
+
161
+ }
162
+
163
+
164
+
165
+
166
+ // #region Runner Impl
167
+
168
+ const projectionMatrixCopy: Matrix4 = new Matrix4();
169
+ const projectionMatrixInverseCopy: Matrix4 = new Matrix4();
170
+
171
+ class Runner {
172
+
173
+ private lastActiveViewBox: ViewBox | null = null;
174
+ private lastViewBoxChangeTime: number = -1;
175
+ private currentX: number = 0;
176
+ private currentY: number = 0;
177
+ private currentZoom: number = 1;
178
+
179
+ update(viewBox: ViewBox) {
180
+
181
+ const context = viewBox.context;
182
+ const camera = viewBox.context.mainCamera;
183
+ if (!(camera instanceof PerspectiveCamera)) return;
184
+
185
+ if (this.lastActiveViewBox !== viewBox) {
186
+ if (this.lastActiveViewBox === null)
187
+ this.lastViewBoxChangeTime = -100; // long ago
188
+ else
189
+ this.lastViewBoxChangeTime = context.time.time;
190
+ this.lastActiveViewBox = viewBox;
191
+ }
192
+
193
+ const domWidth = context.domWidth;
194
+ const domHeight = context.domHeight;
84
195
 
85
- let rectPosX = 0;
86
- let rectPosY = 0;
87
196
  let rectWidth = domWidth;
88
197
  let rectHeight = domHeight;
89
198
  let diffWidth = 1;
90
199
  let diffHeight = 1;
91
200
  // use focus rect if available
92
- const focusRectSize = this.context.focusRectSize;
201
+ const focusRectSize = context.focusRectSize;
93
202
  if (focusRectSize) {
94
- // console.log(focusRectSize)
95
- rectPosX = focusRectSize.x;
96
- rectPosY = focusRectSize.y;
97
203
  rectWidth = focusRectSize.width;
98
204
  rectHeight = focusRectSize.height;
99
205
  diffWidth = domWidth / rectWidth;
@@ -101,22 +207,29 @@ export class ViewBox extends Behaviour {
101
207
  }
102
208
 
103
209
 
210
+ // Copy the projection matrix and restore values so we can reset it later
211
+ projectionMatrixCopy.copy(camera.projectionMatrix);
212
+ projectionMatrixInverseCopy.copy(camera.projectionMatrixInverse);
104
213
  const view = camera.view;
105
214
  const zoom = camera.zoom;
106
215
  const aspect = camera.aspect;
107
216
  const fov = camera.fov;
217
+ // Set values to default so we can calculate the box size correctly
108
218
  camera.view = null;
109
219
  camera.zoom = 1;
110
- camera.fov = this.referenceFieldOfView;
220
+ camera.fov = viewBox.referenceFieldOfView;
111
221
  camera.updateProjectionMatrix();
112
222
 
113
223
 
114
- const boxPosition = this.gameObject.worldPosition;
115
- const boxScale = this.gameObject.worldScale;
224
+ const boxPosition = viewBox.gameObject.worldPosition;
225
+ const boxScale = viewBox.gameObject.worldScale;
116
226
 
117
227
  const cameraPosition = camera.worldPosition;
118
228
  const distance = cameraPosition.distanceTo(boxPosition);
119
229
 
230
+ const timeSinceViewBoxChanged = context.time.time - this.lastViewBoxChangeTime;
231
+ const duration = 1;
232
+ const interpolationDelta = timeSinceViewBoxChanged / duration;// timeSinceViewBoxChanged < 1 ? .2 : 1;
120
233
 
121
234
  // #region camera fixes
122
235
  // If the camera is inside the box, move it out
@@ -124,13 +237,30 @@ export class ViewBox extends Behaviour {
124
237
  const direction = getTempVector(cameraPosition).sub(boxPosition);
125
238
  if (distance < boxSizeMax) {
126
239
  // move camera out of bounds
127
- if (this.debug || debugParam) console.warn("[ViewBox] Moving camera out of bounds", distance, "<", boxSizeMax);
240
+ if (viewBox.debug || debugParam) console.warn("[ViewBox] Moving camera out of bounds", distance, "<", boxSizeMax);
128
241
  const positionDirection = getTempVector(direction);
129
242
  positionDirection.y *= .00000001; // stay on horizontal plane mostly
130
243
  positionDirection.normalize();
131
- const lengthToMove = (boxSizeMax - distance);
244
+ const lengthToMove = (boxSizeMax - distance);
132
245
  const newPosition = cameraPosition.add(positionDirection.multiplyScalar(lengthToMove));
133
- camera.worldPosition = newPosition.lerp(cameraPosition, 1 - this.context.time.deltaTime);
246
+ camera.worldPosition = newPosition.lerp(cameraPosition, 1 - context.time.deltaTime);
247
+ }
248
+
249
+
250
+ if (interpolationDelta < 1) {
251
+ if (interpolationDelta > 0) {
252
+ // TODO: this look at animation can lead to slightly janky shifts currently
253
+ const startRotation = getTempQuaternion(camera.quaternion);
254
+ camera.lookAt(boxPosition);
255
+ const newRotation = getTempQuaternion(camera.quaternion);
256
+ camera.quaternion.copy(startRotation);
257
+ camera.quaternion.slerp(newRotation, Math.pow(interpolationDelta, 3));
258
+ camera.updateMatrixWorld();
259
+ }
260
+ }
261
+ else {
262
+ camera.lookAt(boxPosition);
263
+ camera.updateMatrixWorld();
134
264
  }
135
265
 
136
266
  // Ensure the camera looks at the ViewBox
@@ -144,18 +274,25 @@ export class ViewBox extends Behaviour {
144
274
  // camera.worldQuaternion = rotation;
145
275
  // camera.updateMatrixWorld();
146
276
  // }
147
- const boxPositionInCameraSpace = getTempVector(boxPosition);
148
- camera.worldToLocal(boxPositionInCameraSpace);
149
- camera.lookAt(boxPosition);
150
- camera.updateMatrixWorld();
277
+ // const boxPositionInCameraSpace = getTempVector(boxPosition);
278
+
279
+ // Ensure the camera looks at the box position
280
+ // camera.worldToLocal(boxPositionInCameraSpace);
281
+ // if (interpolationDelta < 1)
282
+ // {
283
+ // const startRotation = interpolationDelta < 1 ? getTempQuaternion(camera.quaternion) : null;
284
+ // if (startRotation !== null) {
285
+ // camera.quaternion.slerpQuaternions(startRotation, camera.quaternion, interpolationDelta);
286
+ // }
287
+ // }
151
288
 
152
289
 
153
290
  // #region calculate fit
154
- const vFOV = this.referenceFieldOfView * Math.PI / 180; // convert vertical fov to radians
291
+ const vFOV = viewBox.referenceFieldOfView * Math.PI / 180; // convert vertical fov to radians
155
292
  const height = 2 * Math.tan(vFOV / 2) * distance; // visible height
156
293
  const width = height * camera.aspect; // visible width
157
294
 
158
- const projectedBox = this.projectBoxIntoCamera(camera, 1);
295
+ const projectedBox = this.projectBoxIntoCamera(viewBox, camera, 1);
159
296
  // return
160
297
  const boxWidth = (projectedBox.maxX - projectedBox.minX);
161
298
  const boxHeight = (projectedBox.maxY - projectedBox.minY);
@@ -166,32 +303,38 @@ export class ViewBox extends Behaviour {
166
303
  width / diffWidth,
167
304
  height / diffHeight
168
305
  );
306
+ const rectZoom = scale / (height * .5);
169
307
  // console.log({ scale, width, height, boxWidth: boxWidth * camera.aspect, boxHeight, diffWidth, diffHeight, aspect: camera.aspect, distance })
170
308
  // this.context.focusRectSettings.zoom = 1.39;
171
309
  // if (!this.context.focusRect) this.context.setCameraFocusRect(this.context.domElement);
172
310
  // return
173
311
  const vec = getTempVector(boxPosition);
174
312
  vec.project(camera);
175
- this.context.focusRectSettings.offsetX = vec.x;
176
- this.context.focusRectSettings.offsetY = vec.y;
177
- this.context.focusRectSettings.zoom = scale / (height * .5);
178
- // if we don't have a focus rect yet, set it to the dom element
179
- if (!this.context.focusRect) this.context.setCameraFocusRect(this.context.domElement);
313
+
314
+ if (interpolationDelta < 1) {
315
+ this.currentX = Mathf.lerp(this.currentX, vec.x, interpolationDelta);
316
+ this.currentY = Mathf.lerp(this.currentY, vec.y, interpolationDelta);
317
+ this.currentZoom = Mathf.lerp(this.currentZoom, rectZoom, interpolationDelta);
318
+ }
319
+ else {
320
+ this.currentX = vec.x;
321
+ this.currentY = vec.y;
322
+ this.currentZoom = rectZoom;
323
+ }
324
+
325
+ // Apply new values
326
+ context.focusRectSettings.offsetX = this.currentX;
327
+ context.focusRectSettings.offsetY = this.currentY;
328
+ context.focusRectSettings.zoom = this.currentZoom;
329
+ if (!context.focusRect) context.setCameraFocusRect(context.domElement); // if we don't have a focus rect yet, set it to the dom element
180
330
 
181
331
  // Reset values
182
332
  camera.view = view;
183
333
  camera.zoom = zoom;
184
334
  camera.aspect = aspect;
185
335
  camera.fov = fov;
186
- // camera.updateProjectionMatrix();
187
-
188
-
189
- // BACKLOG: some code for box scale of an object (different component)
190
- // this.gameObject.worldScale = getTempVector(width, height, worldscale.z);
191
- // this.gameObject.scale.multiplyScalar(.98)
192
- // const minscale = Math.min(width, height);
193
- // console.log(width, height);
194
- // this.gameObject.worldScale = getTempVector(scale, scale, scale);
336
+ camera.projectionMatrix.copy(projectionMatrixCopy);
337
+ camera.projectionMatrixInverse.copy(projectionMatrixInverseCopy);
195
338
  }
196
339
 
197
340
 
@@ -206,7 +349,7 @@ export class ViewBox extends Behaviour {
206
349
 
207
350
 
208
351
 
209
- private projectBoxIntoCamera(camera: Camera, _factor: number) {
352
+ private projectBoxIntoCamera(comp: ViewBox, camera: Camera, _factor: number) {
210
353
  const factor = .5 * _factor;
211
354
 
212
355
  const corners = [
@@ -225,7 +368,7 @@ export class ViewBox extends Behaviour {
225
368
  let maxY = Number.NEGATIVE_INFINITY;
226
369
  for (let i = 0; i < corners.length; i++) {
227
370
  const c = corners[i];
228
- c.applyMatrix4(this.gameObject.matrixWorld);
371
+ c.applyMatrix4(comp.gameObject.matrixWorld);
229
372
  c.project(camera);
230
373
  if (c.x < minX) minX = c.x;
231
374
  if (c.x > maxX) maxX = c.x;
@@ -237,15 +380,15 @@ export class ViewBox extends Behaviour {
237
380
  if (!this._projectedBoxElement) {
238
381
  this._projectedBoxElement = document.createElement("div");
239
382
  }
240
- if (this._projectedBoxElement.parentElement !== this.context.domElement)
241
- this.context.domElement.appendChild(this._projectedBoxElement);
383
+ if (this._projectedBoxElement.parentElement !== comp.context.domElement)
384
+ comp.context.domElement.appendChild(this._projectedBoxElement);
242
385
  this._projectedBoxElement.style.position = "fixed";
243
386
  // dotted but with larger gaps
244
387
  this._projectedBoxElement.style.outline = "2px dashed rgba(255,0,0,.5)";
245
- this._projectedBoxElement.style.left = ((minX * .5 + .5) * this.context.domWidth) + "px";
246
- this._projectedBoxElement.style.top = ((-maxY * .5 + .5) * this.context.domHeight) + "px";
247
- this._projectedBoxElement.style.width = ((maxX - minX) * .5 * this.context.domWidth) + "px";
248
- this._projectedBoxElement.style.height = ((maxY - minY) * .5 * this.context.domHeight) + "px";
388
+ this._projectedBoxElement.style.left = ((minX * .5 + .5) * comp.context.domWidth) + "px";
389
+ this._projectedBoxElement.style.top = ((-maxY * .5 + .5) * comp.context.domHeight) + "px";
390
+ this._projectedBoxElement.style.width = ((maxX - minX) * .5 * comp.context.domWidth) + "px";
391
+ this._projectedBoxElement.style.height = ((maxY - minY) * .5 * comp.context.domHeight) + "px";
249
392
  this._projectedBoxElement.style.pointerEvents = "none";
250
393
  this._projectedBoxElement.style.zIndex = "1000";
251
394
  }
@@ -258,5 +401,4 @@ export class ViewBox extends Behaviour {
258
401
 
259
402
 
260
403
 
261
-
262
- }
404
+ }
@@ -465,6 +465,7 @@ export class WebARSessionRoot extends Behaviour {
465
465
  return;
466
466
  }
467
467
  else {
468
+ // @ts-ignore
468
469
  const anchor = await hit.createAnchor(session.viewerPose!.transform);
469
470
  // make sure the session is still active
470
471
  if (session.running && anchor) {