@needle-tools/engine 4.10.0-next.4f9d92a → 4.10.0-next.870425c

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 (96) hide show
  1. package/CHANGELOG.md +8 -3
  2. package/README.md +2 -1
  3. package/dist/{needle-engine.bundle-D4dO0t5I.umd.cjs → needle-engine.bundle-0b6rexDr.umd.cjs} +139 -137
  4. package/dist/{needle-engine.bundle-BeZ_xmJa.js → needle-engine.bundle-B5GtGvbq.js} +7750 -7653
  5. package/dist/needle-engine.bundle-CicGQeCY.min.js +1652 -0
  6. package/dist/needle-engine.js +15 -14
  7. package/dist/needle-engine.min.js +1 -1
  8. package/dist/needle-engine.umd.cjs +1 -1
  9. package/dist/vendor-CPuBPspY.umd.cjs +1121 -0
  10. package/dist/vendor-DPCU8cUF.min.js +1121 -0
  11. package/dist/vendor-MBoqSyFm.js +16240 -0
  12. package/lib/engine/engine_camera.js +5 -5
  13. package/lib/engine/engine_camera.js.map +1 -1
  14. package/lib/engine/engine_gizmos.d.ts +11 -10
  15. package/lib/engine/engine_gizmos.js +24 -10
  16. package/lib/engine/engine_gizmos.js.map +1 -1
  17. package/lib/engine/engine_license.js +1 -1
  18. package/lib/engine/engine_license.js.map +1 -1
  19. package/lib/engine/engine_lightdata.d.ts +3 -3
  20. package/lib/engine/engine_lightdata.js +10 -10
  21. package/lib/engine/engine_lightdata.js.map +1 -1
  22. package/lib/engine/engine_physics_rapier.js +4 -0
  23. package/lib/engine/engine_physics_rapier.js.map +1 -1
  24. package/lib/engine/engine_scenelighting.d.ts +1 -1
  25. package/lib/engine/engine_scenelighting.js +4 -5
  26. package/lib/engine/engine_scenelighting.js.map +1 -1
  27. package/lib/engine/engine_utils.d.ts +3 -1
  28. package/lib/engine/engine_utils.js +11 -0
  29. package/lib/engine/engine_utils.js.map +1 -1
  30. package/lib/engine/extensions/extension_utils.js +1 -1
  31. package/lib/engine/extensions/extension_utils.js.map +1 -1
  32. package/lib/engine/webcomponents/needle-engine.js +22 -0
  33. package/lib/engine/webcomponents/needle-engine.js.map +1 -1
  34. package/lib/engine/xr/NeedleXRController.d.ts +3 -3
  35. package/lib/engine/xr/NeedleXRController.js +28 -0
  36. package/lib/engine/xr/NeedleXRController.js.map +1 -1
  37. package/lib/engine-components/Camera.d.ts +1 -1
  38. package/lib/engine-components/Camera.js +1 -1
  39. package/lib/engine-components/CameraUtils.js +2 -1
  40. package/lib/engine-components/CameraUtils.js.map +1 -1
  41. package/lib/engine-components/CharacterController.d.ts +2 -2
  42. package/lib/engine-components/CharacterController.js +2 -2
  43. package/lib/engine-components/OrbitControls.d.ts +1 -1
  44. package/lib/engine-components/OrbitControls.js +1 -1
  45. package/lib/engine-components/Skybox.js +22 -4
  46. package/lib/engine-components/Skybox.js.map +1 -1
  47. package/lib/engine-components/debug/LogStats.d.ts +1 -0
  48. package/lib/engine-components/debug/LogStats.js +1 -0
  49. package/lib/engine-components/debug/LogStats.js.map +1 -1
  50. package/lib/engine-components/timeline/PlayableDirector.js +1 -1
  51. package/lib/engine-components/timeline/PlayableDirector.js.map +1 -1
  52. package/lib/engine-components/timeline/TimelineModels.d.ts +37 -2
  53. package/lib/engine-components/timeline/TimelineModels.js +6 -0
  54. package/lib/engine-components/timeline/TimelineModels.js.map +1 -1
  55. package/lib/engine-components/timeline/TimelineTracks.d.ts +2 -1
  56. package/lib/engine-components/timeline/TimelineTracks.js +26 -23
  57. package/lib/engine-components/timeline/TimelineTracks.js.map +1 -1
  58. package/lib/engine-components/web/ScrollFollow.d.ts +2 -0
  59. package/lib/engine-components/web/ScrollFollow.js +114 -99
  60. package/lib/engine-components/web/ScrollFollow.js.map +1 -1
  61. package/lib/engine-components/web/ViewBox.d.ts +33 -3
  62. package/lib/engine-components/web/ViewBox.js +133 -49
  63. package/lib/engine-components/web/ViewBox.js.map +1 -1
  64. package/lib/engine-components/webxr/WebARSessionRoot.js +1 -0
  65. package/lib/engine-components/webxr/WebARSessionRoot.js.map +1 -1
  66. package/lib/engine-components-experimental/Presentation.d.ts +1 -0
  67. package/lib/engine-components-experimental/Presentation.js +1 -0
  68. package/lib/engine-components-experimental/Presentation.js.map +1 -1
  69. package/package.json +2 -1
  70. package/src/engine/engine_camera.ts +5 -7
  71. package/src/engine/engine_gizmos.ts +37 -23
  72. package/src/engine/engine_license.ts +1 -1
  73. package/src/engine/engine_lightdata.ts +11 -11
  74. package/src/engine/engine_physics_rapier.ts +3 -0
  75. package/src/engine/engine_scenelighting.ts +5 -6
  76. package/src/engine/engine_utils.ts +12 -0
  77. package/src/engine/extensions/extension_utils.ts +1 -1
  78. package/src/engine/webcomponents/needle-engine.ts +33 -6
  79. package/src/engine/xr/NeedleXRController.ts +36 -4
  80. package/src/engine-components/Camera.ts +1 -1
  81. package/src/engine-components/CameraUtils.ts +1 -1
  82. package/src/engine-components/CharacterController.ts +2 -2
  83. package/src/engine-components/OrbitControls.ts +1 -1
  84. package/src/engine-components/Skybox.ts +26 -7
  85. package/src/engine-components/debug/LogStats.ts +1 -0
  86. package/src/engine-components/timeline/PlayableDirector.ts +1 -1
  87. package/src/engine-components/timeline/TimelineModels.ts +37 -3
  88. package/src/engine-components/timeline/TimelineTracks.ts +26 -23
  89. package/src/engine-components/web/ScrollFollow.ts +121 -103
  90. package/src/engine-components/web/ViewBox.ts +140 -50
  91. package/src/engine-components/webxr/WebARSessionRoot.ts +1 -0
  92. package/src/engine-components-experimental/Presentation.ts +1 -0
  93. package/dist/needle-engine.bundle-C3bpSNYu.min.js +0 -1650
  94. package/dist/vendor-D0Yvltn9.umd.cjs +0 -1121
  95. package/dist/vendor-DU8tJyl_.js +0 -14366
  96. package/dist/vendor-JyrX4DVM.min.js +0 -1121
