@needle-tools/engine 5.1.0-alpha.3 → 5.1.0-alpha.4

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 (178) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/SKILL.md +4 -1
  3. package/components.needle.json +1 -1
  4. package/dist/needle-engine.bundle-AjVIot3d.min.js +1733 -0
  5. package/dist/{needle-engine.bundle-DF01sSGQ.js → needle-engine.bundle-B7cqsI4c.js} +9149 -8867
  6. package/dist/{needle-engine.bundle-C-ixARur.umd.cjs → needle-engine.bundle-DQCuBTVp.umd.cjs} +154 -154
  7. package/dist/needle-engine.d.ts +754 -199
  8. package/dist/needle-engine.js +583 -584
  9. package/dist/needle-engine.min.js +1 -1
  10. package/dist/needle-engine.umd.cjs +1 -1
  11. package/lib/engine/api.d.ts +3 -0
  12. package/lib/engine/api.js +2 -0
  13. package/lib/engine/api.js.map +1 -1
  14. package/lib/engine/codegen/register_types.js +2 -10
  15. package/lib/engine/codegen/register_types.js.map +1 -1
  16. package/lib/engine/engine_context.js +2 -1
  17. package/lib/engine/engine_context.js.map +1 -1
  18. package/lib/engine/engine_disposable.d.ts +172 -0
  19. package/lib/engine/engine_disposable.js +136 -0
  20. package/lib/engine/engine_disposable.js.map +1 -0
  21. package/lib/engine/engine_gameobject.d.ts +1 -10
  22. package/lib/engine/engine_gameobject.js +20 -118
  23. package/lib/engine/engine_gameobject.js.map +1 -1
  24. package/lib/engine/engine_gltf_builtin_components.js +7 -69
  25. package/lib/engine/engine_gltf_builtin_components.js.map +1 -1
  26. package/lib/engine/engine_instantiate_resolve.d.ts +42 -0
  27. package/lib/engine/engine_instantiate_resolve.js +372 -0
  28. package/lib/engine/engine_instantiate_resolve.js.map +1 -0
  29. package/lib/engine/engine_mainloop_utils.js +2 -2
  30. package/lib/engine/engine_mainloop_utils.js.map +1 -1
  31. package/lib/engine/engine_networking.d.ts +51 -37
  32. package/lib/engine/engine_networking.js +132 -82
  33. package/lib/engine/engine_networking.js.map +1 -1
  34. package/lib/engine/engine_networking.transport.websocket.d.ts +15 -0
  35. package/lib/engine/engine_networking.transport.websocket.js +38 -0
  36. package/lib/engine/engine_networking.transport.websocket.js.map +1 -0
  37. package/lib/engine/engine_networking_instantiate.js +2 -2
  38. package/lib/engine/engine_networking_instantiate.js.map +1 -1
  39. package/lib/engine/engine_networking_types.d.ts +39 -1
  40. package/lib/engine/engine_networking_types.js +7 -0
  41. package/lib/engine/engine_networking_types.js.map +1 -1
  42. package/lib/engine/engine_physics_rapier.d.ts +11 -3
  43. package/lib/engine/engine_physics_rapier.js +88 -25
  44. package/lib/engine/engine_physics_rapier.js.map +1 -1
  45. package/lib/engine/engine_serialization_builtin_serializer.js +1 -5
  46. package/lib/engine/engine_serialization_builtin_serializer.js.map +1 -1
  47. package/lib/engine/engine_serialization_core.d.ts +1 -0
  48. package/lib/engine/engine_serialization_core.js +7 -0
  49. package/lib/engine/engine_serialization_core.js.map +1 -1
  50. package/lib/engine/engine_types.d.ts +19 -11
  51. package/lib/engine/engine_types.js +1 -1
  52. package/lib/engine/engine_types.js.map +1 -1
  53. package/lib/engine/engine_util_decorator.js +7 -2
  54. package/lib/engine/engine_util_decorator.js.map +1 -1
  55. package/lib/engine/engine_utils.d.ts +1 -1
  56. package/lib/engine/engine_utils.js +19 -5
  57. package/lib/engine/engine_utils.js.map +1 -1
  58. package/lib/engine-components/Animator.d.ts +6 -0
  59. package/lib/engine-components/Animator.js +17 -12
  60. package/lib/engine-components/Animator.js.map +1 -1
  61. package/lib/engine-components/AnimatorController.builder.d.ts +113 -0
  62. package/lib/engine-components/AnimatorController.builder.js +195 -0
  63. package/lib/engine-components/AnimatorController.builder.js.map +1 -0
  64. package/lib/engine-components/AnimatorController.d.ts +2 -119
  65. package/lib/engine-components/AnimatorController.js +31 -232
  66. package/lib/engine-components/AnimatorController.js.map +1 -1
  67. package/lib/engine-components/Collider.d.ts +18 -9
  68. package/lib/engine-components/Collider.js +61 -14
  69. package/lib/engine-components/Collider.js.map +1 -1
  70. package/lib/engine-components/Component.d.ts +72 -9
  71. package/lib/engine-components/Component.js +114 -10
  72. package/lib/engine-components/Component.js.map +1 -1
  73. package/lib/engine-components/DragControls.js +0 -7
  74. package/lib/engine-components/DragControls.js.map +1 -1
  75. package/lib/engine-components/EventList.d.ts +31 -9
  76. package/lib/engine-components/EventList.js +37 -76
  77. package/lib/engine-components/EventList.js.map +1 -1
  78. package/lib/engine-components/Joints.d.ts +4 -2
  79. package/lib/engine-components/Joints.js +19 -3
  80. package/lib/engine-components/Joints.js.map +1 -1
  81. package/lib/engine-components/Light.js +9 -1
  82. package/lib/engine-components/Light.js.map +1 -1
  83. package/lib/engine-components/RigidBody.d.ts +12 -4
  84. package/lib/engine-components/RigidBody.js +18 -4
  85. package/lib/engine-components/RigidBody.js.map +1 -1
  86. package/lib/engine-components/api.d.ts +1 -1
  87. package/lib/engine-components/api.js +1 -1
  88. package/lib/engine-components/api.js.map +1 -1
  89. package/lib/engine-components/codegen/components.d.ts +3 -9
  90. package/lib/engine-components/codegen/components.js +3 -9
  91. package/lib/engine-components/codegen/components.js.map +1 -1
  92. package/lib/engine-components/timeline/PlayableDirector.d.ts +16 -6
  93. package/lib/engine-components/timeline/PlayableDirector.js +70 -62
  94. package/lib/engine-components/timeline/PlayableDirector.js.map +1 -1
  95. package/lib/engine-components/timeline/SignalAsset.d.ts +3 -1
  96. package/lib/engine-components/timeline/SignalAsset.js +1 -0
  97. package/lib/engine-components/timeline/SignalAsset.js.map +1 -1
  98. package/lib/engine-components/timeline/TimelineBuilder.d.ts +247 -0
  99. package/lib/engine-components/timeline/TimelineBuilder.js +400 -0
  100. package/lib/engine-components/timeline/TimelineBuilder.js.map +1 -0
  101. package/lib/engine-components/timeline/TimelineModels.d.ts +2 -1
  102. package/lib/engine-components/timeline/TimelineModels.js +3 -0
  103. package/lib/engine-components/timeline/TimelineModels.js.map +1 -1
  104. package/lib/engine-components/timeline/TimelineTracks.d.ts +23 -0
  105. package/lib/engine-components/timeline/TimelineTracks.js +71 -13
  106. package/lib/engine-components/timeline/TimelineTracks.js.map +1 -1
  107. package/lib/engine-components/timeline/index.d.ts +2 -1
  108. package/lib/engine-components/timeline/index.js +2 -0
  109. package/lib/engine-components/timeline/index.js.map +1 -1
  110. package/package.json +2 -83
  111. package/plugins/common/license.js +5 -2
  112. package/plugins/common/worker.js +9 -4
  113. package/plugins/vite/dependencies.js +1 -11
  114. package/plugins/vite/dependency-watcher.js +2 -2
  115. package/plugins/vite/editor-connection.js +3 -3
  116. package/plugins/vite/reload.js +1 -1
  117. package/plugins/vite/server.js +2 -1
  118. package/src/engine/api.ts +4 -0
  119. package/src/engine/codegen/register_types.ts +2 -10
  120. package/src/engine/engine_context.ts +2 -1
  121. package/src/engine/engine_disposable.ts +214 -0
  122. package/src/engine/engine_gameobject.ts +52 -157
  123. package/src/engine/engine_gltf_builtin_components.ts +7 -76
  124. package/src/engine/engine_instantiate_resolve.ts +407 -0
  125. package/src/engine/engine_mainloop_utils.ts +2 -2
  126. package/src/engine/engine_networking.transport.websocket.ts +45 -0
  127. package/src/engine/engine_networking.ts +161 -137
  128. package/src/engine/engine_networking_instantiate.ts +2 -2
  129. package/src/engine/engine_networking_types.ts +41 -1
  130. package/src/engine/engine_physics_rapier.ts +82 -27
  131. package/src/engine/engine_serialization_builtin_serializer.ts +1 -6
  132. package/src/engine/engine_serialization_core.ts +9 -0
  133. package/src/engine/engine_types.ts +24 -15
  134. package/src/engine/engine_util_decorator.ts +7 -2
  135. package/src/engine/engine_utils.ts +16 -5
  136. package/src/engine-components/Animator.ts +18 -11
  137. package/src/engine-components/AnimatorController.builder.ts +261 -0
  138. package/src/engine-components/AnimatorController.ts +19 -291
  139. package/src/engine-components/Collider.ts +66 -18
  140. package/src/engine-components/Component.ts +118 -20
  141. package/src/engine-components/DragControls.ts +0 -9
  142. package/src/engine-components/EventList.ts +45 -83
  143. package/src/engine-components/Joints.ts +20 -4
  144. package/src/engine-components/Light.ts +10 -2
  145. package/src/engine-components/RigidBody.ts +18 -4
  146. package/src/engine-components/api.ts +1 -1
  147. package/src/engine-components/codegen/components.ts +3 -9
  148. package/src/engine-components/timeline/PlayableDirector.ts +67 -65
  149. package/src/engine-components/timeline/SignalAsset.ts +4 -1
  150. package/src/engine-components/timeline/TimelineBuilder.ts +564 -0
  151. package/src/engine-components/timeline/TimelineModels.ts +5 -1
  152. package/src/engine-components/timeline/TimelineTracks.ts +74 -13
  153. package/src/engine-components/timeline/index.ts +2 -1
  154. package/dist/needle-engine.bundle-CHmXdnE1.min.js +0 -1733
  155. package/lib/engine-components/AvatarLoader.d.ts +0 -80
  156. package/lib/engine-components/AvatarLoader.js +0 -232
  157. package/lib/engine-components/AvatarLoader.js.map +0 -1
  158. package/lib/engine-components/avatar/AvatarBlink_Simple.d.ts +0 -11
  159. package/lib/engine-components/avatar/AvatarBlink_Simple.js +0 -77
  160. package/lib/engine-components/avatar/AvatarBlink_Simple.js.map +0 -1
  161. package/lib/engine-components/avatar/AvatarEyeLook_Rotation.d.ts +0 -14
  162. package/lib/engine-components/avatar/AvatarEyeLook_Rotation.js +0 -69
  163. package/lib/engine-components/avatar/AvatarEyeLook_Rotation.js.map +0 -1
  164. package/lib/engine-components/avatar/Avatar_Brain_LookAt.d.ts +0 -29
  165. package/lib/engine-components/avatar/Avatar_Brain_LookAt.js +0 -122
  166. package/lib/engine-components/avatar/Avatar_Brain_LookAt.js.map +0 -1
  167. package/lib/engine-components/avatar/Avatar_MouthShapes.d.ts +0 -15
  168. package/lib/engine-components/avatar/Avatar_MouthShapes.js +0 -80
  169. package/lib/engine-components/avatar/Avatar_MouthShapes.js.map +0 -1
  170. package/lib/engine-components/avatar/Avatar_MustacheShake.d.ts +0 -9
  171. package/lib/engine-components/avatar/Avatar_MustacheShake.js +0 -30
  172. package/lib/engine-components/avatar/Avatar_MustacheShake.js.map +0 -1
  173. package/src/engine-components/AvatarLoader.ts +0 -264
  174. package/src/engine-components/avatar/AvatarBlink_Simple.ts +0 -70
  175. package/src/engine-components/avatar/AvatarEyeLook_Rotation.ts +0 -64
  176. package/src/engine-components/avatar/Avatar_Brain_LookAt.ts +0 -140
  177. package/src/engine-components/avatar/Avatar_MouthShapes.ts +0 -84
  178. package/src/engine-components/avatar/Avatar_MustacheShake.ts +0 -32
