@needle-tools/engine 5.1.0-canary.fbdfce3 → 5.1.0-experimental.03e8105

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 (311) hide show
  1. package/CHANGELOG.md +86 -0
  2. package/SKILL.md +4 -1
  3. package/components.needle.json +1 -1
  4. package/dist/{needle-engine.bundle-BFSj2Fz8.js → needle-engine.bundle-BNqUjnSQ.js} +19180 -18386
  5. package/dist/needle-engine.bundle-Bt8ULD7E.umd.cjs +1733 -0
  6. package/dist/needle-engine.bundle-DF6ovbwD.min.js +1733 -0
  7. package/dist/needle-engine.d.ts +1487 -356
  8. package/dist/needle-engine.js +544 -542
  9. package/dist/needle-engine.min.js +1 -1
  10. package/dist/needle-engine.umd.cjs +1 -1
  11. package/dist/three.js +1 -0
  12. package/dist/three.min.js +21 -21
  13. package/dist/three.umd.cjs +16 -16
  14. package/lib/engine/api.d.ts +8 -1
  15. package/lib/engine/api.js +7 -1
  16. package/lib/engine/api.js.map +1 -1
  17. package/lib/engine/codegen/register_types.js +10 -18
  18. package/lib/engine/codegen/register_types.js.map +1 -1
  19. package/lib/engine/engine_audio.d.ts +68 -0
  20. package/lib/engine/engine_audio.js +172 -0
  21. package/lib/engine/engine_audio.js.map +1 -1
  22. package/lib/engine/engine_camera.fit.js +16 -4
  23. package/lib/engine/engine_camera.fit.js.map +1 -1
  24. package/lib/engine/engine_components.js +1 -1
  25. package/lib/engine/engine_components.js.map +1 -1
  26. package/lib/engine/engine_context.d.ts +21 -8
  27. package/lib/engine/engine_context.js +32 -16
  28. package/lib/engine/engine_context.js.map +1 -1
  29. package/lib/engine/engine_context_eventbus.d.ts +47 -0
  30. package/lib/engine/engine_context_eventbus.js +47 -0
  31. package/lib/engine/engine_context_eventbus.js.map +1 -0
  32. package/lib/engine/engine_disposable.d.ts +172 -0
  33. package/lib/engine/engine_disposable.js +136 -0
  34. package/lib/engine/engine_disposable.js.map +1 -0
  35. package/lib/engine/engine_gameobject.d.ts +1 -10
  36. package/lib/engine/engine_gameobject.js +22 -120
  37. package/lib/engine/engine_gameobject.js.map +1 -1
  38. package/lib/engine/engine_gltf_builtin_components.js +7 -69
  39. package/lib/engine/engine_gltf_builtin_components.js.map +1 -1
  40. package/lib/engine/engine_init.js +7 -7
  41. package/lib/engine/engine_init.js.map +1 -1
  42. package/lib/engine/engine_input.d.ts +24 -5
  43. package/lib/engine/engine_input.js +3 -2
  44. package/lib/engine/engine_input.js.map +1 -1
  45. package/lib/engine/engine_instantiate_resolve.d.ts +42 -0
  46. package/lib/engine/engine_instantiate_resolve.js +372 -0
  47. package/lib/engine/engine_instantiate_resolve.js.map +1 -0
  48. package/lib/engine/engine_license.d.ts +7 -7
  49. package/lib/engine/engine_license.js +183 -57
  50. package/lib/engine/engine_license.js.map +1 -1
  51. package/lib/engine/engine_mainloop_utils.js +7 -4
  52. package/lib/engine/engine_mainloop_utils.js.map +1 -1
  53. package/lib/engine/engine_networking.d.ts +51 -37
  54. package/lib/engine/engine_networking.js +132 -82
  55. package/lib/engine/engine_networking.js.map +1 -1
  56. package/lib/engine/engine_networking.transport.websocket.d.ts +15 -0
  57. package/lib/engine/engine_networking.transport.websocket.js +38 -0
  58. package/lib/engine/engine_networking.transport.websocket.js.map +1 -0
  59. package/lib/engine/engine_networking_blob.js +4 -4
  60. package/lib/engine/engine_networking_blob.js.map +1 -1
  61. package/lib/engine/engine_networking_instantiate.js +2 -2
  62. package/lib/engine/engine_networking_instantiate.js.map +1 -1
  63. package/lib/engine/engine_networking_types.d.ts +39 -1
  64. package/lib/engine/engine_networking_types.js +7 -0
  65. package/lib/engine/engine_networking_types.js.map +1 -1
  66. package/lib/engine/engine_physics_rapier.d.ts +21 -3
  67. package/lib/engine/engine_physics_rapier.js +94 -25
  68. package/lib/engine/engine_physics_rapier.js.map +1 -1
  69. package/lib/engine/engine_scenedata.js +2 -2
  70. package/lib/engine/engine_scenedata.js.map +1 -1
  71. package/lib/engine/engine_serialization_builtin_serializer.js +28 -5
  72. package/lib/engine/engine_serialization_builtin_serializer.js.map +1 -1
  73. package/lib/engine/engine_serialization_core.d.ts +1 -0
  74. package/lib/engine/engine_serialization_core.js +7 -0
  75. package/lib/engine/engine_serialization_core.js.map +1 -1
  76. package/lib/engine/engine_types.d.ts +29 -11
  77. package/lib/engine/engine_types.js +1 -1
  78. package/lib/engine/engine_types.js.map +1 -1
  79. package/lib/engine/engine_util_decorator.js +7 -2
  80. package/lib/engine/engine_util_decorator.js.map +1 -1
  81. package/lib/engine/engine_utils.d.ts +1 -1
  82. package/lib/engine/engine_utils.js +19 -5
  83. package/lib/engine/engine_utils.js.map +1 -1
  84. package/lib/engine/engine_utils_qrcode.js +2 -2
  85. package/lib/engine/engine_utils_qrcode.js.map +1 -1
  86. package/lib/engine/physics/workers/mesh-bvh/GenerateMeshBVHWorker.js +1 -1
  87. package/lib/engine/physics/workers/mesh-bvh/GenerateMeshBVHWorker.js.map +1 -1
  88. package/lib/engine/webcomponents/needle menu/needle-menu-spatial.js +2 -2
  89. package/lib/engine/webcomponents/needle menu/needle-menu-spatial.js.map +1 -1
  90. package/lib/engine/webcomponents/needle menu/needle-menu.d.ts +1 -1
  91. package/lib/engine/webcomponents/needle menu/needle-menu.js +6 -6
  92. package/lib/engine/webcomponents/needle menu/needle-menu.js.map +1 -1
  93. package/lib/engine/webcomponents/needle-engine.d.ts +10 -4
  94. package/lib/engine/webcomponents/needle-engine.js +3 -3
  95. package/lib/engine/webcomponents/needle-engine.js.map +1 -1
  96. package/lib/engine/webcomponents/needle-engine.loading.js +2 -2
  97. package/lib/engine/webcomponents/needle-engine.loading.js.map +1 -1
  98. package/lib/engine/xr/NeedleXRSession.d.ts +3 -2
  99. package/lib/engine/xr/NeedleXRSession.js +50 -14
  100. package/lib/engine/xr/NeedleXRSession.js.map +1 -1
  101. package/lib/engine/xr/TempXRContext.js +2 -2
  102. package/lib/engine/xr/TempXRContext.js.map +1 -1
  103. package/lib/engine/xr/events.d.ts +1 -1
  104. package/lib/engine/xr/events.js.map +1 -1
  105. package/lib/engine-components/Animation.js +17 -16
  106. package/lib/engine-components/Animation.js.map +1 -1
  107. package/lib/engine-components/AnimationBuilder.d.ts +158 -0
  108. package/lib/engine-components/AnimationBuilder.js +305 -0
  109. package/lib/engine-components/AnimationBuilder.js.map +1 -0
  110. package/lib/engine-components/Animator.d.ts +6 -0
  111. package/lib/engine-components/Animator.js +23 -13
  112. package/lib/engine-components/Animator.js.map +1 -1
  113. package/lib/engine-components/AnimatorController.builder.d.ts +191 -0
  114. package/lib/engine-components/AnimatorController.builder.js +263 -0
  115. package/lib/engine-components/AnimatorController.builder.js.map +1 -0
  116. package/lib/engine-components/AnimatorController.d.ts +2 -119
  117. package/lib/engine-components/AnimatorController.js +33 -232
  118. package/lib/engine-components/AnimatorController.js.map +1 -1
  119. package/lib/engine-components/AudioSource.d.ts +19 -3
  120. package/lib/engine-components/AudioSource.js +121 -68
  121. package/lib/engine-components/AudioSource.js.map +1 -1
  122. package/lib/engine-components/Collider.d.ts +18 -9
  123. package/lib/engine-components/Collider.js +61 -14
  124. package/lib/engine-components/Collider.js.map +1 -1
  125. package/lib/engine-components/Component.d.ts +72 -9
  126. package/lib/engine-components/Component.js +114 -10
  127. package/lib/engine-components/Component.js.map +1 -1
  128. package/lib/engine-components/ContactShadows.d.ts +1 -0
  129. package/lib/engine-components/ContactShadows.js +14 -1
  130. package/lib/engine-components/ContactShadows.js.map +1 -1
  131. package/lib/engine-components/DragControls.d.ts +7 -0
  132. package/lib/engine-components/DragControls.js +19 -7
  133. package/lib/engine-components/DragControls.js.map +1 -1
  134. package/lib/engine-components/DropListener.js +3 -0
  135. package/lib/engine-components/DropListener.js.map +1 -1
  136. package/lib/engine-components/EventList.d.ts +31 -9
  137. package/lib/engine-components/EventList.js +37 -76
  138. package/lib/engine-components/EventList.js.map +1 -1
  139. package/lib/engine-components/Joints.d.ts +4 -2
  140. package/lib/engine-components/Joints.js +19 -3
  141. package/lib/engine-components/Joints.js.map +1 -1
  142. package/lib/engine-components/Light.js +9 -1
  143. package/lib/engine-components/Light.js.map +1 -1
  144. package/lib/engine-components/Networking.d.ts +1 -1
  145. package/lib/engine-components/Networking.js +1 -1
  146. package/lib/engine-components/OrbitControls.d.ts +0 -2
  147. package/lib/engine-components/OrbitControls.js +30 -12
  148. package/lib/engine-components/OrbitControls.js.map +1 -1
  149. package/lib/engine-components/RigidBody.d.ts +12 -4
  150. package/lib/engine-components/RigidBody.js +18 -4
  151. package/lib/engine-components/RigidBody.js.map +1 -1
  152. package/lib/engine-components/SceneSwitcher.js +3 -0
  153. package/lib/engine-components/SceneSwitcher.js.map +1 -1
  154. package/lib/engine-components/SeeThrough.js +2 -2
  155. package/lib/engine-components/SeeThrough.js.map +1 -1
  156. package/lib/engine-components/api.d.ts +2 -1
  157. package/lib/engine-components/api.js +2 -1
  158. package/lib/engine-components/api.js.map +1 -1
  159. package/lib/engine-components/codegen/components.d.ts +7 -13
  160. package/lib/engine-components/codegen/components.js +7 -13
  161. package/lib/engine-components/codegen/components.js.map +1 -1
  162. package/lib/engine-components/export/usdz/USDZExporter.js +4 -4
  163. package/lib/engine-components/export/usdz/USDZExporter.js.map +1 -1
  164. package/lib/engine-components/postprocessing/Effects/Tonemapping.utils.d.ts +1 -1
  165. package/lib/engine-components/timeline/PlayableDirector.d.ts +21 -11
  166. package/lib/engine-components/timeline/PlayableDirector.js +75 -67
  167. package/lib/engine-components/timeline/PlayableDirector.js.map +1 -1
  168. package/lib/engine-components/timeline/SignalAsset.d.ts +3 -1
  169. package/lib/engine-components/timeline/SignalAsset.js +1 -0
  170. package/lib/engine-components/timeline/SignalAsset.js.map +1 -1
  171. package/lib/engine-components/timeline/TimelineBuilder.d.ts +413 -0
  172. package/lib/engine-components/timeline/TimelineBuilder.js +506 -0
  173. package/lib/engine-components/timeline/TimelineBuilder.js.map +1 -0
  174. package/lib/engine-components/timeline/TimelineModels.d.ts +2 -1
  175. package/lib/engine-components/timeline/TimelineModels.js +3 -0
  176. package/lib/engine-components/timeline/TimelineModels.js.map +1 -1
  177. package/lib/engine-components/timeline/TimelineTracks.d.ts +37 -6
  178. package/lib/engine-components/timeline/TimelineTracks.js +92 -26
  179. package/lib/engine-components/timeline/TimelineTracks.js.map +1 -1
  180. package/lib/engine-components/timeline/index.d.ts +2 -1
  181. package/lib/engine-components/timeline/index.js +2 -0
  182. package/lib/engine-components/timeline/index.js.map +1 -1
  183. package/lib/engine-components/ui/Canvas.d.ts +1 -1
  184. package/lib/engine-components/ui/Canvas.js +2 -8
  185. package/lib/engine-components/ui/Canvas.js.map +1 -1
  186. package/lib/engine-components/ui/Text.d.ts +1 -0
  187. package/lib/engine-components/ui/Text.js +10 -7
  188. package/lib/engine-components/ui/Text.js.map +1 -1
  189. package/lib/engine-components/web/CursorFollow.d.ts +0 -1
  190. package/lib/engine-components/web/CursorFollow.js +21 -13
  191. package/lib/engine-components/web/CursorFollow.js.map +1 -1
  192. package/lib/engine-components/webxr/WebXRImageTracking.js +4 -0
  193. package/lib/engine-components/webxr/WebXRImageTracking.js.map +1 -1
  194. package/package.json +2 -83
  195. package/plugins/common/cloud.js +6 -1
  196. package/plugins/common/license.js +31 -10
  197. package/plugins/common/worker.js +9 -4
  198. package/plugins/vite/asap.js +17 -8
  199. package/plugins/vite/dependencies.js +29 -10
  200. package/plugins/vite/dependency-watcher.js +2 -2
  201. package/plugins/vite/editor-connection.js +3 -3
  202. package/plugins/vite/license.js +46 -7
  203. package/plugins/vite/local-files-core.js +3 -3
  204. package/plugins/vite/local-files-utils.d.ts +3 -1
  205. package/plugins/vite/local-files-utils.js +29 -5
  206. package/plugins/vite/reload.js +1 -1
  207. package/plugins/vite/server.js +2 -1
  208. package/src/engine/api.ts +11 -1
  209. package/src/engine/codegen/register_types.ts +10 -18
  210. package/src/engine/engine_audio.ts +184 -0
  211. package/src/engine/engine_camera.fit.ts +15 -4
  212. package/src/engine/engine_components.ts +1 -1
  213. package/src/engine/engine_context.ts +34 -18
  214. package/src/engine/engine_context_eventbus.ts +73 -0
  215. package/src/engine/engine_disposable.ts +214 -0
  216. package/src/engine/engine_gameobject.ts +54 -159
  217. package/src/engine/engine_gltf_builtin_components.ts +7 -76
  218. package/src/engine/engine_init.ts +7 -7
  219. package/src/engine/engine_input.ts +28 -7
  220. package/src/engine/engine_instantiate_resolve.ts +407 -0
  221. package/src/engine/engine_license.ts +197 -55
  222. package/src/engine/engine_mainloop_utils.ts +7 -4
  223. package/src/engine/engine_networking.transport.websocket.ts +45 -0
  224. package/src/engine/engine_networking.ts +161 -137
  225. package/src/engine/engine_networking_blob.ts +4 -4
  226. package/src/engine/engine_networking_instantiate.ts +2 -2
  227. package/src/engine/engine_networking_types.ts +41 -1
  228. package/src/engine/engine_physics_rapier.ts +102 -33
  229. package/src/engine/engine_scenedata.ts +3 -3
  230. package/src/engine/engine_serialization_builtin_serializer.ts +32 -9
  231. package/src/engine/engine_serialization_core.ts +9 -0
  232. package/src/engine/engine_types.ts +46 -27
  233. package/src/engine/engine_util_decorator.ts +7 -2
  234. package/src/engine/engine_utils.ts +16 -5
  235. package/src/engine/engine_utils_qrcode.ts +2 -2
  236. package/src/engine/physics/workers/mesh-bvh/GenerateMeshBVHWorker.js +1 -1
  237. package/src/engine/webcomponents/needle menu/needle-menu-spatial.ts +2 -2
  238. package/src/engine/webcomponents/needle menu/needle-menu.ts +6 -6
  239. package/src/engine/webcomponents/needle-engine.loading.ts +6 -6
  240. package/src/engine/webcomponents/needle-engine.ts +12 -6
  241. package/src/engine/xr/NeedleXRSession.ts +48 -13
  242. package/src/engine/xr/TempXRContext.ts +2 -2
  243. package/src/engine/xr/events.ts +1 -1
  244. package/src/engine-components/Animation.ts +19 -16
  245. package/src/engine-components/AnimationBuilder.ts +472 -0
  246. package/src/engine-components/Animator.ts +24 -12
  247. package/src/engine-components/AnimatorController.builder.ts +387 -0
  248. package/src/engine-components/AnimatorController.ts +20 -291
  249. package/src/engine-components/AudioSource.ts +130 -79
  250. package/src/engine-components/Collider.ts +66 -18
  251. package/src/engine-components/Component.ts +118 -20
  252. package/src/engine-components/ContactShadows.ts +15 -1
  253. package/src/engine-components/DragControls.ts +18 -11
  254. package/src/engine-components/DropListener.ts +3 -0
  255. package/src/engine-components/EventList.ts +45 -83
  256. package/src/engine-components/Joints.ts +20 -4
  257. package/src/engine-components/Light.ts +10 -2
  258. package/src/engine-components/Networking.ts +1 -1
  259. package/src/engine-components/OrbitControls.ts +34 -14
  260. package/src/engine-components/RigidBody.ts +18 -4
  261. package/src/engine-components/SceneSwitcher.ts +3 -0
  262. package/src/engine-components/SeeThrough.ts +2 -2
  263. package/src/engine-components/api.ts +2 -1
  264. package/src/engine-components/codegen/components.ts +7 -13
  265. package/src/engine-components/export/usdz/USDZExporter.ts +4 -4
  266. package/src/engine-components/timeline/PlayableDirector.ts +83 -81
  267. package/src/engine-components/timeline/SignalAsset.ts +4 -1
  268. package/src/engine-components/timeline/TimelineBuilder.ts +824 -0
  269. package/src/engine-components/timeline/TimelineModels.ts +5 -1
  270. package/src/engine-components/timeline/TimelineTracks.ts +96 -27
  271. package/src/engine-components/timeline/index.ts +2 -1
  272. package/src/engine-components/ui/Canvas.ts +2 -8
  273. package/src/engine-components/ui/Text.ts +12 -8
  274. package/src/engine-components/web/CursorFollow.ts +21 -14
  275. package/src/engine-components/webxr/WebXRImageTracking.ts +2 -0
  276. package/dist/needle-engine.bundle-CmxIO5uH.min.js +0 -1732
  277. package/dist/needle-engine.bundle-tJIZukCz.umd.cjs +0 -1732
  278. package/lib/engine-components/AvatarLoader.d.ts +0 -80
  279. package/lib/engine-components/AvatarLoader.js +0 -232
  280. package/lib/engine-components/AvatarLoader.js.map +0 -1
  281. package/lib/engine-components/avatar/AvatarBlink_Simple.d.ts +0 -11
  282. package/lib/engine-components/avatar/AvatarBlink_Simple.js +0 -77
  283. package/lib/engine-components/avatar/AvatarBlink_Simple.js.map +0 -1
  284. package/lib/engine-components/avatar/AvatarEyeLook_Rotation.d.ts +0 -14
  285. package/lib/engine-components/avatar/AvatarEyeLook_Rotation.js +0 -69
  286. package/lib/engine-components/avatar/AvatarEyeLook_Rotation.js.map +0 -1
  287. package/lib/engine-components/avatar/Avatar_Brain_LookAt.d.ts +0 -29
  288. package/lib/engine-components/avatar/Avatar_Brain_LookAt.js +0 -122
  289. package/lib/engine-components/avatar/Avatar_Brain_LookAt.js.map +0 -1
  290. package/lib/engine-components/avatar/Avatar_MouthShapes.d.ts +0 -15
  291. package/lib/engine-components/avatar/Avatar_MouthShapes.js +0 -80
  292. package/lib/engine-components/avatar/Avatar_MouthShapes.js.map +0 -1
  293. package/lib/engine-components/avatar/Avatar_MustacheShake.d.ts +0 -9
  294. package/lib/engine-components/avatar/Avatar_MustacheShake.js +0 -30
  295. package/lib/engine-components/avatar/Avatar_MustacheShake.js.map +0 -1
  296. package/plugins/dts-generator/dts.codegen.js +0 -334
  297. package/plugins/dts-generator/dts.scan.js +0 -99
  298. package/plugins/dts-generator/dts.writer.js +0 -59
  299. package/plugins/dts-generator/glb.discovery.js +0 -279
  300. package/plugins/dts-generator/glb.extractor.js +0 -215
  301. package/plugins/dts-generator/glb.reader.js +0 -167
  302. package/plugins/dts-generator/index.js +0 -36
  303. package/plugins/dts-generator/manifest.types.js +0 -174
  304. package/plugins/gltf-packer.mjs +0 -1
  305. package/src/engine-components/AvatarLoader.ts +0 -264
  306. package/src/engine-components/avatar/AvatarBlink_Simple.ts +0 -70
  307. package/src/engine-components/avatar/AvatarEyeLook_Rotation.ts +0 -64
  308. package/src/engine-components/avatar/Avatar_Brain_LookAt.ts +0 -140
  309. package/src/engine-components/avatar/Avatar_MouthShapes.ts +0 -84
  310. package/src/engine-components/avatar/Avatar_MustacheShake.ts +0 -32
  311. package/src/vite-env.d.ts +0 -16
