@zephyr3d/editor 0.3.5 → 0.3.7

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 (48) hide show
  1. package/dist/assets/{index-DlnbO59X.js → index-ySiLtSxz.js} +2 -2
  2. package/dist/assistant/zephyr-types-index.json +14215 -7892
  3. package/dist/index.html +1 -1
  4. package/dist/modules/zephyr3d_backend-webgl.js.map +1 -1
  5. package/dist/modules/zephyr3d_backend-webgpu.js.map +1 -1
  6. package/dist/modules/zephyr3d_base.js +152 -152
  7. package/dist/modules/zephyr3d_base.js.map +1 -1
  8. package/dist/modules/zephyr3d_imgui.js.map +1 -1
  9. package/dist/modules/zephyr3d_loaders.js +6 -6
  10. package/dist/modules/zephyr3d_loaders.js.map +1 -1
  11. package/dist/modules/zephyr3d_scene.js +2538 -348
  12. package/dist/modules/zephyr3d_scene.js.map +1 -1
  13. package/dist/vendor/zephyr3d/backend-webgl/dist/index.js.map +1 -1
  14. package/dist/vendor/zephyr3d/backend-webgpu/dist/index.js.map +1 -1
  15. package/dist/vendor/zephyr3d/base/dist/vfs/common.js +152 -152
  16. package/dist/vendor/zephyr3d/base/dist/vfs/common.js.map +1 -1
  17. package/dist/vendor/zephyr3d/imgui/dist/index.d.ts +7 -0
  18. package/dist/vendor/zephyr3d/imgui/dist/index.js.map +1 -1
  19. package/dist/vendor/zephyr3d/loaders/dist/gltf/gltf_importer.js +6 -6
  20. package/dist/vendor/zephyr3d/loaders/dist/gltf/gltf_importer.js.map +1 -1
  21. package/dist/vendor/zephyr3d/scene/dist/animation/animation.js +87 -0
  22. package/dist/vendor/zephyr3d/scene/dist/animation/animation.js.map +1 -1
  23. package/dist/vendor/zephyr3d/scene/dist/animation/animationcontroller.js +503 -0
  24. package/dist/vendor/zephyr3d/scene/dist/animation/animationcontroller.js.map +1 -0
  25. package/dist/vendor/zephyr3d/scene/dist/animation/animationset.js +782 -156
  26. package/dist/vendor/zephyr3d/scene/dist/animation/animationset.js.map +1 -1
  27. package/dist/vendor/zephyr3d/scene/dist/animation/animationtimeline.js +974 -0
  28. package/dist/vendor/zephyr3d/scene/dist/animation/animationtimeline.js.map +1 -0
  29. package/dist/vendor/zephyr3d/scene/dist/animation/joint_dynamics/convex_collider.js +320 -0
  30. package/dist/vendor/zephyr3d/scene/dist/animation/joint_dynamics/convex_collider.js.map +1 -0
  31. package/dist/vendor/zephyr3d/scene/dist/app/scriptregistry.js +130 -125
  32. package/dist/vendor/zephyr3d/scene/dist/app/scriptregistry.js.map +1 -1
  33. package/dist/vendor/zephyr3d/scene/dist/asset/model.js +64 -63
  34. package/dist/vendor/zephyr3d/scene/dist/asset/model.js.map +1 -1
  35. package/dist/vendor/zephyr3d/scene/dist/index.d.ts +1270 -9
  36. package/dist/vendor/zephyr3d/scene/dist/index.js +3 -1
  37. package/dist/vendor/zephyr3d/scene/dist/index.js.map +1 -1
  38. package/dist/vendor/zephyr3d/scene/dist/material/mixins/lit.js +7 -3
  39. package/dist/vendor/zephyr3d/scene/dist/material/mixins/lit.js.map +1 -1
  40. package/dist/vendor/zephyr3d/scene/dist/utility/serialization/manager.js +1 -0
  41. package/dist/vendor/zephyr3d/scene/dist/utility/serialization/manager.js.map +1 -1
  42. package/dist/vendor/zephyr3d/scene/dist/utility/serialization/scene/animation.js +4 -3
  43. package/dist/vendor/zephyr3d/scene/dist/utility/serialization/scene/animation.js.map +1 -1
  44. package/dist/vendor/zephyr3d/scene/dist/utility/serialization/scene/camera.js +1 -1
  45. package/dist/vendor/zephyr3d/scene/dist/utility/serialization/scene/camera.js.map +1 -1
  46. package/dist/vendor/zephyr3d/scene/dist/utility/serialization/scene/node.js +3 -2
  47. package/dist/vendor/zephyr3d/scene/dist/utility/serialization/scene/node.js.map +1 -1
  48. package/package.json +8 -8
@@ -1,4 +1,4 @@
1
- import { Disposable, makeObservable, releaseObject, retainObject, Vector4, Vector3, DRef, AABB, Frustum, ClipState, Interpolator, Quaternion, randomUUID, DWeakRef, Matrix4x4, nextPowerOf2, weightedAverage, ObservableVector3, ObservableQuaternion, ASSERT, Ray, Vector2, applyMixins, float2half, Observable, objectKeys, halton23, CubeFace, ObservableVector4, castObservable, radian2degree, degree2radian, uint8ArrayToBase64, base64ToUint8Array, mimeTypeOf, clamp01 as clamp01$1, smoothStep, InterpolatorScalar, applyPatch, guessMimeType, diff, PRNG, splitStringByGraphemes, packFloat3, isPowerOf2, clamp as clamp$1, Matrix3x3, PathUtils, textToBase64, HttpFS, MemoryFS, objectEntries, flushPendingDisposals } from '@zephyr3d/base';
1
+ import { Disposable, makeObservable, releaseObject, retainObject, Vector4, Vector3, DRef, AABB, Frustum, ClipState, Interpolator, Quaternion, randomUUID, DWeakRef, Matrix4x4, nextPowerOf2, Observable, weightedAverage, ObservableVector3, ObservableQuaternion, ASSERT, Ray, Vector2, applyMixins, float2half, objectKeys, halton23, CubeFace, ObservableVector4, castObservable, radian2degree, degree2radian, uint8ArrayToBase64, base64ToUint8Array, mimeTypeOf, clamp01 as clamp01$1, smoothStep, InterpolatorScalar, applyPatch, guessMimeType, diff, PRNG, splitStringByGraphemes, packFloat3, isPowerOf2, clamp as clamp$1, Matrix3x3, PathUtils, textToBase64, HttpFS, MemoryFS, objectEntries, flushPendingDisposals } from '@zephyr3d/base';
2
2
  import { PBPrimitiveType, matchVertexBuffer, PBInsideFunctionScope, ProgramBuilder, PBShaderExp, isFloatTextureFormat, getVertexAttributeIndex, VERTEX_ATTRIB_POSITION, VERTEX_ATTRIB_TEXCOORD0, getVertexAttributeFormat, hasDepthChannel, PBStructTypeInfo, PBArrayTypeInfo, PBPrimitiveTypeInfo, getVertexFormatComponentCount, TextureAtlasManager } from '@zephyr3d/device';
3
3
 
4
4
  /** @internal */ let appInstance;
@@ -2645,6 +2645,8 @@ const debugFrameBuffer = {};
2645
2645
  /** @internal */ _tracks;
2646
2646
  _weight;
2647
2647
  /** @internal */ _skeletons;
