@needle-tools/engine 4.9.3 → 4.10.0-beta

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 (110) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/components.needle.json +1 -1
  3. package/dist/{gltf-progressive-DhE1A6hX.min.js → gltf-progressive-CoZbSfPR.min.js} +1 -1
  4. package/dist/{gltf-progressive-egsMzRdv.js → gltf-progressive-DUR9TuAH.js} +3 -3
  5. package/dist/{gltf-progressive-DWiyqrwB.umd.cjs → gltf-progressive-Iy7aSAPk.umd.cjs} +1 -1
  6. package/dist/{needle-engine.bundle-C7LSzO5L.umd.cjs → needle-engine.bundle-42AmEGfk.umd.cjs} +160 -137
  7. package/dist/needle-engine.bundle-C6zhyLF5.min.js +1639 -0
  8. package/dist/{needle-engine.bundle-BAsxNKpA.js → needle-engine.bundle-Dj6faVbC.js} +7665 -7380
  9. package/dist/needle-engine.js +447 -444
  10. package/dist/needle-engine.min.js +1 -1
  11. package/dist/needle-engine.umd.cjs +1 -1
  12. package/dist/{postprocessing-BZOSD1ln.min.js → postprocessing-BHMVuZQ1.min.js} +1 -1
  13. package/dist/{postprocessing-Bb5StX0o.umd.cjs → postprocessing-BsnRNRRS.umd.cjs} +1 -1
  14. package/dist/{postprocessing-BzFF7i-7.js → postprocessing-DQ2pynXW.js} +2 -2
  15. package/dist/{three-BK56xWDs.umd.cjs → three-B-jwTHao.umd.cjs} +11 -11
  16. package/dist/{three-CsHK73Zc.js → three-CJSAehtG.js} +1 -0
  17. package/dist/{three-examples-Bph291U2.min.js → three-examples-BivkhnvN.min.js} +1 -1
  18. package/dist/{three-examples-C9WfZu-X.umd.cjs → three-examples-Deqc1bNw.umd.cjs} +1 -1
  19. package/dist/{three-examples-BvMpKSun.js → three-examples-Doq0rvFU.js} +1 -1
  20. package/dist/{three-mesh-ui-CN6aRT7i.js → three-mesh-ui-CktOi6oI.js} +1 -1
  21. package/dist/{three-mesh-ui-DnxkZWNA.umd.cjs → three-mesh-ui-CsHwj9cJ.umd.cjs} +1 -1
  22. package/dist/{three-mesh-ui-n_qS2BM-.min.js → three-mesh-ui-DhYXcXZe.min.js} +1 -1
  23. package/dist/{three-TNFQHSFa.min.js → three-qw28ZtTy.min.js} +10 -10
  24. package/dist/{vendor-BtJpSuCj.umd.cjs → vendor-D0Yvltn9.umd.cjs} +1 -1
  25. package/dist/{vendor-k9i6CeGi.js → vendor-DU8tJyl_.js} +1 -1
  26. package/dist/{vendor-XJ9xiwrv.min.js → vendor-JyrX4DVM.min.js} +1 -1
  27. package/lib/engine/api.d.ts +1 -0
  28. package/lib/engine/api.js +1 -0
  29. package/lib/engine/api.js.map +1 -1
  30. package/lib/engine/codegen/register_types.js +4 -0
  31. package/lib/engine/codegen/register_types.js.map +1 -1
  32. package/lib/engine/engine_animation.d.ts +21 -1
  33. package/lib/engine/engine_animation.js +32 -1
  34. package/lib/engine/engine_animation.js.map +1 -1
  35. package/lib/engine/engine_camera.fit.d.ts +68 -0
  36. package/lib/engine/engine_camera.fit.js +193 -0
  37. package/lib/engine/engine_camera.fit.js.map +1 -0
  38. package/lib/engine/engine_gizmos.d.ts +2 -2
  39. package/lib/engine/engine_gizmos.js +2 -2
  40. package/lib/engine/engine_physics.js +6 -3
  41. package/lib/engine/engine_physics.js.map +1 -1
  42. package/lib/engine/webcomponents/needle-engine.d.ts +1 -0
  43. package/lib/engine/webcomponents/needle-engine.js +6 -0
  44. package/lib/engine/webcomponents/needle-engine.js.map +1 -1
  45. package/lib/engine/webcomponents/needle-engine.loading.js +59 -23
  46. package/lib/engine/webcomponents/needle-engine.loading.js.map +1 -1
  47. package/lib/engine-components/AnimatorController.js +16 -0
  48. package/lib/engine-components/AnimatorController.js.map +1 -1
  49. package/lib/engine-components/CameraUtils.js +8 -9
  50. package/lib/engine-components/CameraUtils.js.map +1 -1
  51. package/lib/engine-components/OrbitControls.d.ts +4 -47
  52. package/lib/engine-components/OrbitControls.js +30 -178
  53. package/lib/engine-components/OrbitControls.js.map +1 -1
  54. package/lib/engine-components/Renderer.d.ts +2 -2
  55. package/lib/engine-components/Renderer.js +6 -4
  56. package/lib/engine-components/Renderer.js.map +1 -1
  57. package/lib/engine-components/api.d.ts +0 -1
  58. package/lib/engine-components/api.js.map +1 -1
  59. package/lib/engine-components/codegen/components.d.ts +2 -0
  60. package/lib/engine-components/codegen/components.js +2 -0
  61. package/lib/engine-components/codegen/components.js.map +1 -1
  62. package/lib/engine-components/timeline/PlayableDirector.d.ts +28 -6
  63. package/lib/engine-components/timeline/PlayableDirector.js +60 -26
  64. package/lib/engine-components/timeline/PlayableDirector.js.map +1 -1
  65. package/lib/engine-components/timeline/TimelineModels.d.ts +3 -0
  66. package/lib/engine-components/timeline/TimelineModels.js.map +1 -1
  67. package/lib/engine-components/timeline/TimelineTracks.d.ts +7 -0
  68. package/lib/engine-components/timeline/TimelineTracks.js +19 -0
  69. package/lib/engine-components/timeline/TimelineTracks.js.map +1 -1
  70. package/lib/engine-components/web/Clickthrough.d.ts +3 -0
  71. package/lib/engine-components/web/Clickthrough.js +3 -0
  72. package/lib/engine-components/web/Clickthrough.js.map +1 -1
  73. package/lib/engine-components/web/CursorFollow.d.ts +3 -0
  74. package/lib/engine-components/web/CursorFollow.js +3 -0
  75. package/lib/engine-components/web/CursorFollow.js.map +1 -1
  76. package/lib/engine-components/web/HoverAnimation.d.ts +44 -0
  77. package/lib/engine-components/web/HoverAnimation.js +105 -0
  78. package/lib/engine-components/web/HoverAnimation.js.map +1 -0
  79. package/lib/engine-components/web/ScrollFollow.d.ts +18 -4
  80. package/lib/engine-components/web/ScrollFollow.js +143 -25
  81. package/lib/engine-components/web/ScrollFollow.js.map +1 -1
  82. package/lib/engine-components/web/index.d.ts +1 -0
  83. package/lib/engine-components/web/index.js +1 -0
  84. package/lib/engine-components/web/index.js.map +1 -1
  85. package/package.json +1 -1
  86. package/plugins/vite/alias.js +5 -3
  87. package/plugins/vite/poster-client.js +22 -21
  88. package/src/engine/api.ts +2 -1
  89. package/src/engine/codegen/register_types.ts +4 -0
  90. package/src/engine/engine_animation.ts +69 -1
  91. package/src/engine/engine_camera.fit.ts +288 -0
  92. package/src/engine/engine_gizmos.ts +2 -2
  93. package/src/engine/engine_physics.ts +6 -3
  94. package/src/engine/webcomponents/needle-engine.loading.ts +63 -24
  95. package/src/engine/webcomponents/needle-engine.ts +6 -1
  96. package/src/engine-components/AnimatorController.ts +21 -2
  97. package/src/engine-components/CameraUtils.ts +8 -9
  98. package/src/engine-components/OrbitControls.ts +30 -239
  99. package/src/engine-components/Renderer.ts +6 -4
  100. package/src/engine-components/api.ts +0 -1
  101. package/src/engine-components/codegen/components.ts +2 -0
  102. package/src/engine-components/timeline/PlayableDirector.ts +79 -34
  103. package/src/engine-components/timeline/TimelineModels.ts +3 -0
  104. package/src/engine-components/timeline/TimelineTracks.ts +22 -0
  105. package/src/engine-components/web/Clickthrough.ts +3 -0
  106. package/src/engine-components/web/CursorFollow.ts +3 -0
  107. package/src/engine-components/web/HoverAnimation.ts +99 -0
  108. package/src/engine-components/web/ScrollFollow.ts +181 -24
  109. package/src/engine-components/web/index.ts +1 -0
  110. package/dist/needle-engine.bundle-ugr1bBtk.min.js +0 -1616