@@ -42,7 +42,7 @@ export enum ClipExtrapolation {
42
42
  * @category Animation and Sequencing
43
43
  * @see {@link PlayableDirector} for the main component to control timelines in Needle Engine.
44
44
  */
45
- export declare type TrackModel = {
45
+ export type TrackModel = {
46
46
  name: string;
47
47
  type: TrackType;
48
48
  muted: boolean;
@@ -53,6 +53,10 @@ export declare type TrackModel = {
53
53
  volume?: number;
54
54
  }
55
55
 
56
+ export function isTrackModel(obj:unknown): obj is TrackModel {
57
+ return typeof obj === "object" && obj !== null && "name" in obj && "type" in obj && "outputs" in obj && "muted" in obj;
58
+ }
59
+
56
60
  declare type Vec3 = { x: number, y: number, z: number };
57
61
  declare type Quat = { x: number, y: number, z: number, w: number };
58
62
 
@@ -4,13 +4,14 @@ import { isDevEnvironment } from "../../engine/debug/index.js";
4
4
  import { Context } from "../../engine/engine_setup.js";
5
5
  import type { Constructor } from "../../engine/engine_types.js";
6
6
  import { getParam, resolveUrl } from "../../engine/engine_utils.js";
7
- import { setObjectAnimated } from "../AnimationUtils.js";
8
7
  import { Animator } from "../Animator.js"
9
8
  import { AudioSource } from "../AudioSource.js";
10
9
  import { GameObject } from "../Component.js";
11
10
  import type { PlayableDirector } from "./PlayableDirector.js";
12
11
  import { SignalReceiver } from "./SignalAsset.js";
13
12
  import * as Models from "./TimelineModels.js";
13
+ import type { TimelineBuilder } from "./TimelineBuilder.js";
14
+ import { AnimationUtils } from "../../engine/engine_animation.js";
14
15
 
15
16
  const debug = getParam("debugtimeline");
16
17
 
@@ -18,7 +19,7 @@ const debug = getParam("debugtimeline");
18
19
  * A TrackHandler is responsible for evaluating a specific type of timeline track.
19
20
  * A timeline track can be an animation track, audio track, signal track, control track etc and is controlled by a {@link PlayableDirector}.
20
21
  */
21
- export abstract class TrackHandler {
22
+ export abstract class TimelineTrackHandler {
22
23
  director!: PlayableDirector;
23
24
  track!: Models.TrackModel;
24
25
 
@@ -136,7 +137,7 @@ class AnimationClipOffsetData {
136
137
  }
137
138
 
138
139
  // TODO: add support for clip clamp modes (loop, pingpong, clamp)
139
- export class AnimationTrackHandler extends TrackHandler {
140
+ export class TimelineAnimationTrack extends TimelineTrackHandler {
140
141
  /** @internal */
141
142
  models: Array<Models.ClipModel> = [];
142
143
  /** @internal */
@@ -178,7 +179,7 @@ export class AnimationTrackHandler extends TrackHandler {
178
179
  onStateChanged() {
179
180
  if (this._animator) {
180
181
  // We can not check the *isPlaying* state here because the timeline might be paused and evaluated by e.g. ScrollFollow
181
- setObjectAnimated(this._animator.gameObject, this, this.director.enabled && this.director.weight > 0);
182
+ AnimationUtils.setObjectAnimated(this._animator.gameObject, this, this.director.enabled && this.director.weight > 0);
182
183
  }
183
184
  }
184
185
 
@@ -262,7 +263,7 @@ export class AnimationTrackHandler extends TrackHandler {
262
263
  // which overrides the timeline
263
264
  this._animator = GameObject.getComponent(this.target, Animator) ?? null;
264
265
  if (this._animator) {
265
- setObjectAnimated(this._animator.gameObject, this, true);
266
+ AnimationUtils.setObjectAnimated(this._animator.gameObject, this, true);
266
267
  }
267
268
  }
268
269
 
@@ -591,7 +592,21 @@ const muteAudioTracks = getParam("mutetimeline");
591
592
 
592
593
  declare type AudioClipModel = Models.ClipModel & { _didTriggerPlay: boolean };
593
594
 
594
- export class AudioTrackHandler extends TrackHandler {
595
+ /**
596
+ * Handles audio playback for a timeline audio track.
597
+ *
598
+ * **Runtime mutation:** The track model is read fresh every frame during `evaluate()`.
599
+ * You can mutate `track.volume`, `clip.start`, `clip.end`, `clip.asset.volume` etc.
600
+ * at any time — changes take effect on the next frame without rebuilding the timeline.
601
+ *
602
+ * **Audio stopping:** Audio clips are automatically stopped when:
603
+ * - Timeline time moves outside a clip's `[start, end]` range (e.g. jumping or normal playback advancing past a clip)
604
+ * - The track is muted (via `muted = true`)
605
+ * - The director is stopped (`director.stop()`)
606
+ * - The director is paused (`director.pause()`)
607
+ * - The director is disabled or destroyed
608
+ */
609
+ export class TimelineAudioTrack extends TimelineTrackHandler {
595
610
 
596
611
  models: Array<AudioClipModel> = [];
597
612
  listener!: AudioListener;
@@ -600,6 +615,10 @@ export class AudioTrackHandler extends TrackHandler {
600
615
  lastTime: number = 0;
601
616
  audioSource?: AudioSource;
602
617
 
618
+ /** Track-level volume multiplier (0–1). Applied on top of per-clip volume each frame. */
619
+ get volume(): number { return this.track.volume ?? 1; }
620
+ set volume(val: number) { this.track.volume = val; }
621
+
603
622
  private _audioLoader: AudioLoader | null = null;
604
623
 
605
624
  private getAudioFilePath(path: string) {
@@ -796,7 +815,7 @@ export class AudioTrackHandler extends TrackHandler {
796
815
  private static _audioBuffers: Map<string, Promise<AudioBuffer | null>> = new Map();
797
816
 
798
817
  public static dispose() {
799
- AudioTrackHandler._audioBuffers.clear();
818
+ TimelineAudioTrack._audioBuffers.clear();
800
819
  }
801
820
 
802
821
  private handleAudioLoading(model: Models.ClipModel, audio: Audio): Promise<AudioBuffer | null> | null {
@@ -806,8 +825,8 @@ export class AudioTrackHandler extends TrackHandler {
806
825
  // TODO: maybe we should cache the loaders / buffers here by path
807
826
  const path = this.getAudioFilePath(model.asset.clip);
808
827
 
809
- if (AudioTrackHandler._audioBuffers.get(path)) {
810
- const promise = AudioTrackHandler._audioBuffers.get(path)!
828
+ if (TimelineAudioTrack._audioBuffers.get(path)) {
829
+ const promise = TimelineAudioTrack._audioBuffers.get(path)!
811
830
  promise.then((buffer) => {
812
831
  if (buffer) audio.setBuffer(buffer);
813
832
  });
@@ -827,17 +846,17 @@ export class AudioTrackHandler extends TrackHandler {
827
846
  resolve(null);
828
847
  });
829
848
  });
830
- AudioTrackHandler._audioBuffers.set(path, loadingPromise);
849
+ TimelineAudioTrack._audioBuffers.set(path, loadingPromise);
831
850
  return loadingPromise;
832
851
  }
833
852
  }
834
853
 
835
- export class MarkerTrackHandler extends TrackHandler {
854
+ export class TimelineMarkerTrack extends TimelineTrackHandler {
836
855
  models: Array<Models.MarkerModel & Record<string, any>> = [];
837
856
  needsSorting = true;
838
857
 
839
858
  *foreachMarker<T>(type: string | null = null) {
840
- if(this.needsSorting) this.sort();
859
+ if (this.needsSorting) this.sort();
841
860
  for (const model of this.models) {
842
861
  if (model && model.type === type) yield model as T;
843
862
  }
@@ -858,26 +877,34 @@ export class MarkerTrackHandler extends TrackHandler {
858
877
  }
859
878
  }
860
879
 
861
- export class SignalTrackHandler extends TrackHandler {
880
+ export class SignalTrackHandler extends TimelineTrackHandler {
862
881
  models: Models.SignalMarkerModel[] = [];
863
882
  didTrigger: boolean[] = [];
864
883
  receivers: Array<SignalReceiver | null> = [];
865
884
 
866
- // TODO: test when timeline signals are being reset in Unity
867
- // onEnable() {
868
- // for (let i = 0; i < this.didTrigger?.length; i++) {
869
- // this.didTrigger[i] = false;
870
- // }
871
- // }
885
+ private _lastTime: number = -1;
872
886
 
873
- // private _lastTime: number = -1;
887
+ onEnable() {
888
+ // Reset state so signals fire fresh on replay
889
+ this._lastTime = -1;
890
+ for (let i = 0; i < this.didTrigger.length; i++) {
891
+ this.didTrigger[i] = false;
892
+ }
893
+ }
894
+
895
+ onMuteChanged() {
896
+ // When unmuted, sync _lastTime to avoid stale comparisons
897
+ if (!this.track.muted) {
898
+ this._lastTime = -1;
899
+ }
900
+ }
874
901
 
875
902
  evaluate(time: number) {
876
903
  if (this.track.muted) return;
877
904
 
878
- // let lastTime = this._lastTime;
879
- // if (lastTime === -1) lastTime = time;
880
- // this._lastTime = time;
905
+ let lastTime = this._lastTime;
906
+ if (lastTime === -1) lastTime = time;
907
+ this._lastTime = time;
881
908
 
882
909
  const estimatedFrameLengthWithPadding = this.director.context.time.deltaTime * 1.5;
883
910
 
@@ -899,7 +926,6 @@ export class SignalTrackHandler extends TrackHandler {
899
926
  isActive = true;
900
927
  }
901
928
  }
902
- // console.log(time, td, isActive);
903
929
  if (isActive) {
904
930
  if (!wasTriggered) {
905
931
  if (debug)
@@ -917,16 +943,59 @@ export class SignalTrackHandler extends TrackHandler {
917
943
  }
918
944
  }
919
945
  }
920
- else {
921
- if (!model.emitOnce)
946
+ else if (!model.emitOnce) {
947
+ // Only reset didTrigger when time has moved backward past the signal
948
+ // (e.g. timeline looped or was scrubbed back). Don't reset just because
949
+ // the deltaTime-based activation window shrank on a fast frame.
950
+ if (time < model.time && lastTime >= model.time) {
922
951
  this.didTrigger[i] = false;
952
+ }
953
+ }
954
+ }
955
+ }
956
+ }
957
+
958
+ /**
959
+ * Handles activation (visibility) of bound objects for a timeline activation track.
960
+ *
961
+ * Each clip on the track defines a time range during which the bound objects should be active (visible).
962
+ * @see TimelineTrackHandler for details on how tracks and clips work in general, and how to mutate them at runtime.
963
+ * @see PlayableDirector for how to control timeline playback and time.
964
+ * @see TimelineBuilder for how to create and configure timelines and tracks in the editor.
965
+ */
966
+ export class TimelineActivationTrack extends TimelineTrackHandler {
967
+
968
+ evaluate(time: number) {
969
+ if (this.track.muted) return;
970
+ // NE-3241 / NE-3737: Don't evaluate when stopped (preserves "Leave as is" post-playback).
971
+ // But DO evaluate if the user explicitly called director.evaluate().
972
+ if (!this.director.isPlaying && !this.director._isUserEvaluation) return;
973
+
974
+ for (let i = 0; i < this.track.outputs.length; i++) {
975
+ const binding = this.track.outputs[i];
976
+ if (typeof binding !== "object" || binding === null) continue;
977
+
978
+ let isActive = false;
979
+ if (this.track.clips) {
980
+ for (const clip of this.track.clips) {
981
+ if (clip.start <= time && time <= clip.end) {
982
+ isActive = true;
983
+ break;
984
+ }
985
+ }
986
+ }
987
+
988
+ const obj = binding as Object3D;
989
+ if (obj.visible !== undefined && obj.visible !== isActive) {
990
+ obj.visible = isActive;
991
+ if (debug) console.warn(this.director.name, "ActivationTrack-" + i, obj.name, isActive, time);
923
992
  }
924
993
  }
925
994
  }
926
995
  }
927
996
 
928
997
 
929
- export class ControlTrackHandler extends TrackHandler {
998
+ export class TimelineControlTrack extends TimelineTrackHandler {
930
999
  models: Array<Models.ClipModel> = [];
931
1000
  timelines: Array<PlayableDirector | null> = [];
932
1001
 
@@ -1,4 +1,5 @@
1
- export { type ITimelineAnimationCallbacks as ITimelineAnimationOverride } from "./PlayableDirector.js"
1
+ export { DirectorWrapMode, type ITimelineAnimationCallbacks as ITimelineAnimationOverride } from "./PlayableDirector.js"
2
2
  export * from "./SignalAsset.js"
3
+ export * from "./TimelineBuilder.js"
3
4
  export * from "./TimelineModels.js"
4
5
  export * from "./TimelineTracks.js"
@@ -4,7 +4,7 @@ import * as ThreeMeshUI from 'three-mesh-ui'
4
4
  import { Mathf } from "../../engine/engine_math.js";
5
5
  import { serializable } from "../../engine/engine_serialization_decorator.js";
6
6
  import { FrameEvent } from "../../engine/engine_setup.js";
7
- import { delayForFrames, getParam } from "../../engine/engine_utils.js";
7
+ import { getParam } from "../../engine/engine_utils.js";
8
8
  import { type NeedleXREventArgs } from "../../engine/xr/api.js";
9
9
  import { Camera } from "../Camera.js";
10
10
  import { GameObject } from "../Component.js";
@@ -236,19 +236,13 @@ export class Canvas extends UIRootComponent implements ICanvas {
236
236
  }
237
237
  }
238
238
 
239
- async onEnterXR(args: NeedleXREventArgs) {
239
+ onEnterXR(args: NeedleXREventArgs) {
240
240
  // workaround for https://linear.app/needle/issue/NE-4114
241
241
  if (this.screenspace) {
242
242
  if (args.xr.isVR || args.xr.isPassThrough) {
243
243
  this.gameObject.visible = false;
244
244
  }
245
245
  }
246
- else {
247
- this.gameObject.visible = false;
248
- await delayForFrames(1).then(()=>{
249
- this.gameObject.visible = true;
250
- });
251
- }
252
246
  }
253
247
  onLeaveXR(args: NeedleXREventArgs): void {
254
248
  if (this.screenspace) {
@@ -170,12 +170,15 @@ export class Text extends Graphic implements IHasAlphaFactor, ICanvasEventReceiv
170
170
  this._font = val;
171
171
  this.uiObject?.set(this.getTextOpts());
172
172
  this.markDirty();
173
+ // If the font is assigned during deserialization it means the font URL MUST be resolved with the style suffix. If it's assigned at runtime by a user invocation and is absolute then we assume the user is prividing a full path to the font asset json or png
174
+ this._assignedAtRuntime = this.__didAwake;
173
175
  }
174
176
  }
175
177
  get font(): string | null {
176
178
  return this._font;
177
179
  }
178
180
  private _font: string | null = "https://cdn.needle.tools/static/fonts/msdf/arial/arial";
181
+ private _assignedAtRuntime: boolean = true;
179
182
 
180
183
 
181
184
  /**
@@ -274,7 +277,7 @@ export class Text extends Graphic implements IHasAlphaFactor, ICanvasEventReceiv
274
277
  }
275
278
 
276
279
  protected onCreate(_opts: any): void {
277
- if (debug) console.log(this);
280
+ if (debug) console.log("[Text] onCreate", this);
278
281
 
279
282
  // @marwie : Should be fixed. Currently _opts are always fed with :
280
283
  // backgroundOpacity : color.opacity
@@ -442,7 +445,7 @@ export class Text extends Graphic implements IHasAlphaFactor, ICanvasEventReceiv
442
445
  private feedText(text: string, richText: boolean): void {
443
446
  // if (!text || text.length <= 0) return;
444
447
  // if (!text ) return;
445
- if (debug) console.log("feedText", this.uiObject, text, richText);
448
+ if (debug) console.log("[Text] feedText", this.uiObject, text, richText);
446
449
 
447
450
  if (!this.uiObject) return;
448
451
  if (!this._textMeshUi)
@@ -603,16 +606,17 @@ export class Text extends Graphic implements IHasAlphaFactor, ICanvasEventReceiv
603
606
  * @private
604
607
  */
605
608
  private setFont(opts: ThreeMeshUIEveryOptions, fontStyle: FontStyle) {
606
-
609
+
607
610
  if (!this.font) {
608
- if(debug) console.warn("No font set for Text component, skipping font setup");
611
+ if (debug) console.warn("[Text] No font set for Text component, skipping font setup");
609
612
  return;
610
613
  }
611
614
 
612
615
  const fontName = this.font;
616
+
613
617
  const familyName = this.getFamilyNameWithCorrectSuffix(fontName, fontStyle);
614
618
 
615
- if (debug) console.log("Selected font family:" + familyName, this.font);
619
+ if (debug) console.log(`[Text] Selected font family '${familyName}' from ${fontName} with style ${FontStyle[fontStyle]}`);
616
620
 
617
621
  // ensure a font family is register under this name
618
622
  let fontFamily = ThreeMeshUI.FontLibrary.getFontFamily(familyName as string);
@@ -676,7 +680,7 @@ export class Text extends Graphic implements IHasAlphaFactor, ICanvasEventReceiv
676
680
  // e.g. -Medium, -Black, -Thin...
677
681
  const styleName = familyName.substring(styleSeparator + 1)?.toLowerCase();
678
682
  if (unsupportedStyleNames.includes(styleName)) {
679
- if (debug) console.warn("Unsupported font style: " + styleName);
683
+ if (debug) console.warn("[Text] Unsupported font style: " + styleName);
680
684
  return familyName;
681
685
  }
682
686
 
@@ -691,12 +695,12 @@ export class Text extends Graphic implements IHasAlphaFactor, ICanvasEventReceiv
691
695
  const isUpperCase = fontBaseName[0] === fontBaseName[0].toUpperCase();
692
696
  const fontNameWithoutSuffix = familyName.substring(0, styleSeparator > pathSeparatorIndex ? styleSeparator : familyName.length);
693
697
 
694
- if (debug) console.log("Select font: ", familyName, FontStyle[style], fontBaseName, isUpperCase, fontNameWithoutSuffix);
698
+ if (debug) console.log("[Text] Select font: ", familyName, FontStyle[style], fontBaseName, isUpperCase, fontNameWithoutSuffix);
695
699
 
696
700
  /**
697
701
  * If a user provides a font with an absolute URL AND the font name does not end with "-msdf.json" or ".png" (e.g. "https://example.com/fonts/Arial-Bold"), we will assume that the user is providing the full path to the font files and we will not try to modify the font name based on the style. This allows users to have more control over the font files they are using, especially if they are hosting their own fonts or using a custom font provider that does not follow the same naming conventions as our default fonts.
698
702
  */
699
- if (isAbsolute && !(familyName.endsWith("-msdf.json") || familyName.endsWith(".png"))) {
703
+ if (isAbsolute && this._assignedAtRuntime && !(familyName.endsWith("-msdf.json") || familyName.endsWith(".png"))) {
700
704
  return fontNameWithoutSuffix;
701
705
  }
702
706
 
@@ -178,7 +178,6 @@ export class CursorFollow extends Behaviour {
178
178
  * - Cursor that follows terrain or mesh surfaces
179
179
  *
180
180
  * **Important notes:**
181
- * - Requires objects in the scene to have colliders for raycasting to work
182
181
  * - Works best with {@link keepDistance} set to `false` to allow depth changes
183
182
  * - Can be combined with {@link damping} for smooth surface following
184
183
  * - The raycast uses the physics system's raycast functionality
@@ -289,20 +288,28 @@ export class CursorFollow extends Behaviour {
289
288
 
290
289
 
291
290
  if (this.snapToSurface) {
292
- ray.origin = _position;
293
- ray.direction = rayDirection.multiplyScalar(-1);
294
- const hits = this.context.physics.raycastFromRay(ray);
295
- if (hits?.length) {
296
- const hit = hits[0];
297
- if (this.damping > 0) {
298
- this.gameObject.worldPosition = _position.lerp(hit.point, this.context.time.deltaTime / this.damping);
299
- }
300
- else {
301
- this.gameObject.worldPosition = hit.point;
291
+ ray.origin = cameraPosition;
292
+ ray.direction = rayDirection;
293
+ const hits = this.context.physics.raycastFromRay(ray, {
294
+ testObject: obj => {
295
+ return obj !== this.gameObject && !this.gameObject.contains(obj) ? true : false
302
296
  }
303
-
304
- if(debug) {
305
- Gizmos.DrawLine(hit.point, hit.normal!.add(hit.point), 0x00FF00);
297
+ });
298
+ if (hits?.length) {
299
+ // Get the first hit that is not a child of *this* object. Because we do not want to lerp to a surface that is part of the object itself, since that would result in *this* object moving closer and closer to the camera as it tries to snap to itself
300
+ const hit = hits[0];//.find(h => !this.gameObject.contains(h.object));
301
+ if (hit) {
302
+ if (this.damping > 0) {
303
+ this.gameObject.worldPosition = _position.lerp(hit.point, this.context.time.deltaTime / this.damping);
304
+ // this._distance = this.gameObject.worldPosition.distanceTo(cameraPosition);
305
+ }
306
+ else {
307
+ this.gameObject.worldPosition = hit.point;
308
+ // this._distance = hit.point.distanceTo(cameraPosition);
309
+ }
310
+ if (debug) {
311
+ Gizmos.DrawLine(hit.point, hit.normal!.add(hit.point), 0x00FF00);
312
+ }
306
313
  }
307
314
  }
308
315
  }
@@ -886,9 +886,11 @@ async function loadImage(url: string) {
886
886
  }
887
887
  const promise = new Promise<boolean>(res => {
888
888
  _imageElements.set(url, null);
889
+ if(isDevEnvironment() || debug) console.debug(`[WebXRImageTracking] Start loading image for tracking: ${url}`);
889
890
  const imageElement = document.createElement("img") as HTMLImageElement;
890
891
  imageElement.src = url;
891
892
  imageElement.addEventListener("load", async () => {
893
+ if(isDevEnvironment() || debug) console.debug(`[WebXRImageTracking] Loaded image for tracking: ${url}`);
892
894
  const img = await createImageBitmap(imageElement);
893
895
  _imageElements.set(url, img);
894
896
  res(true);