@@ -1,12 +1,12 @@
1
1
  import { CameraHelper, Color, DirectionalLight, DirectionalLightHelper, Light as ThreeLight, OrthographicCamera, PointLight, SpotLight, Vector3 } from "three";
2
2
 
3
3
  import { serializable } from "../engine/engine_serialization_decorator.js";
4
- import { FrameEvent } from "../engine/engine_setup.js";
5
4
  import { setWorldPositionXYZ } from "../engine/engine_three_utils.js";
6
5
  import type { ILight } from "../engine/engine_types.js";
7
6
  import { getParam } from "../engine/engine_utils.js";
8
7
  import type { NeedleXREventArgs } from "../engine/xr/api.js";
9
8
  import { Behaviour, GameObject } from "./Component.js";
9
+ import { isDevEnvironment } from "../engine/debug/index.js";
10
10
 
11
11
  // https://threejs.org/examples/webgl_shadowmap_csm.html
12
12
 
@@ -135,7 +135,10 @@ export class Light extends Behaviour implements ILight {
135
135
  this._type = value;
136
136
  break;
137
137
  default:
138
- throw new Error("Invalid light type: " + value);
138
+ if (debug || isDevEnvironment())
139
+ console.warn(`[Light] Unsupported light type: ${LightType[value]} (${value}) on '${this.name}'`);
140
+ this._type = "unsupported" as any;
141
+ break;
139
142
  }