2648
+ /** @internal */ _frameRate;
2649
+ /** @internal */ _markers;
2648
2650
  /**
2649
2651
  * Creates an animation instance
2650
2652
  * @param name - Name of the animation
@@ -2659,6 +2661,8 @@ const debugFrameBuffer = {};
2659
2661
  this._weight = 1;
2660
2662
  this._autoPlay = false;
2661
2663
  this._skeletons = new Set();
2664
+ this._frameRate = 30;
2665
+ this._markers = [];
2662
2666
  }
2663
2667
  /**
2664
2668
  * Whether this clip is embedded (owned inline by its container/resource).
@@ -2709,6 +2713,21 @@ const debugFrameBuffer = {};
2709
2713
  this._skeletons = val;
2710
2714
  }
2711
2715
  /**
2716
+ * Default frame rate used to convert frame-based marker references.
2717
+ */ get frameRate() {
2718
+ return this._frameRate;
2719
+ }
2720
+ set frameRate(val) {
2721
+ if (Number.isFinite(val) && val > 0) {
2722
+ this._frameRate = val;
2723
+ }
2724
+ }
2725
+ /**
2726
+ * Timeline markers stored on this clip.
2727
+ */ get markers() {
2728
+ return this._markers;
2729
+ }
2730
+ /**
2712
2731
  * Total time span of the clip in seconds.
2713
2732
  *
2714
2733
  * Automatically extended when adding tracks with longer duration.
@@ -2719,6 +2738,74 @@ const debugFrameBuffer = {};
2719
2738
  this._duration = val;
2720
2739
  }
2721
2740
  /**
2741
+ * Add a serializable marker to this clip.
2742
+ *
2743
+ * @param marker - Marker metadata. If `id` is omitted, `name` is used as the id.
2744
+ * @returns The normalized marker.
2745
+ */ addMarker(marker) {
2746
+ if (!marker?.name) {
2747
+ console.error('Animation marker must have a name');
2748
+ return null;
2749
+ }
2750
+ const normalized = {
2751
+ ...marker,
2752
+ id: marker.id || marker.name
2753
+ };
2754
+ if (this.resolveMarkerTime(normalized) === null) {
2755
+ console.error('Animation marker must have either time or frame');
2756
+ return null;
2757
+ }
2758
+ this._markers.push(normalized);
2759
+ this._markers.sort((a, b)=>(this.resolveMarkerTime(a) ?? 0) - (this.resolveMarkerTime(b) ?? 0));
2760
+ return normalized;
2761
+ }
2762
+ /**
2763
+ * Remove markers matching the marker id or name.
2764
+ */ removeMarker(idOrName) {
2765
+ const oldLength = this._markers.length;
2766
+ this._markers = this._markers.filter((marker)=>marker.id !== idOrName && marker.name !== idOrName);
2767
+ return this._markers.length !== oldLength;
2768
+ }
2769
+ /**
2770
+ * Get the first marker matching the marker id or name.
2771
+ */ getMarker(idOrName) {
2772
+ return this._markers.find((marker)=>marker.id === idOrName || marker.name === idOrName) ?? null;
2773
+ }
2774
+ /**
2775
+ * Resolve a marker to seconds on this clip's timeline.
2776
+ */ resolveMarkerTime(marker) {
2777
+ if (typeof marker.time === 'number' && Number.isFinite(marker.time)) {
2778
+ return marker.time;
2779
+ }
2780
+ if (typeof marker.frame === 'number' && Number.isFinite(marker.frame)) {
2781
+ const fps = marker.fps ?? this._frameRate;
2782
+ return fps > 0 ? marker.frame / fps : null;
2783
+ }
2784
+ return null;
2785
+ }
2786
+ /**
2787
+ * Resolve a time reference to seconds on this clip's timeline.
2788
+ */ resolveTimeRef(ref) {
2789
+ if (ref === null || ref === undefined) {
2790
+ return null;
2791
+ }
2792
+ if (typeof ref === 'number') {
2793
+ return Number.isFinite(ref) ? ref : null;
2794
+ }
2795
+ if ('time' in ref) {
2796
+ return Number.isFinite(ref.time) ? ref.time : null;
2797
+ }
2798
+ if ('frame' in ref) {
2799
+ const fps = ref.fps ?? this._frameRate;
2800
+ return Number.isFinite(ref.frame) && fps > 0 ? ref.frame / fps : null;
2801
+ }
2802
+ if ('marker' in ref) {
2803
+ const marker = this.getMarker(ref.marker);
2804
+ return marker ? this.resolveMarkerTime(marker) : null;
2805
+ }
2806
+ return null;
2807
+ }
2808
+ /**
2722
2809
  * Add a skeleton used by this clip.
2723
2810
  *
2724
2811
  * @param skeletonId - Persistent ID of Skeleton to register for this clip.
@@ -5756,6 +5843,224 @@ function copySkeletonReferences(sourceClip, targetClip) {
5756
5843
  return targetClip;
5757
5844
  }
5758
5845
 
5846
+ let nextAnimationPlaybackId = 1;
5847
+ /**
5848
+ * Runtime handle for one playback of an AnimationClip.
5849
+ * @public
5850
+ */ class AnimationPlayback extends Observable {
5851
+ /** @internal */ _animationSet;
5852
+ /** @internal */ _clip;
5853
+ /** @internal */ _options;
5854
+ /** @internal */ _id;
5855
+ /** @internal */ _frameWaiters;
5856
+ /** @internal */ _state;
5857
+ /** @internal */ _time;
5858
+ /** @internal */ _weight;
5859
+ /** @internal */ _speedRatio;
5860
+ /** @internal */ _layer;
5861
+ /** @internal */ _priority;
5862
+ /** @internal */ _interruptible;
5863
+ constructor(animationSet, clip, options){
5864
+ super();
5865
+ this._animationSet = animationSet;
5866
+ this._clip = clip;
5867
+ this._options = {
5868
+ ...options ?? {}
5869
+ };
5870
+ this._id = this._options.id ?? `animation-playback-${nextAnimationPlaybackId++}`;
5871
+ this._state = 'scheduled';
5872
+ this._speedRatio = this._options.speedRatio ?? 1;
5873
+ this._weight = this._options.weight ?? clip.weight ?? 1;
5874
+ this._time = this._speedRatio < 0 ? clip.timeDuration : 0;
5875
+ this._layer = this._options.layer ?? 'default';
5876
+ this._priority = this._options.priority ?? 0;
5877
+ this._interruptible = this._options.interruptible ?? true;
5878
+ this._frameWaiters = new Map();
5879
+ }
5880
+ get id() {
5881
+ return this._id;
5882
+ }
5883
+ get animationSet() {
5884
+ return this._animationSet;
5885
+ }
5886
+ get clip() {
5887
+ return this._clip;
5888
+ }
5889
+ get state() {
5890
+ return this._state;
5891
+ }
5892
+ get time() {
5893
+ return this._time;
5894
+ }
5895
+ set time(value) {
5896
+ this.seek(value);
5897
+ }
5898
+ get normalizedTime() {
5899
+ return this._clip.timeDuration > 0 ? this._time / this._clip.timeDuration : 0;
5900
+ }
5901
+ set normalizedTime(value) {
5902
+ this.seek(value * this._clip.timeDuration);
5903
+ }
5904
+ get weight() {
5905
+ return this._weight;
5906
+ }
5907
+ set weight(value) {
5908
+ this._animationSet.setPlaybackWeight(this, value);
5909
+ }
5910
+ get speedRatio() {
5911
+ return this._speedRatio;
5912
+ }
5913
+ set speedRatio(value) {
5914
+ this._animationSet.setPlaybackSpeedRatio(this, value);
5915
+ }
5916
+ get layer() {
5917
+ return this._layer;
5918
+ }
5919
+ get priority() {
5920
+ return this._priority;
5921
+ }
5922
+ get interruptible() {
5923
+ return this._interruptible;
5924
+ }
5925
+ play() {
5926
+ this._animationSet.startPlayback(this);
5927
+ return this;
5928
+ }
5929
+ pause() {
5930
+ this._animationSet.pausePlayback(this);
5931
+ return this;
5932
+ }
5933
+ resume() {
5934
+ this._animationSet.resumePlayback(this);
5935
+ return this;
5936
+ }
5937
+ stop(options) {
5938
+ this._animationSet.stopPlayback(this, options);
5939
+ return this;
5940
+ }
5941
+ seek(time, options) {
5942
+ this._animationSet.seekPlayback(this, time, options);
5943
+ return this;
5944
+ }
5945
+ fadeTo(weight, duration) {
5946
+ this._animationSet.fadePlaybackTo(this, weight, duration);
5947
+ return this;
5948
+ }
5949
+ crossFadeTo(name, options) {
5950
+ const duration = Math.max(options?.duration ?? 0, 0);
5951
+ this.stop({
5952
+ fadeOut: duration,
5953
+ reason: 'interrupted'
5954
+ });
5955
+ return this._animationSet.play(name, {
5956
+ ...options,
5957
+ fadeIn: duration
5958
+ });
5959
+ }
5960
+ waitForComplete() {
5961
+ return new Promise((resolve)=>{
5962
+ this.once('complete', ()=>resolve(this));
5963
+ });
5964
+ }
5965
+ waitForMarker(idOrName) {
5966
+ if (this._state === 'stopped' || this._state === 'completed') {
5967
+ return Promise.resolve(undefined);
5968
+ }
5969
+ return new Promise((resolve)=>{
5970
+ const handler = (event)=>{
5971
+ if (event.marker.id === idOrName || event.marker.name === idOrName) {
5972
+ this.off('marker', handler);
5973
+ this.off('stop', stop);
5974
+ resolve(event);
5975
+ }
5976
+ };
5977
+ const stop = ()=>{
5978
+ this.off('marker', handler);
5979
+ this.off('stop', stop);
5980
+ resolve(undefined);
5981
+ };
5982
+ this.on('marker', handler);
5983
+ this.on('stop', stop);
5984
+ });
5985
+ }
5986
+ waitForFrame(frame) {
5987
+ if (this._state === 'stopped' || this._state === 'completed') {
5988
+ return Promise.resolve(undefined);
5989
+ }
5990
+ return new Promise((resolve)=>{
5991
+ const waiters = this._frameWaiters.get(frame) ?? [];
5992
+ let settled = false;
5993
+ let stop;
5994
+ const wrappedResolve = (event)=>{
5995
+ if (settled) {
5996
+ return;
5997
+ }
5998
+ settled = true;
5999
+ this.off('stop', stop);
6000
+ resolve(event);
6001
+ };
6002
+ stop = ()=>{
6003
+ const current = this._frameWaiters.get(frame);
6004
+ if (current) {
6005
+ this._frameWaiters.set(frame, current.filter((item)=>item !== wrappedResolve));
6006
+ }
6007
+ wrappedResolve(undefined);
6008
+ };
6009
+ waiters.push(wrappedResolve);
6010
+ this._frameWaiters.set(frame, waiters);
6011
+ this.on('stop', stop);
6012
+ });
6013
+ }
6014
+ /** @internal */ _getOptions() {
6015
+ return this._options;
6016
+ }
6017
+ /** @internal */ _setState(state) {
6018
+ this._state = state;
6019
+ }
6020
+ /** @internal */ _setTime(time) {
6021
+ this._time = time;
6022
+ }
6023
+ /** @internal */ _setWeight(weight) {
6024
+ this._weight = weight;
6025
+ }
6026
+ /** @internal */ _setSpeedRatio(speedRatio) {
6027
+ this._speedRatio = speedRatio;
6028
+ }
6029
+ /** @internal */ _getWatchedFrames() {
6030
+ return [
6031
+ ...this._frameWaiters.keys()
6032
+ ];
6033
+ }
6034
+ /** @internal */ _emitStart(event) {
6035
+ this.dispatchEvent('start', event);
6036
+ }
6037
+ /** @internal */ _emitLoop(event) {
6038
+ this.dispatchEvent('loop', event);
6039
+ }
6040
+ /** @internal */ _emitMarker(event) {
6041
+ this.dispatchEvent('marker', event);
6042
+ }
6043
+ /** @internal */ _emitFrame(event) {
6044
+ this.dispatchEvent('frame', event);
6045
+ const waiters = this._frameWaiters.get(event.frame);
6046
+ if (waiters) {
6047
+ this._frameWaiters.delete(event.frame);
6048
+ waiters.forEach((resolve)=>resolve(event));
6049
+ }
6050
+ }
6051
+ /** @internal */ _emitComplete(event) {
6052
+ this.dispatchEvent('complete', event);
6053
+ }
6054
+ /** @internal */ _emitStop(event) {
6055
+ this.dispatchEvent('stop', event);
6056
+ }
6057
+ /** @internal */ _emitPause(event) {
6058
+ this.dispatchEvent('pause', event);
6059
+ }
6060
+ /** @internal */ _emitResume(event) {
6061
+ this.dispatchEvent('resume', event);
6062
+ }
6063
+ }
5759
6064
  function cloneInterpolator(src) {
5760
6065
  return new Interpolator(src.mode, src.target, src.inputs instanceof Float32Array ? new Float32Array(src.inputs) : [
5761
6066
  ...src.inputs
@@ -6137,26 +6442,26 @@ function bakeHumanoidRotationTracks(sourceClip, srcSkeleton, dstSkeleton, srcRoo
6137
6442
  dstClip.addTrack(remap.dstNode, track);
6138
6443
  }
6139
6444
  }
6140
- /**
6141
- * Animation set
6142
- *
6143
- * Manages a collection of named animation clips for a model and orchestrates:
6144
- * - Playback state (time, loops, speed, weights, fade-in/out).
6145
- * - Blending across multiple tracks targeting the same property via weighted averages.
6146
- * - Skeleton usage and application for clips that drive skeletal animation.
6147
- * - Active track registration and cleanup as clips start/stop.
6148
- *
6149
- * Usage:
6150
- * - Create or retrieve `AnimationClip`s by name.
6151
- * - Start playback with `playAnimation(name, options)`.
6152
- * - Advance animation with `update(deltaSeconds)`.
6153
- * - Optionally adjust weight while playing with `setAnimationWeight(name, weight)`.
6154
- *
6155
- * Lifetime:
6156
- * - Disposing the set releases references to the model, clips, and clears active state.
6157
- *
6158
- * @public
6159
- */ class AnimationSet extends Disposable {
6445
+ /**
6446
+ * Animation set
6447
+ *
6448
+ * Manages a collection of named animation clips for a model and orchestrates:
6449
+ * - Playback state (time, loops, speed, weights, fade-in/out).
6450
+ * - Blending across multiple tracks targeting the same property via weighted averages.
6451
+ * - Skeleton usage and application for clips that drive skeletal animation.
6452
+ * - Active track registration and cleanup as clips start/stop.
6453
+ *
6454
+ * Usage:
6455
+ * - Create or retrieve `AnimationClip`s by name.
6456
+ * - Start playback with `playAnimation(name, options)`.
6457
+ * - Advance animation with `update(deltaSeconds)`.
6458
+ * - Optionally adjust weight while playing with `setAnimationWeight(name, weight)`.
6459
+ *
6460
+ * Lifetime:
6461
+ * - Disposing the set releases references to the model, clips, and clears active state.
6462
+ *
6463
+ * @public
6464
+ */ class AnimationSet extends makeObservable(Disposable)() {
6160
6465
  /** @internal */ _model;
6161
6466
  /** @internal */ _animations;
6162
6467
  /** @internal */ _skeletons;
@@ -6165,10 +6470,18 @@ function bakeHumanoidRotationTracks(sourceClip, srcSkeleton, dstSkeleton, srcRoo
6165
6470
  /** @internal */ _activeSkinBindings;
6166
6471
  /** @internal */ _activeRigs;
6167
6472
  /** @internal */ _activeAnimations;
6168
- /**
6169
- * Create an AnimationSet controlling the provided model.
6170
- *
6171
- * @param model - The SceneNode (model root) controlled by this animation set.
6473
+ /**
6474
+ * Callbacks driven by the same logical clock as the animations (see {@link update}).
6475
+ *
6476
+ * Used by higher-level constructs (e.g. timeline runners) that need to advance
6477
+ * wall-clock-independent timers in sync with playback, so they pause/scale with the
6478
+ * game clock instead of running on real time.
6479
+ * @internal
6480
+ */ _timelineTickers;
6481
+ /**
6482
+ * Create an AnimationSet controlling the provided model.
6483
+ *
6484
+ * @param model - The SceneNode (model root) controlled by this animation set.
6172
6485
  */ constructor(model){
6173
6486
  super();
6174
6487
  this._model = model;
@@ -6179,29 +6492,42 @@ function bakeHumanoidRotationTracks(sourceClip, srcSkeleton, dstSkeleton, srcRoo
6179
6492
  this._activeAnimations = new Map();
6180
6493
  this._skeletons = [];
6181
6494
  this._rigs = [];
6495
+ this._timelineTickers = new Set();
6182
6496
  }
6183
- /**
6184
- * The model (SceneNode) controlled by this animation set.
6497
+ /**
6498
+ * Register a callback advanced by {@link update} using the same delta time as the animations.
6499
+ * @internal
6500
+ */ _registerTimelineTicker(ticker) {
6501
+ this._timelineTickers.add(ticker);
6502
+ }
6503
+ /**
6504
+ * Unregister a callback previously registered with {@link _registerTimelineTicker}.
6505
+ * @internal
6506
+ */ _unregisterTimelineTicker(ticker) {
6507
+ this._timelineTickers.delete(ticker);
6508
+ }
6509
+ /**
6510
+ * The model (SceneNode) controlled by this animation set.
6185
6511
  */ get model() {
6186
6512
  return this._model;
6187
6513
  }
6188
- /**
6189
- * Number of animation clips registered in this set.
6514
+ /**
6515
+ * Number of animation clips registered in this set.
6190
6516
  */ get numAnimations() {
6191
6517
  return Object.getOwnPropertyNames(this._animations).length;
6192
6518
  }
6193
- /**
6194
- * The skeletons used by animations in this set.
6519
+ /**
6520
+ * The skeletons used by animations in this set.
6195
6521
  */ get skeletons() {
6196
6522
  return this._skeletons;
6197
6523
  }
6198
- /**
6199
- * The shared rigs used by animations in this set.
6524
+ /**
6525
+ * The shared rigs used by animations in this set.
6200
6526
  */ get rigs() {
6201
6527
  return this._rigs;
6202
6528
  }
6203
- /**
6204
- * Per-skin bindings used by skinned meshes in this set.
6529
+ /**
6530
+ * Per-skin bindings used by skinned meshes in this set.
6205
6531
  */ get skinBindings() {
6206
6532
  return this._skeletons;
6207
6533
  }
@@ -6223,20 +6549,20 @@ function bakeHumanoidRotationTracks(sourceClip, srcSkeleton, dstSkeleton, srcRoo
6223
6549
  }
6224
6550
  return rig;
6225
6551
  }
6226
- /**
6227
- * Retrieve an animation clip by name.
6228
- *
6229
- * @param name - Name of the animation.
6230
- * @returns The clip if present; otherwise null.
6552
+ /**
6553
+ * Retrieve an animation clip by name.
6554
+ *
6555
+ * @param name - Name of the animation.
6556
+ * @returns The clip if present; otherwise null.
6231
6557
  */ get(name) {
6232
6558
  return this._animations[name] ?? null;
6233
6559
  }
6234
- /**
6235
- * Create and register a new animation clip.
6236
- *
6237
- * @param name - Unique name for the animation clip.
6238
- * @param embedded - Whether the clip is embedded/owned (implementation-specific). Default false.
6239
- * @returns The created clip, or null if the name is empty or not unique.
6560
+ /**
6561
+ * Create and register a new animation clip.
6562
+ *
6563
+ * @param name - Unique name for the animation clip.
6564
+ * @param embedded - Whether the clip is embedded/owned (implementation-specific). Default false.
6565
+ * @returns The created clip, or null if the name is empty or not unique.
6240
6566
  */ createAnimation(name, embedded = false) {
6241
6567
  if (!name || this._animations[name]) {
6242
6568
  console.error('Animation must have unique name');
@@ -6248,12 +6574,12 @@ function bakeHumanoidRotationTracks(sourceClip, srcSkeleton, dstSkeleton, srcRoo
6248
6574
  return animation;
6249
6575
  }
6250
6576
  }
6251
- /**
6252
- * Delete and dispose an animation clip by name.
6253
- *
6254
- * - If the animation is currently playing, it is first stopped (immediately).
6255
- *
6256
- * @param name - Name of the animation to remove.
6577
+ /**
6578
+ * Delete and dispose an animation clip by name.
6579
+ *
6580
+ * - If the animation is currently playing, it is first stopped (immediately).
6581
+ *
6582
+ * @param name - Name of the animation to remove.
6257
6583
  */ deleteAnimation(name) {
6258
6584
  const animation = this._animations[name];
6259
6585
  if (animation) {
@@ -6262,50 +6588,119 @@ function bakeHumanoidRotationTracks(sourceClip, srcSkeleton, dstSkeleton, srcRoo
6262
6588
  animation.dispose();
6263
6589
  }
6264
6590
  }
6265
- /**
6266
- * Get the list of all registered animation names.
6267
- *
6268
- * @returns An array of clip names.
6591
+ /**
6592
+ * Get the list of all registered animation names.
6593
+ *
6594
+ * @returns An array of clip names.
6269
6595
  */ getAnimationNames() {
6270
6596
  return Object.keys(this._animations);
6271
6597
  }
6272
- /**
6273
- * Advance and apply active animations.
6274
- *
6275
- * Responsibilities per call:
6276
- * - Update time cursor for each active clip (respecting speedRatio and looping).
6277
- * - Enforce repeat limits and apply fade-out termination if configured.
6278
- * - For each animated target, blend active tracks (weighted by clip weight × fade-in × fade-out)
6279
- * and apply the resulting state to the target.
6280
- * - Apply shared rig modifiers once, then update skin binding palettes.
6281
- *
6282
- * @param deltaInSeconds - Time step in seconds since last update.
6598
+ /**
6599
+ * Advance and apply active animations.
6600
+ *
6601
+ * Responsibilities per call:
6602
+ * - Update time cursor for each active clip (respecting speedRatio and looping).
6603
+ * - Enforce repeat limits and apply fade-out termination if configured.
6604
+ * - For each animated target, blend active tracks (weighted by clip weight × fade-in × fade-out)
6605
+ * and apply the resulting state to the target.
6606
+ * - Apply shared rig modifiers once, then update skin binding palettes.
6607
+ *
6608
+ * @param deltaInSeconds - Time step in seconds since last update.
6283
6609
  */ update(deltaInSeconds) {
6284
6610
  this._activeAnimations.forEach((v, k)=>{
6611
+ if (!v.started) {
6612
+ v.started = true;
6613
+ const event = this.createPlaybackEvent(v, k);
6614
+ v.playback._emitStart(event);
6615
+ this.dispatchEvent('playbackstart', event);
6616
+ }
6617
+ if (v.paused) {
6618
+ return;
6619
+ }
6285
6620
  if (v.fadeOut > 0 && v.fadeOutStart < 0) {
6286
6621
  v.fadeOutStart = v.animateTime;
6287
6622
  }
6623
+ if (v.completed) {
6624
+ v.animateTime += Math.abs(deltaInSeconds * v.speedRatio);
6625
+ if (v.fadeOut > 0 && v.animateTime - v.fadeOutStart >= v.fadeOut) {
6626
+ this.stopAnimation(k.name, {
6627
+ reason: v.stopReason
6628
+ });
6629
+ }
6630
+ return;
6631
+ }
6288
6632
  // Update animation time
6289
6633
  if (v.firstFrame) {
6290
6634
  v.firstFrame = false;
6291
6635
  } else {
6636
+ const previousTime = v.currentTime;
6292
6637
  const timeAdvance = deltaInSeconds * v.speedRatio;
6293
6638
  v.currentTime += timeAdvance;
6294
6639
  v.animateTime += timeAdvance;
6640
+ const direction = timeAdvance >= 0 ? 1 : -1;
6295
6641
  if (k.timeDuration > 0) {
6296
- if (v.currentTime > k.timeDuration) {
6297
- v.repeatCounter += Math.max(1, Math.floor(v.currentTime / k.timeDuration));
6642
+ if (v.rangeEnd !== null && v.speedRatio >= 0 && v.currentTime >= v.rangeEnd) {
6643
+ v.currentTime = v.rangeEnd;
6644
+ v.playback._setTime(v.currentTime);
6645
+ this.completePlayback(k, v);
6646
+ return;
6647
+ } else if (v.rangeStart !== null && v.speedRatio < 0 && v.currentTime <= v.rangeStart) {
6648
+ v.currentTime = v.rangeStart;
6649
+ v.playback._setTime(v.currentTime);
6650
+ this.completePlayback(k, v);
6651
+ return;
6652
+ } else if (v.currentTime > k.timeDuration) {
6653
+ const loops = Math.max(1, Math.floor(v.currentTime / k.timeDuration));
6654
+ if (v.repeat !== 0 && v.repeatCounter + loops >= v.repeat) {
6655
+ v.repeatCounter += loops;
6656
+ v.currentTime = k.timeDuration;
6657
+ v.playback._setTime(v.currentTime);
6658
+ this.emitCrossedMarkers(v, k, previousTime, v.currentTime, direction);
6659
+ this.emitCrossedFrames(v, k, previousTime, v.currentTime, direction);
6660
+ this.completePlayback(k, v);
6661
+ return;
6662
+ }
6663
+ v.repeatCounter += loops;
6298
6664
  v.currentTime %= k.timeDuration;
6665
+ const event = this.createPlaybackEvent(v, k);
6666
+ v.playback._emitLoop(event);
6667
+ this.dispatchEvent('playbackloop', event);
6299
6668
  } else if (v.currentTime < 0) {
6300
- v.repeatCounter += Math.max(1, Math.ceil(-v.currentTime / k.timeDuration));
6669
+ const loops = Math.max(1, Math.ceil(-v.currentTime / k.timeDuration));
6670
+ if (v.repeat !== 0 && v.repeatCounter + loops >= v.repeat) {
6671
+ v.repeatCounter += loops;
6672
+ v.currentTime = 0;
6673
+ v.playback._setTime(v.currentTime);
6674
+ this.emitCrossedMarkers(v, k, previousTime, v.currentTime, direction);
6675
+ this.emitCrossedFrames(v, k, previousTime, v.currentTime, direction);
6676
+ this.completePlayback(k, v);
6677
+ return;
6678
+ }
6679
+ v.repeatCounter += loops;
6301
6680
  v.currentTime = (v.currentTime % k.timeDuration + k.timeDuration) % k.timeDuration;
6681
+ const event = this.createPlaybackEvent(v, k);
6682
+ v.playback._emitLoop(event);
6683
+ this.dispatchEvent('playbackloop', event);
6302
6684
  }
6303
6685
  }
6686
+ v.playback._setTime(v.currentTime);
6687
+ this.emitCrossedMarkers(v, k, previousTime, v.currentTime, direction);
6688
+ this.emitCrossedFrames(v, k, previousTime, v.currentTime, direction);
6304
6689
  if (v.repeat !== 0 && v.repeatCounter >= v.repeat) {
6305
- this.stopAnimation(k.name);
6690
+ this.completePlayback(k, v);
6306
6691
  } else if (v.fadeOut > 0) {
6307
6692
  if (v.animateTime - v.fadeOutStart >= v.fadeOut) {
6308
- this.stopAnimation(k.name);
6693
+ this.stopAnimation(k.name, {
6694
+ reason: v.stopReason
6695
+ });
6696
+ }
6697
+ }
6698
+ if (v.fadeToDuration > 0) {
6699
+ const t = Math.min(1, (v.animateTime - v.fadeToStart) / v.fadeToDuration);
6700
+ v.weight = v.fadeFromWeight + (v.fadeTargetWeight - v.fadeFromWeight) * t;
6701
+ v.playback._setWeight(v.weight);
6702
+ if (t >= 1) {
6703
+ v.fadeToDuration = 0;
6309
6704
  }
6310
6705
  }
6311
6706
  }
@@ -6345,12 +6740,17 @@ function bakeHumanoidRotationTracks(sourceClip, srcSkeleton, dstSkeleton, srcRoo
6345
6740
  this._skeletons.forEach((v)=>{
6346
6741
  v.get()?.apply();
6347
6742
  });
6743
+ // Advance logical-clock timers (e.g. timeline `wait` steps) with the same delta time,
6744
+ // so they stay in sync with playback and pause/scale with the game clock.
6745
+ if (this._timelineTickers.size > 0) {
6746
+ this._timelineTickers.forEach((ticker)=>ticker(deltaInSeconds));
6747
+ }
6348
6748
  }
6349
- /**
6350
- * Check whether an animation is currently playing.
6351
- *
6352
- * @param name - Optional animation name. If omitted, returns true if any animation is playing.
6353
- * @returns True if playing; otherwise false.
6749
+ /**
6750
+ * Check whether an animation is currently playing.
6751
+ *
6752
+ * @param name - Optional animation name. If omitted, returns true if any animation is playing.
6753
+ * @returns True if playing; otherwise false.
6354
6754
  */ isPlayingAnimation(name) {
6355
6755
  if (name) {
6356
6756
  const animation = this._animations[name];
@@ -6358,23 +6758,23 @@ function bakeHumanoidRotationTracks(sourceClip, srcSkeleton, dstSkeleton, srcRoo
6358
6758
  }
6359
6759
  return this._activeAnimations.size > 0;
6360
6760
  }
6361
- /**
6362
- * Get an animation clip by name.
6363
- *
6364
- * Alias of `get(name)` returning a nullable type.
6365
- *
6366
- * @param name - Name of the animation.
6367
- * @returns The clip if present; otherwise null.
6761
+ /**
6762
+ * Get an animation clip by name.
6763
+ *
6764
+ * Alias of `get(name)` returning a nullable type.
6765
+ *
6766
+ * @param name - Name of the animation.
6767
+ * @returns The clip if present; otherwise null.
6368
6768
  */ getAnimationClip(name) {
6369
6769
  return this._animations[name] ?? null;
6370
6770
  }
6371
- /**
6372
- * Set the runtime blend weight for a currently playing animation.
6373
- *
6374
- * Has no effect if the clip is not active.
6375
- *
6376
- * @param name - Name of the playing animation.
6377
- * @param weight - New weight value used during blending.
6771
+ /**
6772
+ * Set the runtime blend weight for a currently playing animation.
6773
+ *
6774
+ * Has no effect if the clip is not active.
6775
+ *
6776
+ * @param name - Name of the playing animation.
6777
+ * @param weight - New weight value used during blending.
6378
6778
  */ setAnimationWeight(name, weight) {
6379
6779
  const ani = this._animations[name];
6380
6780
  if (!ani) {
@@ -6384,42 +6784,104 @@ function bakeHumanoidRotationTracks(sourceClip, srcSkeleton, dstSkeleton, srcRoo
6384
6784
  const info = this._activeAnimations.get(ani);
6385
6785
  if (info) {
6386
6786
  info.weight = weight;
6787
+ info.targetWeight = weight;
6788
+ info.playback._setWeight(weight);
6387
6789
  }
6388
6790
  }
6389
- /**
6390
- * Start (or update) playback of an animation clip.
6391
- *
6392
- * Behavior:
6393
- * - If the clip is already playing, stops it first.
6394
- * - Otherwise initializes playback state (repeat counter, speed, weight, initial time).
6395
- * - Registers clip tracks and skeletons into the active sets for blending and application.
6396
- *
6397
- * @param name - Name of the animation to play.
6398
- * @param options - Playback options (repeat, speedRatio, fadeIn).
6399
- */ playAnimation(name, options) {
6791
+ /**
6792
+ * Create a playback handle without starting it.
6793
+ */ createPlayback(name, options) {
6400
6794
  const ani = this._animations[name];
6401
6795
  if (!ani) {
6402
6796
  console.error(`Animation ${name} not exists`);
6403
- return;
6797
+ return null;
6404
6798
  }
6405
- if (this.isPlayingAnimation(name)) {
6406
- this.stopAnimation(name);
6799
+ return new AnimationPlayback(this, ani, options);
6800
+ }
6801
+ /**
6802
+ * Start an animation and return its playback handle.
6803
+ */ play(name, options) {
6804
+ const playback = this.createPlayback(name, options);
6805
+ playback?.play();
6806
+ return playback;
6807
+ }
6808
+ /**
6809
+ * Get currently active playbacks.
6810
+ */ getPlaybacks(name) {
6811
+ const playbacks = [];
6812
+ this._activeAnimations.forEach((info, clip)=>{
6813
+ if (!name || clip.name === name) {
6814
+ playbacks.push(info.playback);
6815
+ }
6816
+ });
6817
+ return playbacks;
6818
+ }
6819
+ /**
6820
+ * Get the currently active playback for a clip.
6821
+ */ getPlayback(name) {
6822
+ const clip = this._animations[name];
6823
+ return clip ? this._activeAnimations.get(clip)?.playback ?? null : null;
6824
+ }
6825
+ /**
6826
+ * Start (or update) playback of an animation clip.
6827
+ *
6828
+ * Behavior:
6829
+ * - If the clip is already playing, stops it first.
6830
+ * - Otherwise initializes playback state (repeat counter, speed, weight, initial time).
6831
+ * - Registers clip tracks and skeletons into the active sets for blending and application.
6832
+ *
6833
+ * @param name - Name of the animation to play.
6834
+ * @param options - Playback options (repeat, speedRatio, fadeIn).
6835
+ */ playAnimation(name, options) {
6836
+ this.play(name, options);
6837
+ }
6838
+ /**
6839
+ * Start a previously created playback.
6840
+ * @internal
6841
+ */ startPlayback(playback) {
6842
+ const ani = playback.clip;
6843
+ if (!ani) {
6844
+ return;
6407
6845
  }
6846
+ const options = playback._getOptions();
6408
6847
  const fadeIn = Math.max(options?.fadeIn ?? 0, 0);
6409
6848
  const repeat = options?.repeat ?? 0;
6410
- const speedRatio = options?.speedRatio ?? 1;
6411
- const weight = options?.weight ?? ani.weight ?? 1;
6849
+ const speedRatio = playback.speedRatio;
6850
+ const weight = playback.weight;
6851
+ const rangeStart = ani.resolveTimeRef(options?.range?.start);
6852
+ const rangeEnd = ani.resolveTimeRef(options?.range?.end);
6853
+ const syncSource = this.resolvePlaybackSyncSource(options?.sync);
6854
+ const initialTime = this.computePlaybackInitialTime(ani, speedRatio, rangeStart, rangeEnd, options?.sync, syncSource);
6855
+ if (this.isPlayingAnimation(ani.name)) {
6856
+ this.stopAnimation(ani.name, {
6857
+ reason: 'replaced'
6858
+ });
6859
+ }
6860
+ playback._setState('playing');
6861
+ playback._setTime(initialTime);
6412
6862
  this._activeAnimations.set(ani, {
6863
+ playback,
6413
6864
  repeat,
6414
6865
  weight,
6866
+ targetWeight: weight,
6415
6867
  speedRatio,
6416
6868
  fadeIn,
6417
6869
  fadeOut: 0,
6418
6870
  repeatCounter: 0,
6419
- currentTime: speedRatio < 0 ? ani.timeDuration : 0,
6871
+ currentTime: initialTime,
6420
6872
  animateTime: 0,
6421
6873
  fadeOutStart: 0,
6422
- firstFrame: true
6874
+ fadeToStart: 0,
6875
+ fadeToDuration: 0,
6876
+ fadeFromWeight: weight,
6877
+ fadeTargetWeight: weight,
6878
+ firstFrame: true,
6879
+ started: false,
6880
+ paused: false,
6881
+ stopReason: 'manual',
6882
+ completed: false,
6883
+ rangeStart,
6884
+ rangeEnd
6423
6885
  });
6424
6886
  ani.tracks?.forEach((v, k)=>{
6425
6887
  let nodeTracks = this._activeTracks.get(k);
@@ -6456,18 +6918,257 @@ function bakeHumanoidRotationTracks(sourceClip, srcSkeleton, dstSkeleton, srcRoo
6456
6918
  }
6457
6919
  });
6458
6920
  }
6459
- /**
6460
- * Stop playback of an animation clip.
6461
- *
6462
- * Behavior:
6463
- * - If `options.fadeOut > 0`, marks the clip for fade-out; actual removal occurs after fade completes.
6464
- * - If `fadeOut` is 0 or omitted, immediately:
6465
- * - Removes the clip from active animations.
6466
- * - Unregisters its tracks from active track maps.
6467
- * - Decrements skeleton reference counts; resets and removes skeletons when refcount reaches 0.
6468
- *
6469
- * @param name - Name of the animation to stop.
6470
- * @param options - Optional fade-out configuration.
6921
+ resolvePlaybackSyncSource(sync) {
6922
+ if (!sync?.target) {
6923
+ return null;
6924
+ }
6925
+ const playback = this.resolveActivePlayback(sync.target);
6926
+ if (!playback) {
6927
+ return null;
6928
+ }
6929
+ const info = this._activeAnimations.get(playback.clip);
6930
+ return info?.playback === playback ? {
6931
+ playback,
6932
+ info,
6933
+ clip: playback.clip
6934
+ } : null;
6935
+ }
6936
+ resolveActivePlayback(target) {
6937
+ const named = this.getPlayback(target);
6938
+ if (named) {
6939
+ return named;
6940
+ }
6941
+ for (const info of this._activeAnimations.values()){
6942
+ if (info.playback.id === target) {
6943
+ return info.playback;
6944
+ }
6945
+ }
6946
+ return null;
6947
+ }
6948
+ computePlaybackInitialTime(clip, speedRatio, rangeStart, rangeEnd, sync, syncSource) {
6949
+ const fallback = speedRatio < 0 ? rangeEnd ?? rangeStart ?? clip.timeDuration : rangeStart ?? 0;
6950
+ if (!sync || !syncSource) {
6951
+ return fallback;
6952
+ }
6953
+ const destination = this.getPlaybackEffectiveRange(clip, rangeStart, rangeEnd);
6954
+ if (destination.duration <= 0) {
6955
+ return fallback;
6956
+ }
6957
+ const offset = Number.isFinite(sync.offset) ? sync.offset ?? 0 : 0;
6958
+ const wrap = sync.wrap ?? true;
6959
+ if ((sync.mode ?? 'normalized') === 'time') {
6960
+ return this.constrainPlaybackTime(syncSource.info.currentTime + offset, destination, wrap);
6961
+ }
6962
+ const source = this.getPlaybackEffectiveRange(syncSource.clip, syncSource.info.rangeStart, syncSource.info.rangeEnd);
6963
+ if (source.duration <= 0) {
6964
+ return fallback;
6965
+ }
6966
+ const phase = (syncSource.info.currentTime - source.start) / source.duration + offset;
6967
+ return this.constrainPlaybackTime(destination.start + phase * destination.duration, destination, wrap);
6968
+ }
6969
+ getPlaybackEffectiveRange(clip, rangeStart, rangeEnd) {
6970
+ const start = rangeStart ?? 0;
6971
+ const end = rangeEnd ?? clip.timeDuration;
6972
+ return start <= end ? {
6973
+ start,
6974
+ end,
6975
+ duration: end - start
6976
+ } : {
6977
+ start: end,
6978
+ end: start,
6979
+ duration: start - end
6980
+ };
6981
+ }
6982
+ constrainPlaybackTime(time, range, wrap) {
6983
+ if (!Number.isFinite(time)) {
6984
+ return range.start;
6985
+ }
6986
+ if (!wrap) {
6987
+ return Math.max(range.start, Math.min(range.end, time));
6988
+ }
6989
+ if (time >= range.start && time < range.end) {
6990
+ return time;
6991
+ }
6992
+ const offset = ((time - range.start) % range.duration + range.duration) % range.duration;
6993
+ return range.start + offset;
6994
+ }
6995
+ /**
6996
+ * Stop a playback handle.
6997
+ * @internal
6998
+ */ stopPlayback(playback, options) {
6999
+ if (this._activeAnimations.get(playback.clip)?.playback === playback) {
7000
+ this.stopAnimation(playback.clip.name, options);
7001
+ }
7002
+ }
7003
+ /**
7004
+ * Pause a playback handle.
7005
+ * @internal
7006
+ */ pausePlayback(playback) {
7007
+ const info = this._activeAnimations.get(playback.clip);
7008
+ if (info?.playback === playback && !info.paused) {
7009
+ info.paused = true;
7010
+ playback._setState('paused');
7011
+ const event = this.createPlaybackEvent(info, playback.clip);
7012
+ playback._emitPause(event);
7013
+ this.dispatchEvent('playbackpause', event);
7014
+ }
7015
+ }
7016
+ /**
7017
+ * Resume a playback handle.
7018
+ * @internal
7019
+ */ resumePlayback(playback) {
7020
+ const info = this._activeAnimations.get(playback.clip);
7021
+ if (info?.playback === playback && info.paused) {
7022
+ info.paused = false;
7023
+ playback._setState('playing');
7024
+ const event = this.createPlaybackEvent(info, playback.clip);
7025
+ playback._emitResume(event);
7026
+ this.dispatchEvent('playbackresume', event);
7027
+ }
7028
+ }
7029
+ /**
7030
+ * Seek a playback handle.
7031
+ * @internal
7032
+ */ seekPlayback(playback, time, options) {
7033
+ const clip = playback.clip;
7034
+ const info = this._activeAnimations.get(clip);
7035
+ const duration = clip.timeDuration;
7036
+ const clampedTime = duration > 0 ? Math.max(0, Math.min(duration, time)) : Math.max(0, time);
7037
+ const previousTime = info?.currentTime ?? playback.time;
7038
+ playback._setTime(clampedTime);
7039
+ if (info?.playback === playback) {
7040
+ info.currentTime = clampedTime;
7041
+ if (options?.emitEvents) {
7042
+ const direction = clampedTime >= previousTime ? 1 : -1;
7043
+ this.emitCrossedMarkers(info, clip, previousTime, clampedTime, direction);
7044
+ this.emitCrossedFrames(info, clip, previousTime, clampedTime, direction);
7045
+ }
7046
+ if (options?.apply) {
7047
+ this.update(0);
7048
+ }
7049
+ }
7050
+ }
7051
+ /**
7052
+ * Set playback weight.
7053
+ * @internal
7054
+ */ setPlaybackWeight(playback, weight) {
7055
+ playback._setWeight(weight);
7056
+ const info = this._activeAnimations.get(playback.clip);
7057
+ if (info?.playback === playback) {
7058
+ info.weight = weight;
7059
+ info.targetWeight = weight;
7060
+ }
7061
+ }
7062
+ /**
7063
+ * Set playback speed ratio.
7064
+ * @internal
7065
+ */ setPlaybackSpeedRatio(playback, speedRatio) {
7066
+ playback._setSpeedRatio(speedRatio);
7067
+ const info = this._activeAnimations.get(playback.clip);
7068
+ if (info?.playback === playback) {
7069
+ info.speedRatio = speedRatio;
7070
+ }
7071
+ }
7072
+ /**
7073
+ * Fade playback weight to a target value.
7074
+ * @internal
7075
+ */ fadePlaybackTo(playback, weight, duration) {
7076
+ const info = this._activeAnimations.get(playback.clip);
7077
+ if (info?.playback === playback) {
7078
+ info.fadeFromWeight = info.weight;
7079
+ info.fadeTargetWeight = weight;
7080
+ info.targetWeight = weight;
7081
+ info.fadeToStart = info.animateTime;
7082
+ info.fadeToDuration = Math.max(duration, 0);
7083
+ if (info.fadeToDuration === 0) {
7084
+ info.weight = weight;
7085
+ playback._setWeight(weight);
7086
+ }
7087
+ } else {
7088
+ playback._setWeight(weight);
7089
+ }
7090
+ }
7091
+ createPlaybackEvent(info, clip) {
7092
+ return {
7093
+ playback: info.playback,
7094
+ animationSet: this,
7095
+ clip,
7096
+ time: info.currentTime,
7097
+ normalizedTime: clip.timeDuration > 0 ? info.currentTime / clip.timeDuration : 0
7098
+ };
7099
+ }
7100
+ completePlayback(clip, info) {
7101
+ if (info.completed) {
7102
+ return;
7103
+ }
7104
+ const event = this.createPlaybackEvent(info, clip);
7105
+ const completionFadeOut = Math.max(info.playback._getOptions().completionFadeOut ?? 0, 0);
7106
+ info.playback._setState('completed');
7107
+ info.playback._emitComplete(event);
7108
+ this.dispatchEvent('playbackcomplete', event);
7109
+ if (completionFadeOut > 0) {
7110
+ info.completed = true;
7111
+ info.fadeOut = completionFadeOut;
7112
+ info.fadeOutStart = info.animateTime;
7113
+ info.stopReason = 'completed';
7114
+ return;
7115
+ }
7116
+ this.stopAnimation(clip.name, {
7117
+ reason: 'completed'
7118
+ });
7119
+ }
7120
+ emitCrossedMarkers(info, clip, fromTime, toTime, direction) {
7121
+ for (const marker of this.getCrossedMarkers(clip, fromTime, toTime, direction)){
7122
+ const event = {
7123
+ ...this.createPlaybackEvent(info, clip),
7124
+ marker,
7125
+ direction
7126
+ };
7127
+ info.playback._emitMarker(event);
7128
+ this.dispatchEvent('marker', event);
7129
+ }
7130
+ }
7131
+ emitCrossedFrames(info, clip, fromTime, toTime, direction) {
7132
+ for (const frame of info.playback._getWatchedFrames()){
7133
+ const frameTime = frame / clip.frameRate;
7134
+ const crossed = direction >= 0 ? this.crossedForward(fromTime, toTime, frameTime, clip.timeDuration) : this.crossedBackward(fromTime, toTime, frameTime, clip.timeDuration);
7135
+ if (crossed) {
7136
+ const event = {
7137
+ ...this.createPlaybackEvent(info, clip),
7138
+ frame
7139
+ };
7140
+ info.playback._emitFrame(event);
7141
+ this.dispatchEvent('frame', event);
7142
+ }
7143
+ }
7144
+ }
7145
+ getCrossedMarkers(clip, fromTime, toTime, direction) {
7146
+ return clip.markers.filter((marker)=>{
7147
+ const markerTime = clip.resolveMarkerTime(marker);
7148
+ if (markerTime === null) {
7149
+ return false;
7150
+ }
7151
+ return direction >= 0 ? this.crossedForward(fromTime, toTime, markerTime, clip.timeDuration) : this.crossedBackward(fromTime, toTime, markerTime, clip.timeDuration);
7152
+ });
7153
+ }
7154
+ crossedForward(fromTime, toTime, targetTime, duration) {
7155
+ return toTime >= fromTime ? targetTime > fromTime && targetTime <= toTime : duration > 0 && targetTime > fromTime && targetTime <= duration || targetTime <= toTime;
7156
+ }
7157
+ crossedBackward(fromTime, toTime, targetTime, duration) {
7158
+ return toTime <= fromTime ? targetTime < fromTime && targetTime >= toTime : duration > 0 && targetTime < fromTime && targetTime >= 0 || targetTime >= toTime;
7159
+ }
7160
+ /**
7161
+ * Stop playback of an animation clip.
7162
+ *
7163
+ * Behavior:
7164
+ * - If `options.fadeOut > 0`, marks the clip for fade-out; actual removal occurs after fade completes.
7165
+ * - If `fadeOut` is 0 or omitted, immediately:
7166
+ * - Removes the clip from active animations.
7167
+ * - Unregisters its tracks from active track maps.
7168
+ * - Decrements skeleton reference counts; resets and removes skeletons when refcount reaches 0.
7169
+ *
7170
+ * @param name - Name of the animation to stop.
7171
+ * @param options - Optional fade-out configuration.
6471
7172
  */ stopAnimation(name, options) {
6472
7173
  const ani = this._animations[name];
6473
7174
  if (!ani) {
@@ -6477,9 +7178,12 @@ function bakeHumanoidRotationTracks(sourceClip, srcSkeleton, dstSkeleton, srcRoo
6477
7178
  const info = this._activeAnimations.get(ani);
6478
7179
  if (info) {
6479
7180
  const fadeOut = Math.max(options?.fadeOut ?? 0, 0);
7181
+ const reason = options?.reason ?? 'manual';
6480
7182
  if (fadeOut !== 0) {
6481
7183
  info.fadeOut = fadeOut;
6482
7184
  info.fadeOutStart = -1;
7185
+ info.stopReason = reason;
7186
+ info.playback._setState('stopping');
6483
7187
  } else {
6484
7188
  this._activeAnimations.delete(ani);
6485
7189
  this._activeTracks.forEach((v)=>{
@@ -6517,20 +7221,29 @@ function bakeHumanoidRotationTracks(sourceClip, srcSkeleton, dstSkeleton, srcRoo
6517
7221
  }
6518
7222
  }
6519
7223
  });
7224
+ const event = {
7225
+ ...this.createPlaybackEvent(info, ani),
7226
+ reason
7227
+ };
7228
+ if (reason !== 'completed') {
7229
+ info.playback._setState('stopped');
7230
+ }
7231
+ info.playback._emitStop(event);
7232
+ this.dispatchEvent('playbackstop', event);
6520
7233
  }
6521
7234
  }
6522
7235
  }
6523
- /**
6524
- * Create a skeletal-only masked clip from an existing clip in this set.
6525
- *
6526
- * The generated clip is a regular `AnimationClip`: it contains cloned node transform tracks
6527
- * for the selected rig joints and can be played/blended through the normal animation system.
6528
- * Non-skeletal tracks are skipped by default.
6529
- *
6530
- * @param sourceName - Name of the source clip.
6531
- * @param targetName - Name of the generated clip.
6532
- * @param options - Humanoid semantic or joint-name based mask options.
6533
- * @returns The generated clip, or null on failure.
7236
+ /**
7237
+ * Create a skeletal-only masked clip from an existing clip in this set.
7238
+ *
7239
+ * The generated clip is a regular `AnimationClip`: it contains cloned node transform tracks
7240
+ * for the selected rig joints and can be played/blended through the normal animation system.
7241
+ * Non-skeletal tracks are skipped by default.
7242
+ *
7243
+ * @param sourceName - Name of the source clip.
7244
+ * @param targetName - Name of the generated clip.
7245
+ * @param options - Humanoid semantic or joint-name based mask options.
7246
+ * @returns The generated clip, or null on failure.
6534
7247
  */ createSkeletalMaskedAnimation(sourceName, targetName, options) {
6535
7248
  const sourceClip = this.get(sourceName);
6536
7249
  if (!sourceClip) {
@@ -6737,21 +7450,21 @@ function bakeHumanoidRotationTracks(sourceClip, srcSkeleton, dstSkeleton, srcRoo
6737
7450
  }
6738
7451
  return dstClip;
6739
7452
  }
6740
- /**
6741
- * Copy an animation clip from another AnimationSet into this one.
6742
- *
6743
- * Prerequisites:
6744
- * - Both sets must reference skeletons with identical joint names and counts.
6745
- * - The source clip must exist in `sourceSet`.
6746
- *
6747
- * @param sourceSet - The AnimationSet to copy from.
6748
- * @param animationName - Name of the clip to copy.
6749
- * @param targetName - Name for the new clip in this set. Defaults to `animationName`.
6750
- * @param excludeJoint - Optional predicate; joints whose name returns true are excluded from
6751
- * skeleton structure matching.
6752
- * @returns The newly created AnimationClip, or null on failure.
6753
- *
6754
- * @deprecated Use AnimationSet.copyHumanoidAnimationFrom instead.
7453
+ /**
7454
+ * Copy an animation clip from another AnimationSet into this one.
7455
+ *
7456
+ * Prerequisites:
7457
+ * - Both sets must reference skeletons with identical joint names and counts.
7458
+ * - The source clip must exist in `sourceSet`.
7459
+ *
7460
+ * @param sourceSet - The AnimationSet to copy from.
7461
+ * @param animationName - Name of the clip to copy.
7462
+ * @param targetName - Name for the new clip in this set. Defaults to `animationName`.
7463
+ * @param excludeJoint - Optional predicate; joints whose name returns true are excluded from
7464
+ * skeleton structure matching.
7465
+ * @returns The newly created AnimationClip, or null on failure.
7466
+ *
7467
+ * @deprecated Use AnimationSet.copyHumanoidAnimationFrom instead.
6755
7468
  */ copyAnimationFrom(sourceSet, animationName, targetName, excludeJoint) {
6756
7469
  const destName = targetName ?? animationName;
6757
7470
  const sourceClip = sourceSet.get(animationName);
@@ -6897,8 +7610,8 @@ function bakeHumanoidRotationTracks(sourceClip, srcSkeleton, dstSkeleton, srcRoo
6897
7610
  return ordered;
6898
7611
  }
6899
7612
  }
6900
- /**
6901
- * Reset all skeleton modifiers
7613
+ /**
7614
+ * Reset all skeleton modifiers
6902
7615
  */ resetSkeletonModifiers() {
6903
7616
  for (const sk of this._skeletons){
6904
7617
  for (const modifier of sk.get().modifiers){
@@ -6906,12 +7619,12 @@ function bakeHumanoidRotationTracks(sourceClip, srcSkeleton, dstSkeleton, srcRoo
6906
7619
  }
6907
7620
  }
6908
7621
  }
6909
- /**
6910
- * Dispose the animation set and release owned resources.
6911
- *
6912
- * - Disposes the weak reference to the model.
6913
- * - Disposes all registered animation clips.
6914
- * - Clears active animations, tracks, and skeleton references.
7622
+ /**
7623
+ * Dispose the animation set and release owned resources.
7624
+ *
7625
+ * - Disposes the weak reference to the model.
7626
+ * - Disposes all registered animation clips.
7627
+ * - Clears active animations, tracks, and skeleton references.
6915
7628
  */ onDispose() {
6916
7629
  super.onDispose();
6917
7630
  for(const k in this._animations){
@@ -13718,9 +14431,13 @@ FEATURE_DISABLE_TAA = MeshMaterial.defineFeature();
13718
14431
  pb.vec3('worldPos'),
13719
14432
  pb.vec4('posRange')
13720
14433
  ], function() {
13721
- this.$l.dist = pb.distance(this.posRange.xyz, this.worldPos);
13722
- this.$l.falloff = pb.max(0, pb.sub(1, pb.div(this.dist, this.posRange.w)));
13723
- this.$return(pb.mul(this.falloff, this.falloff));
14434
+ this.$l.d = pb.sub(this.worldPos, this.posRange.xyz);
14435
+ this.$l.dist2 = pb.dot(this.d, this.d);
14436
+ this.$l.lightAtten = pb.div(1, pb.max(this.dist2, 0.0001));
14437
+ this.$l.range2 = pb.mul(this.posRange.w, this.posRange.w);
14438
+ this.$l.f = pb.div(this.dist2, pb.max(this.range2, 0.0001));
14439
+ this.$l.f2 = pb.clamp(pb.sub(1, pb.mul(this.f, this.f)), 0, 1);
14440
+ this.$return(pb.mul(this.lightAtten, pb.mul(this.f2, this.f2)));
13724
14441
  });
13725
14442
  return pb.getGlobalScope()[funcName](worldPos, posRange);
13726
14443
  }
@@ -55763,7 +56480,7 @@ class HeightBoundingGenerator extends RenderMipmap {
55763
56480
  name: 'FXAAEnabled',
55764
56481
  type: 'bool',
55765
56482
  phase: 0,
55766
- default: true,
56483
+ default: false,
55767
56484
  options: {
55768
56485
  label: 'Enabled',
55769
56486
  group: 'PostProcessing/FXAA'
@@ -81681,6 +82398,1474 @@ function dot(ax, ay, bx, by) {
81681
82398
  return ax * bx + ay * by;
81682
82399
  }
81683
82400
 
82401
+ /**
82402
+ * Serializable animation timeline definition.
82403
+ * @public
82404
+ */ class AnimationTimeline {
82405
+ /**
82406
+ * Root step sequence executed by runners created from this timeline.
82407
+ */ steps;
82408
+ /**
82409
+ * Event responses declared on this timeline.
82410
+ */ responses;
82411
+ /**
82412
+ * Create a timeline from a definition object or a root step array.
82413
+ *
82414
+ * @param definition - Timeline definition, or a shorthand array used as the root steps.
82415
+ */ constructor(definition){
82416
+ if (Array.isArray(definition)) {
82417
+ this.steps = definition;
82418
+ this.responses = [];
82419
+ } else {
82420
+ this.steps = definition.steps;
82421
+ this.responses = definition.responses ?? [];
82422
+ }
82423
+ }
82424
+ /**
82425
+ * Create a runtime runner for this timeline.
82426
+ *
82427
+ * @param animationSet - Animation set used to create and update playbacks.
82428
+ * @returns A new stopped runner bound to this timeline and animation set.
82429
+ */ createRunner(animationSet) {
82430
+ return new AnimationTimelineRunner(animationSet, this);
82431
+ }
82432
+ }
82433
+ /**
82434
+ * Runtime interpreter for an AnimationTimeline.
82435
+ *
82436
+ * The interpreter is a synchronous frame-stack state machine advanced by {@link AnimationTimelineRunner.tick}, which is
82437
+ * driven by `AnimationSet.update(dt)` on the same logical clock as the animations. There is no
82438
+ * `async`/`await` in the control flow, so the runtime state can be serialized and replayed.
82439
+ * @public
82440
+ */ class AnimationTimelineRunner extends Observable {
82441
+ /**
82442
+ * Animation set used to start, stop, and query animation playbacks.
82443
+ */ animationSet;
82444
+ /**
82445
+ * Timeline definition interpreted by this runner.
82446
+ */ timeline;
82447
+ _stack;
82448
+ _concurrent;
82449
+ _queued;
82450
+ _pendingEvents;
82451
+ _stopped;
82452
+ _ticking;
82453
+ _lastCompletedPlaybackId;
82454
+ _onTick;
82455
+ /** Tracks playbacks created by this runner so `stop()` can tear them down. */ _ownedPlaybacks;
82456
+ /** Local playback refs that survive after the sequence frame that declared them drains. */ _playbackRefs;
82457
+ /** Ids of playbacks owned by concurrent (keep-active) branches; preserved across main-flow stops. */ _concurrentPlaybackIds;
82458
+ /** Marker/frame crossings observed since the last tick, keyed by playback id. */ _crossedMarkers;
82459
+ _crossedFrames;
82460
+ /**
82461
+ * Create a stopped runner for a timeline.
82462
+ *
82463
+ * @param animationSet - Animation set that owns clips and playbacks referenced by the timeline.
82464
+ * @param timeline - Timeline definition to interpret.
82465
+ */ constructor(animationSet, timeline){
82466
+ super();
82467
+ this.animationSet = animationSet;
82468
+ this.timeline = timeline;
82469
+ this._stack = [];
82470
+ this._concurrent = [];
82471
+ this._queued = [];
82472
+ this._pendingEvents = [];
82473
+ this._stopped = true;
82474
+ this._ticking = false;
82475
+ this._lastCompletedPlaybackId = null;
82476
+ this._onTick = (deltaInSeconds)=>this.tick(deltaInSeconds);
82477
+ this._ownedPlaybacks = new Map();
82478
+ this._playbackRefs = new Map();
82479
+ this._concurrentPlaybackIds = new Set();
82480
+ this._crossedMarkers = new Map();
82481
+ this._crossedFrames = new Map();
82482
+ }
82483
+ /**
82484
+ * Playback currently referenced by the active main-flow scope.
82485
+ *
82486
+ * @returns The current playback, or null when the main flow has no active playback reference.
82487
+ */ get currentPlayback() {
82488
+ const scope = this.activeScope();
82489
+ if (!scope?.currentPlaybackId) {
82490
+ return null;
82491
+ }
82492
+ return this._ownedPlaybacks.get(scope.currentPlaybackId) ?? null;
82493
+ }
82494
+ /**
82495
+ * Whether this runner is stopped.
82496
+ *
82497
+ * @returns True when the runner is stopped; otherwise false.
82498
+ */ get stopped() {
82499
+ return this._stopped;
82500
+ }
82501
+ /**
82502
+ * Playback id for the most recent playback that completed naturally.
82503
+ *
82504
+ * @returns The playback id, or null when no owned playback has completed.
82505
+ */ get lastCompletedPlaybackId() {
82506
+ return this._lastCompletedPlaybackId;
82507
+ }
82508
+ /**
82509
+ * Start or restart the runner from the beginning of the timeline.
82510
+ *
82511
+ * @returns This runner for chaining.
82512
+ */ start() {
82513
+ this._stopped = false;
82514
+ this._stack = [
82515
+ this.makeSeqFrame(this.timeline.steps)
82516
+ ];
82517
+ this._concurrent = [];
82518
+ this._queued.length = 0;
82519
+ this._pendingEvents = [];
82520
+ this._lastCompletedPlaybackId = null;
82521
+ this._playbackRefs.clear();
82522
+ this.animationSet._registerTimelineTicker(this._onTick);
82523
+ // Drain any leading non-blocking steps immediately so e.g. an initial `play` starts now.
82524
+ this.flush();
82525
+ return this;
82526
+ }
82527
+ /**
82528
+ * Stop the runner and all playbacks it owns.
82529
+ *
82530
+ * @param options - Optional stop behavior applied to owned playbacks.
82531
+ * @returns This runner for chaining.
82532
+ */ stop(options) {
82533
+ const wasStopped = this._stopped;
82534
+ this._stopped = true;
82535
+ this._stack = [];
82536
+ this._concurrent = [];
82537
+ this._queued.length = 0;
82538
+ this._pendingEvents = [];
82539
+ this._crossedMarkers.clear();
82540
+ this._crossedFrames.clear();
82541
+ this._lastCompletedPlaybackId = null;
82542
+ const stopOptions = options ?? {
82543
+ reason: 'interrupted'
82544
+ };
82545
+ // Always tear down owned playbacks, even if the control flow already drained: a state whose
82546
+ // script finished may still have a looping clip playing that a transition must stop.
82547
+ this._ownedPlaybacks.forEach((playback)=>{
82548
+ this.detachPlayback(playback);
82549
+ playback.stop(stopOptions);
82550
+ });
82551
+ this._ownedPlaybacks.clear();
82552
+ this._playbackRefs.clear();
82553
+ this._concurrentPlaybackIds.clear();
82554
+ this.animationSet._unregisterTimelineTicker(this._onTick);
82555
+ if (!wasStopped) {
82556
+ this.dispatchEvent('stop', this);
82557
+ }
82558
+ return this;
82559
+ }
82560
+ /**
82561
+ * Append a batch of steps to run after the main stack drains.
82562
+ *
82563
+ * If the runner has already completed, enqueueing steps revives it and registers it for ticking.
82564
+ *
82565
+ * @param steps - Steps to run as the next queued batch.
82566
+ * @returns void
82567
+ */ enqueue(steps) {
82568
+ if (steps.length === 0) {
82569
+ return;
82570
+ }
82571
+ this._queued.push(steps);
82572
+ // Revive a runner that already drained so queued steps are not silently dropped.
82573
+ if (this._stopped) {
82574
+ this._stopped = false;
82575
+ this._stack = [];
82576
+ this.animationSet._registerTimelineTicker(this._onTick);
82577
+ }
82578
+ this.flush();
82579
+ }
82580
+ /**
82581
+ * Run `steps` concurrently with the current control flow (a true parallel branch). Unlike
82582
+ * {@link AnimationTimelineRunner.enqueue}, these do not wait for the main stack to drain.
82583
+ *
82584
+ * @param steps - Steps to run immediately in an independent concurrent branch.
82585
+ * @returns void
82586
+ * @public
82587
+ */ runConcurrent(steps) {
82588
+ if (steps.length === 0) {
82589
+ return;
82590
+ }
82591
+ this._concurrent.push(this.makeSeqFrame(steps, 'concurrent'));
82592
+ if (this._stopped) {
82593
+ this._stopped = false;
82594
+ this.animationSet._registerTimelineTicker(this._onTick);
82595
+ } else {
82596
+ this.ensureTicking();
82597
+ }
82598
+ this.flush();
82599
+ }
82600
+ /**
82601
+ * Dispatch an event to this runner.
82602
+ *
82603
+ * Waiting `waitEvent` frames consume matching events first. If no waiter consumes the event,
82604
+ * the timeline response table is evaluated.
82605
+ *
82606
+ * @param event - Event name to dispatch.
82607
+ * @param payload - Optional payload returned in the result.
82608
+ * @returns The resolved handling result for the event.
82609
+ */ dispatch(event, payload) {
82610
+ // A waiting `waitEvent` frame consumes the event; flush advances past it synchronously.
82611
+ if (this.hasWaiterFor(event)) {
82612
+ this._pendingEvents.push(event);
82613
+ this.flush();
82614
+ return {
82615
+ handled: true,
82616
+ policy: 'consume',
82617
+ event,
82618
+ payload
82619
+ };
82620
+ }
82621
+ const response = this.timeline.responses.find((item)=>item.event === event);
82622
+ if (!response || response.target.ignore) {
82623
+ return {
82624
+ handled: false,
82625
+ policy: response ? 'ignore' : 'none',
82626
+ event,
82627
+ payload
82628
+ };
82629
+ }
82630
+ if (response.target.consume) {
82631
+ return {
82632
+ handled: true,
82633
+ policy: 'consume',
82634
+ event,
82635
+ payload
82636
+ };
82637
+ }
82638
+ if (response.target.targetState !== undefined) {
82639
+ // Only the controller can switch states; bubble up so it can act on its own table.
82640
+ return {
82641
+ handled: false,
82642
+ policy: 'transition',
82643
+ event,
82644
+ payload
82645
+ };
82646
+ }
82647
+ const steps = response.target.steps;
82648
+ if (!steps?.length) {
82649
+ return {
82650
+ handled: false,
82651
+ policy: 'none',
82652
+ event,
82653
+ payload
82654
+ };
82655
+ }
82656
+ if (response.enqueue) {
82657
+ this.enqueue(steps);
82658
+ return {
82659
+ handled: true,
82660
+ policy: 'enqueue',
82661
+ event,
82662
+ payload
82663
+ };
82664
+ }
82665
+ const disposition = response.onActive ?? 'stop';
82666
+ if (disposition === 'keep') {
82667
+ // Run the new steps concurrently with the existing control flow (true parallel branch).
82668
+ this.runConcurrent(steps);
82669
+ } else {
82670
+ const stopOptions = typeof disposition === 'object' ? {
82671
+ ...disposition,
82672
+ reason: 'interrupted'
82673
+ } : undefined;
82674
+ this.stopMainFlow(stopOptions);
82675
+ this._stopped = false;
82676
+ this._stack = [
82677
+ this.makeSeqFrame(steps)
82678
+ ];
82679
+ this.ensureTicking();
82680
+ this.flush();
82681
+ }
82682
+ return {
82683
+ handled: true,
82684
+ policy: 'steps',
82685
+ event,
82686
+ payload
82687
+ };
82688
+ }
82689
+ /**
82690
+ * Run pending non-blocking work synchronously (a zero-delta tick), without advancing any
82691
+ * time-based waits. Lets `start()`/`dispatch()` take effect immediately while keeping all
82692
+ * runtime state in the serializable frame stack.
82693
+ *
82694
+ * @returns This runner for chaining.
82695
+ * @public
82696
+ */ flush() {
82697
+ // Guard against re-entrancy: an `emit` listener firing during a tick may dispatch again.
82698
+ if (!this._ticking) {
82699
+ this.tick(0);
82700
+ }
82701
+ return this;
82702
+ }
82703
+ /**
82704
+ * Advance the timeline by `deltaInSeconds`. Called by `AnimationSet.update`.
82705
+ *
82706
+ * @param deltaInSeconds - Elapsed time in seconds for this tick.
82707
+ * @returns void
82708
+ * @public
82709
+ */ tick(deltaInSeconds) {
82710
+ if (this._stopped && this._queued.length === 0) {
82711
+ return;
82712
+ }
82713
+ this._ticking = true;
82714
+ // Advance the main control-flow stack.
82715
+ if (this._stack.length === 0 && this._queued.length > 0) {
82716
+ const next = this._queued.shift();
82717
+ this._stack = [
82718
+ this.makeSeqFrame(next)
82719
+ ];
82720
+ }
82721
+ if (this._stack.length > 0) {
82722
+ this.tickFrames(this._stack, deltaInSeconds);
82723
+ }
82724
+ // Advance any concurrent (keep-active) branches; drop the ones that finished.
82725
+ if (this._concurrent.length > 0) {
82726
+ this._concurrent = this._concurrent.filter((frame)=>{
82727
+ const stack = [
82728
+ frame
82729
+ ];
82730
+ this.tickFrames(stack, deltaInSeconds);
82731
+ return stack.length > 0;
82732
+ });
82733
+ }
82734
+ // Consumed this tick.
82735
+ this._pendingEvents = [];
82736
+ this._crossedMarkers.clear();
82737
+ this._crossedFrames.clear();
82738
+ this._ticking = false;
82739
+ // Pull queued batches once the stack drains.
82740
+ while(this._stack.length === 0 && this._queued.length > 0){
82741
+ const next = this._queued.shift();
82742
+ this._stack = [
82743
+ this.makeSeqFrame(next)
82744
+ ];
82745
+ this.tickFrames(this._stack, 0);
82746
+ }
82747
+ if (!this._stopped && this._stack.length === 0 && this._concurrent.length === 0 && this._queued.length === 0) {
82748
+ this._stopped = true;
82749
+ this.animationSet._unregisterTimelineTicker(this._onTick);
82750
+ this.dispatchEvent('complete', this);
82751
+ }
82752
+ }
82753
+ /**
82754
+ * Export the runtime state as plain data.
82755
+ *
82756
+ * @returns A serializable snapshot of the runner state.
82757
+ * @public
82758
+ */ serialize() {
82759
+ return {
82760
+ stack: this._stack.map((frame)=>this.cloneFrame(frame)),
82761
+ concurrent: this._concurrent.map((frame)=>this.cloneFrame(frame)),
82762
+ queued: this._queued.map((steps)=>steps.slice()),
82763
+ concurrentPlaybackIds: [
82764
+ ...this._concurrentPlaybackIds
82765
+ ],
82766
+ playbackRefs: Object.fromEntries(this._playbackRefs),
82767
+ stopped: this._stopped
82768
+ };
82769
+ }
82770
+ /**
82771
+ * Restore runtime state previously produced by {@link AnimationTimelineRunner.serialize}.
82772
+ *
82773
+ * Re-create the relevant active playbacks on the AnimationSet before calling this so that
82774
+ * playback-bound frames (play-wait, waitMarker, waitFrame) can re-attach by id.
82775
+ *
82776
+ * @param animationSet - Animation set containing any live playbacks referenced by the state.
82777
+ * @param timeline - Timeline definition to bind to the restored runner.
82778
+ * @param state - Serialized state previously returned by {@link AnimationTimelineRunner.serialize}.
82779
+ * @returns A runner restored to the supplied runtime state.
82780
+ * @public
82781
+ */ static deserialize(animationSet, timeline, state) {
82782
+ const runner = new AnimationTimelineRunner(animationSet, timeline);
82783
+ runner._stack = state.stack.map((frame)=>runner.cloneFrame(frame));
82784
+ runner._concurrent = state.concurrent.map((frame)=>runner.cloneFrame(frame));
82785
+ state.queued.forEach((steps)=>runner._queued.push(steps.slice()));
82786
+ state.concurrentPlaybackIds?.forEach((id)=>runner._concurrentPlaybackIds.add(id));
82787
+ Object.entries(state.playbackRefs ?? {}).forEach(([ref, id])=>runner._playbackRefs.set(ref, id));
82788
+ runner._stopped = state.stopped;
82789
+ // Re-attach to any live playbacks referenced by the restored frames.
82790
+ runner.reattachPlaybacks(runner._stack);
82791
+ runner._concurrent.forEach((frame)=>runner.reattachPlaybacks([
82792
+ frame
82793
+ ]));
82794
+ // Re-attach drained concurrent playbacks tracked only by id, so they survive future stops.
82795
+ runner._concurrentPlaybackIds.forEach((id)=>{
82796
+ if (!runner._ownedPlaybacks.has(id)) {
82797
+ const playback = runner.findLivePlayback(id);
82798
+ if (playback) {
82799
+ runner.attachPlayback(playback);
82800
+ }
82801
+ }
82802
+ });
82803
+ runner._playbackRefs.forEach((id)=>{
82804
+ if (!runner._ownedPlaybacks.has(id)) {
82805
+ const playback = runner.findLivePlayback(id);
82806
+ if (playback) {
82807
+ runner.attachPlayback(playback);
82808
+ }
82809
+ }
82810
+ });
82811
+ if (!runner._stopped) {
82812
+ animationSet._registerTimelineTicker(runner._onTick);
82813
+ }
82814
+ return runner;
82815
+ }
82816
+ // --- internals -------------------------------------------------------------
82817
+ ensureTicking() {
82818
+ if (!this._ticking) {
82819
+ this.animationSet._registerTimelineTicker(this._onTick);
82820
+ }
82821
+ }
82822
+ makeSeqFrame(steps, owner = 'main') {
82823
+ return {
82824
+ kind: 'seq',
82825
+ steps,
82826
+ index: 0,
82827
+ scope: {
82828
+ currentPlaybackId: null,
82829
+ refs: {},
82830
+ owner
82831
+ },
82832
+ child: null
82833
+ };
82834
+ }
82835
+ /** The scope of the innermost active sequence on the main stack (for currentPlayback). */ activeScope() {
82836
+ for(let i = this._stack.length - 1; i >= 0; i--){
82837
+ const scope = this.deepestScope(this._stack[i]);
82838
+ if (scope) {
82839
+ return scope;
82840
+ }
82841
+ }
82842
+ return null;
82843
+ }
82844
+ deepestScope(frame) {
82845
+ if (frame.kind === 'seq') {
82846
+ return frame.child ? this.deepestScope(frame.child) ?? frame.scope : frame.scope;
82847
+ }
82848
+ return null;
82849
+ }
82850
+ /**
82851
+ * Advance a frame stack in place. The top frame runs until it blocks or completes; completed
82852
+ * frames pop and the parent sequence advances. Returns when the stack blocks or empties.
82853
+ */ tickFrames(stack, deltaInSeconds) {
82854
+ this.tickFramesWithLeftover(stack, deltaInSeconds);
82855
+ }
82856
+ /**
82857
+ * Same as {@link AnimationTimelineRunner.tickFrames} but returns the time left unconsumed when the stack drains
82858
+ * completely (so a parent sequence can hand it to its next step).
82859
+ */ tickFramesWithLeftover(stack, deltaInSeconds) {
82860
+ let guard = 0;
82861
+ while(stack.length > 0){
82862
+ if (++guard > 10000) {
82863
+ break;
82864
+ }
82865
+ const frame = stack[stack.length - 1];
82866
+ const result = this.tickFrame(frame, stack, deltaInSeconds);
82867
+ if (result.status === 'block') {
82868
+ return 0;
82869
+ }
82870
+ if (result.status === 'pop') {
82871
+ stack.pop();
82872
+ // Carry any unused time (e.g. a `wait` that overshot its duration this tick) to the next
82873
+ // step so a sequence of waits/actions tracks logical time regardless of frame rate.
82874
+ deltaInSeconds = result.leftover ?? 0;
82875
+ continue;
82876
+ }
82877
+ // 'advanced': the frame pushed a child or moved its cursor; loop again with delta 0 so a
82878
+ // single tick can drain consecutive non-blocking steps.
82879
+ deltaInSeconds = 0;
82880
+ }
82881
+ return deltaInSeconds;
82882
+ }
82883
+ tickFrame(frame, stack, deltaInSeconds) {
82884
+ switch(frame.kind){
82885
+ case 'seq':
82886
+ return this.tickSeq(frame, stack, deltaInSeconds);
82887
+ case 'parallel':
82888
+ return this.tickParallel(frame, deltaInSeconds);
82889
+ case 'wait':
82890
+ frame.remaining -= deltaInSeconds;
82891
+ // On completion, hand back the overshoot (negative remaining) as leftover time.
82892
+ return frame.remaining <= 0 ? {
82893
+ status: 'pop',
82894
+ leftover: -frame.remaining
82895
+ } : {
82896
+ status: 'block'
82897
+ };
82898
+ case 'waitEvent':
82899
+ return this._pendingEvents.includes(frame.event) ? {
82900
+ status: 'pop'
82901
+ } : {
82902
+ status: 'block'
82903
+ };
82904
+ case 'waitMarker':
82905
+ return this.tickWaitMarker(frame);
82906
+ case 'waitFrame':
82907
+ return this.tickWaitFrame(frame);
82908
+ case 'playWait':
82909
+ return this.tickPlayWait(frame);
82910
+ }
82911
+ }
82912
+ tickSeq(frame, _stack, deltaInSeconds) {
82913
+ // Drain steps in a loop so unconsumed time (e.g. a `wait` that overshot) flows into the next
82914
+ // step within the same tick instead of being dropped.
82915
+ let guard = 0;
82916
+ for(;;){
82917
+ if (++guard > 10000) {
82918
+ return {
82919
+ status: 'block'
82920
+ };
82921
+ }
82922
+ if (frame.child) {
82923
+ const childStack = [
82924
+ frame.child
82925
+ ];
82926
+ const leftover = this.tickFramesWithLeftover(childStack, deltaInSeconds);
82927
+ if (childStack.length > 0) {
82928
+ return {
82929
+ status: 'block'
82930
+ };
82931
+ }
82932
+ frame.child = null;
82933
+ frame.index++;
82934
+ deltaInSeconds = leftover;
82935
+ }
82936
+ if (frame.index >= frame.steps.length) {
82937
+ return {
82938
+ status: 'pop',
82939
+ leftover: deltaInSeconds
82940
+ };
82941
+ }
82942
+ const step = frame.steps[frame.index];
82943
+ const blocking = this.beginStep(step, frame);
82944
+ if (blocking) {
82945
+ // Tick the freshly-pushed child immediately with the carried time on the next loop turn.
82946
+ frame.child = blocking;
82947
+ continue;
82948
+ }
82949
+ frame.index++;
82950
+ // A non-blocking step consumes no time; keep the remaining delta for the following step.
82951
+ }
82952
+ }
82953
+ tickParallel(frame, deltaInSeconds) {
82954
+ frame.branches = frame.branches.filter((branch)=>{
82955
+ const branchStack = [
82956
+ branch
82957
+ ];
82958
+ this.tickFrames(branchStack, deltaInSeconds);
82959
+ return branchStack.length > 0;
82960
+ });
82961
+ // Parallel branches may finish at different times; we do not attempt to reconcile a single
82962
+ // leftover across them, so the join simply consumes the whole tick.
82963
+ return frame.branches.length > 0 ? {
82964
+ status: 'block'
82965
+ } : {
82966
+ status: 'pop'
82967
+ };
82968
+ }
82969
+ /**
82970
+ * Execute a non-blocking step immediately and return null, or return a frame to block on.
82971
+ */ beginStep(step, scopeFrame) {
82972
+ const scope = scopeFrame.scope;
82973
+ switch(step.type){
82974
+ case 'sequence':
82975
+ {
82976
+ const child = this.makeSeqFrame(step.steps);
82977
+ // Inherit the parent scope so refs/currentPlayback carry into the nested sequence.
82978
+ child.scope = scope;
82979
+ return child;
82980
+ }
82981
+ case 'parallel':
82982
+ {
82983
+ // Each branch gets an isolated scope so currentPlayback/refs don't race (#7), but inherits
82984
+ // the parent's owner so concurrent branches stay concurrent through nested parallels.
82985
+ const branches = step.steps.map((child)=>{
82986
+ const branchScope = {
82987
+ currentPlaybackId: scope.currentPlaybackId,
82988
+ refs: {
82989
+ ...scope.refs
82990
+ },
82991
+ owner: scope.owner
82992
+ };
82993
+ const seq = this.makeSeqFrame([
82994
+ child
82995
+ ]);
82996
+ seq.scope = branchScope;
82997
+ return seq;
82998
+ });
82999
+ return {
83000
+ kind: 'parallel',
83001
+ branches
83002
+ };
83003
+ }
83004
+ case 'play':
83005
+ {
83006
+ const playback = this.animationSet.play(step.clip, this.resolvePlayOptions(step.options, scope));
83007
+ if (!playback) {
83008
+ return null;
83009
+ }
83010
+ this.attachPlayback(playback);
83011
+ if (scope.owner === 'concurrent') {
83012
+ // Remember concurrent ownership independently of the frames: a non-blocking keep-active
83013
+ // play drains out of `_concurrent` immediately, but its playback must still survive a
83014
+ // later main-flow replacement (#2).
83015
+ this._concurrentPlaybackIds.add(playback.id);
83016
+ }
83017
+ scope.currentPlaybackId = playback.id;
83018
+ if (step.id) {
83019
+ scope.refs[step.id] = playback.id;
83020
+ this._playbackRefs.set(step.id, playback.id);
83021
+ }
83022
+ scope.refs[playback.id] = playback.id;
83023
+ this._playbackRefs.set(playback.id, playback.id);
83024
+ if (step.wait === 'complete') {
83025
+ return {
83026
+ kind: 'playWait',
83027
+ playbackId: playback.id
83028
+ };
83029
+ }
83030
+ return null;
83031
+ }
83032
+ case 'stop':
83033
+ {
83034
+ const playback = this.resolvePlayback(step.target, scope);
83035
+ if (playback) {
83036
+ playback.stop(step.options);
83037
+ } else if (!step.target) {
83038
+ this._ownedPlaybacks.forEach((item)=>item.stop(step.options));
83039
+ }
83040
+ return null;
83041
+ }
83042
+ case 'wait':
83043
+ return step.seconds > 0 ? {
83044
+ kind: 'wait',
83045
+ remaining: step.seconds
83046
+ } : null;
83047
+ case 'waitEvent':
83048
+ return {
83049
+ kind: 'waitEvent',
83050
+ event: step.event
83051
+ };
83052
+ case 'waitMarker':
83053
+ {
83054
+ const playback = this.resolvePlayback(step.target, scope) ?? this.scopeCurrentPlayback(scope);
83055
+ if (!playback) {
83056
+ return null;
83057
+ }
83058
+ return {
83059
+ kind: 'waitMarker',
83060
+ marker: step.marker,
83061
+ playbackId: playback.id,
83062
+ satisfied: false
83063
+ };
83064
+ }
83065
+ case 'waitFrame':
83066
+ {
83067
+ const playback = this.resolvePlayback(step.target, scope) ?? this.scopeCurrentPlayback(scope);
83068
+ if (!playback) {
83069
+ return null;
83070
+ }
83071
+ return {
83072
+ kind: 'waitFrame',
83073
+ frame: step.frame,
83074
+ playbackId: playback.id,
83075
+ satisfied: false
83076
+ };
83077
+ }
83078
+ case 'emit':
83079
+ this.dispatchEvent('emit', step.event, step.payload);
83080
+ return null;
83081
+ }
83082
+ }
83083
+ resolvePlayOptions(options, scope) {
83084
+ const target = options?.sync?.target;
83085
+ if (!target) {
83086
+ return options;
83087
+ }
83088
+ const mapped = this.resolvePlaybackRef(target, scope);
83089
+ if (!mapped) {
83090
+ return options;
83091
+ }
83092
+ return {
83093
+ ...options,
83094
+ sync: {
83095
+ ...options.sync,
83096
+ target: mapped
83097
+ }
83098
+ };
83099
+ }
83100
+ resolvePlaybackRef(target, scope) {
83101
+ return scope.refs[target] ?? this._playbackRefs.get(target) ?? null;
83102
+ }
83103
+ tickWaitMarker(frame) {
83104
+ if (frame.satisfied || !frame.playbackId) {
83105
+ return {
83106
+ status: 'pop'
83107
+ };
83108
+ }
83109
+ const playback = this._ownedPlaybacks.get(frame.playbackId);
83110
+ if (!playback || playback.state === 'stopped' || playback.state === 'completed') {
83111
+ return {
83112
+ status: 'pop'
83113
+ };
83114
+ }
83115
+ const crossed = this._crossedMarkers.get(frame.playbackId);
83116
+ if (crossed && crossed.has(frame.marker)) {
83117
+ return {
83118
+ status: 'pop'
83119
+ };
83120
+ }
83121
+ return {
83122
+ status: 'block'
83123
+ };
83124
+ }
83125
+ tickWaitFrame(frame) {
83126
+ if (frame.satisfied || frame.playbackId === null) {
83127
+ return {
83128
+ status: 'pop'
83129
+ };
83130
+ }
83131
+ const playback = this._ownedPlaybacks.get(frame.playbackId);
83132
+ if (!playback || playback.state === 'stopped' || playback.state === 'completed') {
83133
+ return {
83134
+ status: 'pop'
83135
+ };
83136
+ }
83137
+ const crossed = this._crossedFrames.get(frame.playbackId);
83138
+ if (crossed && crossed.has(frame.frame)) {
83139
+ return {
83140
+ status: 'pop'
83141
+ };
83142
+ }
83143
+ return {
83144
+ status: 'block'
83145
+ };
83146
+ }
83147
+ tickPlayWait(frame) {
83148
+ const playback = this._ownedPlaybacks.get(frame.playbackId);
83149
+ if (!playback || playback.state === 'stopped' || playback.state === 'completed') {
83150
+ return {
83151
+ status: 'pop'
83152
+ };
83153
+ }
83154
+ return {
83155
+ status: 'block'
83156
+ };
83157
+ }
83158
+ attachPlayback(playback) {
83159
+ this._ownedPlaybacks.set(playback.id, playback);
83160
+ playback.on('marker', this.onPlaybackMarker);
83161
+ playback.on('frame', this.onPlaybackFrame);
83162
+ playback.on('stop', this.onPlaybackEnd);
83163
+ playback.on('complete', this.onPlaybackComplete);
83164
+ }
83165
+ detachPlayback(playback) {
83166
+ playback.off('marker', this.onPlaybackMarker);
83167
+ playback.off('frame', this.onPlaybackFrame);
83168
+ playback.off('stop', this.onPlaybackEnd);
83169
+ playback.off('complete', this.onPlaybackComplete);
83170
+ }
83171
+ forgetPlayback(playback) {
83172
+ this.detachPlayback(playback);
83173
+ this._ownedPlaybacks.delete(playback.id);
83174
+ this._concurrentPlaybackIds.delete(playback.id);
83175
+ this.forgetPlaybackRefs(playback.id);
83176
+ }
83177
+ /**
83178
+ * Cleanup when an owned playback ends (externally or naturally). Only touches runner-local
83179
+ * bookkeeping, so it is safe to run inside `AnimationSet.update`'s playback loop. Frames blocked
83180
+ * on this playback observe its absence on the next tick and unblock.
83181
+ */ onPlaybackEnd = (event)=>{
83182
+ const playback = this._ownedPlaybacks.get(event.playback.id);
83183
+ if (playback) {
83184
+ this.forgetPlayback(playback);
83185
+ }
83186
+ };
83187
+ onPlaybackComplete = (event)=>{
83188
+ this._lastCompletedPlaybackId = event.playback.id;
83189
+ const playback = this._ownedPlaybacks.get(event.playback.id);
83190
+ if (playback) {
83191
+ this.forgetPlayback(playback);
83192
+ }
83193
+ };
83194
+ onPlaybackMarker = (event)=>{
83195
+ const id = event.playback.id;
83196
+ let set = this._crossedMarkers.get(id);
83197
+ if (!set) {
83198
+ set = new Set();
83199
+ this._crossedMarkers.set(id, set);
83200
+ }
83201
+ if (event.marker.id !== undefined) {
83202
+ set.add(event.marker.id);
83203
+ }
83204
+ set.add(event.marker.name);
83205
+ };
83206
+ onPlaybackFrame = (event)=>{
83207
+ const id = event.playback.id;
83208
+ let set = this._crossedFrames.get(id);
83209
+ if (!set) {
83210
+ set = new Set();
83211
+ this._crossedFrames.set(id, set);
83212
+ }
83213
+ set.add(event.frame);
83214
+ };
83215
+ resolvePlayback(target, scope) {
83216
+ if (!target) {
83217
+ return null;
83218
+ }
83219
+ const mapped = this.resolvePlaybackRef(target, scope);
83220
+ if (mapped) {
83221
+ const owned = this._ownedPlaybacks.get(mapped);
83222
+ if (owned) {
83223
+ return owned;
83224
+ }
83225
+ }
83226
+ return this._ownedPlaybacks.get(target) ?? this.animationSet.getPlayback(target);
83227
+ }
83228
+ forgetPlaybackRefs(playbackId) {
83229
+ [
83230
+ ...this._playbackRefs.entries()
83231
+ ].forEach(([ref, id])=>{
83232
+ if (id === playbackId) {
83233
+ this._playbackRefs.delete(ref);
83234
+ }
83235
+ });
83236
+ }
83237
+ scopeCurrentPlayback(scope) {
83238
+ return scope.currentPlaybackId ? this._ownedPlaybacks.get(scope.currentPlaybackId) ?? null : null;
83239
+ }
83240
+ hasWaiterFor(event) {
83241
+ const inFrame = (frame)=>{
83242
+ switch(frame.kind){
83243
+ case 'waitEvent':
83244
+ return frame.event === event;
83245
+ case 'seq':
83246
+ return frame.child ? inFrame(frame.child) : false;
83247
+ case 'parallel':
83248
+ return frame.branches.some(inFrame);
83249
+ default:
83250
+ return false;
83251
+ }
83252
+ };
83253
+ return this._stack.some(inFrame) || this._concurrent.some(inFrame);
83254
+ }
83255
+ /** Stop only the main control flow (used when a response replaces it). */ stopMainFlow(options) {
83256
+ const stopOptions = options ?? {
83257
+ reason: 'interrupted'
83258
+ };
83259
+ // Stop every playback owned by the main flow, not just those still referenced by `_stack`:
83260
+ // a sequence that already drained may have left a looping clip playing (e.g. `play idle` with
83261
+ // `repeat: 0`), and replacing the main flow must stop it. Playbacks owned by concurrent
83262
+ // (keep-active) branches are independent parallel tracks and are preserved — tracked by id so
83263
+ // they survive even after their branch frames drained.
83264
+ this._ownedPlaybacks.forEach((playback, id)=>{
83265
+ if (this._concurrentPlaybackIds.has(id)) {
83266
+ return;
83267
+ }
83268
+ this.detachPlayback(playback);
83269
+ playback.stop(stopOptions);
83270
+ this._ownedPlaybacks.delete(id);
83271
+ this.forgetPlaybackRefs(id);
83272
+ });
83273
+ this._stack = [];
83274
+ }
83275
+ collectScopePlaybackIds(frames) {
83276
+ const ids = new Set();
83277
+ const visit = (frame)=>{
83278
+ if (frame.kind === 'seq') {
83279
+ Object.values(frame.scope.refs).forEach((id)=>ids.add(id));
83280
+ if (frame.scope.currentPlaybackId) {
83281
+ ids.add(frame.scope.currentPlaybackId);
83282
+ }
83283
+ if (frame.child) {
83284
+ visit(frame.child);
83285
+ }
83286
+ } else if (frame.kind === 'parallel') {
83287
+ frame.branches.forEach(visit);
83288
+ }
83289
+ };
83290
+ frames.forEach(visit);
83291
+ return ids;
83292
+ }
83293
+ cloneFrame(frame) {
83294
+ switch(frame.kind){
83295
+ case 'seq':
83296
+ return {
83297
+ kind: 'seq',
83298
+ steps: frame.steps,
83299
+ index: frame.index,
83300
+ scope: {
83301
+ currentPlaybackId: frame.scope.currentPlaybackId,
83302
+ refs: {
83303
+ ...frame.scope.refs
83304
+ },
83305
+ owner: frame.scope.owner
83306
+ },
83307
+ child: frame.child ? this.cloneFrame(frame.child) : null
83308
+ };
83309
+ case 'parallel':
83310
+ return {
83311
+ kind: 'parallel',
83312
+ branches: frame.branches.map((b)=>this.cloneFrame(b))
83313
+ };
83314
+ case 'wait':
83315
+ return {
83316
+ kind: 'wait',
83317
+ remaining: frame.remaining
83318
+ };
83319
+ case 'waitEvent':
83320
+ return {
83321
+ kind: 'waitEvent',
83322
+ event: frame.event
83323
+ };
83324
+ case 'waitMarker':
83325
+ return {
83326
+ ...frame
83327
+ };
83328
+ case 'waitFrame':
83329
+ return {
83330
+ ...frame
83331
+ };
83332
+ case 'playWait':
83333
+ return {
83334
+ ...frame
83335
+ };
83336
+ }
83337
+ }
83338
+ reattachPlaybacks(frames) {
83339
+ const ids = this.collectAllPlaybackIds(frames);
83340
+ ids.forEach((id)=>{
83341
+ if (this._ownedPlaybacks.has(id)) {
83342
+ return;
83343
+ }
83344
+ const playback = this.findLivePlayback(id);
83345
+ if (playback) {
83346
+ this.attachPlayback(playback);
83347
+ }
83348
+ });
83349
+ }
83350
+ collectAllPlaybackIds(frames) {
83351
+ const ids = this.collectScopePlaybackIds(frames);
83352
+ const visit = (frame)=>{
83353
+ if (frame.kind === 'playWait') {
83354
+ ids.add(frame.playbackId);
83355
+ } else if ((frame.kind === 'waitMarker' || frame.kind === 'waitFrame') && frame.playbackId) {
83356
+ ids.add(frame.playbackId);
83357
+ } else if (frame.kind === 'seq' && frame.child) {
83358
+ visit(frame.child);
83359
+ } else if (frame.kind === 'parallel') {
83360
+ frame.branches.forEach(visit);
83361
+ }
83362
+ };
83363
+ frames.forEach(visit);
83364
+ return ids;
83365
+ }
83366
+ findLivePlayback(id) {
83367
+ return this.animationSet.getPlaybacks().find((playback)=>playback.id === id) ?? null;
83368
+ }
83369
+ }
83370
+
83371
+ /**
83372
+ * Event-driven animation state controller.
83373
+ *
83374
+ * Each state owns one serializable timeline. External gameplay events are dispatched to the
83375
+ * current timeline first, then to the current state's response table, so different states can
83376
+ * respond to the same event differently.
83377
+ * @public
83378
+ */ class AnimationController extends Observable {
83379
+ /**
83380
+ * Animation set used to create playbacks for all state timelines.
83381
+ */ animationSet;
83382
+ _states;
83383
+ _currentState;
83384
+ _runner;
83385
+ _onRunnerComplete;
83386
+ _onRunnerEmit;
83387
+ /**
83388
+ * Create a controller for an animation set.
83389
+ *
83390
+ * @param animationSet - Animation set that owns the clips and active playbacks.
83391
+ */ constructor(animationSet){
83392
+ super();
83393
+ this.animationSet = animationSet;
83394
+ this._states = new Map();
83395
+ this._currentState = null;
83396
+ this._runner = null;
83397
+ this._onRunnerComplete = null;
83398
+ this._onRunnerEmit = null;
83399
+ }
83400
+ /**
83401
+ * Current state name.
83402
+ *
83403
+ * @returns The active state name, or null when the controller is stopped or has not entered a state.
83404
+ */ get currentState() {
83405
+ return this._currentState;
83406
+ }
83407
+ /**
83408
+ * Current timeline runner.
83409
+ *
83410
+ * @returns The active timeline runner, or null when no state is running.
83411
+ */ get runner() {
83412
+ return this._runner;
83413
+ }
83414
+ /**
83415
+ * Register or replace a named state definition.
83416
+ *
83417
+ * @param name - Unique state name.
83418
+ * @param definition - Timeline and event response configuration for the state.
83419
+ * @returns This controller for chaining.
83420
+ */ addState(name, definition) {
83421
+ if (this._states.has(name)) {
83422
+ console.warn(`AnimationController state ${name} already exists; overwriting`);
83423
+ }
83424
+ this._states.set(name, definition);
83425
+ return this;
83426
+ }
83427
+ /**
83428
+ * Test whether a state has been registered.
83429
+ *
83430
+ * @param name - State name to look up.
83431
+ * @returns True if the controller contains a state with the given name; otherwise false.
83432
+ */ hasState(name) {
83433
+ return this._states.has(name);
83434
+ }
83435
+ /**
83436
+ * Enter a registered state.
83437
+ *
83438
+ * If the requested state is already current and `options.force` is not set, this returns the
83439
+ * existing runner without restarting the timeline. When a transition duration is provided, the
83440
+ * previous runner fades out while the entry plays of the new state fade in.
83441
+ *
83442
+ * @param name - State name to enter.
83443
+ * @param options - Optional transition, re-entry, and stop behavior.
83444
+ * @returns The active runner for the entered state, or null if the state does not exist.
83445
+ */ setState(name, options) {
83446
+ const definition = this._states.get(name);
83447
+ if (!definition) {
83448
+ console.error(`AnimationController state ${name} not exists`);
83449
+ return null;
83450
+ }
83451
+ if (!options?.force && this._currentState === name) {
83452
+ return this._runner;
83453
+ }
83454
+ const previousState = this._currentState;
83455
+ const transition = Math.max(options?.transition ?? definition.transition ?? 0, 0);
83456
+ const returnTarget = this.resolveReturnTarget(options?.returnTo, previousState, options?.returnTransition);
83457
+ this.detachRunner();
83458
+ if (transition > 0) {
83459
+ this._runner?.stop({
83460
+ fadeOut: transition,
83461
+ reason: 'interrupted'
83462
+ });
83463
+ } else {
83464
+ this._runner?.stop(options?.stop ?? {
83465
+ reason: 'interrupted'
83466
+ });
83467
+ }
83468
+ const timeline = new AnimationTimeline(prepareTimelineForTransition(definition.timeline, transition, returnTarget?.transition, options?.sync));
83469
+ this._runner = timeline.createRunner(this.animationSet);
83470
+ const runner = this._runner;
83471
+ this.attachRunner(runner, name, returnTarget);
83472
+ // Enter the new state *before* starting the runner: start() flushes synchronously, so any
83473
+ // initial `emit`/`statecomplete` must observe the controller already in `name`. Otherwise a
83474
+ // listener calling dispatch() would route against the previous state.
83475
+ this._currentState = name;
83476
+ this.dispatchEvent('statechange', name, previousState);
83477
+ // The statechange listener may have re-entered (stop() or another setState()), swapping out or
83478
+ // clearing `_runner`. Only start the runner we created if it is still the active one.
83479
+ if (this._runner === runner && this._currentState === name) {
83480
+ runner.start();
83481
+ }
83482
+ // If a reentrant listener superseded us, `_runner` is the now-active runner (or null after a
83483
+ // reentrant stop()); return that rather than a runner we never started.
83484
+ return this._runner;
83485
+ }
83486
+ /**
83487
+ * Dispatch a gameplay event to the active state.
83488
+ *
83489
+ * The active timeline receives the event first. If it does not handle the event, the current
83490
+ * state's response table may consume it, enqueue or run steps, or transition to another state.
83491
+ *
83492
+ * @param event - Event name to dispatch.
83493
+ * @param payload - Optional event payload passed through result notifications.
83494
+ * @returns The resolved handling result for the event.
83495
+ */ dispatch(event, payload) {
83496
+ if (!this._currentState || !this._runner) {
83497
+ return this.emitResult({
83498
+ handled: false,
83499
+ policy: 'none',
83500
+ event,
83501
+ payload
83502
+ });
83503
+ }
83504
+ const timelineResult = this._runner.dispatch(event, payload);
83505
+ if (timelineResult.handled) {
83506
+ return this.emitResult(timelineResult);
83507
+ }
83508
+ const response = this._states.get(this._currentState)?.responses?.find((item)=>item.event === event);
83509
+ if (!response || response.target.ignore) {
83510
+ return this.emitResult({
83511
+ handled: false,
83512
+ policy: response ? 'ignore' : 'none',
83513
+ event,
83514
+ payload
83515
+ });
83516
+ }
83517
+ if (response.target.consume) {
83518
+ return this.emitResult({
83519
+ handled: true,
83520
+ policy: 'consume',
83521
+ event,
83522
+ payload
83523
+ });
83524
+ }
83525
+ if (response.target.targetState !== undefined) {
83526
+ const transition = typeof response.onActive === 'object' ? response.onActive.fadeOut : undefined;
83527
+ const runner = this.setState(response.target.targetState, {
83528
+ transition,
83529
+ returnTo: response.target.returnTo,
83530
+ returnTransition: response.target.returnTransition
83531
+ });
83532
+ // setState returns null when the target state is not registered: a config error must not be
83533
+ // reported as a successfully handled transition.
83534
+ if (!runner) {
83535
+ return this.emitResult({
83536
+ handled: false,
83537
+ policy: 'transition',
83538
+ event,
83539
+ payload
83540
+ });
83541
+ }
83542
+ return this.emitResult({
83543
+ handled: true,
83544
+ policy: 'transition',
83545
+ event,
83546
+ payload
83547
+ });
83548
+ }
83549
+ const steps = response.target.steps;
83550
+ if (!steps?.length) {
83551
+ return this.emitResult({
83552
+ handled: false,
83553
+ policy: 'none',
83554
+ event,
83555
+ payload
83556
+ });
83557
+ }
83558
+ if (response.enqueue) {
83559
+ this._runner.enqueue(steps);
83560
+ return this.emitResult({
83561
+ handled: true,
83562
+ policy: 'enqueue',
83563
+ event,
83564
+ payload
83565
+ });
83566
+ }
83567
+ const disposition = response.onActive ?? 'stop';
83568
+ if (disposition === 'keep') {
83569
+ this._runner.runConcurrent(steps);
83570
+ } else {
83571
+ const stopOptions = typeof disposition === 'object' ? {
83572
+ ...disposition,
83573
+ reason: 'interrupted'
83574
+ } : undefined;
83575
+ this.detachRunner();
83576
+ this._runner.stop(stopOptions);
83577
+ this._runner = new AnimationTimeline(steps).createRunner(this.animationSet);
83578
+ this.attachRunner(this._runner, this._currentState);
83579
+ this._runner.start();
83580
+ }
83581
+ return this.emitResult({
83582
+ handled: true,
83583
+ policy: 'steps',
83584
+ event,
83585
+ payload
83586
+ });
83587
+ }
83588
+ /**
83589
+ * Stop the active state and clear the current state.
83590
+ *
83591
+ * @param options - Optional stop behavior applied to playbacks owned by the active runner.
83592
+ * @returns void
83593
+ */ stop(options) {
83594
+ const previousState = this._currentState;
83595
+ this.detachRunner();
83596
+ this._runner?.stop(options);
83597
+ this._runner = null;
83598
+ this._currentState = null;
83599
+ if (previousState !== null) {
83600
+ this.dispatchEvent('statechange', null, previousState);
83601
+ }
83602
+ }
83603
+ /**
83604
+ * Stop playback and remove all registered states.
83605
+ *
83606
+ * @returns void
83607
+ */ dispose() {
83608
+ this.stop();
83609
+ this._states.clear();
83610
+ }
83611
+ emitResult(result) {
83612
+ this.dispatchEvent('event', result.event, result.payload, result);
83613
+ return result;
83614
+ }
83615
+ resolveReturnTarget(returnTo, previousState, returnTransition) {
83616
+ if (returnTo === undefined) {
83617
+ return null;
83618
+ }
83619
+ const state = returnTo === true ? previousState : returnTo;
83620
+ if (!state) {
83621
+ return null;
83622
+ }
83623
+ const definition = this._states.get(state);
83624
+ if (!definition) {
83625
+ console.error(`AnimationController return state ${state} not exists`);
83626
+ return null;
83627
+ }
83628
+ return {
83629
+ state,
83630
+ transition: returnTransition ?? definition.transition
83631
+ };
83632
+ }
83633
+ attachRunner(runner, state, returnTarget) {
83634
+ this._onRunnerComplete = ()=>{
83635
+ this.dispatchEvent('statecomplete', state);
83636
+ if (returnTarget && this._runner === runner && this._currentState === state) {
83637
+ const sync = returnTarget.transition && runner.lastCompletedPlaybackId ? {
83638
+ target: runner.lastCompletedPlaybackId,
83639
+ mode: 'normalized'
83640
+ } : undefined;
83641
+ this.setState(returnTarget.state, {
83642
+ transition: returnTarget.transition,
83643
+ sync
83644
+ });
83645
+ }
83646
+ };
83647
+ this._onRunnerEmit = (event, payload)=>{
83648
+ this.dispatchEvent('emit', event, payload);
83649
+ };
83650
+ runner.on('complete', this._onRunnerComplete);
83651
+ runner.on('emit', this._onRunnerEmit);
83652
+ }
83653
+ detachRunner() {
83654
+ if (this._runner) {
83655
+ if (this._onRunnerComplete) {
83656
+ this._runner.off('complete', this._onRunnerComplete);
83657
+ }
83658
+ if (this._onRunnerEmit) {
83659
+ this._runner.off('emit', this._onRunnerEmit);
83660
+ }
83661
+ }
83662
+ this._onRunnerComplete = null;
83663
+ this._onRunnerEmit = null;
83664
+ }
83665
+ }
83666
+ function prepareTimelineForTransition(definition, entryTransition, returnTransition, entrySync) {
83667
+ let timeline = definition;
83668
+ if (entrySync) {
83669
+ timeline = withEntrySync(timeline, entrySync);
83670
+ }
83671
+ const completionFadeOut = Math.max(returnTransition ?? 0, 0);
83672
+ if (completionFadeOut > 0) {
83673
+ timeline = withCompletionFadeOut(timeline, completionFadeOut);
83674
+ }
83675
+ return entryTransition > 0 ? withFadeIn(timeline, entryTransition) : timeline;
83676
+ }
83677
+ /**
83678
+ * Return a copy of the timeline definition whose entry plays use `sync` unless they already
83679
+ * declare a more specific synchronization source.
83680
+ */ function withEntrySync(definition, sync) {
83681
+ const def = Array.isArray(definition) ? {
83682
+ steps: definition
83683
+ } : definition;
83684
+ return {
83685
+ steps: injectEntrySync(def.steps, sync).steps,
83686
+ responses: def.responses
83687
+ };
83688
+ }
83689
+ function injectEntrySync(steps, sync) {
83690
+ let blocked = false;
83691
+ const out = steps.map((step)=>{
83692
+ if (blocked) {
83693
+ return step;
83694
+ }
83695
+ switch(step.type){
83696
+ case 'play':
83697
+ if (step.wait === 'complete') {
83698
+ blocked = true;
83699
+ }
83700
+ return {
83701
+ ...step,
83702
+ options: {
83703
+ ...step.options,
83704
+ sync: step.options?.sync ?? sync
83705
+ }
83706
+ };
83707
+ case 'sequence':
83708
+ {
83709
+ const result = injectEntrySync(step.steps, sync);
83710
+ blocked = result.blocked;
83711
+ return {
83712
+ ...step,
83713
+ steps: result.steps
83714
+ };
83715
+ }
83716
+ case 'parallel':
83717
+ {
83718
+ let anyBranchBlocked = false;
83719
+ const branches = step.steps.map((branch)=>{
83720
+ const result = injectEntrySync([
83721
+ branch
83722
+ ], sync);
83723
+ anyBranchBlocked = anyBranchBlocked || result.blocked;
83724
+ return result.steps[0];
83725
+ });
83726
+ blocked = anyBranchBlocked;
83727
+ return {
83728
+ ...step,
83729
+ steps: branches
83730
+ };
83731
+ }
83732
+ case 'wait':
83733
+ case 'waitEvent':
83734
+ case 'waitMarker':
83735
+ case 'waitFrame':
83736
+ blocked = true;
83737
+ return step;
83738
+ default:
83739
+ return step;
83740
+ }
83741
+ });
83742
+ return {
83743
+ steps: out,
83744
+ blocked
83745
+ };
83746
+ }
83747
+ /**
83748
+ * Return a copy of the timeline definition whose entry plays fade in over `duration`, so a state
83749
+ * transition cross-fades against the previous state's fade-out.
83750
+ *
83751
+ * "Entry plays" are every `play` that starts before the control flow first blocks: plays before a
83752
+ * `wait`/`waitEvent`/`waitMarker`/`waitFrame`, before a blocking `play` (`wait: 'complete'`), and
83753
+ * every branch of a leading `parallel` (all branches start simultaneously). This covers parallel
83754
+ * states whose branches would otherwise snap in at full weight while only the first faded.
83755
+ */ function withFadeIn(definition, duration) {
83756
+ const def = Array.isArray(definition) ? {
83757
+ steps: definition
83758
+ } : definition;
83759
+ return {
83760
+ steps: injectEntryFadeIn(def.steps, duration).steps,
83761
+ responses: def.responses
83762
+ };
83763
+ }
83764
+ /**
83765
+ * Inject `fadeIn` into every entry play in `steps`. Returns the rewritten steps and whether the
83766
+ * flow blocks before reaching the end (so callers stop injecting into later, non-entry steps).
83767
+ */ function injectEntryFadeIn(steps, duration) {
83768
+ let blocked = false;
83769
+ const out = steps.map((step)=>{
83770
+ if (blocked) {
83771
+ return step;
83772
+ }
83773
+ switch(step.type){
83774
+ case 'play':
83775
+ if (step.wait === 'complete') {
83776
+ blocked = true;
83777
+ }
83778
+ return {
83779
+ ...step,
83780
+ options: {
83781
+ ...step.options,
83782
+ fadeIn: duration
83783
+ }
83784
+ };
83785
+ case 'sequence':
83786
+ {
83787
+ const result = injectEntryFadeIn(step.steps, duration);
83788
+ blocked = result.blocked;
83789
+ return {
83790
+ ...step,
83791
+ steps: result.steps
83792
+ };
83793
+ }
83794
+ case 'parallel':
83795
+ {
83796
+ // Every branch starts simultaneously, so inject into all of them. The parallel join only
83797
+ // blocks the steps that follow it if at least one branch blocks; if all branches are
83798
+ // non-blocking they drain in the same flush, so the following steps are still entry plays.
83799
+ let anyBranchBlocked = false;
83800
+ const branches = step.steps.map((branch)=>{
83801
+ const result = injectEntryFadeIn([
83802
+ branch
83803
+ ], duration);
83804
+ anyBranchBlocked = anyBranchBlocked || result.blocked;
83805
+ return result.steps[0];
83806
+ });
83807
+ blocked = anyBranchBlocked;
83808
+ return {
83809
+ ...step,
83810
+ steps: branches
83811
+ };
83812
+ }
83813
+ case 'wait':
83814
+ case 'waitEvent':
83815
+ case 'waitMarker':
83816
+ case 'waitFrame':
83817
+ blocked = true;
83818
+ return step;
83819
+ default:
83820
+ return step;
83821
+ }
83822
+ });
83823
+ return {
83824
+ steps: out,
83825
+ blocked
83826
+ };
83827
+ }
83828
+ /**
83829
+ * Return a copy of the timeline definition whose play steps keep completed playbacks alive long
83830
+ * enough to cross-fade into the automatic return state.
83831
+ */ function withCompletionFadeOut(definition, duration) {
83832
+ const def = Array.isArray(definition) ? {
83833
+ steps: definition
83834
+ } : definition;
83835
+ return {
83836
+ steps: injectCompletionFadeOut(def.steps, duration),
83837
+ responses: def.responses
83838
+ };
83839
+ }
83840
+ function injectCompletionFadeOut(steps, duration) {
83841
+ return steps.map((step)=>{
83842
+ switch(step.type){
83843
+ case 'play':
83844
+ return {
83845
+ ...step,
83846
+ options: {
83847
+ ...step.options,
83848
+ completionFadeOut: step.options?.completionFadeOut ?? duration
83849
+ }
83850
+ };
83851
+ case 'sequence':
83852
+ return {
83853
+ ...step,
83854
+ steps: injectCompletionFadeOut(step.steps, duration)
83855
+ };
83856
+ case 'parallel':
83857
+ return {
83858
+ ...step,
83859
+ steps: step.steps.map((branch)=>injectCompletionFadeOut([
83860
+ branch
83861
+ ], duration)[0])
83862
+ };
83863
+ default:
83864
+ return step;
83865
+ }
83866
+ });
83867
+ }
83868
+
81684
83869
  /**
81685
83870
  * Skeleton modifier for IK solvers.
81686
83871
  *
@@ -88373,21 +90558,21 @@ class GPUClothWrapBinding {
88373
90558
  }
88374
90559
  }
88375
90560
 
88376
- /**
88377
- * Named object interface for model loading
88378
- * @public
90561
+ /**
90562
+ * Named object interface for model loading
90563
+ * @public
88379
90564
  */ class NamedObject {
88380
90565
  name;
88381
- /**
88382
- * Creates an instance of NamedObject
88383
- * @param name - Name of the object
90566
+ /**
90567
+ * Creates an instance of NamedObject
90568
+ * @param name - Name of the object
88384
90569
  */ constructor(name){
88385
90570
  this.name = name;
88386
90571
  }
88387
90572
  }
88388
- /**
88389
- * Heirarchical node interface for model loading
88390
- * @public
90573
+ /**
90574
+ * Heirarchical node interface for model loading
90575
+ * @public
88391
90576
  */ class AssetHierarchyNode extends NamedObject {
88392
90577
  _parent;
88393
90578
  _position;
@@ -88401,10 +90586,10 @@ class GPUClothWrapBinding {
88401
90586
  _weights;
88402
90587
  _children;
88403
90588
  _instances;
88404
- /**
88405
- * Creates an instance of AssetHierarchyNode
88406
- * @param name - Name of the node
88407
- * @param parent - Parent of the node
90589
+ /**
90590
+ * Creates an instance of AssetHierarchyNode
90591
+ * @param name - Name of the node
90592
+ * @param parent - Parent of the node
88408
90593
  */ constructor(name, model, parent){
88409
90594
  super(name);
88410
90595
  model.nodes.push(this);
@@ -88492,9 +90677,9 @@ class GPUClothWrapBinding {
88492
90677
  child.computeTransforms(this._worldMatrix);
88493
90678
  }
88494
90679
  }
88495
- /**
88496
- * Adds a child to this node
88497
- * @param child - The child node to be added
90680
+ /**
90681
+ * Adds a child to this node
90682
+ * @param child - The child node to be added
88498
90683
  */ addChild(child) {
88499
90684
  if (!child || child.parent) {
88500
90685
  throw new Error('AssetHierarchyNode.addChild(): invalid child node');
@@ -88502,9 +90687,9 @@ class GPUClothWrapBinding {
88502
90687
  this._children.push(child);
88503
90688
  child._parent = this;
88504
90689
  }
88505
- /**
88506
- * Removes a child of this node
88507
- * @param child - The child node to be removed
90690
+ /**
90691
+ * Removes a child of this node
90692
+ * @param child - The child node to be removed
88508
90693
  */ removeChild(child) {
88509
90694
  const index = this._children.indexOf(child);
88510
90695
  if (index < 0) {
@@ -88513,10 +90698,10 @@ class GPUClothWrapBinding {
88513
90698
  this._children[index]._parent = null;
88514
90699
  this._children.splice(index, 1);
88515
90700
  }
88516
- /**
88517
- * Attach this node to a skeleton
88518
- * @param skeleton - The skeleton to which to node will attach
88519
- * @param index - The joint index
90701
+ /**
90702
+ * Attach this node to a skeleton
90703
+ * @param skeleton - The skeleton to which to node will attach
90704
+ * @param index - The joint index
88520
90705
  */ attachToSkeleton(skeleton) {
88521
90706
  if (!this._attachToSkeleton) {
88522
90707
  this._attachToSkeleton = new Set();
@@ -88524,18 +90709,18 @@ class GPUClothWrapBinding {
88524
90709
  this._attachToSkeleton.add(skeleton);
88525
90710
  }
88526
90711
  }
88527
- /**
88528
- * Skeleton information for model loading
88529
- * @public
90712
+ /**
90713
+ * Skeleton information for model loading
90714
+ * @public
88530
90715
  */ class AssetSkeleton extends NamedObject {
88531
90716
  /** The pivot node */ pivot;
88532
90717
  /** Joints of the skeleton */ joints;
88533
90718
  /** Inverse of the binding matrices of the joints */ inverseBindMatrices;
88534
90719
  /** Binding pose matrices of the joints */ bindPose;
88535
90720
  /** Explicit humanoid joint mapping, when supplied by the source asset. */ humanoidJointMapping;
88536
- /**
88537
- * Creates an instance of AssetSkeleton
88538
- * @param name - Name of the skeleton
90721
+ /**
90722
+ * Creates an instance of AssetSkeleton
90723
+ * @param name - Name of the skeleton
88539
90724
  */ constructor(name){
88540
90725
  super(name);
88541
90726
  this.name = name;
@@ -88545,10 +90730,10 @@ class GPUClothWrapBinding {
88545
90730
  this.bindPose = [];
88546
90731
  this.humanoidJointMapping = null;
88547
90732
  }
88548
- /**
88549
- * Adds a joint to the skeleton
88550
- * @param joint - The joint node
88551
- * @param inverseBindMatrix - Inverse binding matrix of the joint
90733
+ /**
90734
+ * Adds a joint to the skeleton
90735
+ * @param joint - The joint node
90736
+ * @param inverseBindMatrix - Inverse binding matrix of the joint
88552
90737
  */ addJoint(joint, inverseBindMatrix) {
88553
90738
  joint.attachToSkeleton(this);
88554
90739
  this.joints.push(joint);
@@ -88572,22 +90757,22 @@ class GPUClothWrapBinding {
88572
90757
  return root;
88573
90758
  }
88574
90759
  }
88575
- /**
88576
- * Scene for model loading
88577
- * @public
90760
+ /**
90761
+ * Scene for model loading
90762
+ * @public
88578
90763
  */ class AssetScene extends NamedObject {
88579
90764
  /** Root nodes of the scene */ rootNodes;
88580
- /**
88581
- * Creates an instance of AssetScene
88582
- * @param name - Name of the scene
90765
+ /**
90766
+ * Creates an instance of AssetScene
90767
+ * @param name - Name of the scene
88583
90768
  */ constructor(name){
88584
90769
  super(name);
88585
90770
  this.rootNodes = [];
88586
90771
  }
88587
90772
  }
88588
- /**
88589
- * Model information that can be shared by multiple model nodes
88590
- * @public
90773
+ /**
90774
+ * Model information that can be shared by multiple model nodes
90775
+ * @public
88591
90776
  */ class SharedModel extends Disposable {
88592
90777
  /** @internal */ _nodes;
88593
90778
  /** @internal */ _skeletons;
@@ -88605,8 +90790,8 @@ class GPUClothWrapBinding {
88605
90790
  /** @internal */ _morphTargetGroups;
88606
90791
  /** @internal */ _textureMap;
88607
90792
  /** @internal */ _primitiveMap;
88608
- /**
88609
- * Creates an instance of SharedModel
90793
+ /**
90794
+ * Creates an instance of SharedModel
88610
90795
  */ constructor(){
88611
90796
  super();
88612
90797
  this._nodes = [];
@@ -88876,29 +91061,29 @@ class GPUClothWrapBinding {
88876
91061
  getPrimitiveBaseName(info, index, fallback) {
88877
91062
  return info?.name?.trim() || `${fallback}_mesh_${index}`;
88878
91063
  }
88879
- /**
88880
- * Adds a model-level morph target group.
88881
- * @param group - Morph target group to add
91064
+ /**
91065
+ * Adds a model-level morph target group.
91066
+ * @param group - Morph target group to add
88882
91067
  */ addMorphTargetGroup(group) {
88883
91068
  if (group?.name && group.bindings?.length > 0) {
88884
91069
  this._morphTargetGroups.push(group);
88885
91070
  }
88886
91071
  }
88887
- /**
88888
- * Removes all model-level morph target groups.
91072
+ /**
91073
+ * Removes all model-level morph target groups.
88889
91074
  */ clearMorphTargetGroups() {
88890
91075
  this._morphTargetGroups = [];
88891
91076
  }
88892
- /**
88893
- * Finds a model-level morph target group by name.
88894
- * @param name - Group name
88895
- * @returns The matching group, or null if not found
91077
+ /**
91078
+ * Finds a model-level morph target group by name.
91079
+ * @param name - Group name
91080
+ * @returns The matching group, or null if not found
88896
91081
  */ getMorphTargetGroup(name) {
88897
91082
  return this._morphTargetGroups.find((group)=>group.name === name) ?? null;
88898
91083
  }
88899
- /**
88900
- * Builds morph target groups by collecting morph target names from every mesh.
88901
- * @returns Generated morph target groups
91084
+ /**
91085
+ * Builds morph target groups by collecting morph target names from every mesh.
91086
+ * @returns Generated morph target groups
88902
91087
  */ buildMorphTargetGroupsByName() {
88903
91088
  this.clearMorphTargetGroups();
88904
91089
  const groups = new Map();
@@ -88931,15 +91116,15 @@ class GPUClothWrapBinding {
88931
91116
  this._morphTargetGroups = Array.from(groups.values());
88932
91117
  return this._morphTargetGroups;
88933
91118
  }
88934
- /**
88935
- * Adds a skeleton to the scene
88936
- * @param skeleton - The skeleton to be added
91119
+ /**
91120
+ * Adds a skeleton to the scene
91121
+ * @param skeleton - The skeleton to be added
88937
91122
  */ addSkeleton(skeleton) {
88938
91123
  this._skeletons.push(skeleton);
88939
91124
  }
88940
- /**
88941
- * Adds an animation to the scene
88942
- * @param animation - The animation to be added
91125
+ /**
91126
+ * Adds an animation to the scene
91127
+ * @param animation - The animation to be added
88943
91128
  */ addAnimation(animation) {
88944
91129
  this._animations.push(animation);
88945
91130
  }
@@ -91174,32 +93359,32 @@ let outfitInstanceId = 0;
91174
93359
  /* es-module-lexer 1.7.0 */
91175
93360
  var ImportType;!function(A){A[A.Static=1]="Static",A[A.Dynamic=2]="Dynamic",A[A.ImportMeta=3]="ImportMeta",A[A.StaticSourcePhase=4]="StaticSourcePhase",A[A.DynamicSourcePhase=5]="DynamicSourcePhase",A[A.StaticDeferPhase=6]="StaticDeferPhase",A[A.DynamicDeferPhase=7]="DynamicDeferPhase";}(ImportType||(ImportType={}));const A=1===new Uint8Array(new Uint16Array([1]).buffer)[0];function parse(E,g="@"){if(!C)return init.then((()=>parse(E)));const I=E.length+1,w=(C.__heap_base.value||C.__heap_base)+4*I-C.memory.buffer.byteLength;w>0&&C.memory.grow(Math.ceil(w/65536));const K=C.sa(I-1);if((A?B:Q)(E,new Uint16Array(C.memory.buffer,K,I)),!C.parse())throw Object.assign(new Error(`Parse error ${g}:${E.slice(0,C.e()).split("\n").length}:${C.e()-E.lastIndexOf("\n",C.e()-1)}`),{idx:C.e()});const o=[],D=[];for(;C.ri();){const A=C.is(),Q=C.ie(),B=C.it(),g=C.ai(),I=C.id(),w=C.ss(),K=C.se();let D;C.ip()&&(D=k(E.slice(-1===I?A-1:A,-1===I?Q+1:Q))),o.push({n:D,t:B,s:A,e:Q,ss:w,se:K,d:I,a:g});}for(;C.re();){const A=C.es(),Q=C.ee(),B=C.els(),g=C.ele(),I=E.slice(A,Q),w=I[0],K=B<0?undefined:E.slice(B,g),o=K?K[0]:"";D.push({s:A,e:Q,ls:B,le:g,n:'"'===w||"'"===w?k(I):I,ln:'"'===o||"'"===o?k(K):K});}function k(A){try{return (0,eval)(A)}catch(A){}}return [o,D,!!C.f(),!!C.ms()]}function Q(A,Q){const B=A.length;let C=0;for(;C<B;){const B=A.charCodeAt(C);Q[C++]=(255&B)<<8|B>>>8;}}function B(A,Q){const B=A.length;let C=0;for(;C<B;)Q[C]=A.charCodeAt(C++);}let C;const E=()=>{return A="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GzYCHAtBAEEAKAKwCkECajYCsAoCQAJAAkACQEEAECkiAUHhAEYNACABQfcARg0BQQAoArAKIQEMAgtBACgCsAoiAUECakHACEEKEC8NAUEGIQIMAgtBACgCsAoiAS8BAkHpAEcNACABLwEEQfQARw0AQQQhAiABLwEGQegARg0BC0EAIAFBfmo2ArAKDwtBACABIAJBAXRqNgKwCgJAQQEQKUH7AEYNAEEAIAE2ArAKDwtBACgCsAoiACECA0BBACACQQJqNgKwCgJAAkACQEEBECkiAkEiRg0AIAJBJ0cNAUEnEBpBAEEAKAKwCkECajYCsApBARApIQIMAgtBIhAaQQBBACgCsApBAmo2ArAKQQEQKSECDAELIAIQLCECCwJAIAJBOkYNAEEAIAE2ArAKDwtBAEEAKAKwCkECajYCsAoCQEEBECkiAkEiRg0AIAJBJ0YNAEEAIAE2ArAKDwsgAhAaQQBBACgCsApBAmo2ArAKAkACQEEBECkiAkEsRg0AIAJB/QBGDQFBACABNgKwCg8LQQBBACgCsApBAmo2ArAKQQEQKUH9AEYNAEEAKAKwCiECDAELC0EAKALwCSIBIAA2AhAgAUEAKAKwCkECajYCDAttAQJ/AkACQANAAkAgAEH//wNxIgFBd2oiAkEXSw0AQQEgAnRBn4CABHENAgsgAUGgAUYNASAAIQIgARAoDQJBACECQQBBACgCsAoiAEECajYCsAogAC8BAiIADQAMAgsLIAAhAgsgAkH//wNxC6sBAQR/AkACQEEAKAKwCiICLwEAIgNB4QBGDQAgASEEIAAhBQwBC0EAIAJBBGo2ArAKQQEQKSECQQAoArAKIQUCQAJAIAJBIkYNACACQSdGDQAgAhAsGkEAKAKwCiEEDAELIAIQGkEAQQAoArAKQQJqIgQ2ArAKC0EBECkhA0EAKAKwCiECCwJAIAIgBUYNACAFIARBACAAIAAgAUYiAhtBACABIAIbEAILIAMLcgEEf0EAKAKwCiEAQQAoArQKIQECQAJAA0AgAEECaiECIAAgAU8NAQJAAkAgAi8BACIDQaR/ag4CAQQACyACIQAgA0F2ag4EAgEBAgELIABBBGohAAwACwtBACACNgKwChAlQQAPC0EAIAI2ArAKQd0AC0kBA39BACEDAkAgAkUNAAJAA0AgAC0AACIEIAEtAAAiBUcNASABQQFqIQEgAEEBaiEAIAJBf2oiAg0ADAILCyAEIAVrIQMLIAMLC+wBAgBBgAgLzgEAAHgAcABvAHIAdABtAHAAbwByAHQAZgBvAHIAZQB0AGEAbwB1AHIAYwBlAHIAbwBtAHUAbgBjAHQAaQBvAG4AcwBzAGUAcgB0AHYAbwB5AGkAZQBkAGUAbABlAGMAbwBuAHQAaQBuAGkAbgBzAHQAYQBuAHQAeQBiAHIAZQBhAHIAZQB0AHUAcgBkAGUAYgB1AGcAZwBlAGEAdwBhAGkAdABoAHIAdwBoAGkAbABlAGkAZgBjAGEAdABjAGYAaQBuAGEAbABsAGUAbABzAABB0AkLEAEAAAACAAAAAAQAAEA5AAA=","undefined"!=typeof Buffer?Buffer.from(A,"base64"):Uint8Array.from(atob(A),(A=>A.charCodeAt(0)));var A;};const init=WebAssembly.compile(E()).then(WebAssembly.instantiate).then((({exports:A})=>{C=A;}));
91176
93361
 
91177
- /**
91178
- * Converts JavaScript source to a data URL tied to a logical module id.
91179
- *
91180
- * @param js - The JavaScript source code to embed.
91181
- * @param id - Logical module identifier (used only for sourceURL tagging).
91182
- * @returns A `data:text/javascript;base64,...` URL with an encoded `#id` suffix.
91183
- * @internal
93362
+ /**
93363
+ * Converts JavaScript source to a data URL tied to a logical module id.
93364
+ *
93365
+ * @param js - The JavaScript source code to embed.
93366
+ * @param id - Logical module identifier (used only for sourceURL tagging).
93367
+ * @returns A `data:text/javascript;base64,...` URL with an encoded `#id` suffix.
93368
+ * @internal
91184
93369
  */ function toDataUrl(js, id) {
91185
93370
  const b64 = textToBase64(js);
91186
93371
  return `data:text/javascript;base64,${b64}#${encodeURIComponent(String(id))}`;
91187
93372
  }
91188
- /**
91189
- * Checks whether a specifier is an absolute HTTP(S) URL.
91190
- * @internal
93373
+ /**
93374
+ * Checks whether a specifier is an absolute HTTP(S) URL.
93375
+ * @internal
91191
93376
  */ function isAbsoluteUrl(spec) {
91192
93377
  return /^https?:\/\//i.test(spec);
91193
93378
  }
91194
- /**
91195
- * Checks whether a specifier is a special URL (data: or blob:).
91196
- * @internal
93379
+ /**
93380
+ * Checks whether a specifier is a special URL (data: or blob:).
93381
+ * @internal
91197
93382
  */ function isSpecialUrl(spec) {
91198
93383
  return /^(data|blob):/i.test(spec);
91199
93384
  }
91200
- /**
91201
- * Checks whether a specifier is a bare module (not starting with ./, ../, /, or #/).
91202
- * @internal
93385
+ /**
93386
+ * Checks whether a specifier is a bare module (not starting with ./, ../, /, or #/).
93387
+ * @internal
91203
93388
  */ function isBareModule(spec) {
91204
93389
  return !spec.startsWith('./') && !spec.startsWith('../') && !spec.startsWith('/') && !spec.startsWith('#/');
91205
93390
  }
@@ -91230,32 +93415,32 @@ function hasRawQuery(spec) {
91230
93415
  }
91231
93416
  return query.split('&').some((part)=>part.trim() === 'raw');
91232
93417
  }
91233
- /**
91234
- * Resolves, builds, and serves runtime modules using a VFS.
91235
- *
91236
- * Responsibilities:
91237
- * - Resolve logical module IDs to physical paths or URLs.
91238
- * - In editor mode, bundle local script modules into a single data URL after transpile.
91239
- * - Transpile TypeScript to JavaScript on the fly (requires `window.ts` TypeScript runtime).
91240
- * - Gather static and dynamic import dependencies for tooling.
91241
- *
91242
- * Modes:
91243
- * - Editor mode (`editorMode === true`): local script graphs are bundled to data URLs.
91244
- * - Runtime mode (`editorMode === false`): returns .js URLs directly (with .ts -\> .js mapping).
91245
- *
91246
- * Caching:
91247
- * - Built bundles are memoized in `_built` map keyed by canonical source path.
91248
- *
91249
- * @public
93418
+ /**
93419
+ * Resolves, builds, and serves runtime modules using a VFS.
93420
+ *
93421
+ * Responsibilities:
93422
+ * - Resolve logical module IDs to physical paths or URLs.
93423
+ * - In editor mode, bundle local script modules into a single data URL after transpile.
93424
+ * - Transpile TypeScript to JavaScript on the fly (requires `window.ts` TypeScript runtime).
93425
+ * - Gather static and dynamic import dependencies for tooling.
93426
+ *
93427
+ * Modes:
93428
+ * - Editor mode (`editorMode === true`): local script graphs are bundled to data URLs.
93429
+ * - Runtime mode (`editorMode === false`): returns .js/.mjs URLs directly (with .ts -\> .js mapping).
93430
+ *
93431
+ * Caching:
93432
+ * - Built bundles are memoized in `_built` map keyed by canonical source path.
93433
+ *
93434
+ * @public
91250
93435
  */ class ScriptRegistry {
91251
93436
  _vfs;
91252
93437
  _scriptsRoot;
91253
93438
  _built;
91254
93439
  _building;
91255
93440
  _builtDeps;
91256
- /**
91257
- * @param vfs - The virtual file system for existence checks, reads, and path ops.
91258
- * @param scriptsRoot - Root directory for script resolution (used with `#/` specifiers).
93441
+ /**
93442
+ * @param vfs - The virtual file system for existence checks, reads, and path ops.
93443
+ * @param scriptsRoot - Root directory for script resolution (used with `#/` specifiers).
91259
93444
  */ constructor(vfs, scriptsRoot){
91260
93445
  this._vfs = vfs;
91261
93446
  this._scriptsRoot = scriptsRoot;
@@ -91263,10 +93448,10 @@ function hasRawQuery(spec) {
91263
93448
  this._building = new Map();
91264
93449
  this._builtDeps = new Map();
91265
93450
  }
91266
- /**
91267
- * The active virtual file system.
91268
- *
91269
- * Assigning a new VFS clears the build cache.
93451
+ /**
93452
+ * The active virtual file system.
93453
+ *
93454
+ * Assigning a new VFS clears the build cache.
91270
93455
  */ get VFS() {
91271
93456
  return this._vfs;
91272
93457
  }
@@ -91278,21 +93463,21 @@ function hasRawQuery(spec) {
91278
93463
  this._builtDeps.clear();
91279
93464
  }
91280
93465
  }
91281
- /**
91282
- * The root path used by `#/` specifiers.
93466
+ /**
93467
+ * The root path used by `#/` specifiers.
91283
93468
  */ get scriptsRoot() {
91284
93469
  return this._scriptsRoot;
91285
93470
  }
91286
93471
  set scriptsRoot(path) {
91287
93472
  this._scriptsRoot = path;
91288
93473
  }
91289
- /**
91290
- * Invalidates cached built module output for one logical module id, or clears the full cache.
91291
- *
91292
- * Pass the same logical id shape that callers use with {@link ScriptRegistry.resolveRuntimeUrl},
91293
- * for example `/assets/scripts/foo.ts`, `/assets/scripts/foo.js`, or `/assets/scripts/foo`.
91294
- *
91295
- * @param moduleId - Optional logical module id to invalidate. Omit to clear the entire build cache.
93474
+ /**
93475
+ * Invalidates cached built module output for one logical module id, or clears the full cache.
93476
+ *
93477
+ * Pass the same logical id shape that callers use with {@link ScriptRegistry.resolveRuntimeUrl},
93478
+ * for example `/assets/scripts/foo.ts`, `/assets/scripts/foo.js`, or `/assets/scripts/foo`.
93479
+ *
93480
+ * @param moduleId - Optional logical module id to invalidate. Omit to clear the entire build cache.
91296
93481
  */ invalidate(moduleId) {
91297
93482
  if (!moduleId) {
91298
93483
  this._built.clear();
@@ -91331,22 +93516,22 @@ function hasRawQuery(spec) {
91331
93516
  }
91332
93517
  }
91333
93518
  }
91334
- /**
91335
- * Fetches raw source for a logical module id by probing known extensions.
91336
- *
91337
- * Search order:
91338
- * - If `id` already ends with `.ts` or `.js` and is a file -\> return it.
91339
- * - Else try `.id.ts`, then `.id.js`.
91340
- *
91341
- * @param id - Logical module identifier (absolute or logical path-like).
91342
- * @returns Source code, resolved path, and type (`'js' | 'ts'`), or `undefined` if not found.
93519
+ /**
93520
+ * Fetches raw source for a logical module id by probing known extensions.
93521
+ *
93522
+ * Search order:
93523
+ * - If `id` already ends with `.ts`, `.js`, or `.mjs` and is a file -\> return it.
93524
+ * - Else try `.id.ts`, then `.id.js`, then `.id.mjs`.
93525
+ *
93526
+ * @param id - Logical module identifier (absolute or logical path-like).
93527
+ * @returns Source code, resolved path, and type (`'js' | 'ts'`), or `undefined` if not found.
91343
93528
  */ async fetchSource(id) {
91344
93529
  let type = null;
91345
93530
  let pathWithExt = '';
91346
93531
  if (id.endsWith('.ts')) {
91347
93532
  pathWithExt = id;
91348
93533
  type = 'ts';
91349
- } else if (id.endsWith('.js')) {
93534
+ } else if (id.endsWith('.js') || id.endsWith('.mjs')) {
91350
93535
  pathWithExt = id;
91351
93536
  type = 'js';
91352
93537
  }
@@ -91365,13 +93550,16 @@ function hasRawQuery(spec) {
91365
93550
  'js'
91366
93551
  ];
91367
93552
  if (!type) {
91368
- for (const t of types){
93553
+ for (const t of [
93554
+ ...types,
93555
+ 'mjs'
93556
+ ]){
91369
93557
  pathWithExt = `${id}.${t}`;
91370
93558
  const exists = await this._vfs.exists(pathWithExt);
91371
93559
  if (exists) {
91372
93560
  const stats = await this._vfs.stat(pathWithExt);
91373
93561
  if (stats.isFile) {
91374
- type = t;
93562
+ type = t === 'ts' ? 'ts' : 'js';
91375
93563
  break;
91376
93564
  }
91377
93565
  }
@@ -91388,34 +93576,35 @@ function hasRawQuery(spec) {
91388
93576
  };
91389
93577
  }
91390
93578
  }
91391
- /**
91392
- * Resolves a module entry to a URL suitable for dynamic import.
91393
- *
91394
- * Behavior:
91395
- * - In editor mode, builds the module to a data URL.
91396
- * - Otherwise, returns `.js` URL directly:
91397
- * - If `id` ends with `.js`: return as-is.
91398
- * - If `id` ends with `.ts`: map to `.js` (assumes pre-built file exists).
91399
- * - Else: append `.js`.
91400
- *
91401
- * @param entryId - Entry module identifier (logical or path-like).
91402
- * @returns A URL string that can be used in `import(...)`.
93579
+ /**
93580
+ * Resolves a module entry to a URL suitable for dynamic import.
93581
+ *
93582
+ * Behavior:
93583
+ * - In editor mode, builds the module to a data URL.
93584
+ * - Otherwise, returns `.js` or `.mjs` URL directly:
93585
+ * - If `id` ends with `.js`: return as-is.
93586
+ * - If `id` ends with `.mjs`: return as-is.
93587
+ * - If `id` ends with `.ts`: map to `.js` (assumes pre-built file exists).
93588
+ * - Else: append `.js`.
93589
+ *
93590
+ * @param entryId - Entry module identifier (logical or path-like).
93591
+ * @returns A URL string that can be used in `import(...)`.
91403
93592
  */ async resolveRuntimeUrl(entryId) {
91404
93593
  const id = await this.resolveLogicalId(entryId);
91405
93594
  if (id.startsWith('/assets/@builtins/')) {
91406
93595
  return await this.build(String(id));
91407
93596
  }
91408
- return getApp().editorMode !== 'none' ? await this.build(String(id)) : id.endsWith('.js') ? id : id.endsWith('.ts') ? `${id.slice(0, -3)}.js` : `${id}.js`;
91409
- }
91410
- /**
91411
- * Recursively gathers direct static and dynamic import dependencies for a module.
91412
- *
91413
- * Only relative specifiers (`./` or `../`) are followed. Absolute, special, and bare
91414
- * module specifiers are ignored here.
91415
- *
91416
- * @param entryId - The starting (possibly relative) specifier from `fromId`.
91417
- * @param fromId - The logical id of the module containing `entryId`.
91418
- * @param dependencies - Output map of `resolvedSourcePath -\> file contents`.
93597
+ return getApp().editorMode !== 'none' ? await this.build(String(id)) : id.endsWith('.js') || id.endsWith('.mjs') ? id : id.endsWith('.ts') ? `${id.slice(0, -3)}.js` : `${id}.js`;
93598
+ }
93599
+ /**
93600
+ * Recursively gathers direct static and dynamic import dependencies for a module.
93601
+ *
93602
+ * Only relative specifiers (`./` or `../`) are followed. Absolute, special, and bare
93603
+ * module specifiers are ignored here.
93604
+ *
93605
+ * @param entryId - The starting (possibly relative) specifier from `fromId`.
93606
+ * @param fromId - The logical id of the module containing `entryId`.
93607
+ * @param dependencies - Output map of `resolvedSourcePath -\> file contents`.
91419
93608
  */ async getDependencies(entryId, fromId, dependencies) {
91420
93609
  const reStatic = /\b(?:import|export)\s+[^"']*?from\s+(['"])([^'"]+)\1/g;
91421
93610
  const reDynamic = /\bimport\s*\(\s*(['"])([^'"]+)\1\s*\)/g;
@@ -91443,17 +93632,17 @@ function hasRawQuery(spec) {
91443
93632
  await gather(code, reStatic);
91444
93633
  await gather(code, reDynamic);
91445
93634
  }
91446
- /**
91447
- * Builds a logical module id into a bundled data URL (editor mode pipeline).
91448
- *
91449
- * Steps:
91450
- * - Resolve source path (.ts/.js) via {@link ScriptRegistry.resolveSourcePath}.
91451
- * - Collect reachable local imports without recursively building data URLs.
91452
- * - Transpile local modules to `System.register`.
91453
- * - Emit a single `data:` URL with a small module loader and memoize it in `_built`.
91454
- *
91455
- * @param id - Logical module id to build.
91456
- * @returns Data URL string for dynamic import, or empty string if not found.
93635
+ /**
93636
+ * Builds a logical module id into a bundled data URL (editor mode pipeline).
93637
+ *
93638
+ * Steps:
93639
+ * - Resolve source path (.ts/.js) via {@link ScriptRegistry.resolveSourcePath}.
93640
+ * - Collect reachable local imports without recursively building data URLs.
93641
+ * - Transpile local modules to `System.register`.
93642
+ * - Emit a single `data:` URL with a small module loader and memoize it in `_built`.
93643
+ *
93644
+ * @param id - Logical module id to build.
93645
+ * @returns Data URL string for dynamic import, or empty string if not found.
91457
93646
  */ async build(id) {
91458
93647
  const entry = await this.resolveModuleInfo(String(id));
91459
93648
  if (!entry) {
@@ -91548,7 +93737,7 @@ function hasRawQuery(spec) {
91548
93737
  const ts = this.getTypeScriptRuntime();
91549
93738
  const res = ts.transpileModule(code, {
91550
93739
  compilerOptions: {
91551
- target: ts.ScriptTarget.ES2015,
93740
+ target: ts.ScriptTarget.ES2020,
91552
93741
  module: ts.ModuleKind.ESNext,
91553
93742
  experimentalDecorators: true,
91554
93743
  useDefineForClassFields: false
@@ -91563,19 +93752,20 @@ function hasRawQuery(spec) {
91563
93752
  const res = ts.transpileModule(code, {
91564
93753
  compilerOptions: {
91565
93754
  allowJs: true,
91566
- target: ts.ScriptTarget.ES2015,
93755
+ // Keep modern syntax such as async generators, class fields, and private fields intact.
93756
+ target: ts.ScriptTarget.ES2022,
91567
93757
  module: ts.ModuleKind.System,
91568
93758
  esModuleInterop: true,
91569
93759
  experimentalDecorators: true,
91570
- useDefineForClassFields: false
93760
+ useDefineForClassFields: true
91571
93761
  },
91572
93762
  fileName: logicalId
91573
93763
  });
91574
93764
  return res.outputText || '';
91575
93765
  }
91576
- /**
91577
- * Rewrites local ESM specifiers to canonical source paths and records local deps.
91578
- * External URLs and package imports are left for the native dynamic import path.
93766
+ /**
93767
+ * Rewrites local ESM specifiers to canonical source paths and records local deps.
93768
+ * External URLs and package imports are left for the native dynamic import path.
91579
93769
  */ async rewriteImportsToLogicalIds(code, fromId) {
91580
93770
  await init;
91581
93771
  const [imports] = parse(code);
@@ -91723,21 +93913,21 @@ async function __z3dLoad(spec, parentId = '') {
91723
93913
  }
91724
93914
  `;
91725
93915
  }
91726
- /**
91727
- * Resolves a specifier to a logical id suitable for further processing.
91728
- *
91729
- * Resolution rules:
91730
- * - `#/path`: resolved against `scriptsRoot` via VFS join/normalize.
91731
- * - `./` or `../`: resolved relative to `fromId` directory (requires `fromId`).
91732
- * - `/absolute`: treated as absolute from root (normalized).
91733
- * - Bare module in editor mode: if `/deps.lock.json` exists and contains an entry,
91734
- * map to the dependency's `entry` path; otherwise return as-is.
91735
- * - Else (non-editor bare module): return `spec` unchanged (external).
91736
- *
91737
- * @param spec - Import specifier string.
91738
- * @param fromId - Optional base logical id used for relative resolution.
91739
- * @returns A normalized logical id or an external specifier string.
91740
- * @throws If a relative import is provided without `fromId`.
93916
+ /**
93917
+ * Resolves a specifier to a logical id suitable for further processing.
93918
+ *
93919
+ * Resolution rules:
93920
+ * - `#/path`: resolved against `scriptsRoot` via VFS join/normalize.
93921
+ * - `./` or `../`: resolved relative to `fromId` directory (requires `fromId`).
93922
+ * - `/absolute`: treated as absolute from root (normalized).
93923
+ * - Bare module in editor mode: if `/deps.lock.json` exists and contains an entry,
93924
+ * map to the dependency's `entry` path; otherwise return as-is.
93925
+ * - Else (non-editor bare module): return `spec` unchanged (external).
93926
+ *
93927
+ * @param spec - Import specifier string.
93928
+ * @param fromId - Optional base logical id used for relative resolution.
93929
+ * @returns A normalized logical id or an external specifier string.
93930
+ * @throws If a relative import is provided without `fromId`.
91741
93931
  */ async resolveLogicalId(spec, fromId) {
91742
93932
  const { path: baseSpec, suffix } = splitSpecifierQuery(spec);
91743
93933
  if (baseSpec.startsWith('#/')) {
@@ -91767,16 +93957,16 @@ async function __z3dLoad(spec, parentId = '') {
91767
93957
  }
91768
93958
  return spec;
91769
93959
  }
91770
- /**
91771
- * Resolves a logical id to a concrete source path and type by probing extensions.
91772
- *
91773
- * Rules:
91774
- * - If `logicalId` ends with `.ts` or `.js`/`.mjs` and is a file, return it.
91775
- * - Else probe `logicalId.ts`, `logicalId.js`, `logicalId.mjs` in that order.
91776
- * - Maps `.mjs` to type `'js'`.
91777
- *
91778
- * @param logicalId - The normalized logical module id (path-like).
91779
- * @returns `{ type, path }` or `null` if not found.
93960
+ /**
93961
+ * Resolves a logical id to a concrete source path and type by probing extensions.
93962
+ *
93963
+ * Rules:
93964
+ * - If `logicalId` ends with `.ts` or `.js`/`.mjs` and is a file, return it.
93965
+ * - Else probe `logicalId.ts`, `logicalId.js`, `logicalId.mjs` in that order.
93966
+ * - Maps `.mjs` to type `'js'`.
93967
+ *
93968
+ * @param logicalId - The normalized logical module id (path-like).
93969
+ * @returns `{ type, path }` or `null` if not found.
91780
93970
  */ async resolveSourcePath(logicalId) {
91781
93971
  const { path: normalizedLogicalId } = splitSpecifierQuery(logicalId);
91782
93972
  let type = null;
@@ -93030,5 +95220,5 @@ const DEFAULT_PADDING = 6;
93030
95220
  }
93031
95221
  }
93032
95222
 
93033
- export { AABBTree, ABufferOIT, ATMOSPHERIC_FOG_BIT, AbsNode, AbstractPostEffect, AllConditionNode, AnimationClip, AnimationSet, AnimationTrack, AnyConditionNode, Application, ArcCosNode, ArcSinNode, ArcTan2Node, ArcTanNode, ArccosineHNode, ArcsineHNode, ArctangentHNode, AssetHierarchyNode, AssetManager, AssetScene, AssetSkeleton, AvatarOutfitInstance, AvatarWardrobe, BUILTIN_ASSET_TEST_CUBEMAP, BUILTIN_ASSET_TEXTURE_SHEEN_LUT, BaseCameraController, BaseGraphNode, BaseLight, BaseSprite, BaseTextureNode, BatchGroup, BillboardMatrixNode, BlinnMaterial, Blitter, Bloom, BoundingBox, BoxFilterBlitter, BoxFrameShape, BoxShape, CAMERA_POS_Y, CCDSolver, Camera, CameraNearFarNode, CameraPositionNode, CameraVectorNode, CapsuleShape, CeilNode, ClampNode, Clipmap, ClipmapTerrain, ClipmapTerrainMaterial, ColliderForce, ColorAdjust, CompAddNode, CompComparisonNode, CompDivNode, CompMulNode, CompSubNode, Compositor, ConstantBVec2Node, ConstantBVec3Node, ConstantBVec4Node, ConstantBooleanNode, ConstantScalarNode, ConstantTexture2DArrayNode, ConstantTexture2DNode, ConstantTextureCubeNode, ConstantVec2Node, ConstantVec3Node, ConstantVec4Node, ConstraintType, CopyBlitter, CosHNode, CosNode, CrossProductNode, CubemapSHProjector, CullVisitor, CylinderShape, DDXNode, DDYNode, Degrees2RadiansNode, DepthPass, DirectionalLight, DistanceNode, DotProductNode, DracoMeshDecoder, EPSILON$1 as EPSILON, ESM, ElapsedTimeNode, Engine, EnvConstantAmbient, EnvHemisphericAmbient, EnvLightWrapper, EnvShIBL, Environment, EnvironmentLighting, EqualNode, Exp2Node, ExpNode, FABRIKSolver, FBMWaveGenerator, FFTWaveGenerator, FPSCameraController, FWidthNode, FXAA, FaceForwardNode, FixedGeometryCacheTrack, FloorNode, FmaNode, Fog, FontAsset, FractNode, FunctionCallNode, FunctionInputNode, FunctionOutputNode, GPUClothSystem, GaussianBlurBlitter, GenericMathNode, GerstnerWaveGenerator, GraphNode, GrassInstances, GrassLayer, GrassMaterial, GrassQuadtreeNode, GrassRenderer, Grayscale, HEIGHT_FOG_BIT, Hash1Node, Hash2Node, Hash3Node, HistoryResourceManager, HumanoidBodyRig, HumanoidHandRig, IKAngleConstraint, IKChain, IKConstraint, IKModifier, IKSolver, InputManager, InstanceBindGroupAllocator, InstanceIndexNode, InvProjMatrixNode, InvSqrtNode, InvViewProjMatrixNode, JointDynamicsModifier, JointDynamicsSystem, JointDynamicsSystemController, LIGHT_TYPE_DIRECTIONAL, LIGHT_TYPE_NONE, LIGHT_TYPE_POINT, LIGHT_TYPE_RECT, LIGHT_TYPE_SPOT, LambertMaterial, LengthNode, LightPass, Log2Node, LogNode, LogicallyAndNode, LogicallyOrNode, MAX_CLUSTERED_LIGHTS, MAX_GERSTNER_WAVE_COUNT, MAX_MORPH_ATTRIBUTES, MAX_MORPH_TARGETS, MAX_TERRAIN_MIPMAP_LEVELS, MORPH_ATTRIBUTE_VECTOR_COUNT, MORPH_TARGET_COLOR, MORPH_TARGET_NORMAL, MORPH_TARGET_POSITION, MORPH_TARGET_TANGENT, MORPH_TARGET_TEX0, MORPH_TARGET_TEX1, MORPH_TARGET_TEX2, MORPH_TARGET_TEX3, MORPH_WEIGHTS_VECTOR_COUNT, MSDFText, MSDFTextAtlasManager, MSDFTextSprite, MToonMaterial, MakeVectorNode, Material, MaterialBlueprintIR, MaterialVaryingFlags, MaxNode, Mesh, MeshMaterial, MinNode, MixNode, ModNode, MorphTargetTrack, MultiChainSpringSystem, NamedObject, NodeEulerRotationTrack, NodeRotationTrack, NodeScaleTrack, NodeTranslationTrack, NormalizeNode, NotEqualNode, Octree, OctreeNode, OctreeNodeChunk, OctreePlacement, OrbitCameraController, OrthoCamera, PBRBlockNode, PBRBluePrintMaterial, PBRMetallicRoughnessMaterial, PBRSpecularGlossinessMaterial, PCAGeometryCacheTrack, PCFOPT, PCFPD, PannerNode, ParticleMaterial, ParticleSystem, PerlinNoise2DNode, PerspectiveCamera, PixelNormalNode, PixelWorldPositionNode, PlaneShape, PointLight, PostEffectLayer, PowNode, Primitive, ProjectionMatrixNode, PropertyTrack, PunctualLight, QUEUE_OPAQUE, QUEUE_TRANSPARENT, RENDER_PASS_TYPE_DEPTH, RENDER_PASS_TYPE_LIGHT, RENDER_PASS_TYPE_OBJECT_COLOR, RENDER_PASS_TYPE_SHADOWMAP, Radians2DegreesNode, RaycastVisitor, RectLight, ReflectNode, RefractNode, RenderPass, RenderQueue, RenderTarget, ResolveVertexNormalNode, ResolveVertexPositionNode, ResolveVertexTangentNode, ResourceManager, RotateAboutAxisNode, RuntimeScript, SAO, SSS, SaturateNode, Scene, SceneNode, SceneRenderer, ScreenAdapter, ScreenRenderTarget, ScriptAttachment, ScriptRegistry, ScriptingSystem, SelectionNode, ShaderHelper, ShadowMapPass, ShadowMapper, ShadowRegion, Shape, SharedModel, SignNode, SimplexNoise2DNode, SinHNode, SinNode, Skeleton, SkeletonModifier, SkeletonRig, SkinBinding, SkyEnvTextureNode, SkyRenderer, SmoothStepNode, SphereShape, SpotLight, SpringChain, SpringModifier, SpringSystem, Sprite, SpriteBlockNode, SpriteBlueprintMaterial, SpriteMaterial, SqrtNode, StandardSpriteMaterial, StepNode, SubsurfaceProfile, SwizzleNode, TAA, TAA_DEBUG_ALAPH, TAA_DEBUG_CURRENT_COLOR, TAA_DEBUG_EDGE, TAA_DEBUG_HISTORY_COLOR, TAA_DEBUG_MOTION_VECTOR, TAA_DEBUG_NONE, TAA_DEBUG_STRENGTH, TAA_DEBUG_VELOCITY, TanHNode, TanNode, TetrahedronFrameShape, TetrahedronShape, TextSprite, TextureSampleGrad, TextureSampleNode, Tonemap, TorusShape, TransformNode, TwoBoneIKSolver, UnlitMaterial, VSM, VertexBinormalNode, VertexBlockNode, VertexColorNode, VertexIndexNode, VertexNormalNode, VertexOutputNode, VertexPositionNode, VertexTangentNode, VertexUVNode, ViewMatrixNode, ViewProjMatrixNode, Water, WaterShaderImpl, WeightedBlendedOIT, aerialPerspective, aerialPerspectiveLut, appInstance, applyAngleLimits, applyMaterialMixins, applyResult, applyRuntimeScriptConfig, atmosphereLUTRendered, buildConstraints, buildMSDFShape, buildSurfaceFaces, checkSurfaceCollision, collisionDetection, collisionDetectionCapsule, collisionDetectionSphere, computeMaxDepth, computeNearestPoints, copyTexture, createAPLutProgram, createCapsuleCollider, createGPUClothWrapBindingData, createGeometryCacheState, createGradientNoiseTexture, createMultiScatteringLutProgram, createPlaneCollider, createProgramFFT2H, createProgramFFT2V, createProgramH0, createProgramHk, createProgramOcean, createProgramPostFFT2, createRandomNoiseTexture, createSkyViewLutProgram, createSphereCollider, createSpringConstraint, createSpringParticle, createTransformAccess, createTransmittanceLutProgram, debugTexture, decode2HalfFromRGBA, decodeFloatFromRGBA, decodeNormalizedFloatFromRGBA, decodeRGBM, defineProps, encode2HalfToRGBA, encodeFloatToRGBA, encodeNormalizedFloatToRGBA, encodeRGBM, ensureGeometryCacheMeshBinding, extractLocalTwist, fetchNormalizedFloatForDevice, fetchSampler, gammaToLinear, generateMSDF, getAerialPerspectiveLut, getApp, getAtmosphereParamsStruct, getBatchGroupClass, getCameraClass, getDefaultAtmosphereParams, getDefaultTexture2D, getDefaultTexture2DArray, getDefaultTextureCube, getDevice, getDirectionalLightClass, getEngine, getGraphNodeClass, getInput, getMeshClass, getMultiScattering, getMultiScatteringLut, getNodeTypeComponents, getOrthoCameraClass, getParticleNodeClass, getPerspectiveCameraClass, getPointLightClass, getPunctualLightClass, getRectLightClass, getRuntimeScriptProperties, getSceneNodeClass, getSkyView, getSkyViewLut, getSpotLightClass, getTransmittanceLut, gradient, hash11, hash12, hash13, hash21, hash22, hash23, hash31, hash32, hash33, integralMultiScattering, interleavedGradientNoise, isGPUClothSupported, linearToGamma, mieAbsorption, mieCoefficient, miePhase, mixGeometryCacheBoundingBox, mixinAlbedoColor, mixinBlinnPhong, mixinFoliage, mixinLambert, mixinLight, mixinPBRBluePrint, mixinPBRCommon, mixinPBRMetallicRoughness, mixinPBRSpecularGlossness, mixinTextureProps, mixinVertexColor, multiScatteringLut, noise3D, normalizeRuntimeScriptConfig, normalizeScriptAttachmentConfig, normalizeScriptAttachments, ozoneAbsorption, panoramaToCubemap, perlinNoise2D, perlinNoise3D, prefilterCubemap, pushInCollisionDetection, pushInFromCapsule, pushInFromCollider, pushInFromSphere, pushoutFromCapsule, pushoutFromCollider, pushoutFromSphere, rayIntersectSphere, rayleighCoefficient, rayleighPhase, renderAPLut, renderAtmosphereLUTs, renderMultiScatteringLut, renderSkyViewLut, renderTransmittanceLut, resolveCapsuleCollision, resolvePlaneCollision, resolveSphereCollision, restoreGeometryCacheMeshBinding, scattering, scriptProp, setApp, setSceneMeshAssetBinding, simulate, skyBox, skyViewLut, smoothNoise3D, sortRootPointsByProximity, temporalResolve, transmittance, transmittanceLut, transmittanceLutToUV, transmittanceToSky, tryGetApp, uniformSphereSamples, updateColliderFromNode, uvToTransmittanceLut, uvToViewDir, valueNoise, viewDirToUV, whiteNoise, worleyFBM, worleyNoise };
95223
+ export { AABBTree, ABufferOIT, ATMOSPHERIC_FOG_BIT, AbsNode, AbstractPostEffect, AllConditionNode, AnimationClip, AnimationController, AnimationPlayback, AnimationSet, AnimationTimeline, AnimationTimelineRunner, AnimationTrack, AnyConditionNode, Application, ArcCosNode, ArcSinNode, ArcTan2Node, ArcTanNode, ArccosineHNode, ArcsineHNode, ArctangentHNode, AssetHierarchyNode, AssetManager, AssetScene, AssetSkeleton, AvatarOutfitInstance, AvatarWardrobe, BUILTIN_ASSET_TEST_CUBEMAP, BUILTIN_ASSET_TEXTURE_SHEEN_LUT, BaseCameraController, BaseGraphNode, BaseLight, BaseSprite, BaseTextureNode, BatchGroup, BillboardMatrixNode, BlinnMaterial, Blitter, Bloom, BoundingBox, BoxFilterBlitter, BoxFrameShape, BoxShape, CAMERA_POS_Y, CCDSolver, Camera, CameraNearFarNode, CameraPositionNode, CameraVectorNode, CapsuleShape, CeilNode, ClampNode, Clipmap, ClipmapTerrain, ClipmapTerrainMaterial, ColliderForce, ColorAdjust, CompAddNode, CompComparisonNode, CompDivNode, CompMulNode, CompSubNode, Compositor, ConstantBVec2Node, ConstantBVec3Node, ConstantBVec4Node, ConstantBooleanNode, ConstantScalarNode, ConstantTexture2DArrayNode, ConstantTexture2DNode, ConstantTextureCubeNode, ConstantVec2Node, ConstantVec3Node, ConstantVec4Node, ConstraintType, CopyBlitter, CosHNode, CosNode, CrossProductNode, CubemapSHProjector, CullVisitor, CylinderShape, DDXNode, DDYNode, Degrees2RadiansNode, DepthPass, DirectionalLight, DistanceNode, DotProductNode, DracoMeshDecoder, EPSILON$1 as EPSILON, ESM, ElapsedTimeNode, Engine, EnvConstantAmbient, EnvHemisphericAmbient, EnvLightWrapper, EnvShIBL, Environment, EnvironmentLighting, EqualNode, Exp2Node, ExpNode, FABRIKSolver, FBMWaveGenerator, FFTWaveGenerator, FPSCameraController, FWidthNode, FXAA, FaceForwardNode, FixedGeometryCacheTrack, FloorNode, FmaNode, Fog, FontAsset, FractNode, FunctionCallNode, FunctionInputNode, FunctionOutputNode, GPUClothSystem, GaussianBlurBlitter, GenericMathNode, GerstnerWaveGenerator, GraphNode, GrassInstances, GrassLayer, GrassMaterial, GrassQuadtreeNode, GrassRenderer, Grayscale, HEIGHT_FOG_BIT, Hash1Node, Hash2Node, Hash3Node, HistoryResourceManager, HumanoidBodyRig, HumanoidHandRig, IKAngleConstraint, IKChain, IKConstraint, IKModifier, IKSolver, InputManager, InstanceBindGroupAllocator, InstanceIndexNode, InvProjMatrixNode, InvSqrtNode, InvViewProjMatrixNode, JointDynamicsModifier, JointDynamicsSystem, JointDynamicsSystemController, LIGHT_TYPE_DIRECTIONAL, LIGHT_TYPE_NONE, LIGHT_TYPE_POINT, LIGHT_TYPE_RECT, LIGHT_TYPE_SPOT, LambertMaterial, LengthNode, LightPass, Log2Node, LogNode, LogicallyAndNode, LogicallyOrNode, MAX_CLUSTERED_LIGHTS, MAX_GERSTNER_WAVE_COUNT, MAX_MORPH_ATTRIBUTES, MAX_MORPH_TARGETS, MAX_TERRAIN_MIPMAP_LEVELS, MORPH_ATTRIBUTE_VECTOR_COUNT, MORPH_TARGET_COLOR, MORPH_TARGET_NORMAL, MORPH_TARGET_POSITION, MORPH_TARGET_TANGENT, MORPH_TARGET_TEX0, MORPH_TARGET_TEX1, MORPH_TARGET_TEX2, MORPH_TARGET_TEX3, MORPH_WEIGHTS_VECTOR_COUNT, MSDFText, MSDFTextAtlasManager, MSDFTextSprite, MToonMaterial, MakeVectorNode, Material, MaterialBlueprintIR, MaterialVaryingFlags, MaxNode, Mesh, MeshMaterial, MinNode, MixNode, ModNode, MorphTargetTrack, MultiChainSpringSystem, NamedObject, NodeEulerRotationTrack, NodeRotationTrack, NodeScaleTrack, NodeTranslationTrack, NormalizeNode, NotEqualNode, Octree, OctreeNode, OctreeNodeChunk, OctreePlacement, OrbitCameraController, OrthoCamera, PBRBlockNode, PBRBluePrintMaterial, PBRMetallicRoughnessMaterial, PBRSpecularGlossinessMaterial, PCAGeometryCacheTrack, PCFOPT, PCFPD, PannerNode, ParticleMaterial, ParticleSystem, PerlinNoise2DNode, PerspectiveCamera, PixelNormalNode, PixelWorldPositionNode, PlaneShape, PointLight, PostEffectLayer, PowNode, Primitive, ProjectionMatrixNode, PropertyTrack, PunctualLight, QUEUE_OPAQUE, QUEUE_TRANSPARENT, RENDER_PASS_TYPE_DEPTH, RENDER_PASS_TYPE_LIGHT, RENDER_PASS_TYPE_OBJECT_COLOR, RENDER_PASS_TYPE_SHADOWMAP, Radians2DegreesNode, RaycastVisitor, RectLight, ReflectNode, RefractNode, RenderPass, RenderQueue, RenderTarget, ResolveVertexNormalNode, ResolveVertexPositionNode, ResolveVertexTangentNode, ResourceManager, RotateAboutAxisNode, RuntimeScript, SAO, SSS, SaturateNode, Scene, SceneNode, SceneRenderer, ScreenAdapter, ScreenRenderTarget, ScriptAttachment, ScriptRegistry, ScriptingSystem, SelectionNode, ShaderHelper, ShadowMapPass, ShadowMapper, ShadowRegion, Shape, SharedModel, SignNode, SimplexNoise2DNode, SinHNode, SinNode, Skeleton, SkeletonModifier, SkeletonRig, SkinBinding, SkyEnvTextureNode, SkyRenderer, SmoothStepNode, SphereShape, SpotLight, SpringChain, SpringModifier, SpringSystem, Sprite, SpriteBlockNode, SpriteBlueprintMaterial, SpriteMaterial, SqrtNode, StandardSpriteMaterial, StepNode, SubsurfaceProfile, SwizzleNode, TAA, TAA_DEBUG_ALAPH, TAA_DEBUG_CURRENT_COLOR, TAA_DEBUG_EDGE, TAA_DEBUG_HISTORY_COLOR, TAA_DEBUG_MOTION_VECTOR, TAA_DEBUG_NONE, TAA_DEBUG_STRENGTH, TAA_DEBUG_VELOCITY, TanHNode, TanNode, TetrahedronFrameShape, TetrahedronShape, TextSprite, TextureSampleGrad, TextureSampleNode, Tonemap, TorusShape, TransformNode, TwoBoneIKSolver, UnlitMaterial, VSM, VertexBinormalNode, VertexBlockNode, VertexColorNode, VertexIndexNode, VertexNormalNode, VertexOutputNode, VertexPositionNode, VertexTangentNode, VertexUVNode, ViewMatrixNode, ViewProjMatrixNode, Water, WaterShaderImpl, WeightedBlendedOIT, aerialPerspective, aerialPerspectiveLut, appInstance, applyAngleLimits, applyMaterialMixins, applyResult, applyRuntimeScriptConfig, atmosphereLUTRendered, buildConstraints, buildMSDFShape, buildSurfaceFaces, checkSurfaceCollision, collisionDetection, collisionDetectionCapsule, collisionDetectionSphere, computeMaxDepth, computeNearestPoints, copyTexture, createAPLutProgram, createCapsuleCollider, createGPUClothWrapBindingData, createGeometryCacheState, createGradientNoiseTexture, createMultiScatteringLutProgram, createPlaneCollider, createProgramFFT2H, createProgramFFT2V, createProgramH0, createProgramHk, createProgramOcean, createProgramPostFFT2, createRandomNoiseTexture, createSkyViewLutProgram, createSphereCollider, createSpringConstraint, createSpringParticle, createTransformAccess, createTransmittanceLutProgram, debugTexture, decode2HalfFromRGBA, decodeFloatFromRGBA, decodeNormalizedFloatFromRGBA, decodeRGBM, defineProps, encode2HalfToRGBA, encodeFloatToRGBA, encodeNormalizedFloatToRGBA, encodeRGBM, ensureGeometryCacheMeshBinding, extractLocalTwist, fetchNormalizedFloatForDevice, fetchSampler, gammaToLinear, generateMSDF, getAerialPerspectiveLut, getApp, getAtmosphereParamsStruct, getBatchGroupClass, getCameraClass, getDefaultAtmosphereParams, getDefaultTexture2D, getDefaultTexture2DArray, getDefaultTextureCube, getDevice, getDirectionalLightClass, getEngine, getGraphNodeClass, getInput, getMeshClass, getMultiScattering, getMultiScatteringLut, getNodeTypeComponents, getOrthoCameraClass, getParticleNodeClass, getPerspectiveCameraClass, getPointLightClass, getPunctualLightClass, getRectLightClass, getRuntimeScriptProperties, getSceneNodeClass, getSkyView, getSkyViewLut, getSpotLightClass, getTransmittanceLut, gradient, hash11, hash12, hash13, hash21, hash22, hash23, hash31, hash32, hash33, integralMultiScattering, interleavedGradientNoise, isGPUClothSupported, linearToGamma, mieAbsorption, mieCoefficient, miePhase, mixGeometryCacheBoundingBox, mixinAlbedoColor, mixinBlinnPhong, mixinFoliage, mixinLambert, mixinLight, mixinPBRBluePrint, mixinPBRCommon, mixinPBRMetallicRoughness, mixinPBRSpecularGlossness, mixinTextureProps, mixinVertexColor, multiScatteringLut, noise3D, normalizeRuntimeScriptConfig, normalizeScriptAttachmentConfig, normalizeScriptAttachments, ozoneAbsorption, panoramaToCubemap, perlinNoise2D, perlinNoise3D, prefilterCubemap, pushInCollisionDetection, pushInFromCapsule, pushInFromCollider, pushInFromSphere, pushoutFromCapsule, pushoutFromCollider, pushoutFromSphere, rayIntersectSphere, rayleighCoefficient, rayleighPhase, renderAPLut, renderAtmosphereLUTs, renderMultiScatteringLut, renderSkyViewLut, renderTransmittanceLut, resolveCapsuleCollision, resolvePlaneCollision, resolveSphereCollision, restoreGeometryCacheMeshBinding, scattering, scriptProp, setApp, setSceneMeshAssetBinding, simulate, skyBox, skyViewLut, smoothNoise3D, sortRootPointsByProximity, temporalResolve, transmittance, transmittanceLut, transmittanceLutToUV, transmittanceToSky, tryGetApp, uniformSphereSamples, updateColliderFromNode, uvToTransmittanceLut, uvToViewDir, valueNoise, viewDirToUV, whiteNoise, worleyFBM, worleyNoise };
93034
95224
  //# sourceMappingURL=zephyr3d_scene.js.map