@@ -1,4 +1,4 @@
1
- import { Camera, PerspectiveCamera, 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
4
  import { Gizmos } from "../../engine/engine_gizmos.js";
@@ -6,45 +6,90 @@ import { serializable } from "../../engine/engine_serialization_decorator.js";
6
6
  import { getTempVector } from "../../engine/engine_three_utils.js";
7
7
  import { registerType } from "../../engine/engine_typestore.js";
8
8
  import { getParam } from "../../engine/engine_utils.js";
9
+ import { RGBAColor } from "../../engine/js-extensions/RGBAColor.js";
9
10
  import { Behaviour } from "../Component.js";
10
11
 
11
- const debugParam = getParam("debugviewbox");
12
12
 
13
+ const debugParam = getParam("debugviewbox");
14
+ const disabledGizmoColor = new RGBAColor(.5, .5, .5, .5);
15
+
16
+ /**
17
+ * 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
+ *
19
+ * Add the ViewBox to an object into your scene
20
+ *
21
+ * @link [Example on needle.run](https://viewbox-demo-z23hmxbz2gkayo-z1nyzm6.needle.run/)
22
+ * @link [Scrollytelling Demo using animated Viewbox](https://scrollytelling-bike-z23hmxb2gnu5a.needle.run/)
23
+ * @link [Example on Stackblitz](https://stackblitz.com/edit/needle-engine-view-box-example)
24
+ *
25
+ * @example Add a Viewbox component to an object in your scene
26
+ * ```ts
27
+ const viewBox = new Object3D();
28
+ viewBox.scale.set(0, 0, 0);
29
+ viewBox.addComponent(ViewBox, { debug: true });
30
+ scene.add(viewBox);
31
+ * ```
32
+
33
+ * @category Camera
34
+ * @group Components
35
+ * @component
36
+ */
13
37
  @registerType
14
38
  export class ViewBox extends Behaviour {
15
39
 
16
- static instances: ViewBox[] = [];
40
+ static readonly instances: ViewBox[] = [];
17
41
 
42
+ /**
43
+ * The reference field of view is used to calculate the box size. This should usually be the same as your camera's fov.
44
+ * @default -1 (meaning it will use the camera fov on the first frame)
45
+ */
18
46
  @serializable()
19
- referenceFieldOfView: number = 60;
47
+ referenceFieldOfView: number = -1;
20
48
 
49
+ /**
50
+ * Enable debug logs and rendering for this component instance
51
+ */
21
52
  @serializable()
22
53
  debug: boolean = false;
23
54
 
24
- awake() {
25
- // this.referenceFieldOfView = (this.context.mainCamera as PerspectiveCamera)?.fov || 60;
26
- // setInterval(()=>{
27
- // this.enabled = !this.enabled
28
- // }, 1000)
29
- }
30
55
  onEnable(): void {
31
56
  if (debugParam || this.debug || isDevEnvironment()) console.debug("[ViewBox] Using camera fov:", this.referenceFieldOfView);
57
+ // register instance
32
58
  ViewBox.instances.push(this);
59
+
60
+ this.removeUpdateCallback();
61
+ this.context.pre_render_callbacks.push(this.internalUpdate);
33
62
  }
34
63
 
35
64
  onDisable(): void {
65
+ if (debugParam || this.debug) console.debug("[ViewBox] Disabled");
66
+ // unregister instance
36
67
  const idx = ViewBox.instances.indexOf(this);
37
68
  if (idx !== -1) ViewBox.instances.splice(idx, 1);
69
+ this._projectedBoxElement?.remove();
70
+ this.removeUpdateCallback();
38
71
  }
39
72
 
40
- onBeforeRender() {
73
+ private removeUpdateCallback() {
74
+ // remove prerender callback
75
+ const cbIdx = this.context.pre_render_callbacks.indexOf(this.internalUpdate);
76
+ if (cbIdx !== -1) this.context.pre_render_callbacks.splice(cbIdx, 1);
77
+ }
78
+
79
+ private static readonly _tempProjectionMatrix: Matrix4 = new Matrix4();
80
+ private static readonly _tempProjectionMatrixInverse: Matrix4 = new Matrix4();
81
+
82
+ private internalUpdate = () => {
41
83
  if (this.context.isInXR) return;
84
+ if (this.destroyed || !this.activeAndEnabled) return;
42
85
  const isActive = ViewBox.instances[ViewBox.instances.length - 1] === this;
43
86
  if (!isActive) {
44
- if (debugParam || this.debug) Gizmos.DrawWireBox(this.gameObject.worldPosition, this.gameObject.worldScale, 0x333333);
87
+ if (debugParam || this.debug) {
88
+ Gizmos.DrawWireBox(this.gameObject.worldPosition, this.gameObject.worldScale, disabledGizmoColor);
89
+ }
45
90
  return;
46
91
  }
47
- if (debugParam || this.debug) Gizmos.DrawWireBox(this.gameObject.worldPosition, this.gameObject.worldScale, 0xdddd00);
92
+ if (debugParam || this.debug) Gizmos.DrawWireBox(this.gameObject.worldPosition, this.gameObject.worldScale, 0xdddd00, 0, true, this.gameObject.worldQuaternion);
48
93
 
49
94
  // calculate box size to fit the camera frustrum size at the current position (just scale)
50
95
  const camera = this.context.mainCamera;
@@ -54,6 +99,11 @@ export class ViewBox extends Behaviour {
54
99
  return;
55
100
  }
56
101
 
102
+ if (this.referenceFieldOfView === undefined || this.referenceFieldOfView === -1) {
103
+ this.referenceFieldOfView = camera.fov;
104
+ console.debug("[ViewBox] No referenceFieldOfView set, using camera fov:", this.referenceFieldOfView);
105
+ }
106
+
57
107
  if (this.referenceFieldOfView === undefined || this.referenceFieldOfView <= 0) {
58
108
  if (debugParam || this.debug) console.warn("[ViewBox] No valid referenceFieldOfView set, cannot adjust box size:", this.referenceFieldOfView);
59
109
  return;
@@ -62,8 +112,6 @@ export class ViewBox extends Behaviour {
62
112
  const domWidth = this.context.domWidth;
63
113
  const domHeight = this.context.domHeight;
64
114
 
65
- let rectPosX = 0;
66
- let rectPosY = 0;
67
115
  let rectWidth = domWidth;
68
116
  let rectHeight = domHeight;
69
117
  let diffWidth = 1;
@@ -71,53 +119,91 @@ export class ViewBox extends Behaviour {
71
119
  // use focus rect if available
72
120
  const focusRectSize = this.context.focusRectSize;
73
121
  if (focusRectSize) {
74
- // console.log(focusRectSize)
75
- rectPosX = focusRectSize.x;
76
- rectPosY = focusRectSize.y;
77
122
  rectWidth = focusRectSize.width;
78
123
  rectHeight = focusRectSize.height;
79
124
  diffWidth = domWidth / rectWidth;
80
125
  diffHeight = domHeight / rectHeight;
81
126
  }
82
127
 
83
- // const view = camera.view;
128
+
129
+ // Copy the projection matrix and restore values so we can reset it later
130
+ ViewBox._tempProjectionMatrix.copy(camera.projectionMatrix);
131
+ ViewBox._tempProjectionMatrixInverse.copy(camera.projectionMatrixInverse);
84
132
  const view = camera.view;
85
133
  const zoom = camera.zoom;
86
134
  const aspect = camera.aspect;
87
135
  const fov = camera.fov;
136
+ // Set values to default so we can calculate the box size correctly
88
137
  camera.view = null;
89
138
  camera.zoom = 1;
90
- // camera.aspect = rectWidth / rectHeight;
91
139
  camera.fov = this.referenceFieldOfView;
92
140
  camera.updateProjectionMatrix();
93
141
 
142
+
94
143
  const boxPosition = this.gameObject.worldPosition;
95
144
  const boxScale = this.gameObject.worldScale;
96
145
 
146
+ const cameraPosition = camera.worldPosition;
147
+ const distance = cameraPosition.distanceTo(boxPosition);
148
+
149
+
150
+ // #region camera fixes
151
+ // If the camera is inside the box, move it out
152
+ const boxSizeMax = Math.max(boxScale.x, boxScale.y, boxScale.z);
153
+ const direction = getTempVector(cameraPosition).sub(boxPosition);
154
+ if (distance < boxSizeMax) {
155
+ // move camera out of bounds
156
+ if (this.debug || debugParam) console.warn("[ViewBox] Moving camera out of bounds", distance, "<", boxSizeMax);
157
+ const positionDirection = getTempVector(direction);
158
+ positionDirection.y *= .00000001; // stay on horizontal plane mostly
159
+ positionDirection.normalize();
160
+ const lengthToMove = (boxSizeMax - distance);
161
+ const newPosition = cameraPosition.add(positionDirection.multiplyScalar(lengthToMove));
162
+ camera.worldPosition = newPosition.lerp(cameraPosition, 1 - this.context.time.deltaTime);
163
+ }
164
+
165
+ // Ensure the camera looks at the ViewBox
166
+ // TOOD: smooth lookat over multiple frames if we have multiple viewboxes
167
+ // const dot = direction.normalize().dot(camera.worldForward);
168
+ // if (dot < .9) {
169
+ // console.log(dot);
170
+ // const targetRotation = direction;
171
+ // const rotation = getTempQuaternion();
172
+ // rotation.setFromUnitVectors(camera.worldForward.multiplyScalar(-1), targetRotation);
173
+ // camera.worldQuaternion = rotation;
174
+ // camera.updateMatrixWorld();
175
+ // }
176
+ const boxPositionInCameraSpace = getTempVector(boxPosition);
177
+ camera.worldToLocal(boxPositionInCameraSpace);
178
+ camera.lookAt(boxPosition);
179
+ camera.updateMatrixWorld();
97
180
 
98
181
 
99
- // const fov = this.referenceFieldOfView
100
- const distance = camera.worldPosition.distanceTo(boxPosition);
182
+ // #region calculate fit
101
183
  const vFOV = this.referenceFieldOfView * Math.PI / 180; // convert vertical fov to radians
102
184
  const height = 2 * Math.tan(vFOV / 2) * distance; // visible height
103
185
  const width = height * camera.aspect; // visible width
104
186
 
105
- const projectedBox = this.projectBoxIntoCamera(boxPosition, boxScale, camera, height * .5);
187
+ const projectedBox = this.projectBoxIntoCamera(camera, 1);
188
+ // return
106
189
  const boxWidth = (projectedBox.maxX - projectedBox.minX);
107
190
  const boxHeight = (projectedBox.maxY - projectedBox.minY);
108
191
 
109
- // TODO: take the rect size different into account
110
192
  const scale = this.fit(
111
193
  boxWidth * camera.aspect,
112
194
  boxHeight,
113
195
  width / diffWidth,
114
196
  height / diffHeight
115
197
  );
198
+ // console.log({ scale, width, height, boxWidth: boxWidth * camera.aspect, boxHeight, diffWidth, diffHeight, aspect: camera.aspect, distance })
199
+ // this.context.focusRectSettings.zoom = 1.39;
200
+ // if (!this.context.focusRect) this.context.setCameraFocusRect(this.context.domElement);
201
+ // return
116
202
  const vec = getTempVector(boxPosition);
117
203
  vec.project(camera);
118
204
  this.context.focusRectSettings.offsetX = vec.x;
119
205
  this.context.focusRectSettings.offsetY = vec.y;
120
- this.context.focusRectSettings.zoom = scale;
206
+ this.context.focusRectSettings.zoom = scale / (height * .5);
121
207
  // if we don't have a focus rect yet, set it to the dom element
122
208
  if (!this.context.focusRect) this.context.setCameraFocusRect(this.context.domElement);
123
209
 
@@ -126,7 +212,8 @@ export class ViewBox extends Behaviour {
126
212
  camera.zoom = zoom;
127
213
  camera.aspect = aspect;
128
214
  camera.fov = fov;
129
- // camera.updateProjectionMatrix();
215
+ camera.projectionMatrix.copy(ViewBox._tempProjectionMatrix);
216
+ camera.projectionMatrixInverse.copy(ViewBox._tempProjectionMatrixInverse);
130
217
 
131
218
 
132
219
  // BACKLOG: some code for box scale of an object (different component)
@@ -149,19 +236,18 @@ export class ViewBox extends Behaviour {
149
236
 
150
237
 
151
238
 
152
- private projectBoxIntoCamera(position: Vector3, scale: Vector3, camera: Camera, diff: number) {
153
-
154
- const factor = .5 * diff;
239
+ private projectBoxIntoCamera(camera: Camera, _factor: number) {
240
+ const factor = .5 * _factor;
155
241
 
156
242
  const corners = [
157
- getTempVector(-scale.x * factor, -scale.y * factor, -scale.z * factor),
158
- getTempVector(scale.x * factor, -scale.y * factor, -scale.z * factor),
159
- getTempVector(-scale.x * factor, scale.y * factor, -scale.z * factor),
160
- getTempVector(scale.x * factor, scale.y * factor, -scale.z * factor),
161
- getTempVector(-scale.x * factor, -scale.y * factor, scale.z * factor),
162
- getTempVector(scale.x * factor, -scale.y * factor, scale.z * factor),
163
- getTempVector(-scale.x * factor, scale.y * factor, scale.z * factor),
164
- getTempVector(scale.x * factor, scale.y * factor, scale.z * factor),
243
+ getTempVector(-factor, -factor, -factor),
244
+ getTempVector(factor, -factor, -factor),
245
+ getTempVector(-factor, factor, -factor),
246
+ getTempVector(factor, factor, -factor),
247
+ getTempVector(-factor, -factor, factor),
248
+ getTempVector(factor, -factor, factor),
249
+ getTempVector(-factor, factor, factor),
250
+ getTempVector(factor, factor, factor),
165
251
  ];
166
252
  let minX = Number.POSITIVE_INFINITY;
167
253
  let maxX = Number.NEGATIVE_INFINITY;
@@ -169,7 +255,7 @@ export class ViewBox extends Behaviour {
169
255
  let maxY = Number.NEGATIVE_INFINITY;
170
256
  for (let i = 0; i < corners.length; i++) {
171
257
  const c = corners[i];
172
- c.add(position);
258
+ c.applyMatrix4(this.gameObject.matrixWorld);
173
259
  c.project(camera);
174
260
  if (c.x < minX) minX = c.x;
175
261
  if (c.x > maxX) maxX = c.x;
@@ -177,18 +263,22 @@ export class ViewBox extends Behaviour {
177
263
  if (c.y > maxY) maxY = c.y;
178
264
  }
179
265
 
180
- // if(!this._projectedBoxElement) {
181
- // this._projectedBoxElement = document.createElement("div");
182
- // this.context.domElement.appendChild(this._projectedBoxElement);
183
- // }
184
- // this._projectedBoxElement.style.position = "fixed";
185
- // this._projectedBoxElement.style.outline = "10px solid red";
186
- // this._projectedBoxElement.style.left = ((minX * .5 + .5) * this.context.domWidth) + "px";
187
- // this._projectedBoxElement.style.top = ((-maxY * .5 + .5) * this.context.domHeight) + "px";
188
- // this._projectedBoxElement.style.width = ((maxX - minX) * .5 * this.context.domWidth) + "px";
189
- // this._projectedBoxElement.style.height = ((maxY - minY) * .5 * this.context.domHeight) + "px";
190
- // this._projectedBoxElement.style.pointerEvents = "none";
191
- // this._projectedBoxElement.style.zIndex = "1000";
266
+ if (debugParam) {
267
+ if (!this._projectedBoxElement) {
268
+ this._projectedBoxElement = document.createElement("div");
269
+ }
270
+ if (this._projectedBoxElement.parentElement !== this.context.domElement)
271
+ this.context.domElement.appendChild(this._projectedBoxElement);
272
+ this._projectedBoxElement.style.position = "fixed";
273
+ // dotted but with larger gaps
274
+ this._projectedBoxElement.style.outline = "2px dashed rgba(255,0,0,.5)";
275
+ this._projectedBoxElement.style.left = ((minX * .5 + .5) * this.context.domWidth) + "px";
276
+ this._projectedBoxElement.style.top = ((-maxY * .5 + .5) * this.context.domHeight) + "px";
277
+ this._projectedBoxElement.style.width = ((maxX - minX) * .5 * this.context.domWidth) + "px";
278
+ this._projectedBoxElement.style.height = ((maxY - minY) * .5 * this.context.domHeight) + "px";
279
+ this._projectedBoxElement.style.pointerEvents = "none";
280
+ this._projectedBoxElement.style.zIndex = "1000";
281
+ }
192
282
 
193
283
 
194
284
  return { minX, maxX, minY, maxY };
@@ -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) {
@@ -1,6 +1,7 @@
1
1
  import type { KeyCode } from "../engine/engine_input.js";
2
2
  import { Behaviour } from "../engine-components/Component.js";
3
3
 
4
+ /** @internal */
4
5
  export class PresentationMode extends Behaviour {
5
6
 
6
7
  toggleKey: KeyCode = "KeyP";