140
143
  }
141
144
  private _type: ILight["type"] = "point";
@@ -474,6 +477,11 @@ export class Light extends Behaviour implements ILight {
474
477
  // const pointHelper = new PointLightHelper(pointLight, this.range, this.color);
475
478
  // scene.add(pointHelper);
476
479
  break;
480
+
481
+ default:
482
+ if (debug) console.warn(`[Light] Unsupported light type: ${LightType[this.type as any]} (${this.type}) on '${this.name}'`);
483
+ break;
484
+
477
485
  }
478
486
  }
479
487
 
@@ -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() {
@@ -34,7 +34,7 @@
34
34
  * @module Built-in Components
35
35
  */
36
36
 
37
- export { AnimatorControllerBuilder, type ConditionMode, type StateOptions, type TransitionOptions } from "./AnimatorController.js";
37
+ export { AnimatorControllerBuilder, type ConditionMode, type StateOptions, type TransitionOptions } from "./AnimatorController.builder.js";
38
38
  export * from "./codegen/components.js";
39
39
  export { Collider } from "./Collider.js"; // export abstract type
40
40
  export { Behaviour, Component, GameObject } from "./Component.js";
@@ -6,18 +6,10 @@ export { Animation } from "../Animation.js";
6
6
  export { Keyframe } from "../AnimationCurve.js";
7
7
  export { AnimationCurve } from "../AnimationCurve.js";
8
8
  export { Animator } from "../Animator.js";
9
- export { AnimatorControllerBuilder } from "../AnimatorController.js";
9
+ export { AnimatorControllerBuilder } from "../AnimatorController.builder.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,10 +160,12 @@ 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";
163
+ export { TimelineBuilder } from "../timeline/TimelineBuilder.js";
171
164
  export { AnimationTrackHandler } from "../timeline/TimelineTracks.js";
172
165
  export { AudioTrackHandler } from "../timeline/TimelineTracks.js";
173
166
  export { MarkerTrackHandler } from "../timeline/TimelineTracks.js";
174
167
  export { SignalTrackHandler } from "../timeline/TimelineTracks.js";
168
+ export { ActivationTrackHandler } from "../timeline/TimelineTracks.js";
175
169
  export { ControlTrackHandler } from "../timeline/TimelineTracks.js";
176
170
  export { TransformGizmo } from "../TransformGizmo.js";
177
171
  export { BaseUIComponent } from "../ui/BaseUIComponent.js";
@@ -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';
@@ -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
 
@@ -391,6 +403,20 @@ export class PlayableDirector extends Behaviour {
391
403
  return this._markerTracks;
392
404
  }
393
405
 
406
+ /**
407
+ * @returns all activation tracks of the timeline
408
+ */
409
+ get activationTracks(): Tracks.ActivationTrackHandler[] {
410
+ return this._activationTracks;
411
+ }
412
+
413
+ /**
414
+ * @returns all tracks of the timeline
415
+ */
416
+ get tracks(): ReadonlyArray<Tracks.TrackHandler> {
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,19 +438,17 @@ 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;
@@ -433,6 +457,7 @@ export class PlayableDirector extends Behaviour {
433
457
  private readonly _signalTracks: Array<Tracks.SignalTrackHandler> = [];
434
458
  private readonly _markerTracks: Array<Tracks.MarkerTrackHandler> = [];
435
459
  private readonly _controlTracks: Array<Tracks.ControlTrackHandler> = [];
460
+ private readonly _activationTracks: Array<Tracks.ActivationTrackHandler> = [];
436
461
  private readonly _customTracks: Array<Tracks.TrackHandler> = [];
437
462
 
438
463
  private readonly _tracksArray: Array<Array<Tracks.TrackHandler>> = [];
@@ -443,6 +468,7 @@ export class PlayableDirector extends Behaviour {
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
538
  if (this._isStopping && handler instanceof Tracks.AnimationTrackHandler) {
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);
@@ -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.ActivationTrackHandler();
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