@needle-tools/engine 3.1.0-alpha.2 → 3.2.0-alpha

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 (88) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/needle-engine.js +9024 -8814
  3. package/dist/needle-engine.min.js +264 -264
  4. package/dist/needle-engine.umd.cjs +266 -266
  5. package/lib/engine/codegen/register_types.js +2 -0
  6. package/lib/engine/codegen/register_types.js.map +1 -1
  7. package/lib/engine/debug/debug_overlay.js +2 -0
  8. package/lib/engine/debug/debug_overlay.js.map +1 -1
  9. package/lib/engine/engine_context_registry.d.ts +2 -0
  10. package/lib/engine/engine_context_registry.js +3 -0
  11. package/lib/engine/engine_context_registry.js.map +1 -1
  12. package/lib/engine/engine_gltf_builtin_components.js +2 -0
  13. package/lib/engine/engine_gltf_builtin_components.js.map +1 -1
  14. package/lib/engine/engine_license.js +1 -1
  15. package/lib/engine/engine_license.js.map +1 -1
  16. package/lib/engine/engine_lightdata.js +1 -1
  17. package/lib/engine/engine_lightdata.js.map +1 -1
  18. package/lib/engine/engine_networking_auto.d.ts +3 -3
  19. package/lib/engine/engine_networking_auto.js +7 -3
  20. package/lib/engine/engine_networking_auto.js.map +1 -1
  21. package/lib/engine/engine_rendererdata.d.ts +1 -0
  22. package/lib/engine/engine_rendererdata.js +39 -15
  23. package/lib/engine/engine_rendererdata.js.map +1 -1
  24. package/lib/engine/engine_serialization_builtin_serializer.js +10 -1
  25. package/lib/engine/engine_serialization_builtin_serializer.js.map +1 -1
  26. package/lib/engine/extensions/NEEDLE_lighting_settings.js +14 -11
  27. package/lib/engine/extensions/NEEDLE_lighting_settings.js.map +1 -1
  28. package/lib/engine/extensions/NEEDLE_lightmaps.js.map +1 -1
  29. package/lib/engine/extensions/NEEDLE_progressive.js +11 -4
  30. package/lib/engine/extensions/NEEDLE_progressive.js.map +1 -1
  31. package/lib/engine/extensions/extensions.js +4 -0
  32. package/lib/engine/extensions/extensions.js.map +1 -1
  33. package/lib/engine-components/AudioSource.d.ts +1 -0
  34. package/lib/engine-components/AudioSource.js +6 -3
  35. package/lib/engine-components/AudioSource.js.map +1 -1
  36. package/lib/engine-components/Camera.js +1 -1
  37. package/lib/engine-components/Camera.js.map +1 -1
  38. package/lib/engine-components/Component.js.map +1 -1
  39. package/lib/engine-components/Renderer.d.ts +5 -0
  40. package/lib/engine-components/Renderer.js +64 -17
  41. package/lib/engine-components/Renderer.js.map +1 -1
  42. package/lib/engine-components/RendererLightmap.d.ts +1 -2
  43. package/lib/engine-components/RendererLightmap.js +8 -10
  44. package/lib/engine-components/RendererLightmap.js.map +1 -1
  45. package/lib/engine-components/SceneSwitcher.d.ts +32 -0
  46. package/lib/engine-components/SceneSwitcher.js +228 -0
  47. package/lib/engine-components/SceneSwitcher.js.map +1 -0
  48. package/lib/engine-components/ScreenCapture.js +1 -0
  49. package/lib/engine-components/ScreenCapture.js.map +1 -1
  50. package/lib/engine-components/VideoPlayer.d.ts +2 -0
  51. package/lib/engine-components/VideoPlayer.js +7 -3
  52. package/lib/engine-components/VideoPlayer.js.map +1 -1
  53. package/lib/engine-components/XRFlag.js +3 -0
  54. package/lib/engine-components/XRFlag.js.map +1 -1
  55. package/lib/engine-components/codegen/components.d.ts +1 -0
  56. package/lib/engine-components/codegen/components.js +1 -0
  57. package/lib/engine-components/codegen/components.js.map +1 -1
  58. package/lib/engine-components/timeline/PlayableDirector.js +0 -2
  59. package/lib/engine-components/timeline/PlayableDirector.js.map +1 -1
  60. package/lib/engine-components/timeline/TimelineTracks.js +9 -3
  61. package/lib/engine-components/timeline/TimelineTracks.js.map +1 -1
  62. package/lib/tsconfig.tsbuildinfo +1 -1
  63. package/package.json +1 -1
  64. package/src/engine/codegen/register_types.js +2 -0
  65. package/src/engine/debug/debug_overlay.ts +1 -0
  66. package/src/engine/engine_context_registry.ts +3 -0
  67. package/src/engine/engine_gltf_builtin_components.ts +3 -0
  68. package/src/engine/engine_license.ts +1 -1
  69. package/src/engine/engine_lightdata.ts +1 -1
  70. package/src/engine/engine_networking_auto.ts +13 -6
  71. package/src/engine/engine_rendererdata.ts +35 -16
  72. package/src/engine/engine_serialization_builtin_serializer.ts +10 -1
  73. package/src/engine/extensions/NEEDLE_lighting_settings.ts +15 -13
  74. package/src/engine/extensions/NEEDLE_lightmaps.ts +3 -2
  75. package/src/engine/extensions/NEEDLE_progressive.ts +9 -3
  76. package/src/engine/extensions/extensions.ts +6 -0
  77. package/src/engine-components/AudioSource.ts +6 -3
  78. package/src/engine-components/Camera.ts +1 -1
  79. package/src/engine-components/Component.ts +0 -1
  80. package/src/engine-components/Renderer.ts +69 -20
  81. package/src/engine-components/RendererLightmap.ts +7 -11
  82. package/src/engine-components/SceneSwitcher.ts +224 -0
  83. package/src/engine-components/ScreenCapture.ts +1 -0
  84. package/src/engine-components/VideoPlayer.ts +16 -11
  85. package/src/engine-components/XRFlag.ts +3 -0
  86. package/src/engine-components/codegen/components.ts +1 -0
  87. package/src/engine-components/timeline/PlayableDirector.ts +0 -1
  88. package/src/engine-components/timeline/TimelineTracks.ts +9 -3
