@hology/core 0.0.191 → 0.0.192
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.
- package/dist/effects/sequence/sequence-actor.d.ts +4 -0
- package/dist/effects/sequence/sequence-actor.js +1 -1
- package/dist/effects/sequence/sequence-data.d.ts +5 -0
- package/dist/effects/sequence/sequence-data.js +1 -1
- package/dist/effects/sequence/sequence-player.d.ts +3 -0
- package/dist/effects/sequence/sequence-player.js +1 -1
- package/dist/effects/vfx/behaviours.d.ts +7 -0
- package/dist/effects/vfx/behaviours.js +1 -1
- package/dist/effects/vfx/index.d.ts +2 -0
- package/dist/effects/vfx/index.js +1 -1
- package/dist/effects/vfx/initializsers.d.ts +6 -4
- package/dist/effects/vfx/initializsers.js +1 -1
- package/dist/effects/vfx/vfx-actor.d.ts +10 -1
- package/dist/effects/vfx/vfx-actor.js +1 -1
- package/dist/effects/vfx/vfx-asset.d.ts +12 -0
- package/dist/effects/vfx/vfx-asset.js +1 -1
- package/dist/effects/vfx/vfx-binding-runtime.d.ts +42 -0
- package/dist/effects/vfx/vfx-binding-runtime.js +4 -0
- package/dist/effects/vfx/vfx-defs.d.ts +58 -3
- package/dist/effects/vfx/vfx-defs.js +1 -1
- package/dist/effects/vfx/vfx-input-runtime.d.ts +17 -0
- package/dist/effects/vfx/vfx-input-runtime.js +4 -0
- package/dist/effects/vfx/vfx-materializer.d.ts +2 -1
- package/dist/effects/vfx/vfx-materializer.js +1 -1
- package/dist/effects/vfx/vfx-param.d.ts +3 -1
- package/dist/effects/vfx/vfx-param.js +1 -1
- package/dist/effects/vfx/vfx-service.d.ts +6 -2
- package/dist/effects/vfx/vfx-service.js +1 -1
- package/dist/gameplay/actors/builtin/components/character/character-animation.d.ts +3 -0
- package/dist/gameplay/actors/builtin/components/character/character-animation.js +1 -1
- package/dist/gameplay/actors/builtin/navmesh-actor.js +1 -1
- package/dist/gameplay/services/asset-loader.d.ts +3 -0
- package/dist/gameplay/services/asset-loader.js +1 -1
- package/dist/gameplay/services/physics/physics-system.js +1 -1
- package/dist/gameplay/services/render.d.ts +12 -0
- package/dist/gameplay/services/render.js +1 -1
- package/dist/rendering.d.ts +2 -0
- package/dist/rendering.js +1 -1
- package/dist/scene/asset-resource-loader.d.ts +1 -0
- package/dist/scene/asset-resource-loader.js +1 -1
- package/dist/scene/collision/collision-shape-import.js +1 -1
- package/dist/scene/collision/collision-shape.d.ts +5 -4
- package/dist/scene/collision/collision-shape.js +1 -1
- package/dist/scene/materializer.d.ts +4 -0
- package/dist/scene/materializer.js +1 -1
- package/dist/scene/model.d.ts +53 -2
- package/dist/scene/storage/storage.d.ts +6 -1
- package/dist/scene/storage/storage.js +1 -1
- package/dist/shader/builtin/decal-standard-shader.d.ts +1 -1
- package/dist/shader/builtin/lambert-shader.js +1 -1
- package/dist/shader/builtin/standard-shader.d.ts +8 -6
- package/dist/shader/builtin/standard-shader.js +1 -1
- package/dist/shader/builtin/toon-shader.js +1 -1
- package/dist/shader/parameter.d.ts +6 -0
- package/dist/shader-nodes/time.d.ts +1 -0
- package/dist/shader-nodes/time.js +1 -1
- package/dist/test/collision-shape-import.test.d.ts +2 -0
- package/dist/test/collision-shape-import.test.js +4 -0
- package/dist/test/vfx-input-bindings.test.d.ts +2 -0
- package/dist/test/vfx-input-bindings.test.js +4 -0
- package/dist/utils/obb-utils.d.ts +12 -0
- package/dist/utils/obb-utils.js +4 -0
- package/package.json +2 -2
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -125,6 +125,10 @@ export declare class SequenceActor extends BaseActor implements SequenceAction {
|
|
|
125
125
|
* Seek to a specific time
|
|
126
126
|
*/
|
|
127
127
|
seek(time: number): void;
|
|
128
|
+
/**
|
|
129
|
+
* Refresh a specific asset used in the sequence
|
|
130
|
+
*/
|
|
131
|
+
refreshAsset(assetId: string): void;
|
|
128
132
|
/**
|
|
129
133
|
* Update the sequence (called automatically from render loop)
|
|
130
134
|
*/
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{__decorate as e,__metadata as t}from"tslib";import{Actor as s,BaseActor as r}from"../../gameplay/actors/actor.js";import{inject as a}from"../../gameplay/inject.js";import{ViewController as i}from"../../gameplay/services/render.js";import{AssetLoader as o}from"../../gameplay/services/asset-loader.js";import{World as l}from"../../gameplay/services/world.js";import{ActorFactory as p}from"../../gameplay/actors/factory.js";import{VfxService as n}from"../../effects/vfx/vfx-service.js";import{Parameter as h}from"../../shader/parameter.js";import{createSequenceData as c}from"./sequence-data.js";import{SequencePlayer as y,SequencePlaybackState as d}from"./sequence-player.js";import{Subject as m,takeUntil as u}from"rxjs";let f=class extends r{constructor(){super(...arguments),this.sequenceData=c(),this.sequenceAssetId=null,this.onComplete=new m,this.onLoop=new m,this.player=new y,this.viewController=a(i),this.assetLoader=a(o),this.world=a(l),this.actorFactory=a(p),this.vfxService=a(n),this.sourceAsset=null}get timescale(){return this.player.timescale}set timescale(e){this.player.timescale=e}get loop(){return this.player.loop}set loop(e){this.player.loop=e}get state(){return this.player.state}get time(){return this.player.time}get duration(){return this.player.duration}get paused(){return this.player.state===d.Paused}get externalTimeControl(){return this.player.externalTimeControl}set externalTimeControl(e){this.player.externalTimeControl=e}async onInit(){this.player.setWorld(this.world),this.player.setAudioListener(this.viewController.audioListener),this.player.setAssetLoader(this.assetLoader),this.player.setActorFactory(this.actorFactory),this.player.setVfxService(this.vfxService),this.viewController.onUpdate(this).pipe(u(this.disposed)).subscribe(e=>this.player.update(e)),this.player.onComplete.pipe(u(this.disposed)).subscribe(()=>this.onComplete.next()),this.player.onLoop.pipe(u(this.disposed)).subscribe(e=>this.onLoop.next(e)),this.sequenceAssetId?await this.loadFromAssetId(this.sequenceAssetId):this.sequenceData&&this.player.load(this.sequenceData)}async loadFromAsset(e){if("sequence"!==e.type)throw new Error(`Asset must be a sequence asset but is ${e.type}`);this.sourceAsset=e,this.sourceAsset.sequence&&(this.sequenceData=this.sourceAsset.sequence,this.player.load(this.sequenceData))}async loadFromAssetId(e){const t=await this.assetLoader.getAsset(e);t&&await this.loadFromAsset(t)}setSequenceData(e){this.sequenceData=e,this.player.load(e)}bindRole(e,t){this.player.bindRole(e,t)}bindObject(e,t){this.player.bindObject(e,t)}play(){this.player.play()}pause(){this.player.pause()}stop(){this.player.stop()}restart(){this.player.restart()}seek(e){this.player.seek(e)}onUpdate(e){}onEndPlay(){this.player.dispose()}dispose(){this.world.removeActor(this)}};e([h({label:"Sequence Data"}),t("design:type",Object)],f.prototype,"sequenceData",void 0),f=e([s()],f);export{f as SequenceActor};/*
|
|
1
|
+
import{__decorate as e,__metadata as t}from"tslib";import{Actor as s,BaseActor as r}from"../../gameplay/actors/actor.js";import{inject as a}from"../../gameplay/inject.js";import{ViewController as i}from"../../gameplay/services/render.js";import{AssetLoader as o}from"../../gameplay/services/asset-loader.js";import{World as l}from"../../gameplay/services/world.js";import{ActorFactory as p}from"../../gameplay/actors/factory.js";import{VfxService as n}from"../../effects/vfx/vfx-service.js";import{Parameter as h}from"../../shader/parameter.js";import{createSequenceData as c}from"./sequence-data.js";import{SequencePlayer as y,SequencePlaybackState as d}from"./sequence-player.js";import{Subject as m,takeUntil as u}from"rxjs";let f=class extends r{constructor(){super(...arguments),this.sequenceData=c(),this.sequenceAssetId=null,this.onComplete=new m,this.onLoop=new m,this.player=new y,this.viewController=a(i),this.assetLoader=a(o),this.world=a(l),this.actorFactory=a(p),this.vfxService=a(n),this.sourceAsset=null}get timescale(){return this.player.timescale}set timescale(e){this.player.timescale=e}get loop(){return this.player.loop}set loop(e){this.player.loop=e}get state(){return this.player.state}get time(){return this.player.time}get duration(){return this.player.duration}get paused(){return this.player.state===d.Paused}get externalTimeControl(){return this.player.externalTimeControl}set externalTimeControl(e){this.player.externalTimeControl=e}async onInit(){this.player.setWorld(this.world),this.player.setAudioListener(this.viewController.audioListener),this.player.setAssetLoader(this.assetLoader),this.player.setActorFactory(this.actorFactory),this.player.setVfxService(this.vfxService),this.viewController.onUpdate(this).pipe(u(this.disposed)).subscribe(e=>this.player.update(e)),this.player.onComplete.pipe(u(this.disposed)).subscribe(()=>this.onComplete.next()),this.player.onLoop.pipe(u(this.disposed)).subscribe(e=>this.onLoop.next(e)),this.sequenceAssetId?await this.loadFromAssetId(this.sequenceAssetId):this.sequenceData&&this.player.load(this.sequenceData)}async loadFromAsset(e){if("sequence"!==e.type)throw new Error(`Asset must be a sequence asset but is ${e.type}`);this.sourceAsset=e,this.sourceAsset.sequence&&(this.sequenceData=this.sourceAsset.sequence,this.player.load(this.sequenceData))}async loadFromAssetId(e){const t=await this.assetLoader.getAsset(e);t&&await this.loadFromAsset(t)}setSequenceData(e){this.sequenceData=e,this.player.load(e)}bindRole(e,t){this.player.bindRole(e,t)}bindObject(e,t){this.player.bindObject(e,t)}play(){this.player.play()}pause(){this.player.pause()}stop(){this.player.stop()}restart(){this.player.restart()}seek(e){this.player.seek(e)}refreshAsset(e){this.player.refreshAsset(e)}onUpdate(e){}onEndPlay(){this.player.dispose()}dispose(){this.world.removeActor(this)}};e([h({label:"Sequence Data"}),t("design:type",Object)],f.prototype,"sequenceData",void 0),f=e([s()],f);export{f as SequenceActor};/*
|
|
2
2
|
* Copyright (©) 2026 Hology Interactive AB. All rights reserved.
|
|
3
3
|
* See the LICENSE.md file for details.
|
|
4
4
|
*/
|
|
@@ -340,6 +340,7 @@ export interface AudioClipInstance {
|
|
|
340
340
|
/**
|
|
341
341
|
* An instance of a visual effect placed on the timeline
|
|
342
342
|
*/
|
|
343
|
+
export type VfxClipEndBehavior = 'finish' | 'kill' | 'expire';
|
|
343
344
|
export interface VfxClipInstance {
|
|
344
345
|
/** Unique identifier */
|
|
345
346
|
id: string;
|
|
@@ -349,6 +350,10 @@ export interface VfxClipInstance {
|
|
|
349
350
|
startTime: number;
|
|
350
351
|
/** Duration of this clip instance */
|
|
351
352
|
duration: number;
|
|
353
|
+
/** How existing particles should be handled when the clip ends */
|
|
354
|
+
endBehavior?: VfxClipEndBehavior;
|
|
355
|
+
/** Maximum remaining particle lifetime after clip end when using expire mode */
|
|
356
|
+
expireWithinSeconds?: number;
|
|
352
357
|
}
|
|
353
358
|
/**
|
|
354
359
|
* An instance of a spawn clip placed on the timeline
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{SerializedParamType as e}from"../../scene/model.js";import{randomString as t}from"../../utils/math.js";import{getAudioParameterDefinition as r}from"./audio-parameters.js";export class Sequence{constructor(e){this.data=e}}export var SequenceRole;!function(e){e.Self="self",e.Target="target",e.Target2="target2",e.Target3="target3",e.Secondary="secondary"}(SequenceRole||(SequenceRole={}));export function createSequenceData(){return{duration:5,loop:!1,playbackRate:1,tracks:[]}}export function createObjectTrack(e="Object Track"){return{id:t(),name:e,type:"object",muted:!1,locked:!1,targetId:null,role:null,subTracks:[]}}export function createCameraTrack(e="Camera Track"){return{id:t(),name:e,type:"camera",muted:!1,locked:!1,cameraSettings:{fov:60,near:.1,far:1e3},subTracks:[]}}export function createAudioTrack(e="Audio Track"){return{id:t(),name:e,type:"audio",muted:!1,locked:!1,clips:[],subTracks:[],volume:1,spatial:!1}}export function createVfxTrack(e="VFX Track"){return{id:t(),name:e,type:"vfx",muted:!1,locked:!1,clips:[],position:[0,0,0],rotation:[0,0,0],scale:[1,1,1],subTracks:[]}}export function createSpawnTrack(e="Spawn Track"){return{id:t(),name:e,type:"spawn",muted:!1,locked:!1,spawnType:"mesh",role:null,clips:[],subTracks:[]}}export function createSpawnClipInstance(e,r){return{id:t(),startTime:e,duration:r}}export function createGroupTrack(e="Group"){return{id:t(),name:e,type:"group",muted:!1,locked:!1,collapsed:!1,childTracks:[],subTracks:[]}}export function createTransformSubTrack(){return{id:t(),name:"Transform",type:"transform",muted:!1,components:{position:!0,rotation:!0,scale:!0},keyframes:[]}}export function createAnimationSubTrack(e="Animation"){return{id:t(),name:e,type:"animation",muted:!1,clips:[]}}export function createAudioParameterSubTrack(e="volume"){const a=r(e);return{id:t(),name:a.displayName,type:"audioParameter",muted:!1,parameter:e,keyframes:[]}}export function createPropertySubTrack(r="",a=e.Number){return{id:t(),name:r||"Property",type:"property",muted:!1,propertyPath:r,propertyType:a,keyframes:[]}}export function createTransformKeyframe(e){return{id:t(),time:e,position:[0,0,0],rotation:[0,0,0],scale:[1,1,1],interpolation:"cubic"}}export function createPropertyKeyframe(r,a={type:e.Number,value:0}){return{id:t(),time:r,value:a,interpolation:"linear"}}export function createAnimationClipInstance(e,r,a){return{id:t(),animationClipAssetId:e,startTime:r,duration:a,clipStartOffset:0,clipEndOffset:0,playbackRate:1,fadeInDuration:.2,rootMotion:!1}}export function createAudioClipInstance(e,r,a){return{id:t(),audioAssetId:e,startTime:r,duration:a,clipStartOffset:0,playbackRate:1,volume:1,fadeInDuration:0,fadeOutDuration:0,volumeRandomization:0,pitchRandomization:0}}export function createVfxClipInstance(e,r,a){return{id:t(),vfxAssetId:e,startTime:r,duration:a}}export function createEventSubTrack(e="Events"){return{id:t(),name:e,type:"event",muted:!1,events:[]}}export function createSequenceEventData(e,r=""){return{id:t(),time:e,functionName:r,arguments:[]}}/*
|
|
1
|
+
import{SerializedParamType as e}from"../../scene/model.js";import{randomString as t}from"../../utils/math.js";import{getAudioParameterDefinition as r}from"./audio-parameters.js";export class Sequence{constructor(e){this.data=e}}export var SequenceRole;!function(e){e.Self="self",e.Target="target",e.Target2="target2",e.Target3="target3",e.Secondary="secondary"}(SequenceRole||(SequenceRole={}));export function createSequenceData(){return{duration:5,loop:!1,playbackRate:1,tracks:[]}}export function createObjectTrack(e="Object Track"){return{id:t(),name:e,type:"object",muted:!1,locked:!1,targetId:null,role:null,subTracks:[]}}export function createCameraTrack(e="Camera Track"){return{id:t(),name:e,type:"camera",muted:!1,locked:!1,cameraSettings:{fov:60,near:.1,far:1e3},subTracks:[]}}export function createAudioTrack(e="Audio Track"){return{id:t(),name:e,type:"audio",muted:!1,locked:!1,clips:[],subTracks:[],volume:1,spatial:!1}}export function createVfxTrack(e="VFX Track"){return{id:t(),name:e,type:"vfx",muted:!1,locked:!1,clips:[],position:[0,0,0],rotation:[0,0,0],scale:[1,1,1],subTracks:[]}}export function createSpawnTrack(e="Spawn Track"){return{id:t(),name:e,type:"spawn",muted:!1,locked:!1,spawnType:"mesh",role:null,clips:[],subTracks:[]}}export function createSpawnClipInstance(e,r){return{id:t(),startTime:e,duration:r}}export function createGroupTrack(e="Group"){return{id:t(),name:e,type:"group",muted:!1,locked:!1,collapsed:!1,childTracks:[],subTracks:[]}}export function createTransformSubTrack(){return{id:t(),name:"Transform",type:"transform",muted:!1,components:{position:!0,rotation:!0,scale:!0},keyframes:[]}}export function createAnimationSubTrack(e="Animation"){return{id:t(),name:e,type:"animation",muted:!1,clips:[]}}export function createAudioParameterSubTrack(e="volume"){const a=r(e);return{id:t(),name:a.displayName,type:"audioParameter",muted:!1,parameter:e,keyframes:[]}}export function createPropertySubTrack(r="",a=e.Number){return{id:t(),name:r||"Property",type:"property",muted:!1,propertyPath:r,propertyType:a,keyframes:[]}}export function createTransformKeyframe(e){return{id:t(),time:e,position:[0,0,0],rotation:[0,0,0],scale:[1,1,1],interpolation:"cubic"}}export function createPropertyKeyframe(r,a={type:e.Number,value:0}){return{id:t(),time:r,value:a,interpolation:"linear"}}export function createAnimationClipInstance(e,r,a){return{id:t(),animationClipAssetId:e,startTime:r,duration:a,clipStartOffset:0,clipEndOffset:0,playbackRate:1,fadeInDuration:.2,rootMotion:!1}}export function createAudioClipInstance(e,r,a){return{id:t(),audioAssetId:e,startTime:r,duration:a,clipStartOffset:0,playbackRate:1,volume:1,fadeInDuration:0,fadeOutDuration:0,volumeRandomization:0,pitchRandomization:0}}export function createVfxClipInstance(e,r,a){return{id:t(),vfxAssetId:e,startTime:r,duration:a,endBehavior:"finish",expireWithinSeconds:.25}}export function createEventSubTrack(e="Events"){return{id:t(),name:e,type:"event",muted:!1,events:[]}}export function createSequenceEventData(e,r=""){return{id:t(),time:e,functionName:r,arguments:[]}}/*
|
|
2
2
|
* Copyright (©) 2026 Hology Interactive AB. All rights reserved.
|
|
3
3
|
* See the LICENSE.md file for details.
|
|
4
4
|
*/
|
|
@@ -249,6 +249,8 @@ export declare class SequencePlayer {
|
|
|
249
249
|
*/
|
|
250
250
|
private stopAllAudio;
|
|
251
251
|
private evaluateVfxTrack;
|
|
252
|
+
private getVfxClipKey;
|
|
253
|
+
private applyInactiveVfxClipBehavior;
|
|
252
254
|
/**
|
|
253
255
|
* Ensure VFX actor exists for a clip (create if needed, reuse if exists)
|
|
254
256
|
*/
|
|
@@ -319,5 +321,6 @@ export declare class SequencePlayer {
|
|
|
319
321
|
private restoreOriginalParents;
|
|
320
322
|
private evaluateAudioSubTracks;
|
|
321
323
|
private stringToRandom;
|
|
324
|
+
refreshAsset(assetId: string): void;
|
|
322
325
|
}
|
|
323
326
|
//# sourceMappingURL=sequence-player.d.ts.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{BehaviorSubject as t,Subject as e}from"rxjs";import*as i from"three";import{AnimationMixer as s,Euler as a,Quaternion as o,Vector3 as n}from"three";import{BaseActor as r}from"../../gameplay/actors/actor.js";import c from"../../gameplay/actors/builtin/index.js";import{getAudioParameterDefinition as l}from"./audio-parameters.js";import{SkeletonUtils as u}from"three-stdlib";import{CharacterAnimationComponent as h,CharacterMovementComponent as d}from"../../gameplay/actors/index.js";import{RootMotionClip as p}from"../../gameplay/animation/root-motion.js";export var SequencePlaybackState;!function(t){t.Stopped="stopped",t.Playing="playing",t.Paused="paused"}(SequencePlaybackState||(SequencePlaybackState={}));export class SequencePlayer{constructor(){this._state=new t(SequencePlaybackState.Stopped),this._time=new t(0),this._duration=0,this._timescale=1,this._loop=!1,this._externalTimeControl=!1,this.bindings=new Map,this.roleBindings=new Map,this.sequenceData=null,this.onComplete=new e,this.onLoop=new e,this.onTimeUpdate=this._time.asObservable(),this.onStateChange=this._state.asObservable(),this.loopCount=0,this.sequenceCamera=null,this.originalCamera=null,this.audioListener=null,this.audioBuffers=new Map,this.loadingAudioBuffers=new Map,this.animationClips=new Map,this.loadingAnimationClips=new Set,this.audioByClip=new Map,this.activeAudioClips=new Set,this.audioGainNodes=new Map,this.audioStartInFlight=new Map,this.lastAudioStartAttemptTime=new Map,this.audioStartRetryIntervalSeconds=.25,this.world=null,this.assetLoader=null,this.actorFactory=null,this.vfxService=null,this.spawnedInstances=new Map,this.activeClips=new Set,this.pendingSpawns=new Set,this.vfxActors=new Map,this.activeVfxClips=new Set,this.clearCounter=0,this.audioFilters=new Map,this.audioPannerNodes=new Map,this.firedEvents=new Set}get state(){return this._state.value}get time(){return this._time.value}get duration(){return this._duration}get timescale(){return this._timescale}set timescale(t){this._timescale=Math.max(.01,t)}get loop(){return this._loop}set loop(t){this._loop=t}get externalTimeControl(){return this._externalTimeControl}set externalTimeControl(t){this._externalTimeControl=t}setWorld(t){this.world!==t&&(this.world=t,this.clearCounter++,this.vfxActors.clear(),this.spawnedInstances.clear(),this.bindings.clear(),this.activeClips.clear(),this.activeVfxClips.clear(),this.activeAudioClips.clear(),this.audioStartInFlight.clear(),this.lastAudioStartAttemptTime.clear(),this.loadingAudioBuffers.clear(),this.audioFilters.clear(),this.audioPannerNodes.clear(),this.pendingSpawns.clear(),this._state.next(SequencePlaybackState.Stopped))}setAudioListener(t){this.audioListener=t}setAssetLoader(t){if(this.assetLoader=t,this.sequenceData){const t=this.clearCounter;this.preloadAudioBuffersForSequence(this.sequenceData,t)}}setActorFactory(t){this.actorFactory=t}setVfxService(t){this.vfxService=t}load(t){if(this.stop(),this.clearCounter++,this.sequenceData=t,this._duration=t.duration,this._loop=t.loop,this._timescale=t.playbackRate,this.bindings.clear(),this.loopCount=0,this.assetLoader){const e=this.clearCounter;this.preloadAudioBuffersForSequence(t,e)}}getAudioAssetIdsForSequence(t){const e=new Set;for(const i of t.tracks){if("audio"!==i.type)continue;const t=i;for(const i of t.clips??[])i.audioAssetId&&e.add(i.audioAssetId)}return[...e]}async preloadAudioBuffersForSequence(t,e){if(!this.assetLoader)return;const i=this.getAudioAssetIdsForSequence(t);0!==i.length&&await Promise.all(i.map(async t=>{if(e!==this.clearCounter)return;if(this.audioBuffers.has(t))return;let i=this.loadingAudioBuffers.get(t);i||(i=this.assetLoader.getAudioByAssetId(t),this.loadingAudioBuffers.set(t,i));try{const s=await i;if(e!==this.clearCounter)return;this.audioBuffers.set(t,s)}catch(t){}finally{this.loadingAudioBuffers.get(t)===i&&this.loadingAudioBuffers.delete(t)}}))}bindRole(t,e){this.roleBindings.set(t,e),this.sequenceData&&this.resolveBindings()}bindObject(t,e){if(this.sequenceData)for(const i of this.sequenceData.tracks)"object"===i.type&&i.targetId===t&&this.createBinding(i,e)}resolveBindings(){if(this.sequenceData)for(const t of this.sequenceData.tracks)this.resolveTrackBinding(t)}resolveTrackBinding(t){if("object"===t.type){const e=t;e.role&&this.roleBindings.has(e.role)&&this.createBinding(t,this.roleBindings.get(e.role))}else if("spawn"===t.type){const e=t;e.role&&this.roleBindings.has(e.role)&&this.createBinding(t,this.roleBindings.get(e.role))}else if("group"===t.type)for(const e of t.childTracks)this.resolveTrackBinding(e)}createBinding(t,e){const i=e instanceof r?e.object:e,a={track:t,target:e,activeActions:new Map,originalTransform:{position:i.position.clone(),rotation:i.rotation.clone(),scale:i.scale.clone()},originalParent:i.parent};t.subTracks.some(t=>"animation"===t.type)&&(a.mixer=new s(i)),this.bindings.set(t.id,a)}play(){this._state.value!==SequencePlaybackState.Playing&&this.sequenceData&&(this._state.value===SequencePlaybackState.Stopped&&(this._time.next(0),this.loopCount=0,this.resolveBindings(),this.captureOriginalTransforms()),this._state.next(SequencePlaybackState.Playing),this.resumeVfx())}pause(){this._state.value===SequencePlaybackState.Playing&&(this._state.next(SequencePlaybackState.Paused),this.pauseAudio(),this.pauseVfx())}pauseAudio(){for(const t of this.audioByClip.values())t.pause()}pauseVfx(){for(const t of this.vfxActors.values())t.pause()}resumeVfx(){for(const[t,e]of this.vfxActors.entries())(this.activeVfxClips.has(t)||e.getParticleCount()>0)&&e.play()}stop(){this._state.next(SequencePlaybackState.Stopped),this._time.next(0),this.clearCounter++,this.loopCount=0,this.pauseAudio(),this.restoreOriginalTransforms(),this.restoreOriginalParents(),this.stopAllAudio(),this.stopAllAnimations(),this.despawnAll(),this.stopAllVfx(),this.firedEvents.clear()}restart(){this.stop(),this.play()}seek(t){const e=Math.max(0,Math.min(t,this._duration));this._time.next(e),this.evaluate(e)}update(t){if(this._state.value!==SequencePlaybackState.Playing)return;if(!this.sequenceData)return;if(this._externalTimeControl){for(const e of this.bindings.values())e.mixer&&e.mixer.update(t*this._timescale);for(const e of this.spawnedInstances.values())e.mixer&&e.mixer.update(t*this._timescale);return}const e=this._time.value+t*this._timescale;if(e>=this._duration){if(!this._loop)return this._time.next(this._duration),this.evaluate(this._duration),this._state.next(SequencePlaybackState.Stopped),void this.onComplete.next();this.loopCount++,this._time.next(e%this._duration),this.firedEvents.clear(),this.onLoop.next(this.loopCount)}else this._time.next(e);this.evaluate(this._time.value);for(const e of this.bindings.values())e.mixer&&e.mixer.update(t*this._timescale);for(const e of this.spawnedInstances.values())e.mixer&&e.mixer.update(t*this._timescale)}evaluate(t){if(this.sequenceData)for(const e of this.sequenceData.tracks){if(!e.muted&&("object"===e.type||"spawn"===e.type||"vfx"===e.type)){const t=this.bindings.get(e.id);this.updateTrackParenting(e,t)}this.evaluateTrack(e,t)}}evaluateTrack(t,e){if(t.muted)return;const i=this.bindings.get(t.id);switch(t.type){case"object":i&&this.evaluateObjectTrack(t,i,e);break;case"camera":this.evaluateCameraTrack(t,e);break;case"audio":this.evaluateAudioTrack(t,e);break;case"vfx":this.evaluateVfxTrack(t,e);break;case"spawn":this.evaluateSpawnTrack(t,i,e);break;case"group":for(const i of t.childTracks)this.evaluateTrack(i,e)}}evaluateObjectTrack(t,e,i){this.evaluateSubTracks(t.subTracks,e,i)}evaluateSubTracks(t,e,i){const s=e.target instanceof r?e.target.object:e.target;if(s)for(const a of t)if(!a.muted)switch(a.type){case"transform":this.evaluateTransformSubTrack(a,s,i);break;case"animation":this.evaluateAnimationSubTrack(a,e,i);break;case"visibility":this.evaluateVisibilitySubTrack(a,s,i);break;case"property":this.evaluatePropertySubTrack(a,e.target||null,i);break;case"event":this.evaluateEventSubTrack(a,e.target||null,i)}}evaluateTransformSubTrack(t,e,i){const s=t.keyframes;if(0===s.length)return;const{prev:a,next:o,t:n}=this.findKeyframes(s,i);if(a)if(o&&a!==o){const i=this.applyInterpolation(n,a.interpolation);t.components.position&&a.position&&o.position&&e.position.lerpVectors(f.fromArray(a.position),m.fromArray(o.position),i),t.components.rotation&&a.rotation&&o.rotation&&(g.setFromEuler(v.fromArray([...a.rotation,"XYZ"])),A.setFromEuler(b.fromArray([...o.rotation,"XYZ"])),e.quaternion.slerpQuaternions(g,A,i)),t.components.scale&&a.scale&&o.scale&&e.scale.lerpVectors(f.fromArray(a.scale),m.fromArray(o.scale),i)}else t.components.position&&a.position&&e.position.fromArray(a.position),t.components.rotation&&a.rotation&&e.rotation.fromArray([...a.rotation,"XYZ"]),t.components.scale&&a.scale&&e.scale.fromArray(a.scale)}evaluateAnimationSubTrack(t,e,s){if(!e.mixer||!e.activeActions)return;if(e.target instanceof r?e.target.object:e.target)for(const a of t.clips){const o=a.startTime+a.duration,n=s>=a.startTime&&s<o,c=`${t.id}-${a.id}`;let l=e.activeActions.get(c),u=!1;if(n){if(!l){u=!0;const t=a.animationClipAssetId;if(null==t)continue;const s=this.animationClips.get(t);if(null==s){this.ensureAnimationClip(t);continue}const o=e;let n=!1;if(e.target instanceof r){o.charAnimComponent||(o.charAnimComponent=e.target.getComponent(h)??void 0);let t=s;a.clipEndOffset>0&&(t=t.clone(),t.duration=Math.max(0,t.duration-a.clipEndOffset));const i=o.charAnimComponent;if(null!=i){const s=a.rootMotion?p.fromClip(t,!1):t;if(this._externalTimeControl)l=i.beginExternalAnimationControl(s,{timeScale:a.playbackRate});else if(i.play(s,{inPlace:!a.rootMotion,loop:!1,priority:20,timeScale:a.playbackRate,fadeTime:a.fadeInDuration??.2,offset:a.clipStartOffset}),l=i.getFullBodyAction(),a.rootMotion){const t=e.target.getComponent(d);t&&t.setRootMotionAction(l)}e.activeActions.set(c,l),o.charAnimActionKeys||(o.charAnimActionKeys=new Set),o.charAnimActionKeys.add(c),n=!0}}if(!n){let t=s;a.clipEndOffset>0&&(t=t.clone(),t.duration=Math.max(0,t.duration-a.clipEndOffset)),l=e.mixer.clipAction(t),l.setLoop(i.LoopOnce,1),l.clampWhenFinished=!0,l.timeScale=a.playbackRate,a.clipStartOffset>0&&(l.time=a.clipStartOffset),e.activeActions.set(c,l),l.play()}}if(l){const t=e;if(this._externalTimeControl){t.charAnimComponent?.beginExternalControl();const i=(s-a.startTime)*a.playbackRate+a.clipStartOffset,o=Math.min(i,l.getClip().duration);(u||Math.abs(l.time-o)>.001)&&(l.time=o,l.paused=!1,l.play(),t.charAnimComponent?t.charAnimComponent.getMixer()?.update(0):e.mixer&&e.mixer.update(0))}else t.charAnimComponent?.endExternalControl()}}else if(l){e.activeActions.delete(c);const t=e;t.charAnimActionKeys?.has(c)?(t.charAnimActionKeys.delete(c),0===t.charAnimActionKeys.size&&t.charAnimComponent&&(t.charAnimComponent.stopSequenceAnimation(),t.charAnimComponent=void 0)):(l.stop(),this._externalTimeControl&&e.mixer&&e.mixer.update(0))}}}evaluateVisibilitySubTrack(t,e,i){const s=t.keyframes;if(0===s.length)return;let a=s[0];for(const t of s){if(!(t.time<=i))break;a=t}e.visible=a.visible}evaluatePropertySubTrack(t,e,i){}evaluateEventSubTrack(t,e,i){if(e instanceof r&&this._state.value===SequencePlaybackState.Playing)for(const s of t.events){const a=`${t.id}-${s.id}`;if(i>=s.time&&!this.firedEvents.has(a)){if(this.firedEvents.add(a),!s.functionName)continue;const t=e[s.functionName];if("function"==typeof t)try{const i=s.arguments.map(t=>t.value);t.call(e,...i)}catch(t){console.error(`Failed to call sequence event ${s.functionName}:`,t)}else console.warn(`Sequence event method '${s.functionName}' not found on actor`)}else s.time>i&&this.firedEvents.delete(a)}}evaluateCameraTrack(t,e){}evaluateAudioTrack(t,e){if(!this.assetLoader||!t.clips||0===t.clips.length)return;const i=this._state.value===SequencePlaybackState.Playing,s=this.evaluateAudioSubTracks(t,e);t.volume;for(const a of t.clips){const o=a.startTime+a.duration,n=e>=a.startTime&&e<o,r=`${t.id}-${a.id}`;if(n){if(this.activeAudioClips.has(r)||this.activeAudioClips.add(r),i){const i=this.audioByClip.get(r);i&&i.isPlaying||this.tryStartAudioClip(t,a,r,e,s)}const o=this.audioByClip.get(r);o&&this.applyAudioParameters(o,t,a,r,e,s)}else this.activeAudioClips.has(r)&&(this.activeAudioClips.delete(r),this.lastAudioStartAttemptTime.delete(r),this.audioStartInFlight.delete(r),this.stopAudioClip(r))}}tryStartAudioClip(t,e,i,s,a){if(this.audioStartInFlight.has(i))return;const o=this.lastAudioStartAttemptTime.get(i);if(void 0!==o&&s-o<this.audioStartRetryIntervalSeconds)return;this.lastAudioStartAttemptTime.set(i,s);const n=this.playAudioClip(t,e,i,s,a).catch(()=>{}).finally(()=>{this.audioStartInFlight.get(i)===n&&this.audioStartInFlight.delete(i)});this.audioStartInFlight.set(i,n)}applyAudioParameters(t,e,s,a,o,n){const r=n.volume??1,c=this.stringToRandom(a+e.id),l=s.volumeRandomization?(2*c-1)*s.volumeRandomization:0;let u=1;const h=o-s.startTime;h<s.fadeInDuration&&s.fadeInDuration>0?u=h/s.fadeInDuration:h>s.duration-s.fadeOutDuration&&s.fadeOutDuration>0&&(u=(s.duration-h)/s.fadeOutDuration);const d=Math.max(0,(e.volume??1)*r*(s.volume+l)*u);t.setVolume(d);let p=n.detune??0;if(s.pitchRandomization){p+=(2*this.stringToRandom(a+"_pitch")-1)*s.pitchRandomization}if(t.setDetune(p),t.hasPlaybackControl&&t.setPlaybackRate(s.playbackRate*this.timescale),this.audioListener&&this.audioListener.context){const s=this.audioListener.context,o=[],r=n.lowpass,c=n.highpass;let l=this.audioFilters.get(a);if(void 0!==r||void 0!==c)l||(l=s.createBiquadFilter(),this.audioFilters.set(a,l)),void 0!==r?(l.type="lowpass",l.frequency.value=r):void 0!==c&&(l.type="highpass",l.frequency.value=c),o.push(l);else if(l){try{l.disconnect()}catch{}this.audioFilters.delete(a)}const u=n.pan;if(!e.spatial&&t instanceof i.Audio&&void 0!==u&&"function"==typeof s.createStereoPanner){let t=this.audioPannerNodes.get(a);t||(t=s.createStereoPanner(),this.audioPannerNodes.set(a,t)),t.pan.value=u,o.push(t)}else{const t=this.audioPannerNodes.get(a);if(t){try{t.disconnect()}catch{}this.audioPannerNodes.delete(a)}}t.setFilters(o)}e.spatial&&t instanceof i.PositionalAudio?this.updatePositionalAudioTransform(t,e,s,n.spatialBlend):void 0!==n.pan&&this.audioListener}updatePositionalAudioTransform(t,e,s,a){let o=null,n=null;const r=s.attachedToTrackId||e.parent?.trackId,c=s.attachmentSocketName||e.parent?.socketName;if(r){const t=this.sequenceData?.tracks.find(t=>t.id===r);if(t){const e=this.getActiveObjectsForTrack(t);if(e.length>0){const t=e[0];if(o=f,n=g,t.getWorldPosition(o),t.getWorldQuaternion(n),c){const e=this.findSocket(t,c);e&&(e.getWorldPosition(o),e.getWorldQuaternion(n))}}}}if(o&&n){const e="number"==typeof a?i.MathUtils.clamp(a,0,1):1;if(e<1&&this.audioListener){const t=y;this.audioListener.getWorldPosition(t),o=o.clone().lerp(t,1-e)}t.position.copy(o),t.quaternion.copy(n),t.updateMatrixWorld()}}async playAudioClip(t,e,s,a,o){if(!this.assetLoader||!e.audioAssetId||null==this.audioListener)return;if("suspended"===this.audioListener.context.state)try{await this.audioListener.context.resume()}catch(t){return void console.warn("Failed to resume audio context:",t)}this.stopAudioClip(s);const n=this.clearCounter;try{let a=this.audioBuffers.get(e.audioAssetId);if(!a){let t=this.loadingAudioBuffers.get(e.audioAssetId);t||(t=this.assetLoader.getAudioByAssetId(e.audioAssetId),this.loadingAudioBuffers.set(e.audioAssetId,t));try{a=await t,this.audioBuffers.set(e.audioAssetId,a)}finally{this.loadingAudioBuffers.delete(e.audioAssetId)}}if(n!==this.clearCounter)return;const r=this._time.value,c=e.startTime+e.duration;if(!(r>=e.startTime&&r<c))return;const l=Math.max(0,r-e.startTime),u=e.clipStartOffset+l*e.playbackRate;if(u>=a.duration||l>=e.duration)return;let h=this.audioByClip.get(s);const d=!0===t.spatial,p=h&&!0===h.isPositionalAudio;if(h&&d!==p&&(h.isPlaying&&h.stop(),h.disconnect(),this.audioByClip.delete(s),h=void 0),!h){if(d){const t=new i.PositionalAudio(this.audioListener);t.setRefDistance(10),t.setRolloffFactor(1),h=t}else h=new i.Audio(this.audioListener);this.world&&this.world.scene.add(h),this.audioByClip.set(s,h)}Math.min((e.duration-l)/e.playbackRate,(a.duration-u)/e.playbackRate);h.setLoopStart(e.clipStartOffset),h.setLoopEnd(Math.min(e.clipStartOffset+e.duration,a.duration)),h.setLoop(!0),h.setBuffer(a),this.applyAudioParameters(h,t,e,s,r,o),h.isPlaying||h.play()}catch(t){console.error(`Failed to play audio clip ${e.id}:`,t)}}stopAudioClip(t){const e=this.audioByClip.get(t);if(e){try{e.isPlaying&&e.stop()}catch(t){}e.parent&&e.parent.remove(e),this.audioByClip.delete(t)}const i=this.audioGainNodes.get(t);i&&(i.disconnect(),this.audioGainNodes.delete(t));const s=this.audioFilters.get(t);if(s){try{s.disconnect()}catch{}this.audioFilters.delete(t)}const a=this.audioPannerNodes.get(t);if(a){try{a.disconnect()}catch{}this.audioPannerNodes.delete(t)}}stopAllAudio(){for(const t of this.audioByClip.keys())this.stopAudioClip(t);this.activeAudioClips.clear(),this.audioStartInFlight.clear(),this.lastAudioStartAttemptTime.clear()}evaluateVfxTrack(t,e){if(!this.vfxService||!this.world||!t.clips||0===t.clips.length)return;const i=new Set,s=new Map;for(const a of t.clips){const o=a.startTime+a.duration;if(e>=a.startTime&&e<o){const e=`${t.id}-${a.id}-${a.vfxAssetId}`;i.add(e),s.has(e)||s.set(e,a)}}const a=[];for(const e of this.activeVfxClips)e.startsWith(`${t.id}-`)&&(i.has(e)||a.push(e));for(const t of a){this.activeVfxClips.delete(t);const e=this.vfxActors.get(t);e&&!e.paused&&e.stop()}for(const a of i){const i=this.vfxActors.get(a);if(this.activeVfxClips.has(a))i&&(this.updateVfxTransform(t,i,e),this._state.value===SequencePlaybackState.Playing&&i.play());else{this.activeVfxClips.add(a);const e=s.get(a);this.ensureVfxActor(t,e,a)}}}async ensureVfxActor(t,e,i){if(!this.vfxService||!e.vfxAssetId)return;if(this.vfxActors.has(i)){const t=this.vfxActors.get(i);return t.restart(),void t.play()}const s=this.clearCounter;try{const a=await this.vfxService.createFromAssetId(e.vfxAssetId,this.world.scene);if(s!==this.clearCounter)return void(this.world&&this.world.removeActor(a));const{position:o,rotation:n,scale:r}=this.getVfxTransform(t,e.startTime);a.object.position.copy(o),a.object.rotation.copy(n),a.object.scale.copy(r),this.vfxActors.set(i,a),this._state.value===SequencePlaybackState.Playing&&a.play()}catch(t){console.error(`Failed to create VFX actor for clip ${e.id}:`,t)}}getVfxTransform(t,e){const i=new n,s=new a,r=new n(1,1,1),c=t.subTracks.find(t=>"transform"===t.type);if(c&&c.keyframes.length>0){const{prev:t,next:a,t:n}=this.findKeyframes(c.keyframes,e);if(t&&(t.position&&i.fromArray(t.position),t.rotation&&s.fromArray([...t.rotation,"XYZ"]),t.scale&&r.fromArray(t.scale)),a&&t!==a&&t.time!==a.time){const e=this.applyInterpolation(n,t.interpolation);if(t.position&&a.position&&i.lerpVectors(f.fromArray(t.position),m.fromArray(a.position),e),t.rotation&&a.rotation){g.setFromEuler(v.fromArray([...t.rotation,"XYZ"])),A.setFromEuler(b.fromArray([...a.rotation,"XYZ"]));const i=new o;i.slerpQuaternions(g,A,e),s.setFromQuaternion(i)}t.scale&&a.scale&&r.lerpVectors(f.fromArray(t.scale),m.fromArray(a.scale),e)}}else i.fromArray(t.position),s.fromArray([...t.rotation,"XYZ"]),r.fromArray(t.scale);return{position:i,rotation:s,scale:r}}updateVfxTransform(t,e,i){const{position:s,rotation:a,scale:o}=this.getVfxTransform(t,i);e.object.position.copy(s),e.object.rotation.copy(a),e.object.scale.copy(o)}resolveActorType(t){const e=this.actorFactory.classes[t];if(null!=e)return e;const i=c[t];return i||(console.warn(`Could not resolve actor type: ${t}`),null)}async spawnActorForClip(t,e,i,s){if(!this.world||!t.actorType)return null;const a=this.resolveActorType(t.actorType);if(!a)return null;try{return await this.world.spawnActor(a,i,s)}catch(e){return console.error(`Failed to spawn actor ${t.actorType}:`,e),null}}async spawnPrefabForClip(t,e,i,s){if(!this.world||!this.assetLoader||!t.prefabId)return null;try{const e=await this.assetLoader.getPrefabById(t.prefabId);if(!e)return null;return await this.world.spawnPrefab(e,i,s)}catch(e){return console.error(`Failed to spawn prefab ${t.prefabId}:`,e),null}}async spawnMeshForClip(t,e,i,s){if(!this.world||!this.assetLoader||!t.meshId)return null;try{const e=await this.assetLoader.getAsset(t.meshId),a=await this.assetLoader.getModelByAssetId(t.meshId),o=u.clone(a.scene);if(o.position.copy(i),o.rotation.copy(s),e){await this.assetLoader.applyMaterials(e,o);const t=e.mesh?.rescale;null!=t&&1!==t&&o.scale.multiplyScalar(t)}return this.world.scene.add(o),o}catch(e){return console.error(`Failed to spawn mesh ${t.meshId}:`,e),null}}despawnInstance(t){if(this.world)switch(t?.type){case"actor":this.world.removeActor(t.instance);break;case"prefab":this.world.removePrefab(t.instance);break;case"mesh":this.world.scene.remove(t.instance)}}despawnAll(){for(const t of this.spawnedInstances.values())this.despawnInstance(t);this.spawnedInstances.clear(),this.activeClips.clear(),this.pendingSpawns.clear()}getInitialTransform(t,e,i){const s=new n,o=new a,r=t.subTracks.find(t=>"transform"===t.type);if(r&&r.keyframes.length>0){const{prev:t}=this.findKeyframes(r.keyframes,i);t&&(t.position&&s.fromArray(t.position),t.rotation&&o.fromArray([...t.rotation,"XYZ"]))}else e.initialPosition&&s.fromArray(e.initialPosition),e.initialRotation&&o.fromArray([...e.initialRotation,"XYZ"]);return{position:s,rotation:o}}evaluateSpawnTrack(t,e,i){if(!t.clips||0===t.clips.length)return;const s=e||this.bindings.get(t.id);if(s&&s.target)for(const e of t.clips){const a=e.startTime+e.duration,o=i>=e.startTime&&i<a,n=`${t.id}-${e.id}`;if(o){this.activeClips.has(n)||(this.activeClips.add(n),this.spawnedInstances.set(n,{type:"proxy",target:s.target}));(s.target instanceof r?s.target.object:s.target)&&this.evaluateSubTracks(t.subTracks,s,i)}else this.activeClips.has(n)&&(this.activeClips.delete(n),this.spawnedInstances.delete(n))}else for(const e of t.clips){const s=e.startTime+e.duration,a=i>=e.startTime&&i<s,o=`${t.id}-${e.id}`,n=this.spawnedInstances.has(o),r=this.pendingSpawns.has(o);if(!a||n||r){if(a&&n){const e=this.spawnedInstances.get(o);e&&"proxy"!==e.type&&this.evaluateSubTracks(t.subTracks,e,i)}else if(!a&&n){const t=this.spawnedInstances.get(o);t&&(this.despawnInstance(t),this.spawnedInstances.delete(o),this.activeClips.delete(o))}}else{const{position:i,rotation:s}=this.getInitialTransform(t,e,e.startTime);this.pendingSpawns.add(o),this.spawnForClip(t,e,i,s,o)}}}getInstanceObject(t){switch(t.type){case"actor":return t.instance.object;case"prefab":return t.instance.mainActor?.object||t.instance.object;case"mesh":return t.instance;case"proxy":return t.target instanceof r?t.target.object:t.target;default:return null}}async spawnForClip(t,e,i,a,o){const n=this.clearCounter;let r=null;try{switch(t.spawnType){case"actor":const s=await this.spawnActorForClip(t,e,i,a);s&&(r={type:"actor",instance:s});break;case"prefab":const o=await this.spawnPrefabForClip(t,e,i,a);o&&(r={type:"prefab",instance:o});break;case"mesh":const n=await this.spawnMeshForClip(t,e,i,a);n&&(r={type:"mesh",instance:n})}if(n!==this.clearCounter)return void(r&&this.despawnInstance(r));const c=this._time.value,l=c>=e.startTime&&c<e.startTime+e.duration;if(r&&l){const e=this.getInstanceObject(r);"proxy"===r.type||("actor"===r.type?r.target=r.instance:"prefab"===r.type?r.target=r.instance.mainActor||r.instance.object:r.target=e);t.subTracks.some(t=>"animation"===t.type)&&e&&(r.mixer=new s(e),r.activeActions=new Map),this.spawnedInstances.set(o,r),this.activeClips.add(o)}else r&&this.despawnInstance(r)}finally{this.pendingSpawns.delete(o)}}findKeyframes(t,e){if(0===t.length)return{prev:null,next:null,t:0};let i=null,s=null;for(let a=0;a<t.length;a++)if(t[a].time<=e&&(i=t[a]),t[a].time>e&&!s){s=t[a];break}if(i||(i=t[0]),s||(s=t[t.length-1]),i===s||i.time===s.time)return{prev:i,next:i,t:0};return{prev:i,next:s,t:(e-i.time)/(s.time-i.time)}}applyInterpolation(t,e){switch(e){case"linear":case"catmullRom":default:return t;case"step":return 0;case"cubic":return t<.5?4*t*t*t:1-Math.pow(-2*t+2,3)/2}}async ensureAnimationClip(t){if(this.assetLoader&&!this.animationClips.has(t)&&!this.loadingAnimationClips.has(t)){this.loadingAnimationClips.add(t);try{const e=await this.assetLoader.getAnimationClipByAssetId(t);e&&this.animationClips.set(t,e)}catch(e){console.error(`Failed to load animation clip for asset ${t}:`,e)}finally{this.loadingAnimationClips.delete(t)}}}captureOriginalTransforms(){for(const t of this.bindings.values()){const e=t.target instanceof r?t.target.object:t.target;e&&(t.originalTransform={position:e.position.clone(),rotation:e.rotation.clone(),scale:e.scale.clone()})}}restoreOriginalTransforms(){for(const t of this.bindings.values())if(t.originalTransform){const e=t.target instanceof r?t.target.object:t.target;e&&(e.position.copy(t.originalTransform.position),e.rotation.copy(t.originalTransform.rotation),e.scale.copy(t.originalTransform.scale))}}stopAllAnimations(){for(const t of this.bindings.values()){for(const e of t.activeActions.values())e.stop();t.activeActions.clear(),t.mixer&&t.mixer.stopAllAction(),t.charAnimComponent&&(t.charAnimComponent.stopSequenceAnimation(),t.charAnimComponent=void 0),t.charAnimActionKeys?.clear()}}stopAllVfx(){for(const t of this.vfxActors.values())t.paused||t.stop();this.activeVfxClips.clear()}dispose(){if(this.stop(),this.bindings.clear(),this.roleBindings.clear(),this.sequenceData=null,this.world)for(const t of this.vfxActors.values())this.world.removeActor(t);this.vfxActors.clear(),this.stopAllAudio(),this.audioBuffers.clear(),this.loadingAudioBuffers.clear(),this.animationClips.clear(),this.audioByClip.clear()}updateTrackParenting(t,e){const i=this.getActiveObjectsForTrack(t);if(0===i.length)return;let s=null;const a=t.parent?.trackId;if(a){const e=this.sequenceData?.tracks.find(t=>t.id===a);if(e){const i=this.getActiveObjectsForTrack(e);if(i.length>0&&(s=i[0],t.parent?.socketName)){const e=s.getObjectByName(t.parent.socketName);e&&(s=e)}}}for(const a of i)s?a.parent!==s&&(s.add(a),e&&"object"===t.type&&(e.currentParent=s)):e?.currentParent&&e.currentParent===a.parent?this.restoreObjectParent(e,a):a.parent&&a.parent!==this.world?.scene&&"object"!==t.type&&this.world?.scene.add(a)}findSocket(t,e){if(t.name===e)return t;if(t instanceof i.SkinnedMesh){const i=t.skeleton.bones.find(t=>t.name===e);if(i)return i}return t.getObjectByName(e)||null}getActiveObjectsForTrack(t){if("object"===t.type){const e=this.bindings.get(t.id);if(e&&e.target)return[e.target instanceof r?e.target.object:e.target]}else{if("spawn"===t.type){const e=[];for(const[i,s]of this.spawnedInstances)if(i.startsWith(t.id+"-")&&this.activeClips.has(i)){const t=this.getInstanceObject(s);t&&e.push(t)}return e}if("vfx"===t.type){const e=[];for(const i of this.activeVfxClips)if(i.startsWith(t.id+"-")){const t=this.vfxActors.get(i);t&&e.push(t.object)}return e}}return[]}restoreObjectParent(t,e){t.originalParent?t.originalParent.add(e):this.world&&this.world.scene.add(e),t.currentParent=void 0}restoreOriginalParents(){for(const t of this.bindings.values())if(t.currentParent){const e=t.target instanceof r?t.target.object:t.target;e&&this.restoreObjectParent(t,e)}}evaluateAudioSubTracks(t,e){const i={};for(const s of t.subTracks)if("audioParameter"===s.type){const t=s,a=s.keyframes;if(0===a.length)continue;const{prev:o,next:n,t:r}=this.findKeyframes(a,e);if(!o)continue;let c;if(n&&o!==n){const t=o.value.value,e=n.value.value;if("step"===o.interpolation)c=t;else if("cubic"===o.interpolation){c=t+(e-t)*(r*r*(3-2*r))}else c=t+(e-t)*r}else c=o.value.value;if("number"==typeof c){const e=l(t.parameter),i=e.min,s=e.max;"number"==typeof i&&(c=Math.max(i,c)),"number"==typeof s&&(c=Math.min(s,c))}void 0!==c&&(i[t.parameter]=c)}return i}stringToRandom(t){let e=0;for(let i=0;i<t.length;i++){e=(e<<5)-e+t.charCodeAt(i),e&=e}const i=1e4*Math.sin(e);return i-Math.floor(i)}}const f=new n,m=new n,y=new n,g=new o,A=new o,v=new a,b=new a;/*
|
|
1
|
+
import{BehaviorSubject as t,Subject as e}from"rxjs";import*as i from"three";import{AnimationMixer as s,Euler as a,Quaternion as o,Vector3 as n}from"three";import{BaseActor as r}from"../../gameplay/actors/actor.js";import c from"../../gameplay/actors/builtin/index.js";import{getAudioParameterDefinition as l}from"./audio-parameters.js";import{SkeletonUtils as h}from"three-stdlib";import{CharacterAnimationComponent as u,CharacterMovementComponent as d}from"../../gameplay/actors/index.js";import{RootMotionClip as p}from"../../gameplay/animation/root-motion.js";export var SequencePlaybackState;!function(t){t.Stopped="stopped",t.Playing="playing",t.Paused="paused"}(SequencePlaybackState||(SequencePlaybackState={}));export class SequencePlayer{constructor(){this._state=new t(SequencePlaybackState.Stopped),this._time=new t(0),this._duration=0,this._timescale=1,this._loop=!1,this._externalTimeControl=!1,this.bindings=new Map,this.roleBindings=new Map,this.sequenceData=null,this.onComplete=new e,this.onLoop=new e,this.onTimeUpdate=this._time.asObservable(),this.onStateChange=this._state.asObservable(),this.loopCount=0,this.sequenceCamera=null,this.originalCamera=null,this.audioListener=null,this.audioBuffers=new Map,this.loadingAudioBuffers=new Map,this.animationClips=new Map,this.loadingAnimationClips=new Set,this.audioByClip=new Map,this.activeAudioClips=new Set,this.audioGainNodes=new Map,this.audioStartInFlight=new Map,this.lastAudioStartAttemptTime=new Map,this.audioStartRetryIntervalSeconds=.25,this.world=null,this.assetLoader=null,this.actorFactory=null,this.vfxService=null,this.spawnedInstances=new Map,this.activeClips=new Set,this.pendingSpawns=new Set,this.vfxActors=new Map,this.activeVfxClips=new Set,this.clearCounter=0,this.audioFilters=new Map,this.audioPannerNodes=new Map,this.firedEvents=new Set}get state(){return this._state.value}get time(){return this._time.value}get duration(){return this._duration}get timescale(){return this._timescale}set timescale(t){this._timescale=Math.max(.01,t)}get loop(){return this._loop}set loop(t){this._loop=t}get externalTimeControl(){return this._externalTimeControl}set externalTimeControl(t){this._externalTimeControl=t}setWorld(t){this.world!==t&&(this.world=t,this.clearCounter++,this.vfxActors.clear(),this.spawnedInstances.clear(),this.bindings.clear(),this.activeClips.clear(),this.activeVfxClips.clear(),this.activeAudioClips.clear(),this.audioStartInFlight.clear(),this.lastAudioStartAttemptTime.clear(),this.loadingAudioBuffers.clear(),this.audioFilters.clear(),this.audioPannerNodes.clear(),this.pendingSpawns.clear(),this._state.next(SequencePlaybackState.Stopped))}setAudioListener(t){this.audioListener=t}setAssetLoader(t){if(this.assetLoader=t,this.sequenceData){const t=this.clearCounter;this.preloadAudioBuffersForSequence(this.sequenceData,t)}}setActorFactory(t){this.actorFactory=t}setVfxService(t){this.vfxService=t}load(t){if(this.stop(),this.clearCounter++,this.sequenceData=t,this._duration=t.duration,this._loop=t.loop,this._timescale=t.playbackRate,this.bindings.clear(),this.loopCount=0,this.assetLoader){const e=this.clearCounter;this.preloadAudioBuffersForSequence(t,e)}}getAudioAssetIdsForSequence(t){const e=new Set;for(const i of t.tracks){if("audio"!==i.type)continue;const t=i;for(const i of t.clips??[])i.audioAssetId&&e.add(i.audioAssetId)}return[...e]}async preloadAudioBuffersForSequence(t,e){if(!this.assetLoader)return;const i=this.getAudioAssetIdsForSequence(t);0!==i.length&&await Promise.all(i.map(async t=>{if(e!==this.clearCounter)return;if(this.audioBuffers.has(t))return;let i=this.loadingAudioBuffers.get(t);i||(i=this.assetLoader.getAudioByAssetId(t),this.loadingAudioBuffers.set(t,i));try{const s=await i;if(e!==this.clearCounter)return;this.audioBuffers.set(t,s)}catch(t){}finally{this.loadingAudioBuffers.get(t)===i&&this.loadingAudioBuffers.delete(t)}}))}bindRole(t,e){this.roleBindings.set(t,e),this.sequenceData&&this.resolveBindings()}bindObject(t,e){if(this.sequenceData)for(const i of this.sequenceData.tracks)"object"===i.type&&i.targetId===t&&this.createBinding(i,e)}resolveBindings(){if(this.sequenceData)for(const t of this.sequenceData.tracks)this.resolveTrackBinding(t)}resolveTrackBinding(t){if("object"===t.type){const e=t;e.role&&this.roleBindings.has(e.role)&&this.createBinding(t,this.roleBindings.get(e.role))}else if("spawn"===t.type){const e=t;e.role&&this.roleBindings.has(e.role)&&this.createBinding(t,this.roleBindings.get(e.role))}else if("group"===t.type)for(const e of t.childTracks)this.resolveTrackBinding(e)}createBinding(t,e){const i=e instanceof r?e.object:e,a={track:t,target:e,activeActions:new Map,originalTransform:{position:i.position.clone(),rotation:i.rotation.clone(),scale:i.scale.clone()},originalParent:i.parent};t.subTracks.some(t=>"animation"===t.type)&&(a.mixer=new s(i)),this.bindings.set(t.id,a)}play(){this._state.value!==SequencePlaybackState.Playing&&this.sequenceData&&(this._state.value===SequencePlaybackState.Stopped&&(this._time.next(0),this.loopCount=0,this.resolveBindings(),this.captureOriginalTransforms()),this._state.next(SequencePlaybackState.Playing),this.resumeVfx())}pause(){this._state.value===SequencePlaybackState.Playing&&(this._state.next(SequencePlaybackState.Paused),this.pauseAudio(),this.pauseVfx())}pauseAudio(){for(const t of this.audioByClip.values())t.pause()}pauseVfx(){for(const t of this.vfxActors.values())t.pause()}resumeVfx(){for(const[t,e]of this.vfxActors.entries())(this.activeVfxClips.has(t)||e.getParticleCount()>0)&&e.play()}stop(){this._state.next(SequencePlaybackState.Stopped),this._time.next(0),this.clearCounter++,this.loopCount=0,this.pauseAudio(),this.restoreOriginalTransforms(),this.restoreOriginalParents(),this.stopAllAudio(),this.stopAllAnimations(),this.despawnAll(),this.stopAllVfx(),this.firedEvents.clear()}restart(){this.stop(),this.play()}seek(t){const e=Math.max(0,Math.min(t,this._duration));this._time.next(e),this.evaluate(e)}update(t){if(this._state.value!==SequencePlaybackState.Playing)return;if(!this.sequenceData)return;if(this._externalTimeControl){for(const e of this.bindings.values())e.mixer&&e.mixer.update(t*this._timescale);for(const e of this.spawnedInstances.values())e.mixer&&e.mixer.update(t*this._timescale);return}const e=this._time.value+t*this._timescale;if(e>=this._duration){if(!this._loop)return this._time.next(this._duration),this.evaluate(this._duration),this._state.next(SequencePlaybackState.Stopped),void this.onComplete.next();this.loopCount++,this._time.next(e%this._duration),this.firedEvents.clear(),this.onLoop.next(this.loopCount)}else this._time.next(e);this.evaluate(this._time.value);for(const e of this.bindings.values())e.mixer&&e.mixer.update(t*this._timescale);for(const e of this.spawnedInstances.values())e.mixer&&e.mixer.update(t*this._timescale)}evaluate(t){if(this.sequenceData)for(const e of this.sequenceData.tracks){if(!e.muted&&("object"===e.type||"spawn"===e.type||"vfx"===e.type)){const t=this.bindings.get(e.id);this.updateTrackParenting(e,t)}this.evaluateTrack(e,t)}}evaluateTrack(t,e){if(t.muted)return;const i=this.bindings.get(t.id);switch(t.type){case"object":i&&this.evaluateObjectTrack(t,i,e);break;case"camera":this.evaluateCameraTrack(t,e);break;case"audio":this.evaluateAudioTrack(t,e);break;case"vfx":this.evaluateVfxTrack(t,e);break;case"spawn":this.evaluateSpawnTrack(t,i,e);break;case"group":for(const i of t.childTracks)this.evaluateTrack(i,e)}}evaluateObjectTrack(t,e,i){this.evaluateSubTracks(t.subTracks,e,i)}evaluateSubTracks(t,e,i){const s=e.target instanceof r?e.target.object:e.target;if(s)for(const a of t)if(!a.muted)switch(a.type){case"transform":this.evaluateTransformSubTrack(a,s,i);break;case"animation":this.evaluateAnimationSubTrack(a,e,i);break;case"visibility":this.evaluateVisibilitySubTrack(a,s,i);break;case"property":this.evaluatePropertySubTrack(a,e.target||null,i);break;case"event":this.evaluateEventSubTrack(a,e.target||null,i)}}evaluateTransformSubTrack(t,e,i){const s=t.keyframes;if(0===s.length)return;const{prev:a,next:o,t:n}=this.findKeyframes(s,i);if(a)if(o&&a!==o){const i=this.applyInterpolation(n,a.interpolation);t.components.position&&a.position&&o.position&&e.position.lerpVectors(f.fromArray(a.position),m.fromArray(o.position),i),t.components.rotation&&a.rotation&&o.rotation&&(g.setFromEuler(A.fromArray([...a.rotation,"XYZ"])),v.setFromEuler(b.fromArray([...o.rotation,"XYZ"])),e.quaternion.slerpQuaternions(g,v,i)),t.components.scale&&a.scale&&o.scale&&e.scale.lerpVectors(f.fromArray(a.scale),m.fromArray(o.scale),i)}else t.components.position&&a.position&&e.position.fromArray(a.position),t.components.rotation&&a.rotation&&e.rotation.fromArray([...a.rotation,"XYZ"]),t.components.scale&&a.scale&&e.scale.fromArray(a.scale)}evaluateAnimationSubTrack(t,e,s){if(!e.mixer||!e.activeActions)return;if(e.target instanceof r?e.target.object:e.target)for(const a of t.clips){const o=a.startTime+a.duration,n=s>=a.startTime&&s<o,c=`${t.id}-${a.id}`;let l=e.activeActions.get(c),h=!1;if(n){if(!l){h=!0;const t=a.animationClipAssetId;if(null==t)continue;const s=this.animationClips.get(t);if(null==s){this.ensureAnimationClip(t);continue}const o=e;let n=!1;if(e.target instanceof r){o.charAnimComponent||(o.charAnimComponent=e.target.getComponent(u)??void 0);let t=s;a.clipEndOffset>0&&(t=t.clone(),t.duration=Math.max(0,t.duration-a.clipEndOffset));const i=o.charAnimComponent;if(null!=i){const s=a.rootMotion?p.fromClip(t,!1):t;if(this._externalTimeControl)l=i.beginExternalAnimationControl(s,{timeScale:a.playbackRate});else if(i.play(s,{inPlace:!a.rootMotion,loop:!1,priority:20,timeScale:a.playbackRate,fadeTime:a.fadeInDuration??.2,offset:a.clipStartOffset}),l=i.getFullBodyAction(),a.rootMotion){const t=e.target.getComponent(d);t&&t.setRootMotionAction(l)}e.activeActions.set(c,l),o.charAnimActionKeys||(o.charAnimActionKeys=new Set),o.charAnimActionKeys.add(c),n=!0}}if(!n){let t=s;a.clipEndOffset>0&&(t=t.clone(),t.duration=Math.max(0,t.duration-a.clipEndOffset)),l=e.mixer.clipAction(t),l.setLoop(i.LoopOnce,1),l.clampWhenFinished=!0,l.timeScale=a.playbackRate,a.clipStartOffset>0&&(l.time=a.clipStartOffset),e.activeActions.set(c,l),l.play()}}if(l){const t=e;if(this._externalTimeControl){t.charAnimComponent?.beginExternalControl();const i=(s-a.startTime)*a.playbackRate+a.clipStartOffset,o=Math.min(i,l.getClip().duration);(h||Math.abs(l.time-o)>.001)&&(l.time=o,l.paused=!1,l.play(),t.charAnimComponent?t.charAnimComponent.getMixer()?.update(0):e.mixer&&e.mixer.update(0))}else t.charAnimComponent?.endExternalControl()}}else if(l){e.activeActions.delete(c);const t=e;t.charAnimActionKeys?.has(c)?(t.charAnimActionKeys.delete(c),0===t.charAnimActionKeys.size&&t.charAnimComponent&&(t.charAnimComponent.stopSequenceAnimation(),t.charAnimComponent=void 0)):(l.stop(),this._externalTimeControl&&e.mixer&&e.mixer.update(0))}}}evaluateVisibilitySubTrack(t,e,i){const s=t.keyframes;if(0===s.length)return;let a=s[0];for(const t of s){if(!(t.time<=i))break;a=t}e.visible=a.visible}evaluatePropertySubTrack(t,e,i){}evaluateEventSubTrack(t,e,i){if(e instanceof r&&this._state.value===SequencePlaybackState.Playing)for(const s of t.events){const a=`${t.id}-${s.id}`;if(i>=s.time&&!this.firedEvents.has(a)){if(this.firedEvents.add(a),!s.functionName)continue;const t=e[s.functionName];if("function"==typeof t)try{const i=s.arguments.map(t=>t.value);t.call(e,...i)}catch(t){console.error(`Failed to call sequence event ${s.functionName}:`,t)}else console.warn(`Sequence event method '${s.functionName}' not found on actor`)}else s.time>i&&this.firedEvents.delete(a)}}evaluateCameraTrack(t,e){}evaluateAudioTrack(t,e){if(!this.assetLoader||!t.clips||0===t.clips.length)return;const i=this._state.value===SequencePlaybackState.Playing,s=this.evaluateAudioSubTracks(t,e);t.volume;for(const a of t.clips){const o=a.startTime+a.duration,n=e>=a.startTime&&e<o,r=`${t.id}-${a.id}`;if(n){if(this.activeAudioClips.has(r)||this.activeAudioClips.add(r),i){const i=this.audioByClip.get(r);i&&i.isPlaying||this.tryStartAudioClip(t,a,r,e,s)}const o=this.audioByClip.get(r);o&&this.applyAudioParameters(o,t,a,r,e,s)}else this.activeAudioClips.has(r)&&(this.activeAudioClips.delete(r),this.lastAudioStartAttemptTime.delete(r),this.audioStartInFlight.delete(r),this.stopAudioClip(r))}}tryStartAudioClip(t,e,i,s,a){if(this.audioStartInFlight.has(i))return;const o=this.lastAudioStartAttemptTime.get(i);if(void 0!==o&&s-o<this.audioStartRetryIntervalSeconds)return;this.lastAudioStartAttemptTime.set(i,s);const n=this.playAudioClip(t,e,i,s,a).catch(()=>{}).finally(()=>{this.audioStartInFlight.get(i)===n&&this.audioStartInFlight.delete(i)});this.audioStartInFlight.set(i,n)}applyAudioParameters(t,e,s,a,o,n){const r=n.volume??1,c=this.stringToRandom(a+e.id),l=s.volumeRandomization?(2*c-1)*s.volumeRandomization:0;let h=1;const u=o-s.startTime;u<s.fadeInDuration&&s.fadeInDuration>0?h=u/s.fadeInDuration:u>s.duration-s.fadeOutDuration&&s.fadeOutDuration>0&&(h=(s.duration-u)/s.fadeOutDuration);const d=Math.max(0,(e.volume??1)*r*(s.volume+l)*h);t.setVolume(d);let p=n.detune??0;if(s.pitchRandomization){p+=(2*this.stringToRandom(a+"_pitch")-1)*s.pitchRandomization}if(t.setDetune(p),t.hasPlaybackControl&&t.setPlaybackRate(s.playbackRate*this.timescale),this.audioListener&&this.audioListener.context){const s=this.audioListener.context,o=[],r=n.lowpass,c=n.highpass;let l=this.audioFilters.get(a);if(void 0!==r||void 0!==c)l||(l=s.createBiquadFilter(),this.audioFilters.set(a,l)),void 0!==r?(l.type="lowpass",l.frequency.value=r):void 0!==c&&(l.type="highpass",l.frequency.value=c),o.push(l);else if(l){try{l.disconnect()}catch{}this.audioFilters.delete(a)}const h=n.pan;if(!e.spatial&&t instanceof i.Audio&&void 0!==h&&"function"==typeof s.createStereoPanner){let t=this.audioPannerNodes.get(a);t||(t=s.createStereoPanner(),this.audioPannerNodes.set(a,t)),t.pan.value=h,o.push(t)}else{const t=this.audioPannerNodes.get(a);if(t){try{t.disconnect()}catch{}this.audioPannerNodes.delete(a)}}t.setFilters(o)}e.spatial&&t instanceof i.PositionalAudio?this.updatePositionalAudioTransform(t,e,s,n.spatialBlend):void 0!==n.pan&&this.audioListener}updatePositionalAudioTransform(t,e,s,a){let o=null,n=null;const r=s.attachedToTrackId||e.parent?.trackId,c=s.attachmentSocketName||e.parent?.socketName;if(r){const t=this.sequenceData?.tracks.find(t=>t.id===r);if(t){const e=this.getActiveObjectsForTrack(t);if(e.length>0){const t=e[0];if(o=f,n=g,t.getWorldPosition(o),t.getWorldQuaternion(n),c){const e=this.findSocket(t,c);e&&(e.getWorldPosition(o),e.getWorldQuaternion(n))}}}}if(o&&n){const e="number"==typeof a?i.MathUtils.clamp(a,0,1):1;if(e<1&&this.audioListener){const t=y;this.audioListener.getWorldPosition(t),o=o.clone().lerp(t,1-e)}t.position.copy(o),t.quaternion.copy(n),t.updateMatrixWorld()}}async playAudioClip(t,e,s,a,o){if(!this.assetLoader||!e.audioAssetId||null==this.audioListener)return;if("suspended"===this.audioListener.context.state)try{await this.audioListener.context.resume()}catch(t){return void console.warn("Failed to resume audio context:",t)}this.stopAudioClip(s);const n=this.clearCounter;try{let a=this.audioBuffers.get(e.audioAssetId);if(!a){let t=this.loadingAudioBuffers.get(e.audioAssetId);t||(t=this.assetLoader.getAudioByAssetId(e.audioAssetId),this.loadingAudioBuffers.set(e.audioAssetId,t));try{a=await t,this.audioBuffers.set(e.audioAssetId,a)}finally{this.loadingAudioBuffers.delete(e.audioAssetId)}}if(n!==this.clearCounter)return;const r=this._time.value,c=e.startTime+e.duration;if(!(r>=e.startTime&&r<c))return;const l=Math.max(0,r-e.startTime),h=e.clipStartOffset+l*e.playbackRate;if(h>=a.duration||l>=e.duration)return;let u=this.audioByClip.get(s);const d=!0===t.spatial,p=u&&!0===u.isPositionalAudio;if(u&&d!==p&&(u.isPlaying&&u.stop(),u.disconnect(),this.audioByClip.delete(s),u=void 0),!u){if(d){const t=new i.PositionalAudio(this.audioListener);t.setRefDistance(10),t.setRolloffFactor(1),u=t}else u=new i.Audio(this.audioListener);this.world&&this.world.scene.add(u),this.audioByClip.set(s,u)}Math.min((e.duration-l)/e.playbackRate,(a.duration-h)/e.playbackRate);u.setLoopStart(e.clipStartOffset),u.setLoopEnd(Math.min(e.clipStartOffset+e.duration,a.duration)),u.setLoop(!0),u.setBuffer(a),this.applyAudioParameters(u,t,e,s,r,o),u.isPlaying||u.play()}catch(t){console.error(`Failed to play audio clip ${e.id}:`,t)}}stopAudioClip(t){const e=this.audioByClip.get(t);if(e){try{e.isPlaying&&e.stop()}catch(t){}e.parent&&e.parent.remove(e),this.audioByClip.delete(t)}const i=this.audioGainNodes.get(t);i&&(i.disconnect(),this.audioGainNodes.delete(t));const s=this.audioFilters.get(t);if(s){try{s.disconnect()}catch{}this.audioFilters.delete(t)}const a=this.audioPannerNodes.get(t);if(a){try{a.disconnect()}catch{}this.audioPannerNodes.delete(t)}}stopAllAudio(){for(const t of this.audioByClip.keys())this.stopAudioClip(t);this.activeAudioClips.clear(),this.audioStartInFlight.clear(),this.lastAudioStartAttemptTime.clear()}evaluateVfxTrack(t,e){if(!this.vfxService||!this.world||!t.clips||0===t.clips.length)return;const i=new Set,s=new Map;for(const a of t.clips){const o=this.getVfxClipKey(t,a);s.set(o,a);const n=a.startTime+a.duration;e>=a.startTime&&e<n&&i.add(o)}const a=[];for(const e of this.activeVfxClips)e.startsWith(`${t.id}-`)&&(i.has(e)||a.push(e));for(const t of a)this.activeVfxClips.delete(t);for(const[a,o]of s.entries()){const s=this.vfxActors.get(a);i.has(a)&&!this.activeVfxClips.has(a)?(this.activeVfxClips.add(a),this.ensureVfxActor(t,o,a)):i.has(a)&&s?(this.updateVfxTransform(t,s,e),this._state.value===SequencePlaybackState.Playing&&s.play()):s&&this.applyInactiveVfxClipBehavior(o,s,e)}}getVfxClipKey(t,e){return`${t.id}-${e.id}-${e.vfxAssetId}`}applyInactiveVfxClipBehavior(t,e,i){const s=t.endBehavior??"finish";if("finish"===s)return void e.stop();if("kill"===s)return void e.applyClipEndBehavior(s);const a=t.startTime+t.duration,o=Math.max(i-a,0),n=Math.max(0,t.expireWithinSeconds??.25),r=Math.max(0,n-o);e.applyClipEndBehavior(s,r)}async ensureVfxActor(t,e,i){if(!this.vfxService||!e.vfxAssetId)return;if(this.vfxActors.has(i)){const t=this.vfxActors.get(i);return t.restart(),void t.play()}const s=this.clearCounter;try{const a=await this.vfxService.createFromAssetId(e.vfxAssetId,this.world.scene);if(s!==this.clearCounter)return void(this.world&&this.world.removeActor(a));const{position:o,rotation:n,scale:r}=this.getVfxTransform(t,e.startTime);a.object.position.copy(o),a.object.rotation.copy(n),a.object.scale.copy(r),this.vfxActors.set(i,a),this._state.value===SequencePlaybackState.Playing&&a.play()}catch(t){console.error(`Failed to create VFX actor for clip ${e.id}:`,t)}}getVfxTransform(t,e){const i=new n,s=new a,r=new n(1,1,1),c=t.subTracks.find(t=>"transform"===t.type);if(c&&c.keyframes.length>0){const{prev:t,next:a,t:n}=this.findKeyframes(c.keyframes,e);if(t&&(t.position&&i.fromArray(t.position),t.rotation&&s.fromArray([...t.rotation,"XYZ"]),t.scale&&r.fromArray(t.scale)),a&&t!==a&&t.time!==a.time){const e=this.applyInterpolation(n,t.interpolation);if(t.position&&a.position&&i.lerpVectors(f.fromArray(t.position),m.fromArray(a.position),e),t.rotation&&a.rotation){g.setFromEuler(A.fromArray([...t.rotation,"XYZ"])),v.setFromEuler(b.fromArray([...a.rotation,"XYZ"]));const i=new o;i.slerpQuaternions(g,v,e),s.setFromQuaternion(i)}t.scale&&a.scale&&r.lerpVectors(f.fromArray(t.scale),m.fromArray(a.scale),e)}}else i.fromArray(t.position),s.fromArray([...t.rotation,"XYZ"]),r.fromArray(t.scale);return{position:i,rotation:s,scale:r}}updateVfxTransform(t,e,i){const{position:s,rotation:a,scale:o}=this.getVfxTransform(t,i);e.object.position.copy(s),e.object.rotation.copy(a),e.object.scale.copy(o)}resolveActorType(t){const e=this.actorFactory.classes[t];if(null!=e)return e;const i=c[t];return i||(console.warn(`Could not resolve actor type: ${t}`),null)}async spawnActorForClip(t,e,i,s){if(!this.world||!t.actorType)return null;const a=this.resolveActorType(t.actorType);if(!a)return null;try{return await this.world.spawnActor(a,i,s)}catch(e){return console.error(`Failed to spawn actor ${t.actorType}:`,e),null}}async spawnPrefabForClip(t,e,i,s){if(!this.world||!this.assetLoader||!t.prefabId)return null;try{const e=await this.assetLoader.getPrefabById(t.prefabId);if(!e)return null;return await this.world.spawnPrefab(e,i,s)}catch(e){return console.error(`Failed to spawn prefab ${t.prefabId}:`,e),null}}async spawnMeshForClip(t,e,i,s){if(!this.world||!this.assetLoader||!t.meshId)return null;try{const e=await this.assetLoader.getAsset(t.meshId),a=await this.assetLoader.getModelByAssetId(t.meshId),o=h.clone(a.scene);if(o.position.copy(i),o.rotation.copy(s),e){await this.assetLoader.applyMaterials(e,o);const t=e.mesh?.rescale;null!=t&&1!==t&&o.scale.multiplyScalar(t)}return this.world.scene.add(o),o}catch(e){return console.error(`Failed to spawn mesh ${t.meshId}:`,e),null}}despawnInstance(t){if(this.world)switch(t?.type){case"actor":this.world.removeActor(t.instance);break;case"prefab":this.world.removePrefab(t.instance);break;case"mesh":this.world.scene.remove(t.instance)}}despawnAll(){for(const t of this.spawnedInstances.values())this.despawnInstance(t);this.spawnedInstances.clear(),this.activeClips.clear(),this.pendingSpawns.clear()}getInitialTransform(t,e,i){const s=new n,o=new a,r=t.subTracks.find(t=>"transform"===t.type);if(r&&r.keyframes.length>0){const{prev:t}=this.findKeyframes(r.keyframes,i);t&&(t.position&&s.fromArray(t.position),t.rotation&&o.fromArray([...t.rotation,"XYZ"]))}else e.initialPosition&&s.fromArray(e.initialPosition),e.initialRotation&&o.fromArray([...e.initialRotation,"XYZ"]);return{position:s,rotation:o}}evaluateSpawnTrack(t,e,i){if(!t.clips||0===t.clips.length)return;const s=e||this.bindings.get(t.id);if(s&&s.target)for(const e of t.clips){const a=e.startTime+e.duration,o=i>=e.startTime&&i<a,n=`${t.id}-${e.id}`;if(o){this.activeClips.has(n)||(this.activeClips.add(n),this.spawnedInstances.set(n,{type:"proxy",target:s.target}));(s.target instanceof r?s.target.object:s.target)&&this.evaluateSubTracks(t.subTracks,s,i)}else this.activeClips.has(n)&&(this.activeClips.delete(n),this.spawnedInstances.delete(n))}else for(const e of t.clips){const s=e.startTime+e.duration,a=i>=e.startTime&&i<s,o=`${t.id}-${e.id}`,n=this.spawnedInstances.has(o),r=this.pendingSpawns.has(o);if(!a||n||r){if(a&&n){const e=this.spawnedInstances.get(o);e&&"proxy"!==e.type&&this.evaluateSubTracks(t.subTracks,e,i)}else if(!a&&n){const t=this.spawnedInstances.get(o);t&&(this.despawnInstance(t),this.spawnedInstances.delete(o),this.activeClips.delete(o))}}else{const{position:i,rotation:s}=this.getInitialTransform(t,e,e.startTime);this.pendingSpawns.add(o),this.spawnForClip(t,e,i,s,o)}}}getInstanceObject(t){switch(t.type){case"actor":return t.instance.object;case"prefab":return t.instance.mainActor?.object||t.instance.object;case"mesh":return t.instance;case"proxy":return t.target instanceof r?t.target.object:t.target;default:return null}}async spawnForClip(t,e,i,a,o){const n=this.clearCounter;let r=null;try{switch(t.spawnType){case"actor":const s=await this.spawnActorForClip(t,e,i,a);s&&(r={type:"actor",instance:s});break;case"prefab":const o=await this.spawnPrefabForClip(t,e,i,a);o&&(r={type:"prefab",instance:o});break;case"mesh":const n=await this.spawnMeshForClip(t,e,i,a);n&&(r={type:"mesh",instance:n})}if(n!==this.clearCounter)return void(r&&this.despawnInstance(r));const c=this._time.value,l=c>=e.startTime&&c<e.startTime+e.duration;if(r&&l){const e=this.getInstanceObject(r);"proxy"===r.type||("actor"===r.type?r.target=r.instance:"prefab"===r.type?r.target=r.instance.mainActor||r.instance.object:r.target=e);t.subTracks.some(t=>"animation"===t.type)&&e&&(r.mixer=new s(e),r.activeActions=new Map),this.spawnedInstances.set(o,r),this.activeClips.add(o)}else r&&this.despawnInstance(r)}finally{this.pendingSpawns.delete(o)}}findKeyframes(t,e){if(0===t.length)return{prev:null,next:null,t:0};let i=null,s=null;for(let a=0;a<t.length;a++)if(t[a].time<=e&&(i=t[a]),t[a].time>e&&!s){s=t[a];break}if(i||(i=t[0]),s||(s=t[t.length-1]),i===s||i.time===s.time)return{prev:i,next:i,t:0};return{prev:i,next:s,t:(e-i.time)/(s.time-i.time)}}applyInterpolation(t,e){switch(e){case"linear":case"catmullRom":default:return t;case"step":return 0;case"cubic":return t<.5?4*t*t*t:1-Math.pow(-2*t+2,3)/2}}async ensureAnimationClip(t){if(this.assetLoader&&!this.animationClips.has(t)&&!this.loadingAnimationClips.has(t)){this.loadingAnimationClips.add(t);try{const e=await this.assetLoader.getAnimationClipByAssetId(t);e&&this.animationClips.set(t,e)}catch(e){console.error(`Failed to load animation clip for asset ${t}:`,e)}finally{this.loadingAnimationClips.delete(t)}}}captureOriginalTransforms(){for(const t of this.bindings.values()){const e=t.target instanceof r?t.target.object:t.target;e&&(t.originalTransform={position:e.position.clone(),rotation:e.rotation.clone(),scale:e.scale.clone()})}}restoreOriginalTransforms(){for(const t of this.bindings.values())if(t.originalTransform){const e=t.target instanceof r?t.target.object:t.target;e&&(e.position.copy(t.originalTransform.position),e.rotation.copy(t.originalTransform.rotation),e.scale.copy(t.originalTransform.scale))}}stopAllAnimations(){for(const t of this.bindings.values()){for(const e of t.activeActions.values())e.stop();t.activeActions.clear(),t.mixer&&t.mixer.stopAllAction(),t.charAnimComponent&&(t.charAnimComponent.stopSequenceAnimation(),t.charAnimComponent=void 0),t.charAnimActionKeys?.clear()}}stopAllVfx(){for(const t of this.vfxActors.values())t.paused||t.stop();this.activeVfxClips.clear()}dispose(){if(this.stop(),this.bindings.clear(),this.roleBindings.clear(),this.sequenceData=null,this.world)for(const t of this.vfxActors.values())this.world.removeActor(t);this.vfxActors.clear(),this.stopAllAudio(),this.audioBuffers.clear(),this.loadingAudioBuffers.clear(),this.animationClips.clear(),this.audioByClip.clear()}updateTrackParenting(t,e){const i=this.getActiveObjectsForTrack(t);if(0===i.length)return;let s=null;const a=t.parent?.trackId;if(a){const e=this.sequenceData?.tracks.find(t=>t.id===a);if(e){const i=this.getActiveObjectsForTrack(e);if(i.length>0&&(s=i[0],t.parent?.socketName)){const e=s.getObjectByName(t.parent.socketName);e&&(s=e)}}}for(const a of i)s?a.parent!==s&&(s.add(a),e&&"object"===t.type&&(e.currentParent=s)):e?.currentParent&&e.currentParent===a.parent?this.restoreObjectParent(e,a):a.parent&&a.parent!==this.world?.scene&&"object"!==t.type&&this.world?.scene.add(a)}findSocket(t,e){if(t.name===e)return t;if(t instanceof i.SkinnedMesh){const i=t.skeleton.bones.find(t=>t.name===e);if(i)return i}return t.getObjectByName(e)||null}getActiveObjectsForTrack(t){if("object"===t.type){const e=this.bindings.get(t.id);if(e&&e.target)return[e.target instanceof r?e.target.object:e.target]}else{if("spawn"===t.type){const e=[];for(const[i,s]of this.spawnedInstances)if(i.startsWith(t.id+"-")&&this.activeClips.has(i)){const t=this.getInstanceObject(s);t&&e.push(t)}return e}if("vfx"===t.type){const e=[];for(const i of this.activeVfxClips)if(i.startsWith(t.id+"-")){const t=this.vfxActors.get(i);t&&e.push(t.object)}return e}}return[]}restoreObjectParent(t,e){t.originalParent?t.originalParent.add(e):this.world&&this.world.scene.add(e),t.currentParent=void 0}restoreOriginalParents(){for(const t of this.bindings.values())if(t.currentParent){const e=t.target instanceof r?t.target.object:t.target;e&&this.restoreObjectParent(t,e)}}evaluateAudioSubTracks(t,e){const i={};for(const s of t.subTracks)if("audioParameter"===s.type){const t=s,a=s.keyframes;if(0===a.length)continue;const{prev:o,next:n,t:r}=this.findKeyframes(a,e);if(!o)continue;let c;if(n&&o!==n){const t=o.value.value,e=n.value.value;if("step"===o.interpolation)c=t;else if("cubic"===o.interpolation){c=t+(e-t)*(r*r*(3-2*r))}else c=t+(e-t)*r}else c=o.value.value;if("number"==typeof c){const e=l(t.parameter),i=e.min,s=e.max;"number"==typeof i&&(c=Math.max(i,c)),"number"==typeof s&&(c=Math.min(s,c))}void 0!==c&&(i[t.parameter]=c)}return i}stringToRandom(t){let e=0;for(let i=0;i<t.length;i++){e=(e<<5)-e+t.charCodeAt(i),e&=e}const i=1e4*Math.sin(e);return i-Math.floor(i)}refreshAsset(t){this.assetLoader?.clearCacheById(t),this.vfxService?.clearPool(t);for(const[e,i]of this.vfxActors.entries())e.endsWith(`-${t}`)&&(this.world&&this.world.removeActor(i),this.vfxActors.delete(e),this.activeVfxClips.delete(e));for(const[e,i]of this.spawnedInstances.entries()){const s=e.split("-")[0],a=this.sequenceData?.tracks.find(t=>t.id===s);!a||a.prefabId!==t&&a.meshId!==t||(this.despawnInstance(i),this.spawnedInstances.delete(e),this.activeClips.delete(e))}}}const f=new n,m=new n,y=new n,g=new o,v=new o,A=new a,b=new a;/*
|
|
2
2
|
* Copyright (©) 2026 Hology Interactive AB. All rights reserved.
|
|
3
3
|
* See the LICENSE.md file for details.
|
|
4
4
|
*/
|
|
@@ -10,6 +10,7 @@ export declare class RotatePosition extends Behaviour {
|
|
|
10
10
|
private angle;
|
|
11
11
|
constructor(axis: THREE.Vector3, angle: number);
|
|
12
12
|
private axis3D;
|
|
13
|
+
setAxisAngle(axis: THREE.Vector3, angle: number): void;
|
|
13
14
|
mutate(target: Particle | Emitter, time: number, index: number): void;
|
|
14
15
|
}
|
|
15
16
|
export declare enum AxisDirection {
|
|
@@ -29,6 +30,7 @@ export declare class OrientAlongVelocity extends Behaviour {
|
|
|
29
30
|
export declare class FollowParent extends Behaviour {
|
|
30
31
|
private speed;
|
|
31
32
|
constructor(speed: number);
|
|
33
|
+
setSpeed(speed: number): void;
|
|
32
34
|
initialize(particle: Particle): void;
|
|
33
35
|
mutate(target: Particle | Emitter, time: number, index: number): void;
|
|
34
36
|
}
|
|
@@ -36,6 +38,7 @@ export declare class Scale extends Behaviour {
|
|
|
36
38
|
private scaleA;
|
|
37
39
|
private scaleB;
|
|
38
40
|
constructor(scaleA: number, scaleB: number, easing: EasingFunction);
|
|
41
|
+
setScaleRange(scaleA: number, scaleB: number): void;
|
|
39
42
|
/**
|
|
40
43
|
* Set the behaviour's initial properties on the particle.
|
|
41
44
|
*/
|
|
@@ -46,6 +49,7 @@ export declare class ScaleComponents extends Behaviour {
|
|
|
46
49
|
private scaleA;
|
|
47
50
|
private scaleB;
|
|
48
51
|
constructor(scaleA: Vector3, scaleB: Vector3, easing: EasingFunction);
|
|
52
|
+
setScaleRange(scaleA: Vector3, scaleB: Vector3): void;
|
|
49
53
|
/**
|
|
50
54
|
* Set the behaviour's initial properties on the particle.
|
|
51
55
|
*/
|
|
@@ -55,6 +59,7 @@ export declare class ScaleComponents extends Behaviour {
|
|
|
55
59
|
export declare class LinearDamping extends Behaviour {
|
|
56
60
|
private factor;
|
|
57
61
|
constructor(factor: number);
|
|
62
|
+
setFactor(factor: number): void;
|
|
58
63
|
mutate(target: Particle | Emitter, time: number, index: number): void;
|
|
59
64
|
}
|
|
60
65
|
export declare enum AxisPlane {
|
|
@@ -67,6 +72,7 @@ export declare class Disperse extends Behaviour {
|
|
|
67
72
|
private distance;
|
|
68
73
|
private plane;
|
|
69
74
|
constructor(distance: number, plane: AxisPlane, easing?: EasingFunction);
|
|
75
|
+
setDistance(distance: number): void;
|
|
70
76
|
initialize(particle: Particle): void;
|
|
71
77
|
mutate(target: Particle | Emitter, time: number, index: number): void;
|
|
72
78
|
}
|
|
@@ -77,6 +83,7 @@ export declare class MoveOverLife extends Behaviour {
|
|
|
77
83
|
private displacement;
|
|
78
84
|
private initial;
|
|
79
85
|
constructor(x: number, y: number, z: number, easing?: EasingFunction);
|
|
86
|
+
setTarget(x: number, y: number, z: number): void;
|
|
80
87
|
initialize(particle: Particle): void;
|
|
81
88
|
mutate(target: Particle | Emitter, time: number, index: number): void;
|
|
82
89
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{Vector3 as i}from"three";import{Behaviour as t,Vector3D as e,ease as s,MathUtils as o}from"@hology/nebula";import*as n from"three";export class RotatePosition extends t{constructor(i,t){super(),this.axis=i,this.angle=t,this.axis3D=new e(this.axis.x,this.axis.y,this.axis.z)}mutate(i,t,e){this.energize(i,t),null!=i.old&&0!=i.old.position.lengthSq()&&(a.copy(i.old.position),a.sub(i.parent.position),a.applyAxisAngle(this.axis3D,this.angle*t),a.add(i.parent.position),a.sub(i.old.position).divideScalar(t),i.velocity.sub(a)),a.copy(i.position),a.sub(i.parent.position),a.applyAxisAngle(this.axis3D,this.angle*t),a.add(i.parent.position),a.sub(i.position).divideScalar(t),i.velocity.add(a)}}const a=new i;export var AxisDirection;!function(i){i[i.x=0]="x",i[i.y=1]="y",i[i.z=2]="z",i[i.mx=3]="mx",i[i.my=4]="my",i[i.mz=5]="mz"}(AxisDirection||(AxisDirection={}));export class OrientAlongVelocity extends t{constructor(i){super(),this.axisDirection=i}initialize(i){i.transform.orientAlongVelocity=!0}mutate(i,t,e){i.target,n.Object3D}}export class FollowParent extends t{constructor(i){super(),this.speed=i}initialize(i){}mutate(i,t,e){const s=i.parent.parentParticle??i.parent;if(0===this.speed)return i.position.copy(s.old.position),i.acceleration.copy(s.acceleration),void i.velocity.copy(s.velocity);const o=r.copy(s.position).sub(i.position),n=(o.length(),o.normalize());l.copy(n).multiplyScalar(this.speed),i.velocity.copy(l)}}export class Scale extends t{constructor(i,t,e){super(void 0,e),this.scaleA=i,this.scaleB=t}initialize(i){i.transform.scaleA=i.scale*this.scaleA,i.transform.scaleB=i.scale*this.scaleB,i.transform.oldRadius=i.radius}mutate(i,t,e){this.energize(i,t),i.scale=o.lerp(i.transform.scaleA,i.transform.scaleB,this.energy),i.scale<5e-4&&(i.scale=0),i.radius=i.transform.oldRadius*i.scale}}export class ScaleComponents extends t{constructor(i,t,e){super(void 0,e),this.scaleA=i,this.scaleB=t}initialize(t){let e=t.transform.initialScale;e?(e=e.clone(),t.transform.initialScale=e):(e=new i(1,1,1),t.transform.initialScale=e),t.transform.scaleComponentsA=e.clone().multiply(this.scaleA),t.transform.scaleComponentsB=e.clone().multiply(this.scaleB)}mutate(i,t,e){this.energize(i,t);const s=i.transform.scaleComponentsA,o=i.transform.scaleComponentsB;i.transform.initialScale.lerpVectors(o,s,this.energy)}}export class LinearDamping extends t{constructor(i){super(),this.factor=i}mutate(i,t,e){this.energize(i,t);const s=r.copy(i.velocity).multiplyScalar(-this.factor*t);i.velocity.add(s)}}const r=new e,l=new e;export var AxisPlane;!function(i){i[i.xy=0]="xy",i[i.xz=1]="xz",i[i.yz=2]="yz",i[i.xyz=3]="xyz"}(AxisPlane||(AxisPlane={}));export class Disperse extends t{constructor(i,t,e=s.easeLinear){super(void 0,e,void 0,!0),this.distance=i,this.plane=t}initialize(i){i.transform.initialPos=i.position.clone();const t=c.copy(i.transform.initialPos).sub(i.parent.position).normalize();switch(this.plane){case AxisPlane.xy:t.setComponent(2,0);break;case AxisPlane.xz:t.setComponent(1,0);break;case AxisPlane.yz:t.setComponent(0,0)}i.transform.displacement=t.clone().multiplyScalar(this.distance),i.transform.prevEnergy=1}mutate(i,t,e){this.energize(i,t);i.transform.initialPos;const s=i.transform.displacement,o=i.transform.prevEnergy-this.energy;i.transform.prevEnergy=this.energy;i.position.x+=s.x*o,i.position.y+=s.y*o,i.position.z+=s.z*o}}export class MoveOverLife extends t{constructor(i,t,e,o=s.easeLinear){super(void 0,o,void 0,!0),this.x=i,this.y=t,this.z=e}initialize(i){i.transform.initialPos=i.position.clone()}mutate(i,t,e){this.energize(i,t);const s=i.transform.initialPos,o=1-this.energy;i.position.x=s.x+this.x*o,i.position.y=s.y+this.y*o,i.position.z=s.z+this.z*o}}const c=new e,p=new WeakMap;export class AnimationBehavior extends t{constructor(i,t=1,e=1,o=n.LoopOnce,a=!0,r=s.easeLinear){super(void 0,r,void 0,!0),this.clip=i,this.timeScale=t,this.weight=e,this.loop=o,this.clampWhenFinished=a}initialize(i){this.mixer?.stopAllAction(),this.mixer=null,this.action=null}mutate(i,t,e){if(this.energize(i,t),null==this.mixer){const t=i.target;if(t instanceof n.Object3D){let i=p.get(t);i?this.mixer=i:(this.mixer=new n.AnimationMixer(t.children[0]),p.set(t,this.mixer)),this.action=this.mixer.clipAction(this.clip),this.action.timeScale=this.timeScale,this.action.weight=this.weight,this.action.loop=this.loop,this.action.clampWhenFinished=this.clampWhenFinished,this.action.play()}}this.mixer&&this.mixer.update(t)}}/*
|
|
1
|
+
import{Vector3 as i}from"three";import{Behaviour as t,Vector3D as e,ease as s,MathUtils as o}from"@hology/nebula";import*as n from"three";export class RotatePosition extends t{constructor(i,t){super(),this.axis=i,this.angle=t,this.axis3D=new e(this.axis.x,this.axis.y,this.axis.z)}setAxisAngle(i,t){this.axis=i.clone(),this.axis3D=new e(i.x,i.y,i.z),this.angle=t}mutate(i,t,e){this.energize(i,t),null!=i.old&&0!=i.old.position.lengthSq()&&(a.copy(i.old.position),a.sub(i.parent.position),a.applyAxisAngle(this.axis3D,this.angle*t),a.add(i.parent.position),a.sub(i.old.position).divideScalar(t),i.velocity.sub(a)),a.copy(i.position),a.sub(i.parent.position),a.applyAxisAngle(this.axis3D,this.angle*t),a.add(i.parent.position),a.sub(i.position).divideScalar(t),i.velocity.add(a)}}const a=new i;export var AxisDirection;!function(i){i[i.x=0]="x",i[i.y=1]="y",i[i.z=2]="z",i[i.mx=3]="mx",i[i.my=4]="my",i[i.mz=5]="mz"}(AxisDirection||(AxisDirection={}));export class OrientAlongVelocity extends t{constructor(i){super(),this.axisDirection=i}initialize(i){i.transform.orientAlongVelocity=!0}mutate(i,t,e){i.target,n.Object3D}}export class FollowParent extends t{constructor(i){super(),this.speed=i}setSpeed(i){this.speed=i}initialize(i){}mutate(i,t,e){const s=i.parent.parentParticle??i.parent;if(0===this.speed)return i.position.copy(s.old.position),i.acceleration.copy(s.acceleration),void i.velocity.copy(s.velocity);const o=r.copy(s.position).sub(i.position),n=(o.length(),o.normalize());l.copy(n).multiplyScalar(this.speed),i.velocity.copy(l)}}export class Scale extends t{constructor(i,t,e){super(void 0,e),this.scaleA=i,this.scaleB=t}setScaleRange(i,t){this.scaleA=i,this.scaleB=t}initialize(i){i.transform.scaleA=i.scale*this.scaleA,i.transform.scaleB=i.scale*this.scaleB,i.transform.oldRadius=i.radius}mutate(i,t,e){this.energize(i,t),i.scale=o.lerp(i.transform.scaleA,i.transform.scaleB,this.energy),i.scale<5e-4&&(i.scale=0),i.radius=i.transform.oldRadius*i.scale}}export class ScaleComponents extends t{constructor(i,t,e){super(void 0,e),this.scaleA=i,this.scaleB=t}setScaleRange(i,t){this.scaleA=i.clone(),this.scaleB=t.clone()}initialize(t){let e=t.transform.initialScale;e?(e=e.clone(),t.transform.initialScale=e):(e=new i(1,1,1),t.transform.initialScale=e),t.transform.scaleComponentsA=e.clone().multiply(this.scaleA),t.transform.scaleComponentsB=e.clone().multiply(this.scaleB)}mutate(i,t,e){this.energize(i,t);const s=i.transform.scaleComponentsA,o=i.transform.scaleComponentsB;i.transform.initialScale.lerpVectors(o,s,this.energy)}}export class LinearDamping extends t{constructor(i){super(),this.factor=i}setFactor(i){this.factor=i}mutate(i,t,e){this.energize(i,t);const s=r.copy(i.velocity).multiplyScalar(-this.factor*t);i.velocity.add(s)}}const r=new e,l=new e;export var AxisPlane;!function(i){i[i.xy=0]="xy",i[i.xz=1]="xz",i[i.yz=2]="yz",i[i.xyz=3]="xyz"}(AxisPlane||(AxisPlane={}));export class Disperse extends t{constructor(i,t,e=s.easeLinear){super(void 0,e,void 0,!0),this.distance=i,this.plane=t}setDistance(i){this.distance=i}initialize(i){i.transform.initialPos=i.position.clone();const t=c.copy(i.transform.initialPos).sub(i.parent.position).normalize();switch(this.plane){case AxisPlane.xy:t.setComponent(2,0);break;case AxisPlane.xz:t.setComponent(1,0);break;case AxisPlane.yz:t.setComponent(0,0)}i.transform.displacement=t.clone().multiplyScalar(this.distance),i.transform.prevEnergy=1}mutate(i,t,e){this.energize(i,t);i.transform.initialPos;const s=i.transform.displacement,o=i.transform.prevEnergy-this.energy;i.transform.prevEnergy=this.energy;i.position.x+=s.x*o,i.position.y+=s.y*o,i.position.z+=s.z*o}}export class MoveOverLife extends t{constructor(i,t,e,o=s.easeLinear){super(void 0,o,void 0,!0),this.x=i,this.y=t,this.z=e}setTarget(i,t,e){this.x=i,this.y=t,this.z=e}initialize(i){i.transform.initialPos=i.position.clone()}mutate(i,t,e){this.energize(i,t);const s=i.transform.initialPos,o=1-this.energy;i.position.x=s.x+this.x*o,i.position.y=s.y+this.y*o,i.position.z=s.z+this.z*o}}const c=new e,p=new WeakMap;export class AnimationBehavior extends t{constructor(i,t=1,e=1,o=n.LoopOnce,a=!0,r=s.easeLinear){super(void 0,r,void 0,!0),this.clip=i,this.timeScale=t,this.weight=e,this.loop=o,this.clampWhenFinished=a}initialize(i){this.mixer?.stopAllAction(),this.mixer=null,this.action=null}mutate(i,t,e){if(this.energize(i,t),null==this.mixer){const t=i.target;if(t instanceof n.Object3D){let i=p.get(t);i?this.mixer=i:(this.mixer=new n.AnimationMixer(t.children[0]),p.set(t,this.mixer)),this.action=this.mixer.clipAction(this.clip),this.action.timeScale=this.timeScale,this.action.weight=this.weight,this.action.loop=this.loop,this.action.clampWhenFinished=this.clampWhenFinished,this.action.play()}}this.mixer&&this.mixer.update(t)}}/*
|
|
2
2
|
* Copyright (©) 2026 Hology Interactive AB. All rights reserved.
|
|
3
3
|
* See the LICENSE.md file for details.
|
|
4
4
|
*/
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export*from"./vfx-asset.js";export*from"./vfx-materializer.js";export*from"./vfx-actor.js";export*from"./vfx-service.js";/*
|
|
1
|
+
export*from"./vfx-asset.js";export*from"./vfx-materializer.js";export*from"./vfx-binding-runtime.js";export*from"./vfx-input-runtime.js";export*from"./vfx-actor.js";export*from"./vfx-service.js";/*
|
|
2
2
|
* Copyright (©) 2026 Hology Interactive AB. All rights reserved.
|
|
3
3
|
* See the LICENSE.md file for details.
|
|
4
4
|
*/
|
|
@@ -7,6 +7,7 @@ export declare class DefaultInitializer extends Initializer {
|
|
|
7
7
|
export declare class Rotation extends Initializer {
|
|
8
8
|
private rotation;
|
|
9
9
|
constructor(rotation: Vector3);
|
|
10
|
+
setRotation(rotation: Vector3): void;
|
|
10
11
|
initialize(target: Particle): void;
|
|
11
12
|
}
|
|
12
13
|
export declare const randomRotationAxis: readonly ["XYZ", "X", "Y", "Z"];
|
|
@@ -20,31 +21,32 @@ export declare class RandomRotation extends Initializer {
|
|
|
20
21
|
export declare class InitialScale extends Initializer {
|
|
21
22
|
private scale;
|
|
22
23
|
constructor(scale: number);
|
|
24
|
+
setScale(scale: number): void;
|
|
23
25
|
initialize(particle: Particle): void;
|
|
24
26
|
}
|
|
25
27
|
export declare class RandomScale extends Initializer {
|
|
26
28
|
private min;
|
|
27
29
|
private max;
|
|
28
30
|
constructor(min: number, max: number);
|
|
31
|
+
setRange(min: number, max: number): void;
|
|
29
32
|
initialize(particle: Particle): void;
|
|
30
33
|
}
|
|
31
34
|
export declare class InitialScaleComponents extends Initializer {
|
|
32
35
|
private scale;
|
|
33
36
|
constructor(scale: Vector3);
|
|
37
|
+
setScaleComponents(scale: Vector3): void;
|
|
34
38
|
initialize(particle: Particle): void;
|
|
35
39
|
}
|
|
36
40
|
export declare class AdditiveVelocity extends Initializer {
|
|
37
41
|
private min;
|
|
38
42
|
private max?;
|
|
39
|
-
|
|
40
|
-
private minY;
|
|
41
|
-
private minZ;
|
|
42
|
-
constructor(min: Vector3, max?: Vector3);
|
|
43
|
+
constructor(min: Vector3 | (() => Vector3), max?: Vector3 | (() => Vector3));
|
|
43
44
|
initialize(particle: Particle): void;
|
|
44
45
|
}
|
|
45
46
|
export declare class RandomDirection extends Initializer {
|
|
46
47
|
private speed;
|
|
47
48
|
constructor(speed: number);
|
|
49
|
+
setSpeed(speed: number): void;
|
|
48
50
|
initialize(target: Emitter | Particle): void;
|
|
49
51
|
}
|
|
50
52
|
/**
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{Vector3 as t}from"three";import{Span as i,Initializer as e
|
|
1
|
+
import{Vector3 as t}from"three";import{Span as i,Initializer as e}from"@hology/nebula";export class DefaultInitializer extends e{init(t,i){i.parent=t}initialize(t){}}export class Rotation extends e{constructor(t){super(),this.rotation=t}setRotation(t){this.rotation=t.clone()}initialize(t){"number"!=typeof t.rotation&&t.rotation.copy(this.rotation)}}export const randomRotationAxis=["XYZ","X","Y","Z"];export class RandomRotation extends e{constructor(t){super(),this.axis=t}initialize(t){switch(this.axis){case"XYZ":t.rotation.set(this.randomAngle(),this.randomAngle(),this.randomAngle());break;case"X":t.rotation.x=this.randomAngle();break;case"Y":t.rotation.y=this.randomAngle();break;case"Z":t.rotation.z=this.randomAngle()}}randomAngle(){return 2*Math.PI*Math.random()}}export class InitialScale extends e{constructor(t){super(),this.scale=t}setScale(t){this.scale=t}initialize(t){t.scale=this.scale}}export class RandomScale extends e{constructor(t,i){super(),this.min=t,this.max=i}setRange(t,i){this.min=t,this.max=i}initialize(t){t.scale=n(this.min,this.max)}}export class InitialScaleComponents extends e{constructor(t){super(),this.scale=t}setScaleComponents(t){this.scale=t.clone()}initialize(t){t.transform.initialScale=this.scale.clone()}}export class AdditiveVelocity extends e{constructor(t,i){super(),this.min=t,this.max=i}initialize(t){const i=o(this.min),e=null!=this.max?o(this.max):void 0;t.velocity.x+=null!=e?n(i.x,e.x):i.x,t.velocity.y+=null!=e?n(i.y,e.y):i.y,t.velocity.z+=null!=e?n(i.z,e.z):i.z}}export class RandomDirection extends e{constructor(t){super(),this.speed=t}setSpeed(t){this.speed=t}initialize(t){s.set(2*Math.random()-1,2*Math.random()-1,2*Math.random()-1).normalize().multiplyScalar(this.speed),t.velocity.x+=s.x,t.velocity.y+=s.y,t.velocity.z+=s.z}}export class RandomVelocity extends e{constructor(t,e){super(),this.min=t,this.max=e,this.x=new i(this.min.x,this.max.x),this.y=new i(this.min.y,this.max.y),this.z=new i(this.min.z,this.max.z)}initialize(t){t.velocity.x+=this.x.getValue(),t.velocity.y+=this.y.getValue(),t.velocity.z+=this.z.getValue()}}const s=new t;function n(t,i){return Math.random()*(i-t)+t}function o(t){return"function"==typeof t?t():t}/*
|
|
2
2
|
* Copyright (©) 2026 Hology Interactive AB. All rights reserved.
|
|
3
3
|
* See the LICENSE.md file for details.
|
|
4
4
|
*/
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { BaseActor } from '../../gameplay/actors/actor.js';
|
|
2
2
|
import { Asset } from '../../scene/model.js';
|
|
3
|
+
import type { VfxClipEndBehavior } from '../sequence/sequence-data.js';
|
|
3
4
|
export declare class VfxActor extends BaseActor {
|
|
4
5
|
/**
|
|
5
6
|
* A multiplier for the speed of the visual effect.
|
|
@@ -17,13 +18,18 @@ export declare class VfxActor extends BaseActor {
|
|
|
17
18
|
private particleSystemContainer;
|
|
18
19
|
private physics;
|
|
19
20
|
private shaderProvider;
|
|
21
|
+
private inputRuntime;
|
|
22
|
+
private inputOverrides;
|
|
20
23
|
/**
|
|
21
24
|
* Replace visual effect with its definition in an asset
|
|
22
25
|
*/
|
|
23
|
-
fromAsset(asset: Asset): Promise<void>;
|
|
26
|
+
fromAsset(asset: Asset, initialInputs?: Record<string, unknown>): Promise<void>;
|
|
27
|
+
setInput(inputNameOrId: string, value: unknown): void;
|
|
28
|
+
setInputs(inputs: Record<string, unknown>): void;
|
|
24
29
|
play(): void;
|
|
25
30
|
pause(): void;
|
|
26
31
|
stop(): void;
|
|
32
|
+
applyClipEndBehavior(behavior: VfxClipEndBehavior | undefined, remainingLifeSeconds?: number): void;
|
|
27
33
|
restart(): void;
|
|
28
34
|
private _worldPos;
|
|
29
35
|
private _worldRot;
|
|
@@ -43,5 +49,8 @@ export declare class VfxActor extends BaseActor {
|
|
|
43
49
|
*/
|
|
44
50
|
getParticleCount(): number;
|
|
45
51
|
onEndPlay(): void;
|
|
52
|
+
private updateSystem;
|
|
53
|
+
private applyEmitterClipEndBehavior;
|
|
54
|
+
private stopEmitterEmission;
|
|
46
55
|
}
|
|
47
56
|
//# sourceMappingURL=vfx-actor.d.ts.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{__decorate as t}from"tslib";import*as s from"three";import{Rate as e}from"@hology/nebula";import{Actor as i,BaseActor as r}from"../../gameplay/actors/actor.js";import{inject as o}from"../../gameplay/inject.js";import{AssetLoader as a}from"../../gameplay/services/asset-loader.js";import{ViewController as
|
|
1
|
+
import{__decorate as t}from"tslib";import*as s from"three";import{Rate as e}from"@hology/nebula";import{Actor as i,BaseActor as r}from"../../gameplay/actors/actor.js";import{inject as o}from"../../gameplay/inject.js";import{AssetLoader as a}from"../../gameplay/services/asset-loader.js";import{ViewController as n}from"../../gameplay/services/render.js";import{World as h}from"../../gameplay/services/world.js";import{DelayRate as m}from"./rates.js";import{materializeVfx as p}from"./vfx-materializer.js";import{PhysicsSystem as l}from"../../gameplay/services/physics/physics-system.js";import{ShaderProvider as c}from"../../gameplay/services/shader-provider.js";import{AssetResourceLoader as d}from"../../scene/asset-resource-loader.js";import{AssetsProvider as u}from"../../scene/assets-provider.js";import{VfxInputRuntime as y}from"./vfx-input-runtime.js";let f=class extends r{constructor(){super(...arguments),this.timescale=1,this.paused=!0,this.assetLoader=o(a),this.assetManagerService=o(d),this.assetService=o(u),this.world=o(h),this.view=o(n),this.physics=o(l),this.shaderProvider=o(c),this.inputRuntime=new y,this.inputOverrides=new Map,this._worldPos=new s.Vector3,this._worldRot=new s.Quaternion,this._worldEul=new s.Euler,this.max=0}async fromAsset(t,s){if("vfx"!==t.type)throw"Asset must be a VFX asset but is "+t.type;null!=this.system&&(this.system.destroy(),this.system.emitters.forEach(t=>t.reset())),this.sourceAsset=t,this.disposeSystem&&this.disposeSystem(),this.world.scene.add(this.object),this.inputRuntime.setDefinitions(this.sourceAsset.vfx.inputs??[]);for(const[t,s]of this.inputOverrides)this.inputRuntime.setInput(t,s);null!=s&&this.setInputs(s);const{system:e,dispose:i,container:r}=await p(this.sourceAsset,this.object,{getAsset:t=>this.assetLoader.getAsset(t),getMaterial:t=>this.assetLoader.getMaterialByAssetId(t),getTexture:t=>this.assetLoader.getTextureByAssetId(t),getMesh:t=>this.assetLoader.getModelByAssetId(t).then(t=>t.scene)},this.view,this.physics,this.shaderProvider,this.assetService,this.assetManagerService,this.inputRuntime);this.system=e,this.disposeSystem=i,this.particleSystemContainer=r,this.object.visible=!1,this.object.matrixAutoUpdate=!1,this.object.matrixWorldAutoUpdate=!1,this.particleSystemContainer.matrixAutoUpdate=!1,this.particleSystemContainer.matrixWorldAutoUpdate=!1}setInput(t,e){this.inputOverrides.set(t,function(t){if(t instanceof s.Vector3||t instanceof s.Color)return t.clone();if(Array.isArray(t))return[...t];return t}(e)),this.inputRuntime.setInput(t,e)}setInputs(t){for(const[s,e]of Object.entries(t))this.setInput(s,e)}play(){null==this.particleSystemContainer.parent&&this.world.scene.add(this.particleSystemContainer),this.object.matrixAutoUpdate=!0,this.object.matrixWorldAutoUpdate=!0,this.particleSystemContainer.matrixAutoUpdate=!0,this.particleSystemContainer.matrixWorldAutoUpdate=!0,this.object.visible=!0,this.paused=!1;this.system.emitters.every(t=>t.dead)&&this.restart()}pause(){this.paused=!0}stop(){this.system.emitters.forEach(t=>{this.stopEmitterEmission(t)})}applyClipEndBehavior(t,s){const e=t??"finish";if("finish"!==e){if(null!=this.system&&null!=this.system.emitters)for(const t of this.system.emitters)this.applyEmitterClipEndBehavior(t,e,s)}else this.stop()}restart(){if(null!=this.system&&null!=this.system.emitters)for(const t of this.system.emitters){const s=t.rate;s instanceof m?s.restart():s.nextTime=0,t.removeAllParticles()}}onUpdate(t){this.paused||(this.object.getWorldPosition(this._worldPos),this.object.getWorldQuaternion(this._worldRot),this._worldEul.setFromQuaternion(this._worldRot),this.system?.emitters.forEach(t=>{"world"===t._space&&(t.setPosition(this._worldPos),t.setRotation(this._worldEul))}),this.updateSystem(t*this.timescale))}getParticleCount(){return this.system?.getCount()??0}onEndPlay(){this.stop(),null!=this.disposeSystem&&this.disposeSystem()}updateSystem(t){this.system?.update(t)}applyEmitterClipEndBehavior(t,s,e,i=!1){this.stopEmitterEmission(t,i);const r=t.particles??[];if("kill"===s)t.removeAllParticles?.();else{const t=Math.max(0,e??0);for(const s of r)s.life=Math.min(s.life,s.age+t)}const o=t.childEmitters??[];for(const t of o)this.applyEmitterClipEndBehavior(t,s,e,!0)}stopEmitterEmission(t,s=!1){const i=t.rate;i instanceof e&&(i.nextTime=1/0),s&&t.stopEmit?.()}};f=t([i()],f);export{f as VfxActor};/*
|
|
2
2
|
* Copyright (©) 2026 Hology Interactive AB. All rights reserved.
|
|
3
3
|
* See the LICENSE.md file for details.
|
|
4
4
|
*/
|
|
@@ -48,6 +48,15 @@ export type OutputCustomShader = {
|
|
|
48
48
|
shader: string;
|
|
49
49
|
shaderParams: Record<string, CustomParamValue>;
|
|
50
50
|
};
|
|
51
|
+
export declare const VfxEffectInputTypes: readonly ["number", "vec3", "color"];
|
|
52
|
+
export type VfxEffectInputType = (typeof VfxEffectInputTypes)[number];
|
|
53
|
+
export type VfxEffectInputValue = number | [number, number, number] | string;
|
|
54
|
+
export type VfxEffectInputDefinition = {
|
|
55
|
+
id: string;
|
|
56
|
+
name: string;
|
|
57
|
+
type: VfxEffectInputType;
|
|
58
|
+
defaultValue: VfxEffectInputValue;
|
|
59
|
+
};
|
|
51
60
|
export type OutputTextureSetting = {
|
|
52
61
|
opacityChannel?: 'alpha' | 'red' | 'none';
|
|
53
62
|
};
|
|
@@ -145,8 +154,11 @@ export type VfxAssetData = {
|
|
|
145
154
|
* it can be useful for certain effects.
|
|
146
155
|
*/
|
|
147
156
|
localSpace?: boolean;
|
|
157
|
+
inputs?: VfxEffectInputDefinition[];
|
|
148
158
|
emitters: EmitterData[];
|
|
149
159
|
};
|
|
160
|
+
export declare function defaultVfxEffectInputValue(type: VfxEffectInputType): VfxEffectInputValue;
|
|
161
|
+
export declare function createVfxEffectInputDefinition(name?: string, type?: VfxEffectInputType): VfxEffectInputDefinition;
|
|
150
162
|
export declare function EmitterDataConstructor(): {
|
|
151
163
|
id: string;
|
|
152
164
|
name: string;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{SerializedParamType as
|
|
1
|
+
import{SerializedParamType as e}from"../../scene/model.js";import*as t from"three";import{randomString as n}from"../../utils/math.js";export const BlendingModes=["additive","subtractive","multiply","normal"];export const ThreeBlendingMode={normal:t.NormalBlending,additive:t.AdditiveBlending,subtractive:t.SubtractiveBlending,multiply:t.MultiplyBlending};export const FlipbookModes=["clamp","loop"];export const VfxEffectInputTypes=["number","vec3","color"];export const ParticleChildSpawnEvents=["collision","start"];export function defaultVfxEffectInputValue(e){switch(e){case"number":return 0;case"vec3":return[0,0,0];case"color":return"#ffffff"}}export function createVfxEffectInputDefinition(e="input",t="number"){return{id:n(),name:e,type:t,defaultValue:defaultVfxEffectInputValue(t)}}export function EmitterDataConstructor(){return{id:n(),name:"Emitter",rate:{type:"continuous",count:1,time:.5,delay:0},position:[0,0,0],rotation:[0,0,0],initializers:[{id:n(),enabled:!0,type:"lifetime",params:{duration:{type:e.Number,value:1}}}],behaviours:[],children:[],output:{type:"sprite",texture:null,opacityChannel:"alpha",color:"#ffffff",blendingMode:"normal",space:"world",softness:0,intensity:1,bloom:!1}}}/*
|
|
2
2
|
* Copyright (©) 2026 Hology Interactive AB. All rights reserved.
|
|
3
3
|
* See the LICENSE.md file for details.
|
|
4
4
|
*/
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Behaviour, Initializer } from "@hology/nebula";
|
|
2
|
+
import { CustomParamValue } from "../../scene/model.js";
|
|
3
|
+
import { VfxInputRuntime } from "./vfx-input-runtime.js";
|
|
4
|
+
export type VfxBindingMode = 'spawn' | 'live';
|
|
5
|
+
export type VfxBindableParameterType = 'number' | 'color' | 'string' | 'select' | 'vec2' | 'vec3' | 'curve' | 'animationclip' | 'boolean';
|
|
6
|
+
export type VfxBindableParameterDefinition = {
|
|
7
|
+
type: VfxBindableParameterType;
|
|
8
|
+
default: unknown;
|
|
9
|
+
requires?: Record<string, unknown>;
|
|
10
|
+
bindable?: VfxBindingMode;
|
|
11
|
+
};
|
|
12
|
+
export type VfxParameterDefinitions = Record<string, VfxBindableParameterDefinition>;
|
|
13
|
+
export interface VfxBindingBuildContext {
|
|
14
|
+
get<T = unknown>(key: string): T;
|
|
15
|
+
resolveAll(): Record<string, unknown>;
|
|
16
|
+
resolveMode(mode: VfxBindingMode): Record<string, unknown>;
|
|
17
|
+
hasBindings(mode?: VfxBindingMode): boolean;
|
|
18
|
+
}
|
|
19
|
+
export type VfxBindingAdapter<TInstance> = {
|
|
20
|
+
buildBound?: (context: VfxBindingBuildContext) => TInstance;
|
|
21
|
+
sync?: (instance: TInstance, params: Record<string, unknown>, phase: VfxBindingMode) => void;
|
|
22
|
+
};
|
|
23
|
+
export interface VfxBoundInitializerDefinition<TInstance extends Initializer = Initializer> {
|
|
24
|
+
build(params?: any): TInstance;
|
|
25
|
+
parameters?: VfxParameterDefinitions;
|
|
26
|
+
bindingAdapter?: VfxBindingAdapter<TInstance>;
|
|
27
|
+
}
|
|
28
|
+
export interface VfxBoundBehaviourDefinition<TInstance extends Behaviour = Behaviour> {
|
|
29
|
+
build(params?: any): TInstance;
|
|
30
|
+
parameters?: VfxParameterDefinitions;
|
|
31
|
+
bindingAdapter?: VfxBindingAdapter<TInstance>;
|
|
32
|
+
}
|
|
33
|
+
export declare function spawnBindingAdapter<TInstance>(sync: (instance: TInstance, params: Record<string, unknown>) => void): VfxBindingAdapter<TInstance>;
|
|
34
|
+
export declare function liveBindingAdapter<TInstance>(sync: (instance: TInstance, params: Record<string, unknown>) => void): VfxBindingAdapter<TInstance>;
|
|
35
|
+
export declare function mixedBindingAdapter<TInstance>(config: {
|
|
36
|
+
spawn?: (instance: TInstance, params: Record<string, unknown>) => void;
|
|
37
|
+
live?: (instance: TInstance, params: Record<string, unknown>) => void;
|
|
38
|
+
buildBound?: (context: VfxBindingBuildContext) => TInstance;
|
|
39
|
+
}): VfxBindingAdapter<TInstance>;
|
|
40
|
+
export declare function createBoundInitializer(definition: VfxBoundInitializerDefinition, serializedParams: Record<string, CustomParamValue>, preparedParams: Record<string, unknown>, inputRuntime?: VfxInputRuntime): Initializer;
|
|
41
|
+
export declare function createBoundBehaviour(definition: VfxBoundBehaviourDefinition, serializedParams: Record<string, CustomParamValue>, preparedParams: Record<string, unknown>, inputRuntime?: VfxInputRuntime): Behaviour;
|
|
42
|
+
//# sourceMappingURL=vfx-binding-runtime.d.ts.map
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import{Behaviour as n,Initializer as e}from"@hology/nebula";import*as t from"three";export function spawnBindingAdapter(n){return{sync(e,t,i){"spawn"===i&&n(e,t)}}}export function liveBindingAdapter(n){return{sync(e,t,i){"live"===i&&n(e,t)}}}export function mixedBindingAdapter(n){return{buildBound:n.buildBound,sync(e,t,i){"spawn"===i?n.spawn?.(e,t):n.live?.(e,t)}}}export function createBoundInitializer(n,e,t,r){const o=new i(n.parameters??{},e,t,r);if(!o.hasBindings())return n.build(t);if(null!=n.bindingAdapter?.buildBound)return n.bindingAdapter.buildBound(o);const a=n.build(t);return null==n.bindingAdapter?.sync?(b(`VFX initializer "${a.type}" has bound params but no binding adapter. Falling back to static values.`),a):new s(a,o,n.bindingAdapter.sync)}export function createBoundBehaviour(n,e,t,s){const o=new i(n.parameters??{},e,t,s);if(!o.hasBindings())return n.build(t);if(null!=n.bindingAdapter?.buildBound)return n.bindingAdapter.buildBound(o);const a=n.build(t);return null==n.bindingAdapter?.sync?(b(`VFX behaviour "${a.type}" has bound params but no binding adapter. Falling back to static values.`),a):new r(a,o,n.bindingAdapter.sync)}class i{constructor(n,e,t,i){this.entries=new Map;for(const[s,r]of Object.entries(n)){const n=p(r.type,t[s]??r.default),u=e[s]?.binding;if("vfxInput"===u?.type&&null!=r.bindable&&null!=i){const e=i.getDefinition(u.inputId);if(null==e){b(`VFX input binding "${u.inputId}" no longer exists. Falling back to the parameter literal value.`),this.entries.set(s,o(s,r.type,n));continue}if(!l(r.type,e.type)){b(`VFX input "${e.name}" does not match the parameter type for "${s}". Falling back to the parameter literal value.`),this.entries.set(s,o(s,r.type,n));continue}this.entries.set(s,{key:s,type:r.type,mode:r.bindable,resolve:()=>a(i,u.inputId,r.type,n)});continue}"vfxInput"===u?.type&&null==r.bindable&&b(`VFX parameter "${s}" is bound to an effect input but the definition is not marked bindable. Falling back to the parameter literal value.`),this.entries.set(s,o(s,r.type,n))}}get(n){return this.entries.get(n)?.resolve()}resolveAll(){const n={};for(const e of this.entries.values())n[e.key]=e.resolve();return n}resolveMode(n){const e={};for(const t of this.entries.values())t.mode===n&&(e[t.key]=t.resolve());return e}hasBindings(n){for(const e of this.entries.values())if(null!=e.mode&&(null==n||e.mode===n))return!0;return!1}}class s extends e{constructor(n,e,t){super(n.type,n.isEnabled),this.inner=n,this.bindings=e,this.sync=t,this.lastSpawnParams=null}reset(){this.inner.reset(),this.lastSpawnParams=null}init(n,e){this.syncMode("spawn"),this.inner.init(n,e)}initialize(n){this.syncMode("spawn"),this.inner.initialize(n)}syncMode(n){const e=this.bindings.resolveMode(n);0!==Object.keys(e).length&&(c(this.lastSpawnParams,e)||(this.sync(this.inner,this.bindings.resolveAll(),n),this.lastSpawnParams=u(e)))}}class r extends n{constructor(n,e,t){super(n.life,n.easing,n.type,n.isEnabled),this.inner=n,this.bindings=e,this.sync=t,this.lastSpawnParams=null,this.lastLiveParams=null,this.id=n.id}reset(n,e){this.inner.reset(n,e),this.lastSpawnParams=null,this.lastLiveParams=null}initialize(n){this.syncMode("live"),this.syncMode("spawn"),this.inner.initialize(n)}applyBehaviour(n,e,t){this.syncMode("live"),this.inner.applyBehaviour(n,e,t)}mutate(n,e,t){this.syncMode("live"),this.inner.mutate(n,e,t)}destroy(){this.inner.destroy()}syncMode(n){const e=this.bindings.resolveMode(n);if(0===Object.keys(e).length)return;c("spawn"===n?this.lastSpawnParams:this.lastLiveParams,e)||(this.sync(this.inner,this.bindings.resolveAll(),n),"spawn"===n?this.lastSpawnParams=u(e):this.lastLiveParams=u(e))}}function o(n,e,t){return{key:n,type:e,mode:null,resolve:()=>p(e,t)}}function a(n,e,i,s){switch(i){case"number":return n.getNumber(e,"number"==typeof s?s:0);case"vec3":return n.getVector3(e,s instanceof t.Vector3?s:new t.Vector3);case"color":return n.getColor(e,s instanceof t.Color?s:new t.Color("#ffffff"));default:return p(i,s)}}function l(n,e){return"number"===n&&"number"===e||"vec3"===n&&"vec3"===e||"color"===n&&"color"===e}function u(n){const e={};for(const[t,i]of Object.entries(n))e[t]=p(h(i),i);return e}function c(n,e){if(null==n)return!1;const t=Object.keys(n),i=Object.keys(e);if(t.length!==i.length)return!1;for(const t of i)if(!d(n[t],e[t]))return!1;return!0}function d(n,e){return n instanceof t.Vector3&&e instanceof t.Vector3||n instanceof t.Color&&e instanceof t.Color?n.equals(e):n===e}function p(n,e){switch(n){case"vec3":return e instanceof t.Vector3?e.clone():e;case"color":return e instanceof t.Color?e.clone():e;default:return e}}function h(n){return n instanceof t.Vector3?"vec3":n instanceof t.Color?"color":"number"==typeof n?"number":"boolean"==typeof n?"boolean":"string"}const f=new Set;function b(n){f.has(n)||(f.add(n),console.warn(n))}/*
|
|
2
|
+
* Copyright (©) 2026 Hology Interactive AB. All rights reserved.
|
|
3
|
+
* See the LICENSE.md file for details.
|
|
4
|
+
*/
|