@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
@@ -663,7 +663,15 @@ export class OrbitControls extends Behaviour implements ICameraController {
663
663
  }
664
664
  this._controls.enabled = true;
665
665
 
666
- if (this.context.input.getPointerDown(1) || this.context.input.getPointerDown(2) || this.context.input.mouseWheelChanged || (this.context.input.getPointerPressed(0) && this.context.input.getPointerPositionDelta(0)?.length() || 0 > .1)) {
666
+ // Interrupt programmatic transitions on meaningful new user interaction:
667
+ // - Middle/right button down (always intentional camera control)
668
+ // - Mouse wheel (zoom intent)
669
+ // - Left button drag start: getPointerDown(0) with a position delta — a bare click
670
+ // without movement shouldn't cancel an animation, but starting to drag should.
671
+ // Using getPointerDown (not getPointerPressed) ensures we only interrupt once at
672
+ // drag onset, not continuously every frame during a drag.
673
+ const leftDragStart = this.context.input.getPointerDown(0) && (this.context.input.getPointerPositionDelta(0)?.length() || 0) > .1;
674
+ if (this.context.input.getPointerDown(1) || this.context.input.getPointerDown(2) || this.context.input.mouseWheelChanged || leftDragStart) {
667
675
  this._inputs += 1;
668
676
  }
669
677
  if (this._inputs > 0 && this.allowInterrupt) {
@@ -758,19 +766,26 @@ export class OrbitControls extends Behaviour implements ICameraController {
758
766
 
759
767
  if (this.targetBounds) {
760
768
  // #region target bounds
761
- const targetVector = this._controls.target;
762
769
  const boundsCenter = this.targetBounds.worldPosition;
763
770
  const boundsHalfSize = getTempVector(this.targetBounds.worldScale).multiplyScalar(0.5);
764
771
  const min = getTempVector(boundsCenter).sub(boundsHalfSize);
765
772
  const max = getTempVector(boundsCenter).add(boundsHalfSize);
766
- const newTarget = getTempVector(this._controls.target).clamp(min, max);
767
- const duration = .1;
768
- if (duration <= 0) targetVector.copy(newTarget);
769
- else targetVector.lerp(newTarget, this.context.time.deltaTime / duration);
773
+
770
774
  if (this._lookTargetLerpActive) {
771
- if (duration <= 0) this._lookTargetEndPosition.copy(newTarget);
772
- else this._lookTargetEndPosition.lerp(newTarget, this.context.time.deltaTime / (duration * 5));
775
+ // During a programmatic transition (fitCamera / setLookTargetPosition with immediate: false),
776
+ // only clamp the destination. The look-target lerp (above) handles moving _controls.target
777
+ // towards the endpoint — we must not fight it by also lerping _controls.target here.
778
+ this._lookTargetEndPosition.clamp(min, max);
779
+ }
780
+ else {
781
+ // Interactive use (pan/orbit): smoothly push the target back into bounds
782
+ const targetVector = this._controls.target;
783
+ const newTarget = getTempVector(targetVector).clamp(min, max);
784
+ const duration = .1;
785
+ if (duration <= 0) targetVector.copy(newTarget);
786
+ else targetVector.lerp(newTarget, Math.min(1, this.context.time.deltaTime / duration));
773
787
  }
788
+
774
789
  if (debug) {
775
790
  Gizmos.DrawWireBox(boundsCenter, boundsHalfSize.multiplyScalar(2), 0xffaa00);
776
791
  }
@@ -847,7 +862,8 @@ export class OrbitControls extends Behaviour implements ICameraController {
847
862
  this._controls.update(this.context.time.deltaTime);
848
863
 
849
864
  if (debug) {
850
- Gizmos.DrawWireSphere(this._controls.target, 0.1, 0x00ff00);
865
+ const distance = this._controls.getDistance();
866
+ Gizmos.DrawWireSphere(this._controls.target, 0.01 * distance, 0x00ff00);
851
867
  }
852
868
  }
853
869
  }
@@ -1022,7 +1038,8 @@ export class OrbitControls extends Behaviour implements ICameraController {
1022
1038
 
1023
1039
  if (debug) {
1024
1040
  console.warn("OrbitControls: setLookTargetPosition", position, immediateOrDuration);
1025
- Gizmos.DrawWireSphere(this._lookTargetEndPosition, .2, 0xff0000, 2);
1041
+ const distance = this._controls.getDistance();
1042
+ Gizmos.DrawWireSphere(this._lookTargetEndPosition, 0.01 * distance, 0xff5500, 2);
1026
1043
  }
1027
1044
 
1028
1045
  if (immediateOrDuration === true) {
@@ -1086,13 +1103,16 @@ export class OrbitControls extends Behaviour implements ICameraController {
1086
1103
  // Adapted from https://discourse.threejs.org/t/camera-zoom-to-fit-object/936/24
1087
1104
  // Slower but better implementation that takes bones and exact vertex positions into account: https://github.com/google/model-viewer/blob/04e900c5027de8c5306fe1fe9627707f42811b05/packages/model-viewer/src/three-components/ModelScene.ts#L321
1088
1105
 
1089
- /**
1090
- * Fits the camera to show the objects provided (defaults to the scene if no objects are passed in)
1106
+ /**
1107
+ * Fits the camera to show the objects provided (defaults to the scene if no objects are passed in)
1091
1108
  * @param options The options for fitting the camera. Use to provide objects to fit to, fit direction and size and other settings.
1092
1109
  */
1093
1110
  fitCamera(options?: OrbitFitCameraOptions);
1094
- /** @deprecated Use fitCamera(options) */
1095
- fitCamera(objects?: Object3D | Array<Object3D>, options?: Omit<OrbitFitCameraOptions, "objects">);
1111
+ // Deprecated overload commented out: it accepted Object3D as first arg, which caused
1112
+ // TypeScript autocomplete to show Object3D properties (position, worldPosition, etc.)
1113
+ // instead of OrbitFitCameraOptions. The implementation still handles Object3D at runtime
1114
+ // for backwards-compat — use fitCamera({ objects: [...] }) instead.
1115
+ // fitCamera(objects?: Object3D | Array<Object3D>, options?: Omit<OrbitFitCameraOptions, "objects">);
1096
1116
  fitCamera(objectsOrOptions?: Object3D | Array<Object3D> | OrbitFitCameraOptions, options?: OrbitFitCameraOptions): void {
1097
1117
 
1098
1118
 
@@ -204,12 +204,19 @@ export class Rigidbody extends Behaviour implements IRigidbody {
204
204
 
205
205
  get isRigidbody() { return true; }
206
206
 
207
- /** When true the mass will be automatically calculated by the attached colliders */
207
+ /** When true the mass is automatically computed from the attached colliders using `mass = density × volume`.
208
+ * Each collider's {@link Collider.density} determines how heavy it contributes to the total mass.
209
+ * Disable to set mass explicitly via the `mass` property.
210
+ */
208
211
  @validate()
209
212
  autoMass: boolean = true;
210
213
 
211
- /** By default the mass will be automatically calculated (see `autoMass`) by the physics engine using the collider sizes
212
- * To set the mass manually you can either set the `mass` value or set `autoMass` to `false`
214
+ /** The mass of the rigidbody in kg (when `autoMass` is disabled).
215
+ * When `autoMass` is enabled, reading this returns the computed mass from `density × volume` of all attached colliders.
216
+ * Setting this property automatically disables `autoMass`.
217
+ *
218
+ * **Prefer using {@link Collider.density}** with `autoMass` enabled instead — density scales
219
+ * naturally with collider size, while explicit mass stays fixed regardless of shape changes.
213
220
  */
214
221
  @serializable()
215
222
  set mass(value: number) {
@@ -403,9 +410,16 @@ export class Rigidbody extends Behaviour implements IRigidbody {
403
410
  this.context.physics.engine?.removeBody(this);
404
411
  }
405
412
 
406
- onValidate() {
413
+ onValidate(property?: string) {
407
414
  this._propertiesChanged = true;
415
+ if (property === "autoMass" && !this.autoMass) {
416
+ if (isDevEnvironment() && !Rigidbody._didWarnAutoMass) {
417
+ Rigidbody._didWarnAutoMass = true;
418
+ console.warn("[Rigidbody] autoMass disabled — consider using Collider.density instead of setting mass explicitly. Density scales naturally with collider size.");
419
+ }
420
+ }
408
421
  }
422
+ private static _didWarnAutoMass = false;
409
423
 
410
424
  // need to do this right before updating physics to prevent rendered object glitching through physical bodies
411
425
  * beforePhysics() {
@@ -747,6 +747,9 @@ export class SceneSwitcher extends Behaviour {
747
747
  const openedEvt = new CustomEvent<LoadSceneEvent>("scene-opened", { detail: { scene: scene, switcher: this, index: index } });
748
748
  this.dispatchEvent(openedEvt);
749
749
  this.sceneLoaded?.invoke(this);
750
+ if (this._currentSceneAsset) {
751
+ this.context.events.emit("scene-content-changed", { source: this, object: this._currentSceneAsset });
752
+ }
750
753
  return true;
751
754
  }
752
755
  }
@@ -249,10 +249,10 @@ export class SeeThrough extends Behaviour {
249
249
  }
250
250
 
251
251
  const materials = renderer.sharedMaterials;// : this.rendererMaterials.get(renderer);
252
- if (!materials) return;
252
+ if (!materials?.length) return;
253
253
 
254
254
  const block = MaterialPropertyBlock.get(renderer.gameObject);
255
- const currentOpacity = (block.getOverride("opacity")?.value ?? materials[0].opacity ?? 1);
255
+ const currentOpacity = (block.getOverride("opacity")?.value ?? materials[0]?.opacity ?? 1);
256
256
 
257
257
  let newAlpha = Mathf.lerp(currentOpacity, targetAlpha, duration <= 0 ? 1 : this.context.time.deltaTime / duration);;
258
258
  if (newAlpha >= 0.99) newAlpha = 1;
@@ -34,8 +34,9 @@
34
34
  * @module Built-in Components
35
35
  */
36
36
 
37
+ export { AnimationBuilder, type AnimationKeyframe, type Tween, type AnimationInterpolation } from "./AnimationBuilder.js";
38
+ export { AnimatorControllerBuilder, type ConditionMode, type StateOptions, type TransitionOptions } from "./AnimatorController.builder.js";
37
39
  export * from "./codegen/components.js";
38
- export { AnimatorControllerBuilder, type ConditionMode, type StateOptions, type TransitionOptions } from "./AnimatorController.js";
39
40
  export { Collider } from "./Collider.js"; // export abstract type
40
41
  export { Behaviour, Component, GameObject } from "./Component.js";
41
42
 
@@ -3,21 +3,13 @@
3
3
  export class __Ignore {}
4
4
  export { AlignmentConstraint } from "../AlignmentConstraint.js";
5
5
  export { Animation } from "../Animation.js";
6
+ export { AnimationBuilder } from "../AnimationBuilder.js";
6
7
  export { Keyframe } from "../AnimationCurve.js";
7
8
  export { AnimationCurve } from "../AnimationCurve.js";
8
9
  export { Animator } from "../Animator.js";
9
- export { AnimatorControllerBuilder } from "../AnimatorController.js";
10
10
  export { AnimatorController } from "../AnimatorController.js";
11
11
  export { AudioListener } from "../AudioListener.js";
12
12
  export { AudioSource } from "../AudioSource.js";
13
- export { Avatar_POI } from "../avatar/Avatar_Brain_LookAt.js";
14
- export { Avatar_Brain_LookAt } from "../avatar/Avatar_Brain_LookAt.js";
15
- export { Avatar_MouthShapes } from "../avatar/Avatar_MouthShapes.js";
16
- export { Avatar_MustacheShake } from "../avatar/Avatar_MustacheShake.js";
17
- export { AvatarBlink_Simple } from "../avatar/AvatarBlink_Simple.js";
18
- export { AvatarEyeLook_Rotation } from "../avatar/AvatarEyeLook_Rotation.js";
19
- export { AvatarModel } from "../AvatarLoader.js";
20
- export { AvatarLoader } from "../AvatarLoader.js";
21
13
  export { AxesHelper } from "../AxesHelper.js";
22
14
  export { BasicIKConstraint } from "../BasicIKConstraint.js";
23
15
  export { BoxHelperComponent } from "../BoxHelperComponent.js";
@@ -168,11 +160,13 @@ export { PlayableDirector } from "../timeline/PlayableDirector.js";
168
160
  export { SignalAsset } from "../timeline/SignalAsset.js";
169
161
  export { SignalReceiverEvent } from "../timeline/SignalAsset.js";
170
162
  export { SignalReceiver } from "../timeline/SignalAsset.js";
171
- export { AnimationTrackHandler } from "../timeline/TimelineTracks.js";
172
- export { AudioTrackHandler } from "../timeline/TimelineTracks.js";
173
- export { MarkerTrackHandler } from "../timeline/TimelineTracks.js";
163
+ export { TimelineBuilder } from "../timeline/TimelineBuilder.js";
164
+ export { TimelineAnimationTrack } from "../timeline/TimelineTracks.js";
165
+ export { TimelineAudioTrack } from "../timeline/TimelineTracks.js";
166
+ export { TimelineMarkerTrack } from "../timeline/TimelineTracks.js";
174
167
  export { SignalTrackHandler } from "../timeline/TimelineTracks.js";
175
- export { ControlTrackHandler } from "../timeline/TimelineTracks.js";
168
+ export { TimelineActivationTrack } from "../timeline/TimelineTracks.js";
169
+ export { TimelineControlTrack } from "../timeline/TimelineTracks.js";
176
170
  export { TransformGizmo } from "../TransformGizmo.js";
177
171
  export { BaseUIComponent } from "../ui/BaseUIComponent.js";
178
172
  export { UIRootComponent } from "../ui/BaseUIComponent.js";
@@ -3,7 +3,7 @@ import { Euler, Material, Matrix4, Mesh, Object3D, Quaternion, Vector3 } from "t
3
3
 
4
4
  import { isDevEnvironment, showBalloonMessage, showBalloonWarning } from "../../../engine/debug/index.js";
5
5
  import { findObjectOfType } from "../../../engine/engine_components.js";
6
- import { hasProLicense } from "../../../engine/engine_license.js";
6
+ import { UFadk } from "../../../engine/engine_license.js";
7
7
  import { serializable } from "../../../engine/engine_serialization.js";
8
8
  import { getFormattedDate, Progress } from "../../../engine/engine_time_utils.js";
9
9
  import { DeviceUtilities, getParam } from "../../../engine/engine_utils.js";
@@ -277,7 +277,7 @@ export class USDZExporter extends Behaviour {
277
277
  let name = this.exportFileName ?? this.objectToExport?.name ?? this.name;
278
278
  name += "-" + getFormattedDate(); // seems iOS caches the file in some cases, this ensures we always have a fresh file
279
279
 
280
- if (!hasProLicense()) {
280
+ if (!UFadk()) {
281
281
  if (name !== "") name += "-";
282
282
  name += "MadeWithNeedle";
283
283
  }
@@ -682,7 +682,7 @@ export class USDZExporter extends Behaviour {
682
682
  if (debug)
683
683
  showBalloonMessage("Quicklook url: " + callToActionURL);
684
684
  if (callToActionURL) {
685
- if (!hasProLicense()) {
685
+ if (!UFadk()) {
686
686
  console.warn("Quicklook closed: custom redirects require a Needle Engine Pro license: https://needle.tools/pricing", callToActionURL)
687
687
  }
688
688
  else {
@@ -697,7 +697,7 @@ export class USDZExporter extends Behaviour {
697
697
  private buildQuicklookOverlay(): CustomBranding {
698
698
  const obj: CustomBranding = {};
699
699
  if (this.customBranding) Object.assign(obj, this.customBranding);
700
- if (!hasProLicense()) {
700
+ if (!UFadk()) {
701
701
  console.log("Custom Quicklook banner text requires pro license: https://needle.tools/pricing");
702
702
  obj.callToAction = "Close";
703
703
  obj.checkoutTitle = "🌵 Made with Needle";
@@ -4,7 +4,6 @@ import { isDevEnvironment } from '../../engine/debug/index.js';
4
4
  import { FrameEvent } from '../../engine/engine_context.js';
5
5
  import { isLocalNetwork } from '../../engine/engine_networking_utils.js';
6
6
  import { serializable } from '../../engine/engine_serialization.js';
7
- import type { GuidsMap } from '../../engine/engine_types.js';
8
7
  import { deepClone, delay, getParam } from '../../engine/engine_utils.js';
9
8
  import { Animator } from '../Animator.js';
10
9
  import { AudioListener } from '../AudioListener.js';
@@ -45,7 +44,7 @@ export enum ClipExtrapolation {
45
44
  };
46
45
 
47
46
  /** @internal */
48
- export type CreateTrackFunction = (director: PlayableDirector, track: Models.TrackModel) => Tracks.TrackHandler | undefined | null;
47
+ export type CreateTrackFunction = (director: PlayableDirector, track: Models.TrackModel) => Tracks.TimelineTrackHandler | undefined | null;
49
48
 
50
49
  /**
51
50
  * PlayableDirector is the main component for controlling timelines in Needle Engine.
@@ -115,7 +114,15 @@ export class PlayableDirector extends Behaviour {
115
114
  * The timeline asset containing tracks, clips, and markers that this director will play.
116
115
  * Assign a timeline asset exported from Unity or Blender to enable playback.
117
116
  */
118
- playableAsset?: Models.TimelineAssetModel;
117
+ @serializable()
118
+ get playableAsset(): Models.TimelineAssetModel | undefined { return this._playableAsset; }
119
+ set playableAsset(value: Models.TimelineAssetModel | undefined) {
120
+ if (this._playableAsset !== value) {
121
+ this._playableAsset = value;
122
+ this._needsGraphRebuild = true;
123
+ }
124
+ }
125
+ private _playableAsset?: Models.TimelineAssetModel;
119
126
 
120
127
  /**
121
128
  * When true, the timeline starts playing automatically when the component awakens.
@@ -123,7 +130,7 @@ export class PlayableDirector extends Behaviour {
123
130
  * @default false
124
131
  */
125
132
  @serializable()
126
- playOnAwake?: boolean;
133
+ playOnAwake: boolean = false;
127
134
 
128
135
  /**
129
136
  * Determines how the timeline behaves when it reaches the end of its duration.
@@ -207,14 +214,10 @@ export class PlayableDirector extends Behaviour {
207
214
  onEnable() {
208
215
  if (debug) console.log("[Timeline] OnEnable", this.name, this.playOnAwake);
209
216
 
210
- for (const track of this._audioTracks) {
211
- track.onEnable?.();
212
- }
213
- for (const track of this._customTracks) {
214
- track.onEnable?.();
215
- }
216
- for (const track of this._animationTracks) {
217
- track.onEnable?.();
217
+ for (const tracks of this._allTracks) {
218
+ for (const track of tracks) {
219
+ track.onEnable?.();
220
+ }
218
221
  }
219
222
  if (this.playOnAwake) {
220
223
  this.play();
@@ -265,6 +268,7 @@ export class PlayableDirector extends Behaviour {
265
268
  this.resolveBindings();
266
269
  this.updateTimelineDuration();
267
270
  this.setupAndCreateTrackHandlers();
271
+ this._needsGraphRebuild = false;
268
272
  }
269
273
 
270
274
  /**
@@ -273,6 +277,8 @@ export class PlayableDirector extends Behaviour {
273
277
  */
274
278
  async play() {
275
279
  if (!this.isValid()) return;
280
+ // Ensure graph is built (handles the case where playableAsset is assigned after awake)
281
+ if (this._needsGraphRebuild) this.rebuildGraph();
276
282
  const pauseChanged = this._isPaused == true;
277
283
  this._isPaused = false;
278
284
  if (this._isPlaying) return;
@@ -290,8 +296,13 @@ export class PlayableDirector extends Behaviour {
290
296
  await Promise.all(promises);
291
297
  if (!this._isPlaying) return;
292
298
  }
293
- while (this._audioTracks.length > 0 && this._isPlaying && !AudioSource.userInteractionRegistered && this.waitForAudio)
299
+ let ticks = 0;
300
+ while (this._audioTracks.length > 0 && this._isPlaying && !AudioSource.userInteractionRegistered && this.waitForAudio) {
301
+ if (ticks++ <= 0) {
302
+ if (isDevEnvironment() || debug) console.warn(`[Timeline] Waiting for user interaction to play audio... (timeline '${this.name}' will start as soon as user interacts with the page). To start the timeline immediately without waiting for user interaction, set 'waitForAudio' to false on the PlayableDirector component.`);
303
+ }
294
304
  await delay(200);
305
+ }
295
306
  }
296
307
  this.invokeStateChangedMethodsOnTracks();
297
308
  // Update timeline in LateUpdate to give other scripts time to react to the updated state
@@ -343,6 +354,7 @@ export class PlayableDirector extends Behaviour {
343
354
  * Evaluate the timeline at the current time. This is useful when you want to manually update the timeline e.g. when the timeline is paused and you set `time` to a new value.
344
355
  */
345
356
  evaluate() {
357
+ if (this._needsGraphRebuild) this.rebuildGraph();
346
358
  this.internalEvaluate(true);
347
359
  }
348
360
 
@@ -373,7 +385,7 @@ export class PlayableDirector extends Behaviour {
373
385
  /**
374
386
  * @returns all audio tracks of the timeline
375
387
  */
376
- get audioTracks(): Tracks.AudioTrackHandler[] {
388
+ get audioTracks(): Tracks.TimelineAudioTrack[] {
377
389
  return this._audioTracks;
378
390
  }
379
391
 
@@ -387,10 +399,24 @@ export class PlayableDirector extends Behaviour {
387
399
  /**
388
400
  * @returns all marker tracks of the timeline
389
401
  */
390
- get markerTracks(): Tracks.MarkerTrackHandler[] {
402
+ get markerTracks(): Tracks.TimelineMarkerTrack[] {
391
403
  return this._markerTracks;
392
404
  }
393
405
 
406
+ /**
407
+ * @returns all activation tracks of the timeline
408
+ */
409
+ get activationTracks(): Tracks.TimelineActivationTrack[] {
410
+ return this._activationTracks;
411
+ }
412
+
413
+ /**
414
+ * @returns all tracks of the timeline
415
+ */
416
+ get tracks(): ReadonlyArray<Tracks.TimelineTrackHandler> {
417
+ return this._allTracks.flat();
418
+ }
419
+
394
420
  /**
395
421
  * Iterates over all markers of the timeline, optionally filtering by type
396
422
  *
@@ -412,37 +438,37 @@ export class PlayableDirector extends Behaviour {
412
438
  }
413
439
 
414
440
 
415
- private _guidsMap?: GuidsMap;
416
- /** @internal */
417
- resolveGuids(map: GuidsMap) {
418
- this._guidsMap = map;
419
- }
420
441
 
421
442
  // INTERNALS
422
443
 
444
+ private _needsGraphRebuild: boolean = false;
423
445
  private _isPlaying: boolean = false;
424
446
  private _internalUpdateRoutine: any;
425
447
  private _isPaused: boolean = false;
426
448
  /** internal, true during the time stop() is being processed */
427
449
  private _isStopping: boolean = false;
450
+ /** @internal Whether the current evaluate() was triggered by user code (not the internal update loop or stop/pause). */
451
+ _isUserEvaluation: boolean = false;
428
452
  private _time: number = 0;
429
453
  private _duration: number = 0;
430
454
  private _weight: number = 1;
431
- private readonly _animationTracks: Array<Tracks.AnimationTrackHandler> = [];
432
- private readonly _audioTracks: Array<Tracks.AudioTrackHandler> = [];
455
+ private readonly _animationTracks: Array<Tracks.TimelineAnimationTrack> = [];
456
+ private readonly _audioTracks: Array<Tracks.TimelineAudioTrack> = [];
433
457
  private readonly _signalTracks: Array<Tracks.SignalTrackHandler> = [];
434
- private readonly _markerTracks: Array<Tracks.MarkerTrackHandler> = [];
435
- private readonly _controlTracks: Array<Tracks.ControlTrackHandler> = [];
436
- private readonly _customTracks: Array<Tracks.TrackHandler> = [];
458
+ private readonly _markerTracks: Array<Tracks.TimelineMarkerTrack> = [];
459
+ private readonly _controlTracks: Array<Tracks.TimelineControlTrack> = [];
460
+ private readonly _activationTracks: Array<Tracks.TimelineActivationTrack> = [];
461
+ private readonly _customTracks: Array<Tracks.TimelineTrackHandler> = [];
437
462
 
438
- private readonly _tracksArray: Array<Array<Tracks.TrackHandler>> = [];
439
- private get _allTracks(): Array<Array<Tracks.TrackHandler>> {
463
+ private readonly _tracksArray: Array<Array<Tracks.TimelineTrackHandler>> = [];
464
+ private get _allTracks(): Array<Array<Tracks.TimelineTrackHandler>> {
440
465
  this._tracksArray.length = 0;
441
466
  this._tracksArray.push(this._animationTracks);
442
467
  this._tracksArray.push(this._audioTracks);
443
468
  this._tracksArray.push(this._signalTracks);
444
469
  this._tracksArray.push(this._markerTracks);
445
470
  this._tracksArray.push(this._controlTracks);
471
+ this._tracksArray.push(this._activationTracks);
446
472
  this._tracksArray.push(this._customTracks);
447
473
  return this._tracksArray;
448
474
  }
@@ -503,60 +529,32 @@ export class PlayableDirector extends Behaviour {
503
529
 
504
530
  const time = this._time;
505
531
 
506
- for (const track of this.playableAsset!.tracks) {
507
- if (track.muted) continue;
508
- switch (track.type) {
509
- case Models.TrackType.Activation:
510
- // when the timeline is being disabled or stopped
511
- // then we want to leave objects active state as they were
512
- // see NE-3241
513
- // TODO: support all "post-playback-state" settings an activation track has, this is just "Leave as is"
514
- if (!called_by_user && !this._isPlaying) continue;
515
-
516
- for (let i = 0; i < track.outputs.length; i++) {
517
- const binding = track.outputs[i];
518
- if (typeof binding === "object") {
519
- let isActive: boolean = false;
520
- if (track.clips) {
521
- for (const clip of track.clips) {
522
- if (clip.start <= time && time <= clip.end) {
523
- isActive = true;
524
- }
525
- }
526
- }
527
- const obj = binding as Object3D;
528
- if (obj.visible !== undefined) {
529
- if (obj.visible !== isActive) {
530
- obj.visible = isActive;
531
- if (debug)
532
- console.warn(this.name, "set ActivationTrack-" + i, obj.name, isActive, time);
533
- }
534
- }
535
- }
536
- }
537
- break;
538
-
539
- }
540
- }
541
-
532
+ this._isUserEvaluation = called_by_user;
542
533
  for (const track of this._allTracks) {
543
534
  for (const handler of track) {
544
535
  // When timeline reaches the end "stop()" is called which is evaluating with time 0
545
536
  // We don't want to re-evaluate the animation then in case the timeline is blended with the Animator
546
537
  // e.g then the timeline animation at time 0 is 100% applied on top of the animator animation
547
-
548
- if (this._isStopping && handler instanceof Tracks.AnimationTrackHandler) {
538
+ if (this._isStopping && handler instanceof Tracks.TimelineAnimationTrack) {
549
539
  continue;
550
540
  }
551
541
  handler.evaluate(time);
552
542
  }
553
543
  }
544
+ this._isUserEvaluation = false;
554
545
  }
555
546
 
556
547
  private resolveBindings() {
557
548
  if (!this._clonedPlayableAsset) {
558
549
  this._clonedPlayableAsset = true;
559
- this.playableAsset = deepClone(this.playableAsset);
550
+ // Deep clone so each director owns its own copy of the asset data.
551
+ // Needed because NEEDLE_persistent_assets returns the same shared object for all references —
552
+ // without this, resolving guid strings below would mutate shared data.
553
+ // On instantiated clones this is skipped (_clonedPlayableAsset is copied as true)
554
+ // because the instantiate resolve system already created an independent copy.
555
+ this.playableAsset = deepClone(this.playableAsset, (_obj, _key, val) => {
556
+ return !(val?.isObject3D === true);
557
+ });
560
558
  }
561
559
 
562
560
  if (!this.playableAsset || !this.playableAsset.tracks) return;
@@ -569,15 +567,13 @@ export class PlayableDirector extends Behaviour {
569
567
 
570
568
  for (const track of this.playableAsset.tracks) {
571
569
  for (let i = track.outputs.length - 1; i >= 0; i--) {
572
- let binding = track.outputs[i];
570
+ const binding = track.outputs[i];
573
571
  if (typeof binding === "string") {
574
- if (this._guidsMap && this._guidsMap[binding])
575
- binding = this._guidsMap[binding];
576
572
  const obj = GameObject.findByGuid(binding, root);
577
573
  if (obj === null || typeof obj !== "object") {
578
574
  // if the binding is missing remove it to avoid unnecessary loops
579
575
  track.outputs.splice(i, 1);
580
- console.warn("Failed to resolve binding", binding, track.name, track.type);
576
+ console.warn(`[Timeline] Failed to resolve binding on track '${track.name}' of type '${track.type}' in director '${this.name}' with timeline asset '${this.playableAsset.name}' ('${binding}' not found in scene)`);
581
577
  }
582
578
  else {
583
579
  if (debug)
@@ -592,21 +588,20 @@ export class PlayableDirector extends Behaviour {
592
588
  continue;
593
589
  }
594
590
  // if the binding is missing remove it to avoid unnecessary loops
595
- if (track.type !== Models.TrackType.Audio && track.type !== Models.TrackType.Control && track.type !== Models.TrackType.Marker && track.type !== Models.TrackType.Signal)
596
- console.warn("Missing binding", binding, track.name, track.type, this.name, this.playableAsset.name);
591
+ if (track.type !== Models.TrackType.Audio && track.type !== Models.TrackType.Control && track.type !== Models.TrackType.Marker && track.type !== Models.TrackType.Signal) {
592
+ console.warn(`[Timeline] Missing binding on track '${track.name}' of type '${track.type}' in director '${this.name}' with timeline asset '${this.playableAsset.name}' (null binding)`);
593
+ }
597
594
  }
598
595
  }
599
596
  if (track.type === Models.TrackType.Control) {
600
597
  if (track.clips) {
601
598
  for (let i = 0; i < track.clips.length; i++) {
602
599
  const clip = track.clips[i];
603
- let binding = clip.asset.sourceObject;
600
+ const binding = clip.asset.sourceObject;
604
601
  if (typeof binding === "string") {
605
- if (this._guidsMap && this._guidsMap[binding])
606
- binding = this._guidsMap[binding];
607
602
  const obj = GameObject.findByGuid(binding, root);
608
603
  if (obj === null || typeof obj !== "object") {
609
- console.warn("Failed to resolve sourceObject binding", binding, track.name, clip);
604
+ console.warn(`[Timeline] Failed to resolve sourceObject binding on track '${track.name}' of type '${track.type}' in director '${this.name}' with timeline asset '${this.playableAsset.name}' ('${binding}' not found in scene)`);
610
605
  }
611
606
  else {
612
607
  if (debug)
@@ -649,6 +644,7 @@ export class PlayableDirector extends Behaviour {
649
644
  this._animationTracks.length = 0;
650
645
  this._audioTracks.length = 0;
651
646
  this._signalTracks.length = 0;
647
+ this._activationTracks.length = 0;
652
648
 
653
649
  if (!this.playableAsset) return;
654
650
  let audioListener: AudioListener | null = GameObject.findObjectOfType(AudioListener, this.context);
@@ -656,7 +652,7 @@ export class PlayableDirector extends Behaviour {
656
652
  const type = track.type;
657
653
  const registered = PlayableDirector.createTrackFunctions[type];
658
654
  if (registered !== null && registered !== undefined) {
659
- const res = registered(this, track) as Tracks.TrackHandler;
655
+ const res = registered(this, track) as Tracks.TimelineTrackHandler;
660
656
  if (typeof res.evaluate === "function") {
661
657
  res.director = this;
662
658
  res.track = track;
@@ -679,7 +675,7 @@ export class PlayableDirector extends Behaviour {
679
675
  }
680
676
  const animationClips = binding?.gameObject?.animations;
681
677
  if (animationClips) {
682
- const handler = new Tracks.AnimationTrackHandler();
678
+ const handler = new Tracks.TimelineAnimationTrack();
683
679
  handler.trackOffset = track.trackOffset;
684
680
  handler.director = this;
685
681
  handler.track = track;
@@ -729,7 +725,7 @@ export class PlayableDirector extends Behaviour {
729
725
  }
730
726
  else if (track.type === Models.TrackType.Audio) {
731
727
  if (!track.clips || track.clips.length <= 0) continue;
732
- const audio = new Tracks.AudioTrackHandler();
728
+ const audio = new Tracks.TimelineAudioTrack();
733
729
  audio.director = this;
734
730
  audio.track = track;
735
731
  audio.audioSource = track.outputs.find(o => o instanceof AudioSource) as AudioSource;
@@ -753,7 +749,7 @@ export class PlayableDirector extends Behaviour {
753
749
  signalHandler.director = this;
754
750
  signalHandler.track = track;
755
751
 
756
- const markerHandler: Tracks.MarkerTrackHandler = new Tracks.MarkerTrackHandler();
752
+ const markerHandler: Tracks.TimelineMarkerTrack = new Tracks.TimelineMarkerTrack();
757
753
  markerHandler.director = this;
758
754
  markerHandler.track = track;
759
755
 
@@ -799,7 +795,7 @@ export class PlayableDirector extends Behaviour {
799
795
  this._signalTracks.push(handler);
800
796
  }
801
797
  else if (track.type === Models.TrackType.Control) {
802
- const handler = new Tracks.ControlTrackHandler();
798
+ const handler = new Tracks.TimelineControlTrack();
803
799
  handler.director = this;
804
800
  handler.track = track;
805
801
  if (track.clips) {
@@ -810,6 +806,12 @@ export class PlayableDirector extends Behaviour {
810
806
  handler.resolveSourceObjects(this.context);
811
807
  this._controlTracks.push(handler);
812
808
  }
809
+ else if (track.type === Models.TrackType.Activation) {
810
+ const handler = new Tracks.TimelineActivationTrack();
811
+ handler.director = this;
812
+ handler.track = track;
813
+ this._activationTracks.push(handler);
814
+ }
813
815
  }
814
816
  }
815
817
 
@@ -1,4 +1,5 @@
1
1
  import { serializable } from "../../engine/engine_serialization_decorator.js";
2
+ import { ISignalReceiver } from "../../engine/engine_types.js";
2
3
  import { getParam } from "../../engine/engine_utils.js";
3
4
  import { Behaviour } from "../Component.js";
4
5
  import { EventList } from "../EventList.js";
@@ -33,7 +34,9 @@ export class SignalReceiverEvent {
33
34
  * @category Animation and Sequencing
34
35
  * @group Components
35
36
  */
36
- export class SignalReceiver extends Behaviour {
37
+ export class SignalReceiver extends Behaviour implements ISignalReceiver {
38
+
39
+ readonly isSignalReceiver = true;
37
40
 
38
41
  private static receivers: { [key: string]: SignalReceiver[] } = {};
39
42