@@ -2,18 +2,19 @@ import { Camera as Camera3, Object3D, PerspectiveCamera, Ray, Vector2, Vector3,
2
2
  import { OrbitControls as ThreeOrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
3
3
 
4
4
  import { isDevEnvironment } from "../engine/debug/index.js";
5
+ import { fitCamera, FitCameraOptions } from "../engine/engine_camera.fit.js";
5
6
  import { setCameraController } from "../engine/engine_camera.js";
6
7
  import { Gizmos } from "../engine/engine_gizmos.js";
7
8
  import { InputEventQueue, NEPointerEvent } from "../engine/engine_input.js";
8
9
  import { Mathf } from "../engine/engine_math.js";
9
10
  import { IRaycastOptions, RaycastOptions } from "../engine/engine_physics.js";
10
11
  import { serializable } from "../engine/engine_serialization_decorator.js";
11
- import { getBoundingBox, getTempVector, getWorldPosition } from "../engine/engine_three_utils.js";
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.fitCamera(this.context.scene.children, {
495
- immediate: false
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?: 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
-
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
+ }
@@ -304,10 +304,12 @@ export class Renderer extends Behaviour implements IRenderer {
304
304
  return this._sharedMeshes;
305
305
  }
306
306
 
307
+ // @ts-ignore
307
308
  get sharedMaterial(): SharedMaterial {
308
- return this.sharedMaterials[0] as SharedMaterial;
309
+ return (this.sharedMaterials?.[0]) as SharedMaterial;
309
310
  }
310
311
 
312
+ // @ts-ignore
311
313
  set sharedMaterial(mat: SharedMaterial) {
312
314
  const cur = this.sharedMaterials[0];
313
315
  if (cur === mat) return;
@@ -315,12 +317,12 @@ export class Renderer extends Behaviour implements IRenderer {
315
317
  this.applyLightmapping();
316
318
  }
317
319
 
318
- /**@deprecated please use sharedMaterial */
320
+ /**@deprecated Use sharedMaterial */
319
321
  get material(): SharedMaterial {
320
- return this.sharedMaterials[0] as SharedMaterial;
322
+ return this.sharedMaterials?.[0] as SharedMaterial;
321
323
  }
322
324
 
323
- /**@deprecated please use sharedMaterial */
325
+ /**@deprecated Use sharedMaterial */
324
326
  set material(mat: SharedMaterial) {
325
327
  this.sharedMaterial = mat;
326
328
  }
@@ -53,7 +53,6 @@ import "./AnimationUtilsAutoplay.js"
53
53
 
54
54
  export { DragMode } from "./DragControls.js";
55
55
  export type { DropListenerNetworkEventArguments, DropListenerOnDropArguments } from "./DropListener.js";
56
- export { type FitCameraOptions } from "./OrbitControls.js";
57
56
  export * from "./particlesystem/api.js"
58
57
  export * from "./splines/index.js";
59
58
  export * from "./web/index.js";
@@ -169,6 +169,7 @@ export { SignalReceiverEvent } from "../timeline/SignalAsset.js";
169
169
  export { SignalReceiver } from "../timeline/SignalAsset.js";
170
170
  export { AnimationTrackHandler } from "../timeline/TimelineTracks.js";
171
171
  export { AudioTrackHandler } from "../timeline/TimelineTracks.js";
172
+ export { MarkerTrackHandler } from "../timeline/TimelineTracks.js";
172
173
  export { SignalTrackHandler } from "../timeline/TimelineTracks.js";
173
174
  export { ControlTrackHandler } from "../timeline/TimelineTracks.js";
174
175
  export { TransformGizmo } from "../TransformGizmo.js";
@@ -205,6 +206,7 @@ export { VideoPlayer } from "../VideoPlayer.js";
205
206
  export { Voip } from "../Voip.js";
206
207
  export { ClickThrough } from "../web/Clickthrough.js";
207
208
  export { CursorFollow } from "../web/CursorFollow.js";
209
+ export { HoverAnimation } from "../web/HoverAnimation.js";
208
210
  export { ScrollFollow } from "../web/ScrollFollow.js";
209
211
  export { Avatar } from "../webxr/Avatar.js";
210
212
  export { XRControllerFollow } from "../webxr/controllers/XRControllerFollow.js";
@@ -293,6 +293,41 @@ export class PlayableDirector extends Behaviour {
293
293
  return this._audioTracks;
294
294
  }
295
295
 
296
+ /**
297
+ * @returns all signal tracks of the timeline
298
+ */
299
+ get signalTracks(): Tracks.SignalTrackHandler[] {
300
+ return this._signalTracks;
301
+ }
302
+
303
+ /**
304
+ * @returns all marker tracks of the timeline
305
+ */
306
+ get markerTracks(): Tracks.MarkerTrackHandler[] {
307
+ return this._markerTracks;
308
+ }
309
+
310
+ /**
311
+ * Iterates over all markers of the timeline, optionally filtering by type
312
+ *
313
+ * @example
314
+ * ```ts
315
+ * // Iterate over all ScrollMarkers in the timeline
316
+ * for (const marker of director.foreachMarker<{selector:string}>("ScrollMarker")) {
317
+ * console.log(marker.time, marker.selector);
318
+ * }
319
+ * ```
320
+ *
321
+ */
322
+ *foreachMarker<T extends Record<string, any>>(type: string | null = null): Generator<(T & Models.MarkerModel)> {
323
+ for (const track of this._markerTracks) {
324
+ for (const marker of track.foreachMarker<T>(type)) {
325
+ yield marker as T & Models.MarkerModel;
326
+ }
327
+ }
328
+ }
329
+
330
+
296
331
  private _guidsMap?: GuidsMap;
297
332
  /** @internal */
298
333
  resolveGuids(map: GuidsMap) {
@@ -309,16 +344,18 @@ export class PlayableDirector extends Behaviour {
309
344
  private _time: number = 0;
310
345
  private _duration: number = 0;
311
346
  private _weight: number = 1;
312
- private _animationTracks: Array<Tracks.AnimationTrackHandler> = [];
313
- private _audioTracks: Array<Tracks.AudioTrackHandler> = [];
314
- private _signalTracks: Array<Tracks.SignalTrackHandler> = [];
315
- private _controlTracks: Array<Tracks.ControlTrackHandler> = [];
316
- private _customTracks: Array<Tracks.TrackHandler> = [];
317
-
318
- private _allTracks: Array<Array<Tracks.TrackHandler>> = [
347
+ private readonly _animationTracks: Array<Tracks.AnimationTrackHandler> = [];
348
+ private readonly _audioTracks: Array<Tracks.AudioTrackHandler> = [];
349
+ private readonly _signalTracks: Array<Tracks.SignalTrackHandler> = [];
350
+ private readonly _markerTracks: Array<Tracks.MarkerTrackHandler> = [];
351
+ private readonly _controlTracks: Array<Tracks.ControlTrackHandler> = [];
352
+ private readonly _customTracks: Array<Tracks.TrackHandler> = [];
353
+
354
+ private readonly _allTracks: Array<Array<Tracks.TrackHandler>> = [
319
355
  this._animationTracks,
320
356
  this._audioTracks,
321
357
  this._signalTracks,
358
+ this._markerTracks,
322
359
  this._controlTracks,
323
360
  this._customTracks
324
361
  ];
@@ -335,7 +372,7 @@ export class PlayableDirector extends Behaviour {
335
372
  }
336
373
  }
337
374
 
338
- private *internalUpdate() {
375
+ private * internalUpdate() {
339
376
  while (this._isPlaying && this.activeAndEnabled) {
340
377
  if (!this._isPaused && this._isPlaying) {
341
378
  this._time += this.context.time.deltaTime * this.speed;
@@ -415,26 +452,18 @@ export class PlayableDirector extends Behaviour {
415
452
  }
416
453
  }
417
454
 
418
- // When timeline reaches the end "stop()" is called which is evaluating with time 0
419
- // We don't want to re-evaluate the animation then in case the timeline is blended with the Animator
420
- // e.g then the timeline animation at time 0 is 100% applied on top of the animator animation
421
- if (!this._isStopping) {
422
- for (const handler of this._animationTracks) {
455
+ for (const track of this._allTracks) {
456
+ for (const handler of track) {
457
+ // When timeline reaches the end "stop()" is called which is evaluating with time 0
458
+ // We don't want to re-evaluate the animation then in case the timeline is blended with the Animator
459
+ // e.g then the timeline animation at time 0 is 100% applied on top of the animator animation
460
+
461
+ if (this._isStopping && handler instanceof Tracks.AnimationTrackHandler) {
462
+ continue;
463
+ }
423
464
  handler.evaluate(time);
424
465
  }
425
466
  }
426
- for (const handler of this._audioTracks) {
427
- handler.evaluate(time);
428
- }
429
- for (const sig of this._signalTracks) {
430
- sig.evaluate(time);
431
- }
432
- for (const ctrl of this._controlTracks) {
433
- ctrl.evaluate(time);
434
- }
435
- for (const cust of this._customTracks) {
436
- cust.evaluate(time);
437
- }
438
467
  }
439
468
 
440
469
  private resolveBindings() {
@@ -630,26 +659,42 @@ export class PlayableDirector extends Behaviour {
630
659
  }
631
660
  }
632
661
  else if (track.type === Models.TrackType.Marker) {
633
- const signalHandler: Tracks.SignalTrackHandler = new Tracks.SignalTrackHandler();
634
- signalHandler.director = this;
635
- signalHandler.track = track;
662
+
636
663
  if (track.markers) {
664
+ // For the marker track we create both a signal track handler AND a markertrack handler because a marker track can have signals and markers
665
+ const signalHandler: Tracks.SignalTrackHandler = new Tracks.SignalTrackHandler();
666
+ signalHandler.director = this;
667
+ signalHandler.track = track;
668
+
669
+ const markerHandler: Tracks.MarkerTrackHandler = new Tracks.MarkerTrackHandler();
670
+ markerHandler.director = this;
671
+ markerHandler.track = track;
672
+
637
673
  for (const marker of track.markers) {
638
674
  switch (marker.type) {
639
675
  case Models.MarkerType.Signal:
640
676
  signalHandler.models.push(marker as Models.SignalMarkerModel);
641
677
  signalHandler.didTrigger.push(false);
642
678
  break;
679
+ default:
680
+ markerHandler.models.push(marker);
681
+ break;
643
682
  }
644
683
  }
645
- }
646
- if (signalHandler !== null && signalHandler.models.length > 0) {
647
- const rec = GameObject.getComponent(this.gameObject, SignalReceiver);
648
- if (rec) {
649
- signalHandler.receivers.push(rec);
650
- this._signalTracks.push(signalHandler);
684
+
685
+ if (signalHandler !== null && signalHandler.models.length > 0) {
686
+ const rec = GameObject.getComponent(this.gameObject, SignalReceiver);
687
+ if (rec) {
688
+ signalHandler.receivers.push(rec);
689
+ this._signalTracks.push(signalHandler);
690
+ }
691
+ }
692
+
693
+ if (markerHandler !== null && markerHandler.models.length > 0) {
694
+ this._markerTracks.push(markerHandler);
651
695
  }
652
696
  }
697
+
653
698
  }
654
699
  else if (track.type === Models.TrackType.Signal) {
655
700
  const handler = new Tracks.SignalTrackHandler();
@@ -1,4 +1,5 @@
1
1
  import { AnimationClip, Object3D, Quaternion, Vector3 } from "three";
2
+ import { Behavior } from "three-mesh-ui";
2
3
 
3
4
  export declare type TimelineAssetModel = {
4
5
  name: string;
@@ -90,3 +91,5 @@ export declare class SignalMarkerModel extends MarkerModel {
90
91
  emitOnce: boolean;
91
92
  asset: string;
92
93
  }
94
+
95
+ export type ScrollMarkerModel = MarkerModel & { selector: string };
@@ -830,6 +830,28 @@ export class AudioTrackHandler extends TrackHandler {
830
830
  }
831
831
  }
832
832
 
833
+ export class MarkerTrackHandler extends TrackHandler {
834
+ models: Array<Models.MarkerModel & Record<string, any>> = [];
835
+ isDirty = true;
836
+
837
+ *foreachMarker<T>(type: string | null = null) {
838
+ for (const model of this.models) {
839
+ if (model && model.type === type) yield model as T;
840
+ }
841
+ }
842
+
843
+ onEnable() {
844
+ this.isDirty = true;
845
+ }
846
+
847
+ evaluate(_time: number) {
848
+ if (this.isDirty) {
849
+ this.isDirty = false;
850
+ this.models.sort((a, b) => a.time - b.time);
851
+ }
852
+ }
853
+ }
854
+
833
855
  export class SignalTrackHandler extends TrackHandler {
834
856
  models: Models.SignalMarkerModel[] = [];
835
857
  didTrigger: boolean[] = [];
@@ -21,6 +21,9 @@ 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
24
27
  */
25
28
  export class ClickThrough extends Behaviour {
26
29
 
@@ -4,6 +4,9 @@ import { Behaviour } from "../Component.js";
4
4
 
5
5
  /**
6
6
  * The CursorFollow component makes the object follow the cursor (or touch) position on screen.
7
+ * @category Web
8
+ * @group Components
9
+ * @component
7
10
  */
8
11
  export class CursorFollow extends Behaviour {
9
12