@@ -14,7 +14,7 @@ export class RendererLightmap {
14
14
  set lightmap(tex: Texture | null) {
15
15
  if (tex !== this.lightmapTexture) {
16
16
  this.lightmapTexture = tex;
17
- this.setupLightmap();
17
+ this.applyLightmap();
18
18
  }
19
19
  }
20
20
 
@@ -27,8 +27,6 @@ export class RendererLightmap {
27
27
  private lightmapScaleOffsetUniform = { value: new THREE.Vector4(1, 1, 0, 0) };
28
28
  private lightmapUniform: { value: THREE.Texture | null } = { value: null };
29
29
 
30
- private beforeRenderCallback?: OnBeforeRenderCallback;
31
-
32
30
  constructor(gameObject: GameObject, context: Context) {
33
31
  this.gameObject = gameObject;
34
32
  this.context = context;
@@ -44,20 +42,19 @@ export class RendererLightmap {
44
42
 
45
43
  const debugLightmaps = debug;
46
44
  if (debugLightmaps) this.setLightmapDebugMaterial();
47
- this.setupLightmap();
45
+ this.applyLightmap();
48
46
  }
49
47
 
50
48
  bindOnBeforeRender() {
51
- this.beforeRenderCallback = this.onBeforeRenderThreeComplete.bind(this);
52
- this.context.addBeforeRenderListener(this.gameObject, this.beforeRenderCallback);
53
- // this.gameObject.onBeforeRender = this.onBeforeRenderThreeComplete.bind(this);
49
+ this.context.removeBeforeRenderListener(this.gameObject, this.onBeforeRenderThreeComplete);
50
+ this.context.addBeforeRenderListener(this.gameObject, this.onBeforeRenderThreeComplete);
54
51
  }
55
52
 
56
- private onBeforeRenderThreeComplete(_renderer, _scene, _camera, _geometry, material, _group) {
53
+ private onBeforeRenderThreeComplete = (_renderer, _scene, _camera, _geometry, material, _group) => {
57
54
  this.onBeforeRenderThree(material);
58
55
  }
59
56
 
60
- private setupLightmap() {
57
+ private applyLightmap() {
61
58
 
62
59
  if (this.gameObject.type === "Object3D") {
63
60
  // console.warn("Can not add lightmap. Is this object missing a renderer?");
@@ -65,13 +62,12 @@ export class RendererLightmap {
65
62
  }
66
63
 
67
64
  if (this.gameObject.type === "Group") {
68
- console.warn("Lightmap on multimaterial object is not supported yet... please ask kindly for implementation.");
65
+ console.warn("Lightmap on multimaterial object is not supported yet... please open a feature request on https://github.com/needle-tools/needle-engine-support if your project requires it");
69
66
  return;
70
67
  }
71
68
 
72
69
  console.assert(this.gameObject.type === "Mesh", "Lightmap only works on meshes", this);
73
70
 
74
-
75
71
  const mesh = this.gameObject as unknown as THREE.Mesh;
76
72
  // TODO: ensure uv2 exists
77
73
  if (!mesh.geometry.getAttribute("uv2"))
@@ -0,0 +1,224 @@
1
+ import { AssetReference } from "../engine/engine_addressables";
2
+ import { NeedleEngineHTMLElement } from "../engine/engine_element";
3
+ import { InputEvents } from "../engine/engine_input";
4
+ import { isLocalNetwork } from "../engine/engine_networking_utils";
5
+ import { ContextEvent, ContextRegistry } from "../engine/engine_context_registry";
6
+ import { getParam, isMobileDevice, setParamWithoutReload } from "../engine/engine_utils";
7
+ import { serializable } from "../engine/engine_serialization";
8
+ import { Behaviour, GameObject } from "./Component";
9
+
10
+ const ENGINE_ELEMENT_SCENE_ATTRIBUTE_NAME = "scene";
11
+
12
+ ContextRegistry.registerCallback(ContextEvent.ContextRegistered, _ => {
13
+ if (!NeedleEngineHTMLElement.observedAttributes.includes(ENGINE_ELEMENT_SCENE_ATTRIBUTE_NAME))
14
+ NeedleEngineHTMLElement.observedAttributes.push(ENGINE_ELEMENT_SCENE_ATTRIBUTE_NAME);
15
+ });
16
+
17
+ const couldNotLoadScenePromise = Promise.resolve(false);
18
+
19
+ export class SceneSwitcher extends Behaviour {
20
+
21
+ @serializable(AssetReference)
22
+ scenes!: AssetReference[];
23
+
24
+ /** the url parameter that is set/used to store the currently loaded scene in, set to "" to disable */
25
+ @serializable()
26
+ queryParameterName: string = "scene";
27
+
28
+ /** when enabled the new scene is pushed to the browser navigation history, only works with a valid query parameter set */
29
+ @serializable()
30
+ useHistory: boolean = true;
31
+
32
+ /** when enabled you can switch between scenes using keyboard left, right, A and D or number keys */
33
+ @serializable()
34
+ useKeyboard: boolean = true;
35
+
36
+ /** when enabled you can switch between scenes using swipe (mobile only) */
37
+ @serializable()
38
+ useSwipe: boolean = true;
39
+
40
+ private _currentIndex: number = -1;
41
+ private _currentScene: AssetReference | undefined = undefined;
42
+ private _engineElementOverserver: MutationObserver | undefined = undefined;
43
+
44
+ start() {
45
+ if (!this.tryLoadFromQueryParam()) {
46
+ const value = this.context.domElement.getAttribute(ENGINE_ELEMENT_SCENE_ATTRIBUTE_NAME);
47
+ // let locked = this.lock;
48
+ try {
49
+ // this.lock = false;
50
+ if (value === null || !this.trySelectSceneFromValue(value))
51
+ this.select(0);
52
+ }
53
+ finally {
54
+ // this.lock = locked;
55
+ }
56
+ }
57
+ }
58
+
59
+ onEnable(): void {
60
+ globalThis.addEventListener("popstate", this.onPopState);
61
+ this.context.input.addEventListener(InputEvents.KeyDown, this.onKeyDown);
62
+ this.context.input.addEventListener(InputEvents.PointerMove, this.onPointerMove);
63
+ this.context.input.addEventListener(InputEvents.PointerUp, this.onPointerUp);
64
+
65
+ if (!this._engineElementOverserver) {
66
+ this._engineElementOverserver = new MutationObserver((mutations) => {
67
+ for (const mut of mutations) {
68
+ if (mut.type === "attributes" && mut.attributeName === ENGINE_ELEMENT_SCENE_ATTRIBUTE_NAME) {
69
+ const value = this.context.domElement.getAttribute(ENGINE_ELEMENT_SCENE_ATTRIBUTE_NAME);
70
+ if (value !== null) {
71
+ this.trySelectSceneFromValue(value);
72
+ }
73
+ }
74
+ }
75
+ });
76
+ }
77
+
78
+ this._engineElementOverserver.observe(this.context.domElement, {
79
+ attributes: true
80
+ });
81
+ }
82
+
83
+ onDisable(): void {
84
+ globalThis.removeEventListener("popstate", this.onPopState);
85
+ this.context.input.removeEventListener(InputEvents.KeyDown, this.onKeyDown);
86
+ this.context.input.removeEventListener(InputEvents.PointerMove, this.onPointerMove);
87
+ this.context.input.removeEventListener(InputEvents.PointerUp, this.onPointerUp);
88
+ }
89
+
90
+ private onPopState = async (_state: PopStateEvent) => {
91
+ if (!this.useHistory) return;
92
+ let wasUsingHistory = this.useHistory;
93
+ try {
94
+ this.useHistory = false;
95
+ await this.tryLoadFromQueryParam();
96
+ }
97
+ finally {
98
+ this.useHistory = wasUsingHistory;
99
+ }
100
+ }
101
+
102
+ private normalizedSwipeThresholdX = 0.1;
103
+ private _didSwipe: boolean = false;
104
+ private onPointerMove = (e: any) => {
105
+ if (!this._didSwipe && e.button === 0 && e.pointerType === "touch" && this.context.input.getPointerPressedCount() === 1) {
106
+ const delta = this.context.input.getPointerPositionDelta(e.button);
107
+ if (delta) {
108
+ const normalizedX = delta.x / this.context.domWidth;
109
+ if (normalizedX >= this.normalizedSwipeThresholdX) {
110
+ this._didSwipe = true;
111
+ this.selectPrev();
112
+ }
113
+ else if (normalizedX <= -this.normalizedSwipeThresholdX) {
114
+ this._didSwipe = true;
115
+ this.selectNext();
116
+ }
117
+ }
118
+ }
119
+ }
120
+
121
+ private onPointerUp = (e: any) => {
122
+ if (e.button === 0) {
123
+ this._didSwipe = false;
124
+ }
125
+ };
126
+
127
+ private onKeyDown = (e: any) => {
128
+ if (!this.useKeyboard) return;
129
+ if (!this.scenes) return;
130
+ const key = e.key.toLowerCase();
131
+ if (!key) return;
132
+ const index = parseInt(key) - 1;
133
+ if (index >= 0) {
134
+ this.trySelectSceneFromValue(index);
135
+ return;
136
+ }
137
+ switch (key) {
138
+ case "arrowright":
139
+ case "d":
140
+ this.selectNext();
141
+ break;
142
+ case "arrowleft":
143
+ case "a":
144
+ this.selectPrev();
145
+ break;
146
+ }
147
+ }
148
+
149
+ selectNext() {
150
+ return this.select(this._currentIndex + 1);
151
+ }
152
+
153
+ selectPrev() {
154
+ return this.select(this._currentIndex - 1);
155
+ }
156
+
157
+ select(index: number) {
158
+ if (!this.scenes?.length) return couldNotLoadScenePromise;
159
+ if (index < 0) index = this.scenes.length - 1;
160
+ if (index >= this.scenes.length) index = 0;
161
+ const scene = this.scenes[index];
162
+ return this.switchScene(scene);
163
+ }
164
+
165
+ async switchScene(scene: AssetReference): Promise<boolean> {
166
+ if (scene === this._currentScene) return true;
167
+ if (this._currentScene)
168
+ GameObject.remove(this._currentScene.asset);
169
+ const index = this._currentIndex = this.scenes?.indexOf(scene) ?? -1;
170
+ this._currentScene = scene;
171
+ try {
172
+ await scene.loadAssetAsync();
173
+ if (!scene.asset) return false;
174
+ if (this._currentIndex === index) {
175
+ GameObject.add(scene.asset, this.gameObject);
176
+ // save the loaded scene as an url parameter
177
+ if (this.queryParameterName?.length)
178
+ setParamWithoutReload(this.queryParameterName, index.toString(), this.useHistory);
179
+ return true;
180
+ }
181
+ }
182
+ catch (err) {
183
+ console.error(err);
184
+ }
185
+ return false;
186
+ }
187
+
188
+ private tryLoadFromQueryParam() {
189
+ if (!this.queryParameterName?.length) return false;
190
+ // try restore the scene from the url
191
+ const value = getParam(this.queryParameterName);
192
+ if (typeof value === "boolean") return false;
193
+ return this.trySelectSceneFromValue(value);
194
+ }
195
+
196
+ /** try to select a scene from a string or index */
197
+ private trySelectSceneFromValue(value: string | number) {
198
+
199
+ if (typeof value === "string") {
200
+ const index = parseInt(value as string);
201
+ if (index >= 0 && index < this.scenes.length) {
202
+ return this.select(index);;
203
+ }
204
+ else {
205
+ // Try to find a scene with a matching name
206
+ for (let i = 0; i < this.scenes.length; i++) {
207
+ const scene = this.scenes[i];
208
+ if (scene.uri.toLowerCase().includes(value)) {
209
+ return this.select(i);;
210
+ }
211
+ }
212
+ }
213
+ }
214
+ else if (typeof value === "number") {
215
+ if (value >= 0 && value < this.scenes.length) {
216
+ return this.select(value);;
217
+ }
218
+ }
219
+
220
+ if (isLocalNetwork()) console.warn("Unknown scene value or index: \"" + value + "\"", this)
221
+
222
+ return couldNotLoadScenePromise;
223
+ }
224
+ }
@@ -96,6 +96,7 @@ export class ScreenCapture extends Behaviour implements IPointerClickHandler {
96
96
  console.log("Screensharing", this.name, this);
97
97
  AudioSource.registerWaitForAllowAudio(() => {
98
98
  if (this.videoPlayer && this._currentStream && this._currentMode === ScreenCaptureMode.Receiving) {
99
+ this.videoPlayer.playInBackground = true;
99
100
  this.videoPlayer.setVideo(this._currentStream);
100
101
  }
101
102
  });
@@ -52,7 +52,7 @@ export class VideoPlayer extends Behaviour {
52
52
 
53
53
  @serializable()
54
54
  aspectMode: AspectMode = AspectMode.None;
55
-
55
+
56
56
  @serializable(URL)
57
57
  private clip?: string | MediaStream | null = null;
58
58
 
@@ -106,12 +106,12 @@ export class VideoPlayer extends Behaviour {
106
106
  get isPlaying(): boolean {
107
107
  const video = this._videoElement;
108
108
  if (video) {
109
- if(video.currentTime > 0 && !video.paused && !video.ended
109
+ if (video.currentTime > 0 && !video.paused && !video.ended
110
110
  && video.readyState > video.HAVE_CURRENT_DATA)
111
111
  return true;
112
- else if(video.srcObject){
112
+ else if (video.srcObject) {
113
113
  const stream = video.srcObject as MediaStream;
114
- if(stream.active) return true;
114
+ if (stream.active) return true;
115
115
  }
116
116
  }
117
117
  return false;
@@ -149,6 +149,9 @@ export class VideoPlayer extends Behaviour {
149
149
  }
150
150
  private _muted: boolean = false;
151
151
 
152
+ /** Set this to false to pause video playback while the tab is not active */
153
+ playInBackground: boolean = true;
154
+
152
155
  private _crossOrigin: string | null = "anonymous";
153
156
 
154
157
  private audioOutputMode: VideoAudioOutputMode = VideoAudioOutputMode.AudioSource;
@@ -198,11 +201,13 @@ export class VideoPlayer extends Behaviour {
198
201
  window.addEventListener('visibilitychange', _evt => {
199
202
  switch (document.visibilityState) {
200
203
  case "hidden":
201
- this.wasPlaying = this._isPlaying;
202
- this.pause();
204
+ if(!this.playInBackground){
205
+ this.wasPlaying = this._isPlaying;
206
+ this.pause();
207
+ }
203
208
  break;
204
209
  case "visible":
205
- if (this.wasPlaying) this.play();
210
+ if (this.wasPlaying && !this._isPlaying) this.play();
206
211
  break;
207
212
  }
208
213
  });
@@ -260,7 +265,7 @@ export class VideoPlayer extends Behaviour {
260
265
  this.updateVideoElementSettings();
261
266
  this._videoElement?.play().catch(err => {
262
267
  // https://developer.chrome.com/blog/play-request-was-interrupted/
263
- if(debug)
268
+ if (debug)
264
269
  console.error("Error playing video", err, "CODE=" + err.code, this.videoElement?.src, this);
265
270
  setTimeout(() => {
266
271
  if (this._isPlaying && !this.destroyed && this.activeAndEnabled)
@@ -318,7 +323,7 @@ export class VideoPlayer extends Behaviour {
318
323
  this._videoTexture.flipY = false;
319
324
  this._videoTexture.encoding = THREE.sRGBEncoding;
320
325
  this.handleBeginPlaying(playAutomatically);
321
- if(debug)
326
+ if (debug)
322
327
  console.log(this);
323
328
  }
324
329
 
@@ -420,7 +425,7 @@ export class VideoPlayer extends Behaviour {
420
425
  // dont open in fullscreen on ios
421
426
  this._videoElement.playsInline = true;
422
427
  let muted = !this._receivedInput && this.audioOutputMode !== VideoAudioOutputMode.None;
423
- if(!muted && this._muted) muted = true;
428
+ if (!muted && this._muted) muted = true;
424
429
  this._videoElement.muted = muted;
425
430
  if (this.playOnAwake)
426
431
  this._videoElement.autoplay = true;
@@ -540,7 +545,7 @@ class VideoOverlay {
540
545
  if (!this._screenspaceModeQuad) return;
541
546
  this._screenspaceModeQuad.geometry.scale(2, 2, 2);
542
547
  }
543
-
548
+
544
549
  const quad = this._screenspaceModeQuad;
545
550
  this.context.scene.add(quad);
546
551
  this.updateScreenspaceMaterialUniforms();
@@ -79,6 +79,9 @@ export class XRFlag extends Behaviour {
79
79
  XRFlag.firstApply = true;
80
80
  XRFlag.Apply();
81
81
  }
82
+ else {
83
+ this.UpdateVisible(XRState.Global);
84
+ }
82
85
  }
83
86
 
84
87
  onDestroy(): void {
@@ -114,6 +114,7 @@ export { RGBAColor } from "../js-extensions/RGBAColor";
114
114
  export { Rigidbody } from "../RigidBody";
115
115
  export { RotationBySpeedModule } from "../ParticleSystemModules";
116
116
  export { RotationOverLifetimeModule } from "../ParticleSystemModules";
117
+ export { SceneSwitcher } from "../SceneSwitcher";
117
118
  export { ScreenCapture } from "../ScreenCapture";
118
119
  export { ScreenSpaceAmbientOcclusion } from "../postprocessing/Effects/ScreenspaceAmbientOcclusion";
119
120
  export { ShadowCatcher } from "../ShadowCatcher";
@@ -452,7 +452,6 @@ export class PlayableDirector extends Behaviour {
452
452
  const anim = GameObject.getOrAddComponent(binding, Animator);
453
453
  if(anim) binding = anim;
454
454
  }
455
- if (typeof binding.enabled === "boolean") binding.enabled = false;
456
455
  const animationClips = binding?.gameObject?.animations;
457
456
  if (animationClips) {
458
457
  const handler = new Tracks.AnimationTrackHandler();
@@ -145,7 +145,7 @@ export class AnimationTrackHandler extends TrackHandler {
145
145
  onPauseChanged() {
146
146
  // When the timeline is paused the original animator will be enabled again if it was before
147
147
  if (this._animator && this._animatorWasEnabled !== undefined) {
148
- this._animator.enabled = this.director.isPaused ? this._animatorWasEnabled : false;
148
+ this._animator.enabled = this.director.isPlaying === false ? this._animatorWasEnabled : false;
149
149
  }
150
150
  }
151
151
 
@@ -225,7 +225,12 @@ export class AnimationTrackHandler extends TrackHandler {
225
225
  // We need to disable the animator component in case it also animates
226
226
  // which overrides the timeline
227
227
  this._animator = GameObject.getComponent(this.target, Animator) ?? null;
228
- this._animatorWasEnabled = this._animator?.enabled;
228
+ if (this._animator)
229
+ {
230
+ this._animatorWasEnabled = this._animator.enabled;
231
+ this._animator.enabled = false;
232
+ }
233
+ else this._animatorWasEnabled = false;
229
234
  }
230
235
 
231
236
  // Clip Offsets
@@ -323,6 +328,7 @@ export class AnimationTrackHandler extends TrackHandler {
323
328
  // const clip = this.clips[i];
324
329
  let weight = 1;
325
330
  weight *= this.evaluateWeight(time, i, this.models, isActive);
331
+ weight *= this.director.weight;
326
332
 
327
333
  let handleLoop = isInTimeRange;
328
334
  if(doPreExtrapolate){
@@ -370,7 +376,7 @@ export class AnimationTrackHandler extends TrackHandler {
370
376
  else action.time = t;
371
377
 
372
378
  action.timeScale = 0;
373
- const effectiveWeight = weight * this.director.weight;
379
+ const effectiveWeight = weight;
374
380
  action.weight = effectiveWeight;
375
381
  action.clampWhenFinished = true;
376
382
  if (!action.isRunning())