@hology/core 0.0.193 → 0.0.194
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/index.d.ts +5 -0
- package/dist/effects/sequence/index.js +1 -1
- package/dist/effects/sequence/sequence-action.d.ts +2 -1
- package/dist/effects/sequence/sequence-actor.d.ts +9 -0
- package/dist/effects/sequence/sequence-actor.js +1 -1
- package/dist/effects/sequence/sequence-animation-retiming.d.ts +15 -0
- package/dist/effects/sequence/sequence-animation-retiming.js +4 -0
- package/dist/effects/sequence/sequence-data.d.ts +48 -2
- package/dist/effects/sequence/sequence-data.js +1 -1
- package/dist/effects/sequence/sequence-definitions.d.ts +53 -0
- package/dist/effects/sequence/sequence-definitions.js +4 -0
- package/dist/effects/sequence/sequence-event.d.ts +24 -1
- package/dist/effects/sequence/sequence-event.js +1 -1
- package/dist/effects/sequence/sequence-locator.d.ts +14 -0
- package/dist/effects/sequence/sequence-locator.js +4 -0
- package/dist/effects/sequence/sequence-ops.d.ts +48 -0
- package/dist/effects/sequence/sequence-ops.js +4 -0
- package/dist/effects/sequence/sequence-player.d.ts +37 -3
- package/dist/effects/sequence/sequence-player.js +1 -1
- package/dist/effects/sequence/sequence-value-lane.d.ts +21 -0
- package/dist/effects/sequence/sequence-value-lane.js +4 -0
- package/dist/gameplay/actors/builtin/components/character/character-animation.d.ts +1 -0
- package/dist/gameplay/actors/builtin/components/character/character-animation.js +1 -1
- package/dist/gameplay/actors/builtin/components/character/character-movement.js +1 -1
- package/dist/gameplay/actors/builtin/post-process-volume-actor.d.ts +25 -1
- package/dist/gameplay/actors/builtin/post-process-volume-actor.js +1 -1
- package/dist/gameplay/index.d.ts +1 -0
- package/dist/gameplay/index.js +1 -1
- package/dist/gameplay/initiate.d.ts +4 -0
- package/dist/gameplay/initiate.js +1 -1
- package/dist/gameplay/services/game-time-scheduler.d.ts +25 -0
- package/dist/gameplay/services/game-time-scheduler.js +4 -0
- package/dist/gameplay/services/render.d.ts +11 -2
- package/dist/gameplay/services/render.js +1 -1
- package/dist/rendering/color-pass.d.ts +62 -7
- package/dist/rendering/color-pass.js +1 -1
- package/dist/rendering.d.ts +25 -0
- package/dist/rendering.js +1 -1
- package/dist/scene/custom-param-deserialize.d.ts +18 -0
- package/dist/scene/custom-param-deserialize.js +4 -0
- package/dist/scene/materializer.d.ts +1 -0
- package/dist/scene/materializer.js +1 -1
- package/dist/shader/builtin/landscape-composite-shader.js +1 -1
- package/dist/shader/parameter.d.ts +5 -0
- package/dist/test/post-process-color-grading.test.d.ts +2 -0
- package/dist/test/post-process-color-grading.test.js +4 -0
- package/dist/test/sequence-animation-retiming.test.d.ts +2 -0
- package/dist/test/sequence-animation-retiming.test.js +4 -0
- package/package.json +1 -1
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -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 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;/*
|
|
1
|
+
import{BehaviorSubject as t,Subject as e}from"rxjs";import*as i from"three";import{AnimationMixer as s,Euler as a,Object3D as n,Quaternion as o,Vector3 as r}from"three";import{VisualEffect as c}from"../../effects/vfx/vfx-param.js";import{BaseActor as l}from"../../gameplay/actors/actor.js";import u from"../../gameplay/actors/builtin/index.js";import{Prefab as d,PrefabOf as h}from"../../scene/objects/prefab.js";import{customParamValueNeedsAsyncResolution as p,deserializeCustomParamValue as f,deserializeCustomParamValueSync as m}from"../../scene/custom-param-deserialize.js";import{getAudioParameterDefinition as y}from"./audio-parameters.js";import{Sequence as g}from"./sequence-data.js";import{getSequenceSubTrackDefinition as v,getSequenceTrackDefinition as A}from"./sequence-definitions.js";import{applyInterpolation as b,evaluateCustomParamValueKeyframes as w,evaluateNumericKeyframes as C,findKeyframeSpan as S}from"./sequence-value-lane.js";import{buildRetimedAnimationClip as k,getAnimationClipRetimingSignature as T,isAnimationClipUsingRetiming as x}from"./sequence-animation-retiming.js";import{SkeletonUtils as P}from"three-stdlib";import{CharacterAnimationComponent as I,CharacterMovementComponent as q}from"../../gameplay/actors/index.js";import{RootMotionClip as B}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._inEditor=!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 Map,this.retimedAnimationClips=new Map,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.locatorMarkers=new Map,this.clearCounter=0,this.audioFilters=new Map,this.audioPannerNodes=new Map,this.firedEvents=new Set,this.sequenceEventSyncResolvers={actor:t=>this.resolveSequenceEventActorReference(t)},this.sequenceEventAsyncResolvers={actor:t=>this.resolveSequenceEventActorReference(t),texture:t=>this.assetLoader?.getTextureByAssetId(t)??null,sampler2DNode:t=>this.assetLoader?.getTextureByAssetId(t)??null,object3D:async t=>{const e=await(this.assetLoader?.getModelByAssetId(t));return e?.scene??null},material:t=>this.assetLoader?.getMaterialByAssetId(t)??null,audioBuffer:t=>this.assetLoader?.getAudioByAssetId(t)??null,visualEffect:async t=>{if(!this.assetLoader||!this.actorFactory)return null;const e=await this.assetLoader.getAsset(t);return e?new c(this.actorFactory,e):null},prefab:async t=>{const e=await(this.assetLoader?.getPrefabById(t));return e?new d(e.asset):null},prefabActor:async t=>{const e=await(this.assetLoader?.getPrefabById(t));return e?new h(new d(e.asset)):null},sequence:t=>this.resolveSequenceEventSequence(t),animationClip:t=>this.resolveSequenceEventAnimationClip(t)},this.trackRuntimeEvaluators={object:(t,e,i)=>{e&&this.evaluateObjectTrack(t,e,i)},locator:(t,e,i)=>{e&&this.evaluateLocatorTrack(t,e,i)},camera:(t,e,i)=>this.evaluateCameraTrack(t,i),audio:(t,e,i)=>this.evaluateAudioTrack(t,i),vfx:(t,e,i)=>this.evaluateVfxTrack(t,i),spawn:(t,e,i)=>this.evaluateSpawnTrack(t,e,i),group:(t,e,i)=>{if("group"===t.type)for(const e of t.childTracks)this.evaluateTrack(e,i)}},this.subTrackRuntimeEvaluators={transform:(t,e,i)=>{const s=this.getPlayableObject(e);s&&this.evaluateTransformSubTrack(t,s,i)},animation:(t,e,i)=>this.evaluateAnimationSubTrack(t,e,i),visibility:(t,e,i)=>{const s=this.getPlayableObject(e);s&&this.evaluateVisibilitySubTrack(t,s,i)},parameter:(t,e,i)=>this.evaluateParameterSubTrack(t,e.target||null,i),event:(t,e,i)=>this.evaluateEventSubTrack(t,e.target||null,i)}}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}get inEditor(){return this._inEditor}set inEditor(t){this._inEditor=t,this.updateLocatorMarkerVisibility()}setWorld(t){this.world!==t&&(this.clearLocatorBindings(),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.clearRetimedAnimationClipCache(),this._state.next(SequencePlaybackState.Stopped),this.sequenceData&&this.initializeLocatorBindings())}setAudioListener(t){this.audioListener=t}setAssetLoader(t){if(this.assetLoader=t,this.sequenceData){const t=this.clearCounter;this.preloadSequenceResources(this.sequenceData,t)}}setActorFactory(t){this.actorFactory=t}setVfxService(t){this.vfxService=t}load(t){if(this.stop(),this.clearCounter++,this.clearLocatorBindings(),this.sequenceData=t,this._duration=t.duration,this._loop=t.loop,this._timescale=t.playbackRate,this.clearRetimedAnimationClipCache(),this.bindings.clear(),this.initializeLocatorBindings(),this.loopCount=0,this.assetLoader){const e=this.clearCounter;this.preloadSequenceResources(t,e)}}preloadSequenceResources(t,e){return Promise.all([this.preloadAudioBuffersForSequence(t,e),this.preloadAnimationClipsForSequence(t,e)]).then(()=>{})}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)}}))}getAnimationClipAssetIdsForSequence(t){const e=new Set;for(const i of L(t.tracks))if("subTracks"in i)for(const t of i.subTracks)if("animation"===t.type)for(const i of t.clips)i.animationClipAssetId&&e.add(i.animationClipAssetId);return[...e]}async preloadAnimationClipsForSequence(t,e){if(!this.assetLoader)return;const i=this.getAnimationClipAssetIdsForSequence(t);0!==i.length&&await Promise.all(i.map(async t=>{e===this.clearCounter&&await this.ensureAnimationClip(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)}getLocator(t){return this.getLocators(t)[0]??null}getLocators(t){const e=_(t);if(!e||!this.sequenceData)return[];const i=[];for(const t of L(this.sequenceData.tracks)){if("locator"!==t.type)continue;if(_(t.bindingId)!==e)continue;const s=this.bindings.get(t.id),a=s?this.getPlayableObject(s):null;a&&(a.updateWorldMatrix(!0,!1),i.push({trackId:t.id,name:t.name,bindingId:t.bindingId,position:a.getWorldPosition(new r),rotation:a.getWorldQuaternion(new o),scale:a.getWorldScale(new r)}))}return i}resolveBindings(){if(this.sequenceData)for(const t of this.sequenceData.tracks)this.resolveTrackBinding(t)}initializeLocatorBindings(){if(this.sequenceData)for(const t of L(this.sequenceData.tracks)){if("locator"!==t.type||this.bindings.has(t.id))continue;const e=new n;e.name=t.name||"Locator",e.userData.sequenceLocatorTrackId=t.id,this.world?.scene&&null==e.parent&&this.world.scene.add(e);const i=j(t.name);i.visible=this._inEditor,e.add(i),this.locatorMarkers.set(t.id,i),this.disableLocatorInteraction(e),this.createBinding(t,e)}}clearLocatorBindings(){for(const t of this.locatorMarkers.values())F(t);for(const t of this.bindings.values()){if("locator"!==t.track.type)continue;const e=this.getPlayableObject(t);e?.parent&&e.removeFromParent()}this.locatorMarkers.clear()}updateLocatorMarkerVisibility(){for(const t of this.locatorMarkers.values())t.visible=this._inEditor}disableLocatorInteraction(t){t.traverse(t=>{t.raycast=()=>{}})}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 l?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.evaluate(this._time.value),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||"locator"===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);(0,this.trackRuntimeEvaluators[A(t.type).runtimeEvaluator])(t,i,e)}evaluateObjectTrack(t,e,i){this.evaluateSubTracks(t.subTracks,e,i)}evaluateLocatorTrack(t,e,i){this.evaluateSubTracks(t.subTracks,e,i)}evaluateSubTracks(t,e,i){for(const s of t){if(s.muted)continue;(0,this.subTrackRuntimeEvaluators[v(s.type).runtimeEvaluator])(s,e,i)}}evaluateTransformSubTrack(t,e,i){const s=t.keyframes;if(0===s.length)return;const{prev:a,next:n,t:o}=S(s,i);if(a)if(n&&a!==n){const i=b(o,a.interpolation);t.components.position&&a.position&&n.position&&e.position.lerpVectors(M.fromArray(a.position),E.fromArray(n.position),i),t.components.rotation&&a.rotation&&n.rotation&&(R.setFromEuler(D.fromArray([...a.rotation,"XYZ"])),V.setFromEuler($.fromArray([...n.rotation,"XYZ"])),e.quaternion.slerpQuaternions(R,V,i)),t.components.scale&&a.scale&&n.scale&&e.scale.lerpVectors(M.fromArray(a.scale),E.fromArray(n.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 l?e.target.object:e.target)for(const a of t.clips){const n=a.startTime+a.duration,o=s>=a.startTime&&s<n,r=`${t.id}-${a.id}`;let c=e.activeActions.get(r),u=!1;if(o){if(!c){u=!0;const t=a.animationClipAssetId;if(null==t)continue;const s=this.animationClips.get(t);if(null==s){this.ensureAnimationClip(t);continue}const n=this.prepareAnimationPlayback(t,s,a),o=e;let d=!1;if(e.target instanceof l){o.charAnimComponent||(o.charAnimComponent=e.target.getComponent(I)??void 0);const i=o.charAnimComponent;if(null!=i){const l=a.rootMotion?x(a)?this.getPlayableAnimationClip(t,s,a,!0):B.fromClip(n.clip,!1):n.clip;if(this._externalTimeControl)c=i.beginExternalAnimationControl(l,{timeScale:n.timeScale});else if(i.play(l,{inPlace:!a.rootMotion,loop:!1,priority:20,timeScale:n.timeScale,fadeTime:a.fadeInDuration??.2,offset:n.startOffset}),c=i.getFullBodyAction(),a.rootMotion){const t=e.target.getComponent(q);t&&t.setRootMotionAction(c)}e.activeActions.set(r,c),o.charAnimActionKeys||(o.charAnimActionKeys=new Set),o.charAnimActionKeys.add(r),d=!0}}d||(c=e.mixer.clipAction(n.clip),c.setLoop(i.LoopOnce,1),c.clampWhenFinished=!0,c.timeScale=n.timeScale,n.startOffset>0&&(c.time=n.startOffset),e.activeActions.set(r,c),c.play())}if(c){const t=e;if(this._externalTimeControl){t.charAnimComponent?.beginExternalControl();const i=this.getAnimationPlaybackLocalTime(a,s),n=Math.min(i,c.getClip().duration);(u||Math.abs(c.time-n)>.001)&&(c.time=n,c.paused=!1,c.play(),t.charAnimComponent?t.charAnimComponent.getMixer()?.update(0):e.mixer&&e.mixer.update(0))}else t.charAnimComponent?.endExternalControl()}}else if(c){e.activeActions.delete(r);const t=e;t.charAnimActionKeys?.has(r)?(t.charAnimActionKeys.delete(r),0===t.charAnimActionKeys.size&&t.charAnimComponent&&(t.charAnimComponent.stopSequenceAnimation(),t.charAnimComponent=void 0)):(c.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){w(t.keyframes,i)}evaluateParameterSubTrack(t,e,i){if("audioParameter"===t.type)return;const s=w(t.keyframes,i);s&&("property"!==t.type?this.applyResolvedMaterialValue(t,e,s):this.applyResolvedPropertyValue(t,e,s))}evaluateEventSubTrack(t,e,i){if(e instanceof l&&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.editorOnly&&!this._inEditor)continue;if(!s.functionName)continue;const t=e[s.functionName];"function"==typeof t?this.invokeSequenceEvent(e,s.functionName,t,s.arguments??[]):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 n=a.startTime+a.duration,o=e>=a.startTime&&e<n,r=`${t.id}-${a.id}`;if(o){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 n=this.audioByClip.get(r);n&&this.applyAudioParameters(n,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 n=this.lastAudioStartAttemptTime.get(i);if(void 0!==n&&s-n<this.audioStartRetryIntervalSeconds)return;this.lastAudioStartAttemptTime.set(i,s);const o=this.playAudioClip(t,e,i,s,a).catch(()=>{}).finally(()=>{this.audioStartInFlight.get(i)===o&&this.audioStartInFlight.delete(i)});this.audioStartInFlight.set(i,o)}applyAudioParameters(t,e,s,a,n,o){const r=o.volume??1,c=this.stringToRandom(a+e.id),l=s.volumeRandomization?(2*c-1)*s.volumeRandomization:0;let u=1;const d=n-s.startTime;d<s.fadeInDuration&&s.fadeInDuration>0?u=d/s.fadeInDuration:d>s.duration-s.fadeOutDuration&&s.fadeOutDuration>0&&(u=(s.duration-d)/s.fadeOutDuration);const h=Math.max(0,(e.volume??1)*r*(s.volume+l)*u);t.setVolume(h);let p=o.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,n=[],r=o.lowpass,c=o.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),n.push(l);else if(l){try{l.disconnect()}catch{}this.audioFilters.delete(a)}const u=o.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,n.push(t)}else{const t=this.audioPannerNodes.get(a);if(t){try{t.disconnect()}catch{}this.audioPannerNodes.delete(a)}}t.setFilters(n)}e.spatial&&t instanceof i.PositionalAudio?this.updatePositionalAudioTransform(t,e,s,o.spatialBlend):void 0!==o.pan&&this.audioListener}updatePositionalAudioTransform(t,e,s,a){let n=null,o=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(n=M,o=R,t.getWorldPosition(n),t.getWorldQuaternion(o),c){const e=this.findSocket(t,c);e&&(e.getWorldPosition(n),e.getWorldQuaternion(o))}}}}if(n&&o){const e="number"==typeof a?i.MathUtils.clamp(a,0,1):1;if(e<1&&this.audioListener){const t=O;this.audioListener.getWorldPosition(t),n=n.clone().lerp(t,1-e)}t.position.copy(n),t.quaternion.copy(o),t.updateMatrixWorld()}}async playAudioClip(t,e,s,a,n){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 o=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(o!==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 d=this.audioByClip.get(s);const h=!0===t.spatial,p=d&&!0===d.isPositionalAudio;if(d&&h!==p&&(d.isPlaying&&d.stop(),d.disconnect(),this.audioByClip.delete(s),d=void 0),!d){if(h){const t=new i.PositionalAudio(this.audioListener);t.setRefDistance(10),t.setRolloffFactor(1),d=t}else d=new i.Audio(this.audioListener);this.world&&this.world.scene.add(d),this.audioByClip.set(s,d)}Math.min((e.duration-l)/e.playbackRate,(a.duration-u)/e.playbackRate);d.setLoopStart(e.clipStartOffset),d.setLoopEnd(Math.min(e.clipStartOffset+e.duration,a.duration)),d.setLoop(!0),d.setBuffer(a),this.applyAudioParameters(d,t,e,s,r,n),d.isPlaying||d.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 n=this.getVfxClipKey(t,a);s.set(n,a);const o=a.startTime+a.duration;e>=a.startTime&&e<o&&i.add(n)}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,n]of s.entries()){const s=this.vfxActors.get(a);i.has(a)&&!this.activeVfxClips.has(a)?(this.activeVfxClips.add(a),this.ensureVfxActor(t,n,a)):i.has(a)&&s?(this.updateVfxTransform(t,s,e),this._state.value===SequencePlaybackState.Playing&&s.play()):s&&this.applyInactiveVfxClipBehavior(n,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,n=Math.max(i-a,0),o=Math.max(0,t.expireWithinSeconds??.25),r=Math.max(0,o-n);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:n,rotation:o,scale:r}=this.getVfxTransform(t,e.startTime);a.object.position.copy(n),a.object.rotation.copy(o),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 r,s=new a,n=new r(1,1,1),c=t.subTracks.find(t=>"transform"===t.type);if(c&&c.keyframes.length>0){const{prev:t,next:a,t:r}=S(c.keyframes,e);if(t&&(t.position&&i.fromArray(t.position),t.rotation&&s.fromArray([...t.rotation,"XYZ"]),t.scale&&n.fromArray(t.scale)),a&&t!==a&&t.time!==a.time){const e=b(r,t.interpolation);if(t.position&&a.position&&i.lerpVectors(M.fromArray(t.position),E.fromArray(a.position),e),t.rotation&&a.rotation){R.setFromEuler(D.fromArray([...t.rotation,"XYZ"])),V.setFromEuler($.fromArray([...a.rotation,"XYZ"]));const i=new o;i.slerpQuaternions(R,V,e),s.setFromQuaternion(i)}t.scale&&a.scale&&n.lerpVectors(M.fromArray(t.scale),E.fromArray(a.scale),e)}}else i.fromArray(t.position),s.fromArray([...t.rotation,"XYZ"]),n.fromArray(t.scale);return{position:i,rotation:s,scale:n}}updateVfxTransform(t,e,i){const{position:s,rotation:a,scale:n}=this.getVfxTransform(t,i);e.object.position.copy(s),e.object.rotation.copy(a),e.object.scale.copy(n)}resolveActorType(t){const e=this.actorFactory.classes[t];if(null!=e)return e;const i=u[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),n=P.clone(a.scene);if(n.position.copy(i),n.rotation.copy(s),e){await this.assetLoader.applyMaterials(e,n);const t=e.mesh?.rescale;null!=t&&1!==t&&n.scale.multiplyScalar(t)}return this.world.scene.add(n),n}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 r,n=new a,o=t.subTracks.find(t=>"transform"===t.type);if(o&&o.keyframes.length>0){const{prev:t}=S(o.keyframes,i);t&&(t.position&&s.fromArray(t.position),t.rotation&&n.fromArray([...t.rotation,"XYZ"]))}else e.initialPosition&&s.fromArray(e.initialPosition),e.initialRotation&&n.fromArray([...e.initialRotation,"XYZ"]);return{position:s,rotation:n}}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,n=i>=e.startTime&&i<a,o=`${t.id}-${e.id}`;if(n){this.activeClips.has(o)||(this.activeClips.add(o),this.spawnedInstances.set(o,{type:"proxy",target:s.target}));(s.target instanceof l?s.target.object:s.target)&&this.evaluateSubTracks(t.subTracks,s,i)}else this.activeClips.has(o)&&(this.activeClips.delete(o),this.spawnedInstances.delete(o))}else for(const e of t.clips){const s=e.startTime+e.duration,a=i>=e.startTime&&i<s,n=`${t.id}-${e.id}`,o=this.spawnedInstances.has(n),r=this.pendingSpawns.has(n);if(!a||o||r){if(a&&o){const e=this.spawnedInstances.get(n);e&&"proxy"!==e.type&&this.evaluateSubTracks(t.subTracks,e,i)}else if(!a&&o){const t=this.spawnedInstances.get(n);t&&(this.despawnInstance(t),this.spawnedInstances.delete(n),this.activeClips.delete(n))}}else{const{position:i,rotation:s}=this.getInitialTransform(t,e,e.startTime);this.pendingSpawns.add(n),this.spawnForClip(t,e,i,s,n)}}}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 l?t.target.object:t.target;default:return null}}async spawnForClip(t,e,i,a,n){const o=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 n=await this.spawnPrefabForClip(t,e,i,a);n&&(r={type:"prefab",instance:n});break;case"mesh":const o=await this.spawnMeshForClip(t,e,i,a);o&&(r={type:"mesh",instance:o})}if(o!==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(n,r),this.activeClips.add(n)}else r&&this.despawnInstance(r)}finally{this.pendingSpawns.delete(n)}}getPlayableObject(t){return t.target instanceof l?t.target.object:t.target??null}applyResolvedPropertyValue(t,e,i){}applyResolvedMaterialValue(t,e,i){}async ensureAnimationClip(t){if(!this.assetLoader)return null;const e=this.animationClips.get(t);if(e)return e;const i=this.loadingAnimationClips.get(t);if(i)return i;const s=(async()=>{try{const e=await this.assetLoader.getAnimationClipByAssetId(t);return e&&this.animationClips.set(t,e),e}catch(e){return console.error(`Failed to load animation clip for asset ${t}:`,e),null}finally{this.loadingAnimationClips.get(t)===s&&this.loadingAnimationClips.delete(t)}})();return this.loadingAnimationClips.set(t,s),s}prepareAnimationPlayback(t,e,i){if(!x(i)){let t=e;return i.clipEndOffset>0&&(t=t.clone(),t.duration=Math.max(0,t.duration-i.clipEndOffset)),{clip:t,timeScale:i.playbackRate,startOffset:i.clipStartOffset}}return{clip:this.getPlayableAnimationClip(t,e,i,!1),timeScale:1,startOffset:0}}getPlayableAnimationClip(t,e,i,s){if(!x(i))return s?B.fromClip(e,!1):e;const a=`${i.id}:${s?"root":"base"}`,n=`${T(i)}|${s?"root":"base"}`,o=this.retimedAnimationClips.get(a);if(o&&o.assetId===t&&o.signature===n)return o.clip;const r=k(e,i),c=s?B.fromClip(r,!1):r;return this.retimedAnimationClips.set(a,{assetId:t,signature:n,clip:c}),c}getAnimationPlaybackLocalTime(t,e){const i=Math.max(0,e-t.startTime);return x(t)?i:i*t.playbackRate+t.clipStartOffset}clearRetimedAnimationClipCache(t){if(null!=t)for(const[e,i]of this.retimedAnimationClips.entries())i.assetId===t&&this.retimedAnimationClips.delete(e);else this.retimedAnimationClips.clear()}captureOriginalTransforms(){for(const t of this.bindings.values()){const e=t.target instanceof l?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 l?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.clearLocatorBindings(),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.loadingAnimationClips.clear(),this.clearRetimedAnimationClipCache(),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=this.findSocket(s,t.parent.socketName);e&&(s=e)}}}for(const a of i)s?a.parent!==s&&(s.add(a),!e||"object"!==t.type&&"locator"!==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&&"locator"!==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 l?e.target.object:e.target]}else if("locator"===t.type){const e=this.bindings.get(t.id);if(e&&e.target instanceof n)return[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 l?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=y(t.parameter),n=C(t.keyframes,e,{min:a.min,max:a.max});void 0!==n&&(i[t.parameter]=n)}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.animationClips.delete(t),this.loadingAnimationClips.delete(t),this.clearRetimedAnimationClipCache(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))}}async invokeSequenceEvent(t,e,i,s){let a=!1;for(let t=0;t<s.length;t++)if(p(s[t])){a=!0;break}if(a)try{const e=new Array(s.length);for(let t=0;t<s.length;t++)e[t]=f(s[t],this.sequenceEventAsyncResolvers);const a=await Promise.all(e);await i.call(t,...a)}catch(t){console.error(`Failed to call sequence event ${e}:`,t)}else try{const e=new Array(s.length);for(let t=0;t<s.length;t++)e[t]=m(s[t],this.sequenceEventSyncResolvers);i.call(t,...e)}catch(t){console.error(`Failed to call sequence event ${e}:`,t)}}resolveSequenceEventActorReference(t){if(!this.world||null==t)return null;const e=t,i="object"==typeof e&&null!=e?.id?e.id:"string"==typeof e?e:null;if(null==i)return null;for(const t of this.world.actors){const e=t.object.userData.src??t.object.userData._src;if(e?.id===i)return t}return null}resolveSequenceEventSequence(t){return function(t){return"object"==typeof t&&null!=t&&"duration"in t&&"tracks"in t}(t)?new g(t):this.assetLoader&&"string"==typeof t?this.assetLoader.getSequenceById(t):null}async resolveSequenceEventAnimationClip(t){const e="string"==typeof t?t:"object"==typeof t&&null!=t?t.assetId??null:null;return null!=e&&this.assetLoader?this.assetLoader.getAnimationClipByAssetId(e):null}}function L(t){const e=[];for(const i of t)e.push(i),"group"===i.type&&e.push(...L(i.childTracks));return e}function _(t){if("string"!=typeof t)return null;const e=t.trim();return e.length>0?e:null}function j(t){const e=new i.Group;e.name=`SequenceLocatorMarker:${t}`,e.renderOrder=1e3;const s=new i.AxesHelper(.35);s.renderOrder=1e3;const a=Array.isArray(s.material)?s.material:[s.material];for(const t of a)t.depthTest=!1,t.transparent=!0,t.opacity=.9,t.toneMapped=!1;e.add(s);const n=function(t){if("undefined"==typeof document)return null;const e=document.createElement("canvas");e.width=256,e.height=64;const s=e.getContext("2d");if(!s)return null;s.clearRect(0,0,e.width,e.height),s.fillStyle="rgba(16, 18, 24, 0.78)",function(t,e,i,s,a,n){const o=e+s,r=i+a;t.beginPath(),t.moveTo(e+n,i),t.lineTo(o-n,i),t.quadraticCurveTo(o,i,o,i+n),t.lineTo(o,r-n),t.quadraticCurveTo(o,r,o-n,r),t.lineTo(e+n,r),t.quadraticCurveTo(e,r,e,r-n),t.lineTo(e,i+n),t.quadraticCurveTo(e,i,e+n,i),t.closePath()}(s,0,8,e.width,48,8),s.fill(),s.font="24px sans-serif",s.textAlign="center",s.textBaseline="middle",s.fillStyle="#f8fafc",s.fillText(t||"Locator",e.width/2,e.height/2);const a=new i.CanvasTexture(e);a.needsUpdate=!0;const n=new i.SpriteMaterial({map:a,transparent:!0,depthTest:!1});n.toneMapped=!1;const o=new i.Sprite(n);return o.scale.set(.9,.225,1),o}(t);return n&&(n.position.set(0,.45,0),n.renderOrder=1001,e.add(n)),e.traverse(t=>{t.raycast=()=>{}}),e}function F(t){t.traverse(t=>{const e=t.material;if(Array.isArray(e))for(const t of e)t?.dispose?.(),t?.map?.dispose?.();else e?.dispose?.(),e?.map?.dispose?.()})}const M=new r,E=new r,O=new r,R=new o,V=new o,D=new a,$=new a;/*
|
|
2
2
|
* Copyright (©) 2026 Hology Interactive AB. All rights reserved.
|
|
3
3
|
* See the LICENSE.md file for details.
|
|
4
4
|
*/
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { CustomParamValue } from '../../scene/model.js';
|
|
2
|
+
import { InterpolationMode, PropertyKeyframe } from './sequence-data.js';
|
|
3
|
+
export interface TimedSequenceItem {
|
|
4
|
+
time: number;
|
|
5
|
+
}
|
|
6
|
+
export interface SequenceKeyframeSpan<T extends TimedSequenceItem> {
|
|
7
|
+
prev: T | null;
|
|
8
|
+
next: T | null;
|
|
9
|
+
t: number;
|
|
10
|
+
}
|
|
11
|
+
export interface NumericValueLaneOptions {
|
|
12
|
+
min?: number;
|
|
13
|
+
max?: number;
|
|
14
|
+
}
|
|
15
|
+
export declare function sortTimedSequenceItems<T extends TimedSequenceItem>(items: readonly T[]): T[];
|
|
16
|
+
export declare function findKeyframeSpan<T extends TimedSequenceItem>(keyframes: readonly T[], time: number): SequenceKeyframeSpan<T>;
|
|
17
|
+
export declare function applyInterpolation(t: number, mode: InterpolationMode): number;
|
|
18
|
+
export declare function clampNumericValue(value: number, options?: NumericValueLaneOptions): number;
|
|
19
|
+
export declare function evaluateNumericKeyframes(keyframes: readonly PropertyKeyframe[], time: number, options?: NumericValueLaneOptions): number | undefined;
|
|
20
|
+
export declare function evaluateCustomParamValueKeyframes(keyframes: readonly PropertyKeyframe[], time: number, options?: NumericValueLaneOptions): CustomParamValue | null;
|
|
21
|
+
//# sourceMappingURL=sequence-value-lane.d.ts.map
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import{SerializedParamType as e}from"../../scene/model.js";export function sortTimedSequenceItems(e){return[...e].sort((e,t)=>e.time-t.time)}export function findKeyframeSpan(e,t){if(0===e.length)return{prev:null,next:null,t:0};let n=null,r=null;for(let u=0;u<e.length;u++)if(e[u].time<=t&&(n=e[u]),e[u].time>t&&!r){r=e[u];break}if(n||(n=e[0]),r||(r=e[e.length-1]),n===r||n.time===r.time)return{prev:n,next:n,t:0};return{prev:n,next:r,t:(t-n.time)/(r.time-n.time)}}export function applyInterpolation(e,t){switch(t){case"linear":case"catmullRom":default:return e;case"step":return 0;case"cubic":return e<.5?4*e*e*e:1-Math.pow(-2*e+2,3)/2}}export function clampNumericValue(e,t={}){const n=t.min,r=t.max;let u=e;return"number"==typeof n&&(u=Math.max(n,u)),"number"==typeof r&&(u=Math.min(r,u)),u}export function evaluateNumericKeyframes(e,n,r={}){if(0===e.length)return;const{prev:u,next:a,t:i}=findKeyframeSpan(e,n);if(!u)return;const l=t(u.value);if("number"!=typeof l)return;let m=l;if(a&&a!==u){const e=t(a.value);"number"==typeof e&&(m="step"===u.interpolation?l:l+(e-l)*applyInterpolation(i,u.interpolation))}return clampNumericValue(m,r)}export function evaluateCustomParamValueKeyframes(t,r,u={}){if(0===t.length)return null;const{prev:a,next:i,t:l}=findKeyframeSpan(t,r);if(!a)return null;const m=a.value;if(!i||i===a)return n(m,u);if(m.type===e.Number&&i.value.type===e.Number){const n=evaluateNumericKeyframes(t,r,u);if("number"==typeof n)return{type:e.Number,value:n}}return n("step"!==a.interpolation&&l>=1?i.value:m,u)}function t(t){if(t?.type===e.Number)return"number"==typeof t.value?t.value:void 0}function n(t,n){return t?.type!==e.Number||"number"!=typeof t.value?t:{...t,value:clampNumericValue(t.value,n)}}/*
|
|
2
|
+
* Copyright (©) 2026 Hology Interactive AB. All rights reserved.
|
|
3
|
+
* See the LICENSE.md file for details.
|
|
4
|
+
*/
|
|
@@ -124,6 +124,7 @@ export declare class CharacterAnimationComponent extends ActorComponent {
|
|
|
124
124
|
private rayTestDown;
|
|
125
125
|
private isRelatedToActorHierarchy;
|
|
126
126
|
private isFootIkDebugObject;
|
|
127
|
+
private isSkinnedMeshObject;
|
|
127
128
|
/**
|
|
128
129
|
* Returns the current full-body action.
|
|
129
130
|
* For root motion clips, this is the action that drives character movement.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{__decorate as t}from"tslib";import{ActorComponent as e,Component as o}from"../../../component.js";import{RootMotionClip as i}from"../../../../animation/root-motion.js";import{inject as s}from"../../../../inject.js";import{ViewController as r}from"../../../../services/render.js";import{AnimationMixer as n,Bone as l,Object3D as a,LoopOnce as h,Raycaster as c,Vector3 as d,SkinnedMesh as p,Skeleton as u,Quaternion as f,Matrix4 as g,MathUtils as y,ArrowHelper as m,Group as k}from"three";import{CharacterMovementComponent as S}from"./character-movement.js";import{CCDIKSolver as w}from"three/addons/animation/CCDIKSolver.js";import{World as L}from"../../../../services/world.js";import{PhysicsSystem as b,RayTestResult as v}from"../../../../services/physics/physics-system.js";const I=new d(0,-1,0),B=new d(0,1,0),x=new d(1,1,1),D=new f,F=new d,P=new d,R=new d,A=new d,M=new d,T=new f,W=new f,O=new f,V=new f,C=new g,q=new g,H=new d(1,0,0),E=new d(0,1,0),N=new d(0,0,1);function U(){return{point:new d,normal:new d,hasNormal:!1}}const z=["mixamorigHips","Hips","hips","Pelvis","pelvis"],j={upper:["LeftUpLeg","leftUpLeg","left_up_leg"],lower:["LeftLeg","leftLeg","left_leg"],foot:["LeftFoot","leftFoot","left_foot"]},G={upper:["RightUpLeg","rightUpLeg","right_up_leg"],lower:["RightLeg","rightLeg","right_leg"],foot:["RightFoot","rightFoot","right_foot"]},Q=["left","right"];let _=class extends e{constructor(){super(...arguments),this.viewController=s(r),this.stateMachines=[],this.upperStateMachines=[],this.fadeTime=.2,this.movementSpeed=null,this.upperBodyTimer=0,this.upperBodyOverride=!1,this.fullBodyTimer=0,this.currentFullBodyPriority=-1,this.currentUpperBodyPriority=-1,this.externalControlActive=!1,this.world=s(L),this.physicsSystem=s(b),this.footIkEnabled=!1,this.footIkMoving=!1,this.footIkRayStartHeight=.5,this.footIkRayLength=1.5,this.footIkRaycastMode="physics",this.footIkFootOffset=.08,this.footIkPositionLerpSpeed=28,this.footIkRotationLerpSpeed=14,this.footIkWeightInSpeed=34,this.footIkWeightOutSpeed=30,this.footIkMaxSlopeAngleDeg=40,this.footIkForwardSampleDistance=.14,this.footIkSideSampleDistance=.08,this.footIkMaxHorizontalOffset=.22,this.footIkMaxVerticalOffsetDown=.75,this.footIkMaxVerticalOffsetUp=.2,this.footIkPelvisEnabled=!0,this.footIkPelvisBone="mixamorigHips",this.footIkPelvisLerpSpeed=24,this.footIkPelvisMaxOffsetDown=.4,this.footIkPelvisMaxOffsetUp=.08,this.footIkRequireGrounded=!0,this.footIkSimpleTiltOnly=!1,this.footIkDebug=!1,this.footIkDebugNormalLength=.25,this.footIkDebugAxisLength=.2,this.footIkExtraClearance=.01,this.footIkPlantReleaseUpSpeed=.08,this.footIkPlantReleaseExtension=.9,this.footIkPlantAttachExtension=.96,this.footIkPlantReleaseLift=.055,this.footIkPlantAttachLift=.02,this.leftLegIkBones={upperLeg:"mixamorigLeftUpLeg",lowerLeg:"mixamorigLeftLeg",foot:"mixamorigLeftFoot"},this.rightLegIkBones={upperLeg:"mixamorigRightUpLeg",lowerLeg:"mixamorigRightLeg",foot:"mixamorigRightFoot"},this.ikPelvisOffsetY=0,this.ikLegState={left:{targetPosition:new d,normal:new d(0,1,0),forward:new d(0,0,1),pitch:0,contactOffset:0,planted:!0,lastFootWorldPos:new d,hasLastFootSample:!1,weight:0,top:!1},right:{targetPosition:new d,normal:new d(0,1,0),forward:new d(0,0,1),pitch:0,contactOffset:0,planted:!0,lastFootWorldPos:new d,hasLastFootSample:!1,weight:0,top:!1}},this.ikWarnedMissingBones=!1,this.raycaster=new c,this.raycastIntersections=[],this.footIkRayTo=new d,this.footIkRayTestResult=new v,this.footIkRayTestOptions={excludeTriggers:!0,resolveActor:!1},this.footIkHits={center:U(),toe:U(),heel:U(),side:U()},this.footIkAxesScratch={up:new d,forward:new d,right:new d},this.footIkSampleAxesScratch={forward:new d,right:new d},this.footIkScratch={footWorldPos:new d,centerRayStart:new d,toeRayStart:new d,heelRayStart:new d,sideRayStart:new d,desiredTargetPos:new d,desiredNormal:new d,desiredForward:new d,debugRayEndA:new d,debugRayEndB:new d,debugRayEndC:new d,debugRayEndD:new d,debugRayEndE:new d,debugRayEndF:new d},this.ikPreSolve={left:{upper:new f,lower:new f,foot:new f},right:{upper:new f,lower:new f,foot:new f}},this.getFullBodyClip=J(t=>t.uuid,t=>K(this.fullBodyMask,t)),this.getUpperBodyClip=J(t=>t.uuid,t=>K(this.upperBodyMask,t))}onInit(){this.viewController.onUpdate(this.actor).subscribe(t=>this.updateInternal(t)),this.characterMovement=this.actor.getComponent(S),this.footIkRayTestOptions.excludeActor=this.actor}onEndPlay(){this.disposeFootIkDebug()}raycastDownFirstValid(t,e,o){const i=this.world.scene;this.raycaster.set(t,I),this.raycaster.near=0,this.raycaster.far=e,this.raycastIntersections.length=0,this.raycaster.intersectObject(i,!0,this.raycastIntersections);for(const t of this.raycastIntersections)if(!this.isFootIkDebugObject(t.object)&&!this.isRelatedToActorHierarchy(t.object))return o.point.copy(t.point),null!=t.face?(o.normal.copy(t.face.normal).transformDirection(t.object.matrixWorld).normalize(),o.hasNormal=!0):o.hasNormal=!1,this.raycastIntersections.length=0,!0;return this.raycastIntersections.length=0,!1}rayTestDown(t,e,o){const i=this.footIkRayTo.copy(t).addScaledVector(I,e),s=this.physicsSystem.rayTest(t,i,this.footIkRayTestResult,this.footIkRayTestOptions);return!!s.hasHit&&(o.point.copy(s.hitPoint),o.normal.copy(s.hitNormal),o.normal.lengthSq()>1e-8?(o.normal.normalize(),o.hasNormal=!0):o.hasNormal=!1,!0)}isRelatedToActorHierarchy(t){const e=this.actor?.object;return null!=e&&null!=t&&(Z(t,e)||Z(e,t))}isFootIkDebugObject(t){let e=t;for(;null!=e;){if(!0===e.userData?.footIkDebug)return!0;e=e.parent}return!1}getRootMotionAction(){if(this.fullBodyAction.getClip()instanceof i)return this.fullBodyAction}getFullBodyAction(){return this.fullBodyAction}setup(t,e,o){null!=e&&(this.upperBodyMask=Y(e),this.fullBodyMask=function(t,e){const o=new Set(Y(e).map(t=>t.uuid)),i=[];return t.traverse(t=>{(t instanceof l||t.isBone)&&!o.has(t.uuid)&&i.push(t)}),i}(X(t),e)),null!=this.mixer&&this.mixer.stopAllAction(),this.mixer=new n(t),this.ikRoot=t,this.disposeFootIkDebug(),this.initializeFootIk(t,o),this.syncFootIkDebug()}updateStateMachines(t){this.stateMachines.forEach(e=>{e.step(t);const o=e.current.clip;null!=o&&this.play(o,{priority:0,loop:e.current.options.loop??!0})}),this.upperStateMachines.forEach(e=>{e.step(t);const o=e.current.clip;null!=o?this.playUpper(o,{priority:0,loop:e.current.options.loop??!0}):this.play(this.fullBodyClip)})}updateInternal(t){null!=this.mixer&&(this.externalControlActive||(this.upperBodyTimer+=t*(this.upperBodyAction?.timeScale??1),this.fullBodyTimer+=t*(this.fullBodyAction?.timeScale??1),this.upperBodyAction&&this.upperBodyOverride&&this.upperBodyAction.getClip().duration-2*(this.overrideFadeTimeUpper??this.fadeTime)<this.upperBodyTimer&&(this.upperBodyOverride=!1,null!=this.fullBodyClip&&this.transition(this.upperBodyAction,this.getUpperBodyClip(this.fullBodyClip)),this.upperBodyAction=null,this.overrideFadeTimeUpper=null),this.fullBodyAction&&this.fullBodyAction.loop===h&&this.fullBodyAction.getClip().duration-2*(this.overrideFadeTime??this.fadeTime)<this.fullBodyTimer&&(this.currentFullBodyPriority=-1,this.overrideFadeTime=null),null!=this.characterMovement&&(this.movementSpeed=this.characterMovement.horizontalSpeed),this.updateStateMachines(t),this.syncMovementSpeed(this.fullBodyAction),this.upperBodyOverride||this.syncMovementSpeed(this.upperBodyAction),this.mixer.update(t),this.updateFootIk(t)))}initializeFootIk(t,e){if(this.ikSolver=null,this.ikSkinnedMesh=null,this.ikBoneRefs=null,this.ikLegLengths=null,this.ikPelvisBone=null,this.ikPelvisOffsetY=0,this.ikFootAxes=null,this.ikTargetBones=null,this.ikWarnedMissingBones=!1,this.ikLegState.left.weight=0,this.ikLegState.right.weight=0,!this.footIkEnabled)return;const o=this.findFirstSkinnedMesh(t);if(null==o||null==o.skeleton)return;this.ikSkinnedMesh=o,t.updateWorldMatrix(!0,!0);const i=o.skeleton,s=this.resolveLegBoneRefs(i,this.leftLegIkBones,j),r=this.resolveLegBoneRefs(i,this.rightLegIkBones,G);if(null==s||null==r)return void(this.ikWarnedMissingBones||(this.ikWarnedMissingBones=!0,console.warn(`[CharacterAnimationComponent] Foot IK disabled: missing leg bones. Left=${this.leftLegIkBones.upperLeg}/${this.leftLegIkBones.lowerLeg}/${this.leftLegIkBones.foot}, Right=${this.rightLegIkBones.upperLeg}/${this.rightLegIkBones.lowerLeg}/${this.rightLegIkBones.foot}`)));this.ikBoneRefs={left:s,right:r},this.ikLegLengths={left:this.computeLegLengths(s),right:this.computeLegLengths(r)},this.ikFootAxes={left:this.detectFootAxisBasis(s.foot),right:this.detectFootAxisBasis(r.foot)},this.ikPelvisBone=this.resolvePelvisBone(i,s,r),this.ikLegState.left.targetPosition.copy(s.foot.getWorldPosition(F)),this.ikLegState.right.targetPosition.copy(r.foot.getWorldPosition(F)),this.ikLegState.left.normal.set(0,1,0),this.ikLegState.right.normal.set(0,1,0),this.ikLegState.left.pitch=0,this.ikLegState.right.pitch=0,this.ikLegState.left.contactOffset=this.estimateFootContactOffset(i,"left",s.foot),this.ikLegState.right.contactOffset=this.estimateFootContactOffset(i,"right",r.foot),this.ikLegState.left.planted=!0,this.ikLegState.right.planted=!0,this.ikLegState.left.lastFootWorldPos.copy(s.foot.getWorldPosition(F)),this.ikLegState.right.lastFootWorldPos.copy(r.foot.getWorldPosition(F)),this.ikLegState.left.hasLastFootSample=!0,this.ikLegState.right.hasLastFootSample=!0;const n=this.resolveFootAxes("left",s.foot),a=this.resolveFootAxes("right",r.foot);this.ikLegState.left.forward.copy(n.forward),this.ikLegState.right.forward.copy(a.forward);const h=e??X(t);if(null==h)return;let c=i.getBoneByName("_IKTargetLeftFoot"),d=i.getBoneByName("_IKTargetRightFoot"),p=!1;if(null==c&&(c=new l,c.name="_IKTargetLeftFoot",h.add(c),p=!0),null==d&&(d=new l,d.name="_IKTargetRightFoot",h.add(d),p=!0),this.setBoneWorldPosition(c,s.foot.getWorldPosition(F)),this.setBoneWorldPosition(d,r.foot.getWorldPosition(F)),p||i.bones.indexOf(c)<0||i.bones.indexOf(d)<0){const t=i.bones.slice(),e=i.boneInverses.map(t=>t.clone());t.indexOf(c)<0&&(t.push(c),e.push((new g).copy(c.matrixWorld).invert())),t.indexOf(d)<0&&(t.push(d),e.push((new g).copy(d.matrixWorld).invert())),o.bind(new u(t,e),o.bindMatrix)}this.ikTargetBones={left:c,right:d};const f=o.skeleton.bones,y={target:f.indexOf(c),effector:f.indexOf(s.foot),links:[{index:f.indexOf(s.lower)},{index:f.indexOf(s.upper)}],iteration:8},m={target:f.indexOf(d),effector:f.indexOf(r.foot),links:[{index:f.indexOf(r.lower)},{index:f.indexOf(r.upper)}],iteration:8};if(y.target<0||y.effector<0||y.links.some(t=>t.index<0)||m.target<0||m.effector<0||m.links.some(t=>t.index<0))return console.warn("[CharacterAnimationComponent] Foot IK disabled: failed to resolve IK indexes in skeleton."),void(this.ikSolver=null);this.ikSolver=new w(o,[y,m])}findFirstSkinnedMesh(t){let e;return t.traverse(t=>{null==e&&(t instanceof p||!0===t.isSkinnedMesh)&&(e=t)}),e}resolveLegBoneRefs(t,e,o){const i=this.findBoneByNames(t,[e.upperLeg,...o.upper]),s=this.findBoneByNames(t,[e.lowerLeg,...o.lower]),r=this.findBoneByNames(t,[e.foot,...o.foot]);if(null!=i&&null!=s&&null!=r)return{upper:i,lower:s,foot:r}}findBoneByNames(t,e){for(const o of e){const e=t.getBoneByName(o);if(null!=e)return e}}resolvePelvisBone(t,e,o){const i=this.findBoneByNames(t,[this.footIkPelvisBone,...z]);if(null!=i)return i;const s=e.upper.parent,r=o.upper.parent;return null!=s&&s===r&&(s instanceof l||!0===s.isBone)||null!=s&&(s instanceof l||!0===s.isBone)?s:void 0}computeLegLengths(t){const e=t.upper.getWorldPosition(F),o=t.lower.getWorldPosition(P),i=t.foot.getWorldPosition(R),s=e.distanceTo(o),r=o.distanceTo(i);return{upper:s,lower:r,total:s+r}}setBoneWorldPosition(t,e){const o=t.parent;null==o?t.position.copy(e):t.position.copy(o.worldToLocal(F.copy(e))),t.quaternion.copy(D),t.scale.copy(x),t.updateMatrixWorld(!0)}syncFootIkDebug(){this.footIkDebug&&null!=this.ikRoot?(null!=this.ikDebugRoot&&null!=this.ikDebugLegs||(this.ikDebugRoot=new k,this.ikDebugRoot.name="FootIKDebug",this.ikDebugRoot.userData.footIkDebug=!0,this.ikDebugLegs={left:this.createDebugLeg("LeftFootIK",53503),right:this.createDebugLeg("RightFootIK",16755200)},this.ikDebugRoot.add(this.ikDebugLegs.left.root,this.ikDebugLegs.right.root),this.world.scene.add(this.ikDebugRoot)),this.ikDebugRoot.visible=!0):null!=this.ikDebugRoot&&(this.ikDebugRoot.visible=!1)}disposeFootIkDebug(){null!=this.ikDebugRoot&&(this.ikDebugRoot.removeFromParent(),this.ikDebugRoot.clear()),this.ikDebugRoot=null,this.ikDebugLegs=null}createDebugLeg(t,e){const o=new k;o.name=t,o.userData.footIkDebug=!0;const i=this.createDebugArrow(e),s=this.createDebugArrow(e),r=this.createDebugArrow(e),n=this.createDebugArrow(e),l=this.createDebugArrow(65280),a=this.createDebugArrow(6750054),h=this.createDebugArrow(16711935),c=this.createDebugArrow(16776960),d=this.createDebugArrow(16737792);return o.add(i,s,r,n,l,a,h,c,d),{root:o,rayCenter:i,rayToe:s,rayHeel:r,raySide:n,hitNormal:l,footUp:a,footToTarget:h,solveUpper:c,solveLower:d}}createDebugArrow(t){const e=new m(B,new d,.001,t);return e.userData.footIkDebug=!0,e.traverse(t=>{t.userData.footIkDebug=!0,t.raycast=()=>{}}),e.visible=!1,e}setDebugArrow(t,e,o){F.subVectors(o,e);const i=F.length();i<1e-6?t.visible=!1:(t.visible=!0,t.position.copy(e),t.setDirection(F.multiplyScalar(1/i)),t.setLength(i,Math.min(.08,.25*i),Math.min(.05,.2*i)))}resetDebugVisibility(){null!=this.ikDebugLegs&&(this.ikDebugLegs.left.rayCenter.visible=!1,this.ikDebugLegs.left.rayToe.visible=!1,this.ikDebugLegs.left.rayHeel.visible=!1,this.ikDebugLegs.left.raySide.visible=!1,this.ikDebugLegs.left.hitNormal.visible=!1,this.ikDebugLegs.left.footUp.visible=!1,this.ikDebugLegs.left.footToTarget.visible=!1,this.ikDebugLegs.left.solveUpper.visible=!1,this.ikDebugLegs.left.solveLower.visible=!1,this.ikDebugLegs.right.rayCenter.visible=!1,this.ikDebugLegs.right.rayToe.visible=!1,this.ikDebugLegs.right.rayHeel.visible=!1,this.ikDebugLegs.right.raySide.visible=!1,this.ikDebugLegs.right.hitNormal.visible=!1,this.ikDebugLegs.right.footUp.visible=!1,this.ikDebugLegs.right.footToTarget.visible=!1,this.ikDebugLegs.right.solveUpper.visible=!1,this.ikDebugLegs.right.solveLower.visible=!1)}updateFootIk(t){if(this.syncFootIkDebug(),!this.footIkEnabled||null==this.ikBoneRefs||null==this.ikRoot)return void this.resetDebugVisibility();if(!this.footIkMoving&&(this.movementSpeed??0)>.01)return this.resetFootIkState(),void this.resetDebugVisibility();this.ikRoot.updateWorldMatrix(!0,!0);const e=this.characterMovement?.isGrounded??!0;return this.footIkSimpleTiltOnly?(this.updatePelvisOffset(t,!1),this.updateLegTiltSimple("left",this.ikBoneRefs.left.foot,this.ikLegState.left,e,t),void this.updateLegTiltSimple("right",this.ikBoneRefs.right.foot,this.ikLegState.right,e,t)):null==this.ikLegLengths||null==this.ikTargetBones||null==this.ikSkinnedMesh||null==this.ikSolver?(this.updatePelvisOffset(t,!1),void this.resetDebugVisibility()):(this.updateLegIkTarget("left",this.ikBoneRefs.left,this.ikLegLengths.left,this.ikTargetBones.left,this.ikLegState.left,e,t),this.updateLegIkTarget("right",this.ikBoneRefs.right,this.ikLegLengths.right,this.ikTargetBones.right,this.ikLegState.right,e,t),this.updatePelvisOffset(t,!0),this.setBoneWorldPosition(this.ikTargetBones.left,this.ikLegState.left.targetPosition),this.setBoneWorldPosition(this.ikTargetBones.right,this.ikLegState.right.targetPosition),this.ikPreSolve.left.upper.copy(this.ikBoneRefs.left.upper.quaternion),this.ikPreSolve.left.lower.copy(this.ikBoneRefs.left.lower.quaternion),this.ikPreSolve.left.foot.copy(this.ikBoneRefs.left.foot.quaternion),this.ikPreSolve.right.upper.copy(this.ikBoneRefs.right.upper.quaternion),this.ikPreSolve.right.lower.copy(this.ikBoneRefs.right.lower.quaternion),this.ikPreSolve.right.foot.copy(this.ikBoneRefs.right.foot.quaternion),null!=this.ikDebugLegs&&(this.ikDebugLegs.left.solveUpper.visible=!1,this.ikDebugLegs.left.solveLower.visible=!1,this.ikDebugLegs.right.solveUpper.visible=!1,this.ikDebugLegs.right.solveLower.visible=!1),this.ikSolver.update(),this.blendLegAfterSolve(this.ikBoneRefs.left,this.ikPreSolve.left,this.ikLegState.left.weight),this.blendLegAfterSolve(this.ikBoneRefs.right,this.ikPreSolve.right,this.ikLegState.right.weight),this.applySimpleFootPitch("left",this.ikBoneRefs.left.foot,this.ikLegState.left),void this.applySimpleFootPitch("right",this.ikBoneRefs.right.foot,this.ikLegState.right))}resetFootIkState(){if(null!=this.ikBoneRefs){for(const t of Q){const e=this.ikBoneRefs[t],o=this.ikLegState[t],i=e.foot.getWorldPosition(F);o.targetPosition.copy(i),o.lastFootWorldPos.copy(i),o.hasLastFootSample=!0,o.weight=0,o.normal.set(0,1,0);const s=this.resolveFootAxes(t,e.foot,this.footIkAxesScratch);o.forward.copy(s.forward),o.pitch=0,o.contactOffset=Math.max(o.contactOffset,this.footIkFootOffset+this.footIkExtraClearance),o.planted=!1}this.ikPelvisOffsetY=0}}updatePelvisOffset(t,e){if(null==this.ikPelvisBone)return;let o=0;if(e&&this.footIkPelvisEnabled&&null!=this.ikBoneRefs&&null!=this.ikLegLengths){this.ikLegState.left.top=!1,this.ikLegState.right.top=!1;for(const t of Q){const e=this.ikBoneRefs[t],i=this.ikLegLengths[t],s=this.ikLegState[t];if(!s.planted)continue;const r=e.upper.getWorldPosition(F),n=.998*i.total,l=this.computeRequiredPelvisDrop(r,s.targetPosition,n);if(l>0){const e="left"===t?"right":"left";this.ikLegState[e].top=!0}o=Math.min(o,-l)}o=y.clamp(o,-this.footIkPelvisMaxOffsetDown,this.footIkPelvisMaxOffsetUp)}const i=1-Math.exp(-this.footIkPelvisLerpSpeed*t);this.ikPelvisOffsetY=y.lerp(this.ikPelvisOffsetY,o,i);const s=this.ikPelvisBone.getWorldPosition(P),r=R.copy(s).addScaledVector(B,this.ikPelvisOffsetY);null==this.ikPelvisBone.parent?this.ikPelvisBone.position.copy(r):this.ikPelvisBone.position.copy(this.ikPelvisBone.parent.worldToLocal(r)),this.ikPelvisBone.updateMatrixWorld(!0)}computeFootVerticalSpeed(t,e,o){return!t.hasLastFootSample||o<=1e-6?0:(e.y-t.lastFootWorldPos.y)/o}updateFootSample(t,e){t.lastFootWorldPos.copy(e),t.hasLastFootSample=!0}estimateFootContactOffset(t,e,o){const i=this.footIkFootOffset+this.footIkExtraClearance,s=t.bones.indexOf(o);if(s<0)return i;const r=t.boneInverses[s];if(null==r)return i;const n=this.ikFootAxes?.[e]??this.detectFootAxisBasis(o);let a=0;return o.traverse(e=>{if(!(e instanceof l)||e===o)return;const i=t.bones.indexOf(e);if(i<0)return;const s=C.copy(t.boneInverses[i]).invert(),h=q.copy(r).multiply(s),c=F.setFromMatrixPosition(h);a=Math.min(a,c.dot(n.upLocal))}),Math.max(i,-a+this.footIkExtraClearance)}shouldPlantFoot(t,e,o,i,s,r){if(!e||!o)return!1;const n=this.footIkPlantReleaseExtension,l=this.footIkPlantAttachExtension,a=this.footIkPlantReleaseLift,h=this.footIkPlantAttachLift;return t.planted?!(i>this.footIkPlantReleaseUpSpeed&&(s<n||r>a)):s>=l&&r<=h||i<=0&&s>=n}computeLegExtensionRatio(t,e){const o=t.upper.getWorldPosition(F),i=t.foot.getWorldPosition(P),s=o.distanceTo(i);return e.total<=1e-6?1:y.clamp(s/e.total,0,1.2)}computeRequiredPelvisDrop(t,e,o){const i=A.subVectors(t,e),s=i.length();if(s<=o+1e-6)return 0;const r=i.dot(B),n=o*o-Math.max(s*s-r*r,0);if(n<=0)return s-o;const l=Math.sqrt(n),a=r-l,h=r+l;return a>=0?a:h>=0?h:0}updateLegTiltSimple(t,e,o,i,s){const r=this.footIkScratch,n=this.footIkHits,l=e.getWorldPosition(r.footWorldPos),a=this.computeFootVerticalSpeed(o,l,s),h=this.resolveFootAxes(t,e,this.footIkAxesScratch),c=this.resolveGroundSampleAxes(t,e,h,this.footIkSampleAxesScratch),d=r.centerRayStart.copy(l).addScaledVector(B,this.footIkRayStartHeight),p=r.toeRayStart.copy(d).addScaledVector(c.forward,this.footIkForwardSampleDistance),u=r.heelRayStart.copy(d).addScaledVector(c.forward,-this.footIkForwardSampleDistance),f=r.sideRayStart.copy(d).addScaledVector(c.right,this.footIkSideSampleDistance),g=this.getFirstGroundHit(d,n.center),m=this.getFirstGroundHit(p,n.toe),k=this.getFirstGroundHit(u,n.heel),S=this.getFirstGroundHit(f,n.side),w=r.desiredNormal.set(0,1,0),L=r.desiredForward.copy(c.forward);let b=0,v=0,I=this.footIkWeightOutSpeed;const x=null!=g||null!=m||null!=k;let D=l.y;null!=g?D=g.point.y:null!=m&&null!=k?D=.5*(m.point.y+k.point.y):null!=m?D=m.point.y:null!=k&&(D=k.point.y);const F=this.ikBoneRefs?.[t],P=this.ikLegLengths?.[t],R=null!=F&&null!=P?this.computeLegExtensionRatio(F,P):1,A=Math.max(0,l.y-D,l.y-o.targetPosition.y),M=this.shouldPlantFoot(o,!this.footIkRequireGrounded||i,x,a,R,A);M?this.computeDesiredGroundNormal(c,g,m,k,S,w)&&(this.computeDesiredFootForward(c.forward,c.right,w,m,k,L),b=this.computeDesiredPitchAngle(m,k,c.forward),v=1,I=this.footIkWeightInSpeed):this.computeDesiredFootForward(c.forward,c.right,w,null,null,L);const T=1-Math.exp(-this.footIkPositionLerpSpeed*s),W=1-Math.exp(-I*s);o.normal.lerp(w,T).normalize(),o.forward.lerp(L,T).normalize(),o.pitch=y.lerp(o.pitch,b,T),o.weight=y.lerp(o.weight,v,W),o.planted=M&&v>.001,this.updateFootSample(o,l),this.applySimpleFootPitch(t,e,o),this.updateLegDebug(t,{footWorldPos:l,centerRayStart:d,toeRayStart:p,heelRayStart:u,sideRayStart:f,centerHit:g,toeHit:m,heelHit:k,sideHit:S,desiredNormal:o.normal,desiredTargetPos:r.footWorldPos,weight:o.weight})}updateLegIkTarget(t,e,o,i,s,r,n){const l=this.footIkScratch,a=this.footIkHits,h=e.foot,c=h.getWorldPosition(l.footWorldPos),d=this.computeFootVerticalSpeed(s,c,n),p=this.resolveFootAxes(t,h,this.footIkAxesScratch),u=this.resolveGroundSampleAxes(t,h,p,this.footIkSampleAxesScratch),f=l.centerRayStart.copy(c).addScaledVector(B,this.footIkRayStartHeight),g=l.toeRayStart.copy(f).addScaledVector(u.forward,this.footIkForwardSampleDistance),m=l.heelRayStart.copy(f).addScaledVector(u.forward,-this.footIkForwardSampleDistance),k=l.sideRayStart.copy(f).addScaledVector(u.right,this.footIkSideSampleDistance),S=this.getFirstGroundHit(f,a.center),w=this.getFirstGroundHit(g,a.toe),L=this.getFirstGroundHit(m,a.heel),b=this.getFirstGroundHit(k,a.side),v=l.desiredTargetPos.copy(c),I=l.desiredNormal.set(0,1,0),x=l.desiredForward.copy(u.forward);let D=0,F=0,P=this.footIkWeightOutSpeed;const R=null!=S||null!=w||null!=L;let A=c.y;null!=S?A=S.point.y:null!=w&&null!=L?A=.5*(w.point.y+L.point.y):null!=w?A=w.point.y:null!=L&&(A=L.point.y);const M=this.computeLegExtensionRatio(e,o),T=Math.max(0,c.y-A,c.y-s.targetPosition.y),W=this.shouldPlantFoot(s,!this.footIkRequireGrounded||r,R,d,M,T);if(W){if(v.copy(c),this.computeDesiredGroundNormal(u,S,w,L,b,I)){this.computeDesiredFootForward(u.forward,u.right,I,w,L,x),D=this.computeDesiredPitchAngle(w,L,u.forward);const t=Math.max(0,Math.sin(D))*this.footIkForwardSampleDistance*.45,e=Math.max(this.footIkFootOffset+this.footIkExtraClearance,s.contactOffset);v.y=A+e+t,s.top&&(v.y-=this.ikPelvisOffsetY/2),F=1,P=this.footIkWeightInSpeed}}else this.computeDesiredFootForward(u.forward,u.right,I,null,null,x),v.copy(c),v.y+=this.footIkFootOffset;this.clampDesiredFootTarget(e,o,c,v);const O=1-Math.exp(-this.footIkPositionLerpSpeed*n),V=1-Math.exp(-P*n);s.targetPosition.lerp(v,O),s.normal.lerp(I,O).normalize(),s.forward.lerp(x,O).normalize(),s.pitch=y.lerp(s.pitch,D,O),s.weight=y.lerp(s.weight,F,V),s.planted=W&&F>.001,this.updateFootSample(s,c),this.setBoneWorldPosition(i,s.targetPosition),this.updateLegDebug(t,{footWorldPos:c,centerRayStart:f,toeRayStart:g,heelRayStart:m,sideRayStart:k,centerHit:S,toeHit:w,heelHit:L,sideHit:b,desiredNormal:I,desiredTargetPos:s.targetPosition,weight:s.weight})}updateLegDebug(t,e){if(!this.footIkDebug||null==this.ikDebugLegs||null==this.ikBoneRefs)return;const o=this.ikDebugLegs[t],i=this.ikBoneRefs[t],s=this.resolveFootAxes(t,i.foot,this.footIkAxesScratch),r=this.footIkScratch,n=e.centerHit?.point??r.debugRayEndA.copy(e.centerRayStart).addScaledVector(I,this.footIkRayLength),l=e.toeHit?.point??r.debugRayEndB.copy(e.toeRayStart).addScaledVector(I,this.footIkRayLength),a=e.heelHit?.point??r.debugRayEndC.copy(e.heelRayStart).addScaledVector(I,this.footIkRayLength),h=e.sideHit?.point??r.debugRayEndD.copy(e.sideRayStart).addScaledVector(I,this.footIkRayLength);this.setDebugArrow(o.rayCenter,e.centerRayStart,n),this.setDebugArrow(o.rayToe,e.toeRayStart,l),this.setDebugArrow(o.rayHeel,e.heelRayStart,a),this.setDebugArrow(o.raySide,e.sideRayStart,h);const c=e.centerHit?.point??e.toeHit?.point??e.heelHit?.point??e.sideHit?.point??e.footWorldPos;this.setDebugArrow(o.hitNormal,c,r.debugRayEndE.copy(c).addScaledVector(e.desiredNormal,this.footIkDebugNormalLength)),this.setDebugArrow(o.footUp,e.footWorldPos,r.debugRayEndF.copy(e.footWorldPos).addScaledVector(s.up,this.footIkDebugAxisLength*(.15+e.weight))),this.setDebugArrow(o.footToTarget,e.footWorldPos,e.desiredTargetPos)}getFirstGroundHit(t,e){return"physics"===this.footIkRaycastMode?this.rayTestDown(t,this.footIkRayLength,e)?e:null:"physicsThenRender"===this.footIkRaycastMode?this.rayTestDown(t,this.footIkRayLength,e)||this.raycastDownFirstValid(t,this.footIkRayLength,e)?e:null:this.raycastDownFirstValid(t,this.footIkRayLength,e)?e:null}resolveGroundSampleAxes(t,e,o,i){const s=o??this.resolveFootAxes(t,e),r=i??{forward:new d,right:new d},n=this.actor.object.getWorldDirection(A);n.addScaledVector(B,-n.dot(B)),n.lengthSq()<1e-8&&n.copy(s.forward).addScaledVector(B,-s.forward.dot(B)),n.lengthSq()<1e-8&&n.set(0,0,1),n.normalize();const l=M.crossVectors(B,n);return l.lengthSq()<1e-8?l.copy(s.right):l.normalize(),l.dot(s.right)<0&&l.multiplyScalar(-1),r.forward.copy(n),r.right.copy(l),r}computeDesiredGroundNormal(t,e,o,i,s,r){const n=this.computeAverageHitNormal(e,o,i,s,A);let l=!1,a=0,h=0;if(null!=o&&null!=i){const e=P.subVectors(o.point,i.point).dot(t.forward);Math.abs(e)>1e-5&&(a=(o.point.y-i.point.y)/e,l=!0)}if(null!=s&&null!=e){const o=R.subVectors(s.point,e.point).dot(t.right);Math.abs(o)>1e-5&&(h=(s.point.y-e.point.y)/o,l=!0)}if(l&&(r.copy(B),r.addScaledVector(t.forward,-a),r.addScaledVector(t.right,-h),r.lengthSq()>1e-8?r.normalize():l=!1),l)null!=n&&r.dot(n)<0&&r.multiplyScalar(-1);else{if(null==n)return!1;r.copy(n),l=!0}return r.dot(B)<0&&r.multiplyScalar(-1),this.clampSlopeNormal(r),!0}computeAverageHitNormal(t,e,o,i,s){s.set(0,0,0);let r=0;return null!=t&&t.hasNormal&&(s.add(t.normal),r++),null!=e&&e.hasNormal&&(s.add(e.normal),r++),null!=o&&o.hasNormal&&(s.add(o.normal),r++),null!=i&&i.hasNormal&&(s.add(i.normal),r++),0===r||s.lengthSq()<1e-8?null:(s.multiplyScalar(1/r).normalize(),s)}computeDesiredFootForward(t,e,o,i,s,r){null!=i&&null!=s?r.subVectors(i.point,s.point):r.copy(t),r.addScaledVector(o,-r.dot(o)),r.lengthSq()<1e-8&&(r.copy(this.actor.object.getWorldDirection(A)),r.addScaledVector(o,-r.dot(o))),r.lengthSq()<1e-8&&r.crossVectors(o,e),r.lengthSq()<1e-8&&r.set(0,0,1),r.normalize();const n=A.copy(this.actor.object.getWorldDirection(A));n.addScaledVector(o,-n.dot(o)),n.lengthSq()<1e-8&&(n.copy(t),n.addScaledVector(o,-n.dot(o))),n.lengthSq()>1e-8&&n.normalize(),r.dot(n)<0&&r.multiplyScalar(-1)}computeDesiredPitchAngle(t,e,o){if(null==t||null==e)return 0;const i=P.subVectors(t.point,e.point),s=Math.abs(i.dot(o));if(s<1e-5)return 0;const r=t.point.y-e.point.y,n=y.degToRad(this.footIkMaxSlopeAngleDeg);return y.clamp(-Math.atan2(r,s),-n,n)}applySimpleFootPitch(t,e,o){if(null==e.parent||o.weight<=.001)return;const i=this.ikFootAxes?.[t]??this.detectFootAxisBasis(e),s=o.pitch*o.weight;if(Math.abs(s)<=1e-5)return;const r=this.resolveLocalPitchSign(i),n=T.copy(e.quaternion),l=W.setFromAxisAngle(i.rightLocal,s*r);e.quaternion.copy(n.multiply(l))}resolveLocalPitchSign(t){const e=O.setFromAxisAngle(t.rightLocal,.15);return R.copy(t.upLocal).applyQuaternion(e).sub(t.upLocal).dot(t.forwardLocal)>=0?1:-1}clampDesiredFootTarget(t,e,o,i){const s=P.subVectors(i,o),r=s.dot(B),n=R.copy(s).addScaledVector(B,-r),l=n.length();l>this.footIkMaxHorizontalOffset&&l>1e-6&&n.multiplyScalar(this.footIkMaxHorizontalOffset/l);const a=y.clamp(r,-this.footIkMaxVerticalOffsetDown,this.footIkMaxVerticalOffsetUp);if(i.copy(o),i.add(n),i.addScaledVector(B,a),this.footIkPelvisEnabled)return;const h=t.upper.getWorldPosition(F).clone(),c=P.subVectors(i,h),d=c.length(),p=.995*e.total;d>p&&d>1e-6&&i.copy(h).addScaledVector(c,p/d)}solveTwoBoneLeg(t,e,o){const i=e.upper.getWorldPosition(F).clone(),s=e.lower.getWorldPosition(P).clone(),r=e.foot.getWorldPosition(R).clone(),n=i.distanceTo(s),l=s.distanceTo(r);if(n<1e-5||l<1e-5)return;const a=(new d).subVectors(o,i),h=a.length();if(h<1e-5)return;const c=Math.abs(n-l)+1e-4,p=n+l-1e-4,u=y.clamp(h,c,p),f=a.multiplyScalar(1/h),g=(new d).subVectors(s,i).normalize(),m=(new d).subVectors(r,s).normalize(),k=(new d).crossVectors(g,m);k.lengthSq()<1e-8&&(k.copy(this.actor.object.getWorldDirection(F).cross(B)),k.lengthSq()<1e-8&&k.set(1,0,0)),k.normalize();const S=(u*u+n*n-l*l)/(2*u),w=Math.max(n*n-S*S,0),L=Math.sqrt(w),b=(new d).copy(i).addScaledVector(f,S);let v=(new d).crossVectors(k,f);v.lengthSq()<1e-8&&(v=(new d).crossVectors(f,B),v.lengthSq()<1e-8&&(v=new d(1,0,0))),v.normalize();const I=(new d).copy(b).addScaledVector(v,L),x=(new d).copy(b).addScaledVector(v,-L),D=(new d).subVectors(s,b).dot(v)>=0?I:x;this.rotateBoneToward(e.upper,i,s,D),e.upper.updateMatrixWorld(!0);const A=e.lower.getWorldPosition(P),M=e.foot.getWorldPosition(R);this.rotateBoneToward(e.lower,A,M,o),e.lower.updateMatrixWorld(!0),this.updateSolveDebug(t,i,D,o)}updateSolveDebug(t,e,o,i){if(!this.footIkDebug||null==this.ikDebugLegs)return;const s=this.ikDebugLegs[t];this.setDebugArrow(s.solveUpper,e,o),this.setDebugArrow(s.solveLower,o,i)}rotateBoneToward(t,e,o,i){const s=(new d).subVectors(o,e),r=(new d).subVectors(i,e);if(s.lengthSq()<1e-10||r.lengthSq()<1e-10)return;if(s.normalize(),r.normalize(),s.dot(r)>.9999)return;const n=(new f).setFromUnitVectors(s,r),l=t.getWorldQuaternion(T),a=W.copy(n).multiply(l);if(null==t.parent)return void t.quaternion.copy(a);const h=t.parent.getWorldQuaternion(O),c=V.copy(h).invert().multiply(a);t.quaternion.copy(c)}detectFootAxisBasis(t){const e=t.getWorldQuaternion(T),o=this.actor.object.getWorldDirection(F);o.lengthSq()<1e-6&&o.set(0,0,1),o.normalize();const i=[{local:H.clone(),world:H.clone().applyQuaternion(e)},{local:E.clone(),world:E.clone().applyQuaternion(e)},{local:N.clone(),world:N.clone().applyQuaternion(e)}];i.push({local:H.clone().multiplyScalar(-1),world:H.clone().multiplyScalar(-1).applyQuaternion(e)}),i.push({local:E.clone().multiplyScalar(-1),world:E.clone().multiplyScalar(-1).applyQuaternion(e)}),i.push({local:N.clone().multiplyScalar(-1),world:N.clone().multiplyScalar(-1).applyQuaternion(e)});let s=i[0],r=-1/0;for(const t of i){const e=t.world.dot(B);e>r&&(r=e,s=t)}let n=i[0],l=-1/0;for(const t of i){if(Math.abs(t.world.dot(s.world))>.6)continue;const e=Math.abs(t.world.dot(o));e>l&&(l=e,n=t)}n.world.dot(o)<0&&(n={local:n.local.clone().multiplyScalar(-1),world:n.world.clone().multiplyScalar(-1)});const a=s.local.clone().normalize(),h=n.local.clone().normalize(),c=a.clone().cross(h).normalize();return{upLocal:a,forwardLocal:c.clone().cross(a).normalize(),rightLocal:c}}resolveFootAxes(t,e,o){const i=this.ikFootAxes?.[t]??this.detectFootAxisBasis(e),s=o??{up:new d,forward:new d,right:new d},r=e.getWorldQuaternion(T);return s.up.copy(i.upLocal).applyQuaternion(r).normalize(),s.forward.copy(i.forwardLocal).applyQuaternion(r).normalize(),s.right.copy(i.rightLocal).applyQuaternion(r).normalize(),s}clampSlopeNormal(t){const e=y.clamp(t.dot(B),-1,1),o=Math.acos(e),i=y.degToRad(this.footIkMaxSlopeAngleDeg);if(o<=i||o<1e-5)return;const s=i/o;t.lerpVectors(B,t,s).normalize()}blendLegAfterSolve(t,e,o){if(!(o>=.999)){if(o<=.001)return t.upper.quaternion.copy(e.upper),t.lower.quaternion.copy(e.lower),void t.foot.quaternion.copy(e.foot);this.blendBoneQuaternion(t.upper,e.upper,o),this.blendBoneQuaternion(t.lower,e.lower,o),this.blendBoneQuaternion(t.foot,e.foot,o)}}blendBoneQuaternion(t,e,o){T.copy(t.quaternion),t.quaternion.copy(e).slerp(T,o)}alignFootToGroundNormal(t,e,o,i){if(o.weight<=.001||null==e.parent)return;const s=this.ikFootAxes?.[t]??this.detectFootAxisBasis(e),r=F.copy(o.normal).normalize(),n=P.copy(o.forward);if(n.addScaledVector(r,-n.dot(r)),n.lengthSq()<1e-8){const o=this.resolveFootAxes(t,e);n.copy(o.forward).addScaledVector(r,-o.forward.dot(r))}if(n.lengthSq()<1e-8)return;n.normalize();const l=R.crossVectors(r,n);if(l.lengthSq()<1e-8)return;l.normalize(),n.copy(A.crossVectors(l,r)).normalize(),C.makeBasis(s.rightLocal,s.upLocal,s.forwardLocal),T.setFromRotationMatrix(C),q.makeBasis(l,r,n),W.setFromRotationMatrix(q);const a=O.copy(W).multiply(V.copy(T).invert()),h=e.parent.getWorldQuaternion(V),c=W.copy(h).invert().multiply(a),d=(1-Math.exp(-this.footIkRotationLerpSpeed*i))*o.weight;e.quaternion.slerp(c,y.clamp(d,0,1))}getMixer(){return this.mixer}beginExternalControl(){this.externalControlActive=!0}endExternalControl(){this.externalControlActive=!1}stopSequenceAnimation(){this.currentFullBodyPriority=-1,this.externalControlActive=!1,this.fullBodyTimer=0}beginExternalAnimationControl(t,e={}){$(null!=this.mixer,"Can't begin external control before setup is called"),this.mixer.stopAllAction(),this.currentFullBodyPriority=99,this.fullBodyTimer=0;const o=this.mixer.clipAction(t);return o.setLoop(h,1),o.clampWhenFinished=!0,o.timeScale=e.timeScale??1,o.reset(),o.play(),this.fullBodyAction=o,this.externalControlActive=!0,o}syncMovementSpeed(t){if(null!=t){const e=t.getClip();if(e instanceof i&&e.fixedInPlace&&null!=this.movementSpeed){t.timeScale=e.duration/e.displacement*this.movementSpeed;const o=this.mixer.getRoot();o instanceof a&&(t.timeScale/=o.scale.x)}}}playStateMachine(t){this.stateMachines.push(t)}playUpperStateMachine(t){this.upperStateMachines.push(t)}removeStateMachine(t){const e=this.stateMachines.indexOf(t);e>=0&&this.stateMachines.splice(e,1)}removeUpperStateMachine(t){const e=this.upperStateMachines.indexOf(t);e>=0&&this.upperStateMachines.splice(e,1)}playUpper(t,e={}){const o=e?.priority??1;o<this.currentUpperBodyPriority||(this.currentUpperBodyPriority=o,this.upperBodyAction=this.transition(this.upperBodyAction,this.getUpperBodyClip(t),e),this.upperBodyAction.timeScale=e?.timeScale??1,this.upperBodyTimer=0,this.upperBodyOverride=!0,this.overrideFadeTimeUpper=e.fadeTime)}play(t,e={}){$(null!=this.mixer,"Can't play animation before setup is called");const o=e.priority??1;o<this.currentFullBodyPriority||(this.currentFullBodyPriority=o,this.fullBodyTimer=0,this.upperBodyOverride||(this.upperBodyAction=this.transition(this.upperBodyAction,this.getUpperBodyClip(t),e),this.upperBodyAction.timeScale=e?.timeScale??1),this.fullBodyClip=t,this.fullBodyAction=this.transition(this.fullBodyAction,this.getFullBodyClip(t),e),this.fullBodyAction.timeScale=e?.timeScale??1,this.fullBodyAction.getClip().uuid==this.upperBodyAction.getClip().uuid&&this.upperBodyAction.syncWith(this.fullBodyAction),this.overrideFadeTime=e.fadeTime)}onActionDone(t){return new Promise(e=>{const o=i=>{i.action===t&&(e(i.action),this.mixer.removeEventListener("finished",o))};this.mixer.addEventListener("finished",o)})}transition(t,e,o={}){if(null!=t&&t.getClip().uuid===e.uuid)return t;const i=o?.fadeTime??this.fadeTime;if(t){const s=t,r=this.mixer.clipAction(e);r.reset(),null!=o.offset&&(r.time=o.offset),r.play(),r.enabled=!0,r.setEffectiveTimeScale(1),r.weight=1,s.crossFadeTo(r,i,!0),t=r}else(t=this.mixer.clipAction(e)).reset(),null!=o.offset&&(t.time=o.offset),t.fadeIn(i),t.play();return!1===o.loop&&(t.setLoop(h,1),t.clampWhenFinished=!0),t}};_=t([o({inEditor:!0})],_);export{_ as CharacterAnimationComponent};function K(t,e){if(null==t)return e;const o=e.clone(),i=new Set(t.map(t=>t.name));return o.tracks=o.tracks.filter(t=>i.has(t.name.split(".")[0])),o}function Y(t){return t.flatMap(t=>function(t){const e=[];return t.traverse(t=>{e.push(t)}),e}(t)).filter(t=>t instanceof l)}function $(t,e){if(!1===t||"function"==typeof t&&!1===t())throw new Error(e)}function J(t,e){const o=new Map;return(i,...s)=>{const r=t(i);return o.has(r)||o.set(r,e(i,...s)),o.get(r)}}function X(t){let e;return t.traverse(t=>{(t instanceof l||t.isBone)&&null==e&&(e=t)}),e}function Z(t,e){let o=e;for(;null!=o;){if(o===t)return!0;o=o.parent}return!1}/*
|
|
1
|
+
import{__decorate as t}from"tslib";import{ActorComponent as e,Component as o}from"../../../component.js";import{RootMotionClip as i}from"../../../../animation/root-motion.js";import{inject as s}from"../../../../inject.js";import{ViewController as r}from"../../../../services/render.js";import{AnimationMixer as n,Bone as l,Object3D as a,LoopOnce as h,Raycaster as c,Vector3 as d,SkinnedMesh as p,Skeleton as u,Quaternion as f,Matrix4 as g,MathUtils as y,ArrowHelper as m,Group as k}from"three";import{CharacterMovementComponent as S}from"./character-movement.js";import{CCDIKSolver as w}from"three/addons/animation/CCDIKSolver.js";import{World as b}from"../../../../services/world.js";import{PhysicsSystem as L,RayTestResult as v}from"../../../../services/physics/physics-system.js";const I=new d(0,-1,0),B=new d(0,1,0),x=new d(1,1,1),D=new f,F=new d,P=new d,R=new d,A=new d,M=new d,T=new f,W=new f,O=new f,V=new f,C=new g,q=new g,H=new d(1,0,0),E=new d(0,1,0),N=new d(0,0,1);function U(){return{point:new d,normal:new d,hasNormal:!1}}const z=["mixamorigHips","Hips","hips","Pelvis","pelvis"],j={upper:["LeftUpLeg","leftUpLeg","left_up_leg"],lower:["LeftLeg","leftLeg","left_leg"],foot:["LeftFoot","leftFoot","left_foot"]},G={upper:["RightUpLeg","rightUpLeg","right_up_leg"],lower:["RightLeg","rightLeg","right_leg"],foot:["RightFoot","rightFoot","right_foot"]},Q=["left","right"];let K=class extends e{constructor(){super(...arguments),this.viewController=s(r),this.stateMachines=[],this.upperStateMachines=[],this.fadeTime=.2,this.movementSpeed=null,this.upperBodyTimer=0,this.upperBodyOverride=!1,this.fullBodyTimer=0,this.currentFullBodyPriority=-1,this.currentUpperBodyPriority=-1,this.externalControlActive=!1,this.world=s(b),this.physicsSystem=s(L),this.footIkEnabled=!1,this.footIkMoving=!1,this.footIkRayStartHeight=.5,this.footIkRayLength=1.5,this.footIkRaycastMode="physics",this.footIkFootOffset=.08,this.footIkPositionLerpSpeed=28,this.footIkRotationLerpSpeed=14,this.footIkWeightInSpeed=34,this.footIkWeightOutSpeed=30,this.footIkMaxSlopeAngleDeg=40,this.footIkForwardSampleDistance=.14,this.footIkSideSampleDistance=.08,this.footIkMaxHorizontalOffset=.22,this.footIkMaxVerticalOffsetDown=.75,this.footIkMaxVerticalOffsetUp=.2,this.footIkPelvisEnabled=!0,this.footIkPelvisBone="mixamorigHips",this.footIkPelvisLerpSpeed=24,this.footIkPelvisMaxOffsetDown=.4,this.footIkPelvisMaxOffsetUp=.08,this.footIkRequireGrounded=!0,this.footIkSimpleTiltOnly=!1,this.footIkDebug=!1,this.footIkDebugNormalLength=.25,this.footIkDebugAxisLength=.2,this.footIkExtraClearance=.01,this.footIkPlantReleaseUpSpeed=.08,this.footIkPlantReleaseExtension=.9,this.footIkPlantAttachExtension=.96,this.footIkPlantReleaseLift=.055,this.footIkPlantAttachLift=.02,this.leftLegIkBones={upperLeg:"mixamorigLeftUpLeg",lowerLeg:"mixamorigLeftLeg",foot:"mixamorigLeftFoot"},this.rightLegIkBones={upperLeg:"mixamorigRightUpLeg",lowerLeg:"mixamorigRightLeg",foot:"mixamorigRightFoot"},this.ikPelvisOffsetY=0,this.ikLegState={left:{targetPosition:new d,normal:new d(0,1,0),forward:new d(0,0,1),pitch:0,contactOffset:0,planted:!0,lastFootWorldPos:new d,hasLastFootSample:!1,weight:0,top:!1},right:{targetPosition:new d,normal:new d(0,1,0),forward:new d(0,0,1),pitch:0,contactOffset:0,planted:!0,lastFootWorldPos:new d,hasLastFootSample:!1,weight:0,top:!1}},this.ikWarnedMissingBones=!1,this.raycaster=new c,this.raycastIntersections=[],this.footIkRayTo=new d,this.footIkRayTestResult=new v,this.footIkRayTestOptions={excludeTriggers:!0,resolveActor:!1,collisionFilter:-2},this.footIkHits={center:U(),toe:U(),heel:U(),side:U()},this.footIkAxesScratch={up:new d,forward:new d,right:new d},this.footIkSampleAxesScratch={forward:new d,right:new d},this.footIkScratch={footWorldPos:new d,centerRayStart:new d,toeRayStart:new d,heelRayStart:new d,sideRayStart:new d,desiredTargetPos:new d,desiredNormal:new d,desiredForward:new d,debugRayEndA:new d,debugRayEndB:new d,debugRayEndC:new d,debugRayEndD:new d,debugRayEndE:new d,debugRayEndF:new d},this.ikPreSolve={left:{upper:new f,lower:new f,foot:new f},right:{upper:new f,lower:new f,foot:new f}},this.getFullBodyClip=J(t=>t.uuid,t=>_(this.fullBodyMask,t)),this.getUpperBodyClip=J(t=>t.uuid,t=>_(this.upperBodyMask,t))}onInit(){this.viewController.onUpdate(this.actor).subscribe(t=>this.updateInternal(t)),this.characterMovement=this.actor.getComponent(S),this.footIkRayTestOptions.excludeActor=this.actor}onEndPlay(){this.disposeFootIkDebug()}raycastDownFirstValid(t,e,o){const i=this.world.scene;this.raycaster.set(t,I),this.raycaster.near=0,this.raycaster.far=e,this.raycastIntersections.length=0,this.raycaster.intersectObject(i,!0,this.raycastIntersections);for(const t of this.raycastIntersections)if(!(this.isFootIkDebugObject(t.object)||this.isRelatedToActorHierarchy(t.object)||this.isSkinnedMeshObject(t.object)))return console.log(`Foot IK raycast hit: ${t.object.name}, distance=${t.distance.toFixed(2)}m`,t),o.point.copy(t.point),null!=t.face?(o.normal.copy(t.face.normal).transformDirection(t.object.matrixWorld).normalize(),o.hasNormal=!0):o.hasNormal=!1,this.raycastIntersections.length=0,!0;return this.raycastIntersections.length=0,!1}rayTestDown(t,e,o){const i=this.footIkRayTo.copy(t).addScaledVector(I,e),s=this.physicsSystem.rayTest(t,i,this.footIkRayTestResult,this.footIkRayTestOptions);return!!s.hasHit&&(o.point.copy(s.hitPoint),o.normal.copy(s.hitNormal),o.normal.lengthSq()>1e-8?(o.normal.normalize(),o.hasNormal=!0):o.hasNormal=!1,!0)}isRelatedToActorHierarchy(t){const e=this.actor?.object;return null!=e&&null!=t&&(Z(t,e)||Z(e,t))}isFootIkDebugObject(t){let e=t;for(;null!=e;){if(!0===e.userData?.footIkDebug)return!0;e=e.parent}return!1}isSkinnedMeshObject(t){let e=t;for(;null!=e;){if(e instanceof p||!0===e.isSkinnedMesh)return!0;e=e.parent}return!1}getRootMotionAction(){if(this.fullBodyAction.getClip()instanceof i)return this.fullBodyAction}getFullBodyAction(){return this.fullBodyAction}setup(t,e,o){null!=e&&(this.upperBodyMask=$(e),this.fullBodyMask=function(t,e){const o=new Set($(e).map(t=>t.uuid)),i=[];return t.traverse(t=>{(t instanceof l||t.isBone)&&!o.has(t.uuid)&&i.push(t)}),i}(X(t),e)),null!=this.mixer&&this.mixer.stopAllAction(),this.mixer=new n(t),this.ikRoot=t,this.disposeFootIkDebug(),this.initializeFootIk(t,o),this.syncFootIkDebug()}updateStateMachines(t){this.stateMachines.forEach(e=>{e.step(t);const o=e.current.clip;null!=o&&this.play(o,{priority:0,loop:e.current.options.loop??!0})}),this.upperStateMachines.forEach(e=>{e.step(t);const o=e.current.clip;null!=o?this.playUpper(o,{priority:0,loop:e.current.options.loop??!0}):this.play(this.fullBodyClip)})}updateInternal(t){null!=this.mixer&&(this.externalControlActive||(this.upperBodyTimer+=t*(this.upperBodyAction?.timeScale??1),this.fullBodyTimer+=t*(this.fullBodyAction?.timeScale??1),this.upperBodyAction&&this.upperBodyOverride&&this.upperBodyAction.getClip().duration-2*(this.overrideFadeTimeUpper??this.fadeTime)<this.upperBodyTimer&&(this.upperBodyOverride=!1,null!=this.fullBodyClip&&this.transition(this.upperBodyAction,this.getUpperBodyClip(this.fullBodyClip)),this.upperBodyAction=null,this.overrideFadeTimeUpper=null),this.fullBodyAction&&this.fullBodyAction.loop===h&&this.fullBodyAction.getClip().duration-2*(this.overrideFadeTime??this.fadeTime)<this.fullBodyTimer&&(this.currentFullBodyPriority=-1,this.overrideFadeTime=null),null!=this.characterMovement&&(this.movementSpeed=this.characterMovement.horizontalSpeed),this.updateStateMachines(t),this.syncMovementSpeed(this.fullBodyAction),this.upperBodyOverride||this.syncMovementSpeed(this.upperBodyAction),this.mixer.update(t),this.updateFootIk(t)))}initializeFootIk(t,e){if(this.ikSolver=null,this.ikSkinnedMesh=null,this.ikBoneRefs=null,this.ikLegLengths=null,this.ikPelvisBone=null,this.ikPelvisOffsetY=0,this.ikFootAxes=null,this.ikTargetBones=null,this.ikWarnedMissingBones=!1,this.ikLegState.left.weight=0,this.ikLegState.right.weight=0,!this.footIkEnabled)return;const o=this.findFirstSkinnedMesh(t);if(null==o||null==o.skeleton)return;this.ikSkinnedMesh=o,t.updateWorldMatrix(!0,!0);const i=o.skeleton,s=this.resolveLegBoneRefs(i,this.leftLegIkBones,j),r=this.resolveLegBoneRefs(i,this.rightLegIkBones,G);if(null==s||null==r)return void(this.ikWarnedMissingBones||(this.ikWarnedMissingBones=!0,console.warn(`[CharacterAnimationComponent] Foot IK disabled: missing leg bones. Left=${this.leftLegIkBones.upperLeg}/${this.leftLegIkBones.lowerLeg}/${this.leftLegIkBones.foot}, Right=${this.rightLegIkBones.upperLeg}/${this.rightLegIkBones.lowerLeg}/${this.rightLegIkBones.foot}`)));this.ikBoneRefs={left:s,right:r},this.ikLegLengths={left:this.computeLegLengths(s),right:this.computeLegLengths(r)},this.ikFootAxes={left:this.detectFootAxisBasis(s.foot),right:this.detectFootAxisBasis(r.foot)},this.ikPelvisBone=this.resolvePelvisBone(i,s,r),this.ikLegState.left.targetPosition.copy(s.foot.getWorldPosition(F)),this.ikLegState.right.targetPosition.copy(r.foot.getWorldPosition(F)),this.ikLegState.left.normal.set(0,1,0),this.ikLegState.right.normal.set(0,1,0),this.ikLegState.left.pitch=0,this.ikLegState.right.pitch=0,this.ikLegState.left.contactOffset=this.estimateFootContactOffset(i,"left",s.foot),this.ikLegState.right.contactOffset=this.estimateFootContactOffset(i,"right",r.foot),this.ikLegState.left.planted=!0,this.ikLegState.right.planted=!0,this.ikLegState.left.lastFootWorldPos.copy(s.foot.getWorldPosition(F)),this.ikLegState.right.lastFootWorldPos.copy(r.foot.getWorldPosition(F)),this.ikLegState.left.hasLastFootSample=!0,this.ikLegState.right.hasLastFootSample=!0;const n=this.resolveFootAxes("left",s.foot),a=this.resolveFootAxes("right",r.foot);this.ikLegState.left.forward.copy(n.forward),this.ikLegState.right.forward.copy(a.forward);const h=e??X(t);if(null==h)return;let c=i.getBoneByName("_IKTargetLeftFoot"),d=i.getBoneByName("_IKTargetRightFoot"),p=!1;if(null==c&&(c=new l,c.name="_IKTargetLeftFoot",h.add(c),p=!0),null==d&&(d=new l,d.name="_IKTargetRightFoot",h.add(d),p=!0),this.setBoneWorldPosition(c,s.foot.getWorldPosition(F)),this.setBoneWorldPosition(d,r.foot.getWorldPosition(F)),p||i.bones.indexOf(c)<0||i.bones.indexOf(d)<0){const t=i.bones.slice(),e=i.boneInverses.map(t=>t.clone());t.indexOf(c)<0&&(t.push(c),e.push((new g).copy(c.matrixWorld).invert())),t.indexOf(d)<0&&(t.push(d),e.push((new g).copy(d.matrixWorld).invert())),o.bind(new u(t,e),o.bindMatrix)}this.ikTargetBones={left:c,right:d};const f=o.skeleton.bones,y={target:f.indexOf(c),effector:f.indexOf(s.foot),links:[{index:f.indexOf(s.lower)},{index:f.indexOf(s.upper)}],iteration:8},m={target:f.indexOf(d),effector:f.indexOf(r.foot),links:[{index:f.indexOf(r.lower)},{index:f.indexOf(r.upper)}],iteration:8};if(y.target<0||y.effector<0||y.links.some(t=>t.index<0)||m.target<0||m.effector<0||m.links.some(t=>t.index<0))return console.warn("[CharacterAnimationComponent] Foot IK disabled: failed to resolve IK indexes in skeleton."),void(this.ikSolver=null);this.ikSolver=new w(o,[y,m])}findFirstSkinnedMesh(t){let e;return t.traverse(t=>{null==e&&(t instanceof p||!0===t.isSkinnedMesh)&&(e=t)}),e}resolveLegBoneRefs(t,e,o){const i=this.findBoneByNames(t,[e.upperLeg,...o.upper]),s=this.findBoneByNames(t,[e.lowerLeg,...o.lower]),r=this.findBoneByNames(t,[e.foot,...o.foot]);if(null!=i&&null!=s&&null!=r)return{upper:i,lower:s,foot:r}}findBoneByNames(t,e){for(const o of e){const e=t.getBoneByName(o);if(null!=e)return e}}resolvePelvisBone(t,e,o){const i=this.findBoneByNames(t,[this.footIkPelvisBone,...z]);if(null!=i)return i;const s=e.upper.parent,r=o.upper.parent;return null!=s&&s===r&&(s instanceof l||!0===s.isBone)||null!=s&&(s instanceof l||!0===s.isBone)?s:void 0}computeLegLengths(t){const e=t.upper.getWorldPosition(F),o=t.lower.getWorldPosition(P),i=t.foot.getWorldPosition(R),s=e.distanceTo(o),r=o.distanceTo(i);return{upper:s,lower:r,total:s+r}}setBoneWorldPosition(t,e){const o=t.parent;null==o?t.position.copy(e):t.position.copy(o.worldToLocal(F.copy(e))),t.quaternion.copy(D),t.scale.copy(x),t.updateMatrixWorld(!0)}syncFootIkDebug(){this.footIkDebug&&null!=this.ikRoot?(null!=this.ikDebugRoot&&null!=this.ikDebugLegs||(this.ikDebugRoot=new k,this.ikDebugRoot.name="FootIKDebug",this.ikDebugRoot.userData.footIkDebug=!0,this.ikDebugLegs={left:this.createDebugLeg("LeftFootIK",53503),right:this.createDebugLeg("RightFootIK",16755200)},this.ikDebugRoot.add(this.ikDebugLegs.left.root,this.ikDebugLegs.right.root),this.world.scene.add(this.ikDebugRoot)),this.ikDebugRoot.visible=!0):null!=this.ikDebugRoot&&(this.ikDebugRoot.visible=!1)}disposeFootIkDebug(){null!=this.ikDebugRoot&&(this.ikDebugRoot.removeFromParent(),this.ikDebugRoot.clear()),this.ikDebugRoot=null,this.ikDebugLegs=null}createDebugLeg(t,e){const o=new k;o.name=t,o.userData.footIkDebug=!0;const i=this.createDebugArrow(e),s=this.createDebugArrow(e),r=this.createDebugArrow(e),n=this.createDebugArrow(e),l=this.createDebugArrow(65280),a=this.createDebugArrow(6750054),h=this.createDebugArrow(16711935),c=this.createDebugArrow(16776960),d=this.createDebugArrow(16737792);return o.add(i,s,r,n,l,a,h,c,d),{root:o,rayCenter:i,rayToe:s,rayHeel:r,raySide:n,hitNormal:l,footUp:a,footToTarget:h,solveUpper:c,solveLower:d}}createDebugArrow(t){const e=new m(B,new d,.001,t);return e.userData.footIkDebug=!0,e.traverse(t=>{t.userData.footIkDebug=!0,t.raycast=()=>{}}),e.visible=!1,e}setDebugArrow(t,e,o){F.subVectors(o,e);const i=F.length();i<1e-6?t.visible=!1:(t.visible=!0,t.position.copy(e),t.setDirection(F.multiplyScalar(1/i)),t.setLength(i,Math.min(.08,.25*i),Math.min(.05,.2*i)))}resetDebugVisibility(){null!=this.ikDebugLegs&&(this.ikDebugLegs.left.rayCenter.visible=!1,this.ikDebugLegs.left.rayToe.visible=!1,this.ikDebugLegs.left.rayHeel.visible=!1,this.ikDebugLegs.left.raySide.visible=!1,this.ikDebugLegs.left.hitNormal.visible=!1,this.ikDebugLegs.left.footUp.visible=!1,this.ikDebugLegs.left.footToTarget.visible=!1,this.ikDebugLegs.left.solveUpper.visible=!1,this.ikDebugLegs.left.solveLower.visible=!1,this.ikDebugLegs.right.rayCenter.visible=!1,this.ikDebugLegs.right.rayToe.visible=!1,this.ikDebugLegs.right.rayHeel.visible=!1,this.ikDebugLegs.right.raySide.visible=!1,this.ikDebugLegs.right.hitNormal.visible=!1,this.ikDebugLegs.right.footUp.visible=!1,this.ikDebugLegs.right.footToTarget.visible=!1,this.ikDebugLegs.right.solveUpper.visible=!1,this.ikDebugLegs.right.solveLower.visible=!1)}updateFootIk(t){if(this.syncFootIkDebug(),!this.footIkEnabled||null==this.ikBoneRefs||null==this.ikRoot)return void this.resetDebugVisibility();if(!this.footIkMoving&&(this.movementSpeed??0)>.01)return this.resetFootIkState(),void this.resetDebugVisibility();this.ikRoot.updateWorldMatrix(!0,!0);const e=this.characterMovement?.isGrounded??!0;return this.footIkSimpleTiltOnly?(this.updatePelvisOffset(t,!1),this.updateLegTiltSimple("left",this.ikBoneRefs.left.foot,this.ikLegState.left,e,t),void this.updateLegTiltSimple("right",this.ikBoneRefs.right.foot,this.ikLegState.right,e,t)):null==this.ikLegLengths||null==this.ikTargetBones||null==this.ikSkinnedMesh||null==this.ikSolver?(this.updatePelvisOffset(t,!1),void this.resetDebugVisibility()):(this.updateLegIkTarget("left",this.ikBoneRefs.left,this.ikLegLengths.left,this.ikTargetBones.left,this.ikLegState.left,e,t),this.updateLegIkTarget("right",this.ikBoneRefs.right,this.ikLegLengths.right,this.ikTargetBones.right,this.ikLegState.right,e,t),this.updatePelvisOffset(t,!0),this.setBoneWorldPosition(this.ikTargetBones.left,this.ikLegState.left.targetPosition),this.setBoneWorldPosition(this.ikTargetBones.right,this.ikLegState.right.targetPosition),this.ikPreSolve.left.upper.copy(this.ikBoneRefs.left.upper.quaternion),this.ikPreSolve.left.lower.copy(this.ikBoneRefs.left.lower.quaternion),this.ikPreSolve.left.foot.copy(this.ikBoneRefs.left.foot.quaternion),this.ikPreSolve.right.upper.copy(this.ikBoneRefs.right.upper.quaternion),this.ikPreSolve.right.lower.copy(this.ikBoneRefs.right.lower.quaternion),this.ikPreSolve.right.foot.copy(this.ikBoneRefs.right.foot.quaternion),null!=this.ikDebugLegs&&(this.ikDebugLegs.left.solveUpper.visible=!1,this.ikDebugLegs.left.solveLower.visible=!1,this.ikDebugLegs.right.solveUpper.visible=!1,this.ikDebugLegs.right.solveLower.visible=!1),this.ikSolver.update(),this.blendLegAfterSolve(this.ikBoneRefs.left,this.ikPreSolve.left,this.ikLegState.left.weight),this.blendLegAfterSolve(this.ikBoneRefs.right,this.ikPreSolve.right,this.ikLegState.right.weight),this.applySimpleFootPitch("left",this.ikBoneRefs.left.foot,this.ikLegState.left),void this.applySimpleFootPitch("right",this.ikBoneRefs.right.foot,this.ikLegState.right))}resetFootIkState(){if(null!=this.ikBoneRefs){for(const t of Q){const e=this.ikBoneRefs[t],o=this.ikLegState[t],i=e.foot.getWorldPosition(F);o.targetPosition.copy(i),o.lastFootWorldPos.copy(i),o.hasLastFootSample=!0,o.weight=0,o.normal.set(0,1,0);const s=this.resolveFootAxes(t,e.foot,this.footIkAxesScratch);o.forward.copy(s.forward),o.pitch=0,o.contactOffset=Math.max(o.contactOffset,this.footIkFootOffset+this.footIkExtraClearance),o.planted=!1}this.ikPelvisOffsetY=0}}updatePelvisOffset(t,e){if(null==this.ikPelvisBone)return;let o=0;if(e&&this.footIkPelvisEnabled&&null!=this.ikBoneRefs&&null!=this.ikLegLengths){this.ikLegState.left.top=!1,this.ikLegState.right.top=!1;for(const t of Q){const e=this.ikBoneRefs[t],i=this.ikLegLengths[t],s=this.ikLegState[t];if(!s.planted)continue;const r=e.upper.getWorldPosition(F),n=.998*i.total,l=this.computeRequiredPelvisDrop(r,s.targetPosition,n);if(l>0){const e="left"===t?"right":"left";this.ikLegState[e].top=!0}o=Math.min(o,-l)}o=y.clamp(o,-this.footIkPelvisMaxOffsetDown,this.footIkPelvisMaxOffsetUp)}const i=1-Math.exp(-this.footIkPelvisLerpSpeed*t);this.ikPelvisOffsetY=y.lerp(this.ikPelvisOffsetY,o,i);const s=this.ikPelvisBone.getWorldPosition(P),r=R.copy(s).addScaledVector(B,this.ikPelvisOffsetY);null==this.ikPelvisBone.parent?this.ikPelvisBone.position.copy(r):this.ikPelvisBone.position.copy(this.ikPelvisBone.parent.worldToLocal(r)),this.ikPelvisBone.updateMatrixWorld(!0)}computeFootVerticalSpeed(t,e,o){return!t.hasLastFootSample||o<=1e-6?0:(e.y-t.lastFootWorldPos.y)/o}updateFootSample(t,e){t.lastFootWorldPos.copy(e),t.hasLastFootSample=!0}estimateFootContactOffset(t,e,o){const i=this.footIkFootOffset+this.footIkExtraClearance,s=t.bones.indexOf(o);if(s<0)return i;const r=t.boneInverses[s];if(null==r)return i;const n=this.ikFootAxes?.[e]??this.detectFootAxisBasis(o);let a=0;return o.traverse(e=>{if(!(e instanceof l)||e===o)return;const i=t.bones.indexOf(e);if(i<0)return;const s=C.copy(t.boneInverses[i]).invert(),h=q.copy(r).multiply(s),c=F.setFromMatrixPosition(h);a=Math.min(a,c.dot(n.upLocal))}),Math.max(i,-a+this.footIkExtraClearance)}shouldPlantFoot(t,e,o,i,s,r){if(!e||!o)return!1;const n=this.footIkPlantReleaseExtension,l=this.footIkPlantAttachExtension,a=this.footIkPlantReleaseLift,h=this.footIkPlantAttachLift;return t.planted?!(i>this.footIkPlantReleaseUpSpeed&&(s<n||r>a)):s>=l&&r<=h||i<=0&&s>=n}computeLegExtensionRatio(t,e){const o=t.upper.getWorldPosition(F),i=t.foot.getWorldPosition(P),s=o.distanceTo(i);return e.total<=1e-6?1:y.clamp(s/e.total,0,1.2)}computeRequiredPelvisDrop(t,e,o){const i=A.subVectors(t,e),s=i.length();if(s<=o+1e-6)return 0;const r=i.dot(B),n=o*o-Math.max(s*s-r*r,0);if(n<=0)return s-o;const l=Math.sqrt(n),a=r-l,h=r+l;return a>=0?a:h>=0?h:0}updateLegTiltSimple(t,e,o,i,s){const r=this.footIkScratch,n=this.footIkHits,l=e.getWorldPosition(r.footWorldPos),a=this.computeFootVerticalSpeed(o,l,s),h=this.resolveFootAxes(t,e,this.footIkAxesScratch),c=this.resolveGroundSampleAxes(t,e,h,this.footIkSampleAxesScratch),d=r.centerRayStart.copy(l).addScaledVector(B,this.footIkRayStartHeight),p=r.toeRayStart.copy(d).addScaledVector(c.forward,this.footIkForwardSampleDistance),u=r.heelRayStart.copy(d).addScaledVector(c.forward,-this.footIkForwardSampleDistance),f=r.sideRayStart.copy(d).addScaledVector(c.right,this.footIkSideSampleDistance),g=this.getFirstGroundHit(d,n.center),m=this.getFirstGroundHit(p,n.toe),k=this.getFirstGroundHit(u,n.heel),S=this.getFirstGroundHit(f,n.side),w=r.desiredNormal.set(0,1,0),b=r.desiredForward.copy(c.forward);let L=0,v=0,I=this.footIkWeightOutSpeed;const x=null!=g||null!=m||null!=k;let D=l.y;null!=g?D=g.point.y:null!=m&&null!=k?D=.5*(m.point.y+k.point.y):null!=m?D=m.point.y:null!=k&&(D=k.point.y);const F=this.ikBoneRefs?.[t],P=this.ikLegLengths?.[t],R=null!=F&&null!=P?this.computeLegExtensionRatio(F,P):1,A=Math.max(0,l.y-D,l.y-o.targetPosition.y),M=this.shouldPlantFoot(o,!this.footIkRequireGrounded||i,x,a,R,A);M?this.computeDesiredGroundNormal(c,g,m,k,S,w)&&(this.computeDesiredFootForward(c.forward,c.right,w,m,k,b),L=this.computeDesiredPitchAngle(m,k,c.forward),v=1,I=this.footIkWeightInSpeed):this.computeDesiredFootForward(c.forward,c.right,w,null,null,b);const T=1-Math.exp(-this.footIkPositionLerpSpeed*s),W=1-Math.exp(-I*s);o.normal.lerp(w,T).normalize(),o.forward.lerp(b,T).normalize(),o.pitch=y.lerp(o.pitch,L,T),o.weight=y.lerp(o.weight,v,W),o.planted=M&&v>.001,this.updateFootSample(o,l),this.applySimpleFootPitch(t,e,o),this.updateLegDebug(t,{footWorldPos:l,centerRayStart:d,toeRayStart:p,heelRayStart:u,sideRayStart:f,centerHit:g,toeHit:m,heelHit:k,sideHit:S,desiredNormal:o.normal,desiredTargetPos:r.footWorldPos,weight:o.weight})}updateLegIkTarget(t,e,o,i,s,r,n){const l=this.footIkScratch,a=this.footIkHits,h=e.foot,c=h.getWorldPosition(l.footWorldPos),d=this.computeFootVerticalSpeed(s,c,n),p=this.resolveFootAxes(t,h,this.footIkAxesScratch),u=this.resolveGroundSampleAxes(t,h,p,this.footIkSampleAxesScratch),f=l.centerRayStart.copy(c).addScaledVector(B,this.footIkRayStartHeight),g=l.toeRayStart.copy(f).addScaledVector(u.forward,this.footIkForwardSampleDistance),m=l.heelRayStart.copy(f).addScaledVector(u.forward,-this.footIkForwardSampleDistance),k=l.sideRayStart.copy(f).addScaledVector(u.right,this.footIkSideSampleDistance),S=this.getFirstGroundHit(f,a.center),w=this.getFirstGroundHit(g,a.toe),b=this.getFirstGroundHit(m,a.heel),L=this.getFirstGroundHit(k,a.side),v=l.desiredTargetPos.copy(c),I=l.desiredNormal.set(0,1,0),x=l.desiredForward.copy(u.forward);let D=0,F=0,P=this.footIkWeightOutSpeed;const R=null!=S||null!=w||null!=b;let A=c.y;null!=S?A=S.point.y:null!=w&&null!=b?A=.5*(w.point.y+b.point.y):null!=w?A=w.point.y:null!=b&&(A=b.point.y);const M=this.computeLegExtensionRatio(e,o),T=Math.max(0,c.y-A,c.y-s.targetPosition.y),W=this.shouldPlantFoot(s,!this.footIkRequireGrounded||r,R,d,M,T);if(W){if(v.copy(c),this.computeDesiredGroundNormal(u,S,w,b,L,I)){this.computeDesiredFootForward(u.forward,u.right,I,w,b,x),D=this.computeDesiredPitchAngle(w,b,u.forward);const t=Math.max(0,Math.sin(D))*this.footIkForwardSampleDistance*.45,e=Math.max(this.footIkFootOffset+this.footIkExtraClearance,s.contactOffset);v.y=A+e+t,s.top&&(v.y-=this.ikPelvisOffsetY/2),F=1,P=this.footIkWeightInSpeed}}else this.computeDesiredFootForward(u.forward,u.right,I,null,null,x),v.copy(c),v.y+=this.footIkFootOffset;this.clampDesiredFootTarget(e,o,c,v);const O=1-Math.exp(-this.footIkPositionLerpSpeed*n),V=1-Math.exp(-P*n);s.targetPosition.lerp(v,O),s.normal.lerp(I,O).normalize(),s.forward.lerp(x,O).normalize(),s.pitch=y.lerp(s.pitch,D,O),s.weight=y.lerp(s.weight,F,V),s.planted=W&&F>.001,this.updateFootSample(s,c),this.setBoneWorldPosition(i,s.targetPosition),this.updateLegDebug(t,{footWorldPos:c,centerRayStart:f,toeRayStart:g,heelRayStart:m,sideRayStart:k,centerHit:S,toeHit:w,heelHit:b,sideHit:L,desiredNormal:I,desiredTargetPos:s.targetPosition,weight:s.weight})}updateLegDebug(t,e){if(!this.footIkDebug||null==this.ikDebugLegs||null==this.ikBoneRefs)return;const o=this.ikDebugLegs[t],i=this.ikBoneRefs[t],s=this.resolveFootAxes(t,i.foot,this.footIkAxesScratch),r=this.footIkScratch,n=e.centerHit?.point??r.debugRayEndA.copy(e.centerRayStart).addScaledVector(I,this.footIkRayLength),l=e.toeHit?.point??r.debugRayEndB.copy(e.toeRayStart).addScaledVector(I,this.footIkRayLength),a=e.heelHit?.point??r.debugRayEndC.copy(e.heelRayStart).addScaledVector(I,this.footIkRayLength),h=e.sideHit?.point??r.debugRayEndD.copy(e.sideRayStart).addScaledVector(I,this.footIkRayLength);this.setDebugArrow(o.rayCenter,e.centerRayStart,n),this.setDebugArrow(o.rayToe,e.toeRayStart,l),this.setDebugArrow(o.rayHeel,e.heelRayStart,a),this.setDebugArrow(o.raySide,e.sideRayStart,h);const c=e.centerHit?.point??e.toeHit?.point??e.heelHit?.point??e.sideHit?.point??e.footWorldPos;this.setDebugArrow(o.hitNormal,c,r.debugRayEndE.copy(c).addScaledVector(e.desiredNormal,this.footIkDebugNormalLength)),this.setDebugArrow(o.footUp,e.footWorldPos,r.debugRayEndF.copy(e.footWorldPos).addScaledVector(s.up,this.footIkDebugAxisLength*(.15+e.weight))),this.setDebugArrow(o.footToTarget,e.footWorldPos,e.desiredTargetPos)}getFirstGroundHit(t,e){return"physics"===this.footIkRaycastMode?this.rayTestDown(t,this.footIkRayLength,e)?e:null:"physicsThenRender"===this.footIkRaycastMode?this.rayTestDown(t,this.footIkRayLength,e)||this.raycastDownFirstValid(t,this.footIkRayLength,e)?e:null:this.raycastDownFirstValid(t,this.footIkRayLength,e)?e:null}resolveGroundSampleAxes(t,e,o,i){const s=o??this.resolveFootAxes(t,e),r=i??{forward:new d,right:new d},n=this.actor.object.getWorldDirection(A);n.addScaledVector(B,-n.dot(B)),n.lengthSq()<1e-8&&n.copy(s.forward).addScaledVector(B,-s.forward.dot(B)),n.lengthSq()<1e-8&&n.set(0,0,1),n.normalize();const l=M.crossVectors(B,n);return l.lengthSq()<1e-8?l.copy(s.right):l.normalize(),l.dot(s.right)<0&&l.multiplyScalar(-1),r.forward.copy(n),r.right.copy(l),r}computeDesiredGroundNormal(t,e,o,i,s,r){const n=this.computeAverageHitNormal(e,o,i,s,A);let l=!1,a=0,h=0;if(null!=o&&null!=i){const e=P.subVectors(o.point,i.point).dot(t.forward);Math.abs(e)>1e-5&&(a=(o.point.y-i.point.y)/e,l=!0)}if(null!=s&&null!=e){const o=R.subVectors(s.point,e.point).dot(t.right);Math.abs(o)>1e-5&&(h=(s.point.y-e.point.y)/o,l=!0)}if(l&&(r.copy(B),r.addScaledVector(t.forward,-a),r.addScaledVector(t.right,-h),r.lengthSq()>1e-8?r.normalize():l=!1),l)null!=n&&r.dot(n)<0&&r.multiplyScalar(-1);else{if(null==n)return!1;r.copy(n),l=!0}return r.dot(B)<0&&r.multiplyScalar(-1),this.clampSlopeNormal(r),!0}computeAverageHitNormal(t,e,o,i,s){s.set(0,0,0);let r=0;return null!=t&&t.hasNormal&&(s.add(t.normal),r++),null!=e&&e.hasNormal&&(s.add(e.normal),r++),null!=o&&o.hasNormal&&(s.add(o.normal),r++),null!=i&&i.hasNormal&&(s.add(i.normal),r++),0===r||s.lengthSq()<1e-8?null:(s.multiplyScalar(1/r).normalize(),s)}computeDesiredFootForward(t,e,o,i,s,r){null!=i&&null!=s?r.subVectors(i.point,s.point):r.copy(t),r.addScaledVector(o,-r.dot(o)),r.lengthSq()<1e-8&&(r.copy(this.actor.object.getWorldDirection(A)),r.addScaledVector(o,-r.dot(o))),r.lengthSq()<1e-8&&r.crossVectors(o,e),r.lengthSq()<1e-8&&r.set(0,0,1),r.normalize();const n=A.copy(this.actor.object.getWorldDirection(A));n.addScaledVector(o,-n.dot(o)),n.lengthSq()<1e-8&&(n.copy(t),n.addScaledVector(o,-n.dot(o))),n.lengthSq()>1e-8&&n.normalize(),r.dot(n)<0&&r.multiplyScalar(-1)}computeDesiredPitchAngle(t,e,o){if(null==t||null==e)return 0;const i=P.subVectors(t.point,e.point),s=Math.abs(i.dot(o));if(s<1e-5)return 0;const r=t.point.y-e.point.y,n=y.degToRad(this.footIkMaxSlopeAngleDeg);return y.clamp(-Math.atan2(r,s),-n,n)}applySimpleFootPitch(t,e,o){if(null==e.parent||o.weight<=.001)return;const i=this.ikFootAxes?.[t]??this.detectFootAxisBasis(e),s=o.pitch*o.weight;if(Math.abs(s)<=1e-5)return;const r=this.resolveLocalPitchSign(i),n=T.copy(e.quaternion),l=W.setFromAxisAngle(i.rightLocal,s*r);e.quaternion.copy(n.multiply(l))}resolveLocalPitchSign(t){const e=O.setFromAxisAngle(t.rightLocal,.15);return R.copy(t.upLocal).applyQuaternion(e).sub(t.upLocal).dot(t.forwardLocal)>=0?1:-1}clampDesiredFootTarget(t,e,o,i){const s=P.subVectors(i,o),r=s.dot(B),n=R.copy(s).addScaledVector(B,-r),l=n.length();l>this.footIkMaxHorizontalOffset&&l>1e-6&&n.multiplyScalar(this.footIkMaxHorizontalOffset/l);const a=y.clamp(r,-this.footIkMaxVerticalOffsetDown,this.footIkMaxVerticalOffsetUp);if(i.copy(o),i.add(n),i.addScaledVector(B,a),this.footIkPelvisEnabled)return;const h=t.upper.getWorldPosition(F).clone(),c=P.subVectors(i,h),d=c.length(),p=.995*e.total;d>p&&d>1e-6&&i.copy(h).addScaledVector(c,p/d)}solveTwoBoneLeg(t,e,o){const i=e.upper.getWorldPosition(F).clone(),s=e.lower.getWorldPosition(P).clone(),r=e.foot.getWorldPosition(R).clone(),n=i.distanceTo(s),l=s.distanceTo(r);if(n<1e-5||l<1e-5)return;const a=(new d).subVectors(o,i),h=a.length();if(h<1e-5)return;const c=Math.abs(n-l)+1e-4,p=n+l-1e-4,u=y.clamp(h,c,p),f=a.multiplyScalar(1/h),g=(new d).subVectors(s,i).normalize(),m=(new d).subVectors(r,s).normalize(),k=(new d).crossVectors(g,m);k.lengthSq()<1e-8&&(k.copy(this.actor.object.getWorldDirection(F).cross(B)),k.lengthSq()<1e-8&&k.set(1,0,0)),k.normalize();const S=(u*u+n*n-l*l)/(2*u),w=Math.max(n*n-S*S,0),b=Math.sqrt(w),L=(new d).copy(i).addScaledVector(f,S);let v=(new d).crossVectors(k,f);v.lengthSq()<1e-8&&(v=(new d).crossVectors(f,B),v.lengthSq()<1e-8&&(v=new d(1,0,0))),v.normalize();const I=(new d).copy(L).addScaledVector(v,b),x=(new d).copy(L).addScaledVector(v,-b),D=(new d).subVectors(s,L).dot(v)>=0?I:x;this.rotateBoneToward(e.upper,i,s,D),e.upper.updateMatrixWorld(!0);const A=e.lower.getWorldPosition(P),M=e.foot.getWorldPosition(R);this.rotateBoneToward(e.lower,A,M,o),e.lower.updateMatrixWorld(!0),this.updateSolveDebug(t,i,D,o)}updateSolveDebug(t,e,o,i){if(!this.footIkDebug||null==this.ikDebugLegs)return;const s=this.ikDebugLegs[t];this.setDebugArrow(s.solveUpper,e,o),this.setDebugArrow(s.solveLower,o,i)}rotateBoneToward(t,e,o,i){const s=(new d).subVectors(o,e),r=(new d).subVectors(i,e);if(s.lengthSq()<1e-10||r.lengthSq()<1e-10)return;if(s.normalize(),r.normalize(),s.dot(r)>.9999)return;const n=(new f).setFromUnitVectors(s,r),l=t.getWorldQuaternion(T),a=W.copy(n).multiply(l);if(null==t.parent)return void t.quaternion.copy(a);const h=t.parent.getWorldQuaternion(O),c=V.copy(h).invert().multiply(a);t.quaternion.copy(c)}detectFootAxisBasis(t){const e=t.getWorldQuaternion(T),o=this.actor.object.getWorldDirection(F);o.lengthSq()<1e-6&&o.set(0,0,1),o.normalize();const i=[{local:H.clone(),world:H.clone().applyQuaternion(e)},{local:E.clone(),world:E.clone().applyQuaternion(e)},{local:N.clone(),world:N.clone().applyQuaternion(e)}];i.push({local:H.clone().multiplyScalar(-1),world:H.clone().multiplyScalar(-1).applyQuaternion(e)}),i.push({local:E.clone().multiplyScalar(-1),world:E.clone().multiplyScalar(-1).applyQuaternion(e)}),i.push({local:N.clone().multiplyScalar(-1),world:N.clone().multiplyScalar(-1).applyQuaternion(e)});let s=i[0],r=-1/0;for(const t of i){const e=t.world.dot(B);e>r&&(r=e,s=t)}let n=i[0],l=-1/0;for(const t of i){if(Math.abs(t.world.dot(s.world))>.6)continue;const e=Math.abs(t.world.dot(o));e>l&&(l=e,n=t)}n.world.dot(o)<0&&(n={local:n.local.clone().multiplyScalar(-1),world:n.world.clone().multiplyScalar(-1)});const a=s.local.clone().normalize(),h=n.local.clone().normalize(),c=a.clone().cross(h).normalize();return{upLocal:a,forwardLocal:c.clone().cross(a).normalize(),rightLocal:c}}resolveFootAxes(t,e,o){const i=this.ikFootAxes?.[t]??this.detectFootAxisBasis(e),s=o??{up:new d,forward:new d,right:new d},r=e.getWorldQuaternion(T);return s.up.copy(i.upLocal).applyQuaternion(r).normalize(),s.forward.copy(i.forwardLocal).applyQuaternion(r).normalize(),s.right.copy(i.rightLocal).applyQuaternion(r).normalize(),s}clampSlopeNormal(t){const e=y.clamp(t.dot(B),-1,1),o=Math.acos(e),i=y.degToRad(this.footIkMaxSlopeAngleDeg);if(o<=i||o<1e-5)return;const s=i/o;t.lerpVectors(B,t,s).normalize()}blendLegAfterSolve(t,e,o){if(!(o>=.999)){if(o<=.001)return t.upper.quaternion.copy(e.upper),t.lower.quaternion.copy(e.lower),void t.foot.quaternion.copy(e.foot);this.blendBoneQuaternion(t.upper,e.upper,o),this.blendBoneQuaternion(t.lower,e.lower,o),this.blendBoneQuaternion(t.foot,e.foot,o)}}blendBoneQuaternion(t,e,o){T.copy(t.quaternion),t.quaternion.copy(e).slerp(T,o)}alignFootToGroundNormal(t,e,o,i){if(o.weight<=.001||null==e.parent)return;const s=this.ikFootAxes?.[t]??this.detectFootAxisBasis(e),r=F.copy(o.normal).normalize(),n=P.copy(o.forward);if(n.addScaledVector(r,-n.dot(r)),n.lengthSq()<1e-8){const o=this.resolveFootAxes(t,e);n.copy(o.forward).addScaledVector(r,-o.forward.dot(r))}if(n.lengthSq()<1e-8)return;n.normalize();const l=R.crossVectors(r,n);if(l.lengthSq()<1e-8)return;l.normalize(),n.copy(A.crossVectors(l,r)).normalize(),C.makeBasis(s.rightLocal,s.upLocal,s.forwardLocal),T.setFromRotationMatrix(C),q.makeBasis(l,r,n),W.setFromRotationMatrix(q);const a=O.copy(W).multiply(V.copy(T).invert()),h=e.parent.getWorldQuaternion(V),c=W.copy(h).invert().multiply(a),d=(1-Math.exp(-this.footIkRotationLerpSpeed*i))*o.weight;e.quaternion.slerp(c,y.clamp(d,0,1))}getMixer(){return this.mixer}beginExternalControl(){this.externalControlActive=!0}endExternalControl(){this.externalControlActive=!1}stopSequenceAnimation(){this.currentFullBodyPriority=-1,this.externalControlActive=!1,this.fullBodyTimer=0}beginExternalAnimationControl(t,e={}){Y(null!=this.mixer,"Can't begin external control before setup is called"),this.mixer.stopAllAction(),this.currentFullBodyPriority=99,this.fullBodyTimer=0;const o=this.mixer.clipAction(t);return o.setLoop(h,1),o.clampWhenFinished=!0,o.timeScale=e.timeScale??1,o.reset(),o.play(),this.fullBodyAction=o,this.externalControlActive=!0,o}syncMovementSpeed(t){if(null!=t){const e=t.getClip();if(e instanceof i&&e.fixedInPlace&&null!=this.movementSpeed){t.timeScale=e.duration/e.displacement*this.movementSpeed;const o=this.mixer.getRoot();o instanceof a&&(t.timeScale/=o.scale.x)}}}playStateMachine(t){this.stateMachines.push(t)}playUpperStateMachine(t){this.upperStateMachines.push(t)}removeStateMachine(t){const e=this.stateMachines.indexOf(t);e>=0&&this.stateMachines.splice(e,1)}removeUpperStateMachine(t){const e=this.upperStateMachines.indexOf(t);e>=0&&this.upperStateMachines.splice(e,1)}playUpper(t,e={}){const o=e?.priority??1;o<this.currentUpperBodyPriority||(this.currentUpperBodyPriority=o,this.upperBodyAction=this.transition(this.upperBodyAction,this.getUpperBodyClip(t),e),this.upperBodyAction.timeScale=e?.timeScale??1,this.upperBodyTimer=0,this.upperBodyOverride=!0,this.overrideFadeTimeUpper=e.fadeTime)}play(t,e={}){Y(null!=this.mixer,"Can't play animation before setup is called");const o=e.priority??1;o<this.currentFullBodyPriority||(this.currentFullBodyPriority=o,this.fullBodyTimer=0,this.upperBodyOverride||(this.upperBodyAction=this.transition(this.upperBodyAction,this.getUpperBodyClip(t),e),this.upperBodyAction.timeScale=e?.timeScale??1),this.fullBodyClip=t,this.fullBodyAction=this.transition(this.fullBodyAction,this.getFullBodyClip(t),e),this.fullBodyAction.timeScale=e?.timeScale??1,this.fullBodyAction.getClip().uuid==this.upperBodyAction.getClip().uuid&&this.upperBodyAction.syncWith(this.fullBodyAction),this.overrideFadeTime=e.fadeTime)}onActionDone(t){return new Promise(e=>{const o=i=>{i.action===t&&(e(i.action),this.mixer.removeEventListener("finished",o))};this.mixer.addEventListener("finished",o)})}transition(t,e,o={}){if(null!=t&&t.getClip().uuid===e.uuid)return t;const i=o?.fadeTime??this.fadeTime;if(t){const s=t,r=this.mixer.clipAction(e);r.reset(),null!=o.offset&&(r.time=o.offset),r.play(),r.enabled=!0,r.setEffectiveTimeScale(1),r.weight=1,s.crossFadeTo(r,i,!0),t=r}else(t=this.mixer.clipAction(e)).reset(),null!=o.offset&&(t.time=o.offset),t.fadeIn(i),t.play();return!1===o.loop&&(t.setLoop(h,1),t.clampWhenFinished=!0),t}};K=t([o({inEditor:!0})],K);export{K as CharacterAnimationComponent};function _(t,e){if(null==t)return e;const o=e.clone(),i=new Set(t.map(t=>t.name));return o.tracks=o.tracks.filter(t=>i.has(t.name.split(".")[0])),o}function $(t){return t.flatMap(t=>function(t){const e=[];return t.traverse(t=>{e.push(t)}),e}(t)).filter(t=>t instanceof l)}function Y(t,e){if(!1===t||"function"==typeof t&&!1===t())throw new Error(e)}function J(t,e){const o=new Map;return(i,...s)=>{const r=t(i);return o.has(r)||o.set(r,e(i,...s)),o.get(r)}}function X(t){let e;return t.traverse(t=>{(t instanceof l||t.isBone)&&null==e&&(e=t)}),e}function Z(t,e){let o=e;for(;null!=o;){if(o===t)return!0;o=o.parent}return!1}/*
|
|
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
|
-
import{__decorate as t,__metadata as e}from"tslib";import i from"@dimforge/rapier3d-compat";import{takeUntil as o}from"rxjs";import*as s from"three";import{ArrowHelper as n,MathUtils as r,Vector3 as a}from"three";import{RootMotionClip as c}from"../../../../../gameplay/animation/root-motion.js";import{ActionInput as h,AxisInput as l,RotationInput as p}from"../../../../../gameplay/input/index.js";import{PhysicsSystem as m,RayTestResult as u}from"../../../../../gameplay/services/physics/physics-system.js";import{CapsuleCollisionShape as d}from"../../../../../scene/collision/collision-shape.js";import{PhysicsBodyType as y}from"../../../../services/physics/physics-system.js";import{ActorComponent as g,Component as f}from"../../../component.js";import{CharacterMovementMode as S}from"./modes.js";import{inject as M}from"../../../../../gameplay/inject.js";const
|
|
1
|
+
import{__decorate as t,__metadata as e}from"tslib";import i from"@dimforge/rapier3d-compat";import{takeUntil as o}from"rxjs";import*as s from"three";import{ArrowHelper as n,MathUtils as r,Vector3 as a}from"three";import{RootMotionClip as c}from"../../../../../gameplay/animation/root-motion.js";import{ActionInput as h,AxisInput as l,RotationInput as p}from"../../../../../gameplay/input/index.js";import{PhysicsSystem as m,RayTestResult as u}from"../../../../../gameplay/services/physics/physics-system.js";import{CapsuleCollisionShape as d}from"../../../../../scene/collision/collision-shape.js";import{PhysicsBodyType as y}from"../../../../services/physics/physics-system.js";import{ActorComponent as g,Component as f}from"../../../component.js";import{CharacterMovementMode as S}from"./modes.js";import{inject as M}from"../../../../../gameplay/inject.js";const w=new a,v=new a,x=1/30,b=131070;let I=class extends g{get autoStepMinWidth(){return this.cc.autostepMinWidth()}set autoStepMinWidth(t){this.cc.enableAutostep(this.cc.autostepMaxHeight(),t,this.cc.autostepIncludesDynamicBodies())}get autoStepDynamicObjects(){return this.cc.autostepIncludesDynamicBodies()}set autoStepDynamicObjects(t){this.cc.enableAutostep(this.cc.autostepMaxHeight(),this.cc.autostepMinWidth(),t)}get autoStepMaxHeight(){return this.cc.autostepMaxHeight()}set autoStepMaxHeight(t){this.cc.enableAutostep(t,this.cc.autostepMinWidth(),this.cc.autostepIncludesDynamicBodies())}get snapToGround(){return this.cc.snapToGroundDistance()}set snapToGround(t){this.cc.enableSnapToGround(t)}set offset(t){this.cc.setOffset(t)}get offset(){return this.cc.offset()}set normalNudgeFactor(t){this.cc.setNormalNudgeFactor(t)}get normalNudgeFactor(){return this.cc.normalNudgeFactor()}constructor(){super(),this.directionInput=new l,this.jumpInput=new h,this.sprintInput=new h,this.rotationInput=new p,this.horizontalSpeed=0,this.maxSpeed=8,this.maxSpeedBackwards=8,this.maxSpeedSprint=12,this.jumpVelocity=7,this.fallingMovementControl=.5,this.fallingReorientation=!1,this.gravityOverride=null,this.colliderHeight=2,this.colliderRadius=.5,this.jumpInAir=!1,this.mass=50,this.allowSliding=!0,this.minSlopeSlideAngle=r.degToRad(70),this.maxSlopeClimbAngle=r.degToRad(70),this.applyImpulsesToDynamicBodies=!0,this.normalizedDirection=!0,this.characterCollision=!1,this.enabled=!0,this.velocity=new a,this.mode=S.walking,this.isSprinting=!1,this.pressedJump=!1,this.rayTestResult=new u,this.physicsSystem=M(m),this.resetRootMotion=!1,this.rotateToMovementDirection=!1,this.smoothRotation=!0,this.rotationSpeed=40,this.maxRotationSpeed=100,this.impulse=new a,this.impulseDamping=2;const t=this.cc=this.physicsSystem.getCharacterController(.1);t.enableSnapToGround(.1),t.enableAutostep(0,.1,!1)}onInit(){const t=this.cc;t.setApplyImpulsesToDynamicBodies(this.applyImpulsesToDynamicBodies),t.setMinSlopeSlideAngle(this.minSlopeSlideAngle),t.setMaxSlopeClimbAngle(this.maxSlopeClimbAngle),t.setCharacterMass(this.mass),t.setSlideEnabled(this.allowSliding),this.physicsSystem.addActor(this.actor,[this.createCollisionShape()],{mass:0,type:y.kinematic,continousCollisionDetection:!1,friction:0,restitution:.5,ignoreForNavMesh:!0}),this.rotationInput.rotation.copy(this.actor.rotation);let e=this.rotationInput.rotation.y;const i=new a,n=new a,h=new a,l=new a,p=new a,m=new a,u=new a;let d=0,g=null;const f=new a,M=new a,I=new a,A=new a,R=new a,z=new a,C=new a,k=this.characterCollision?null:b;let E=this.rotateToMovementDirection;this.physicsSystem.beforeStep.pipe(o(this.disposed)).subscribe(o=>{if(this.checkGrounded(o),!this.enabled)return;if(M.set(0,0,0),null!=this.rootMotionAction){if(this.rootMotionAction.getClip()instanceof c){const t=this.rootMotionInterpolant;this.resetRootMotion&&(f.fromArray(t.evaluate(0)),this.resetRootMotion=!1),z.fromArray(t.evaluate(this.rootMotionAction.time)),M.subVectors(z,f),f.copy(z);(this.rootMotionRootBone??this.rootMotionAction.getRoot()).getWorldScale(R),M.multiply(R)}}o>x&&(o=x);const y=null!=this.rootMotionAction&&this.rootMotionAction.enabled,b=y&&0!==M.lengthSq();this.pressedJump=!y&&this.jumpInput.activated,this.isSprinting=!y&&this.sprintInput.activated;const B=E!==this.rotateToMovementDirection;let H=r.euclideanModulo(this.rotationInput.rotation.y-e+Math.PI,2*Math.PI)-Math.PI;B&&(this.actor.object.quaternion.setFromEuler(q.set(0,this.rotationInput.rotation.y,0)),E?this.actor.object.quaternion.setFromEuler(q.set(0,this.rotationInput.rotation.y,0)):(H=0,e=this.rotationInput.rotation.y),E=this.rotateToMovementDirection),h.copy(this.actor.position),l.set(-this.directionInput.vector.x,0,this.directionInput.vector.y).normalize();const N=!this.rotateToMovementDirection&&l.z<0?this.maxSpeedBackwards:(this.isSprinting?this.maxSpeedSprint:this.maxSpeed)*(this.normalizedDirection?1:Math.min(1,this.directionInput.vector.length()));if(u.set(0,0,0),y)e=this.rotationInput.rotation.y;else if(this.rotateToMovementDirection&&!y){if(l.lengthSq()>0&&this.mode!=S.falling){const t=this.smoothRotation,i=q.setFromQuaternion(this.actor.object.quaternion,"YXZ").y,s=Math.atan2(l.x,l.z),n=.99*this.rotationInput.rotation.y+s;if(t){r.clamp(.5*l.dot(p)+.5,.001,1);const t=r.euclideanModulo(n-i+Math.PI,2*Math.PI)-Math.PI;let e=r.clamp(t*this.rotationSpeed*o,-this.maxRotationSpeed*o,this.maxRotationSpeed*o);Math.abs(e)>Math.abs(t)&&(e=t),this.actor.object.quaternion.setFromEuler(q.set(0,i+e,0))}else this.actor.object.quaternion.setFromEuler(q.set(0,n,0));p.lerp(l,o*this.rotationSpeed),e+=H,u.copy(this.actor.object.getWorldDirection(F).normalize())}}else b||(e+=H,this.actor.object.quaternion.multiply(G.setFromEuler(q.set(0,H,0))),l.lengthSq()>0&&u.copy(l).applyQuaternion(this.actor.object.quaternion).normalize());if(C.set(0,0,0),this.rayTestResult.hasHit&&null!=this.rayTestResult.actor&&this.physicsSystem.getLinearVelocity(this.rayTestResult.actor,C),this.mode===S.walking?(0!==u.lengthSq()?(d=Math.min(N,d),d=r.lerp(d,N,4*o)):d=0,m.copy(u).multiplyScalar(d),this.pressedJump&&(this.mode=S.falling,this.velocity.copy(m),this.velocity.y=this.jumpVelocity),m.y=o*this.getEffectiveGravity(),m.add(C)):this.mode===S.falling&&(this.pressedJump&&this.jumpInAir&&(this.mode=S.falling,this.velocity.copy(m),this.velocity.y=this.jumpVelocity),this.velocity.y+=o*this.getEffectiveGravity(),m.copy(this.velocity),m.addScaledVector(u,this.fallingMovementControl),this.fallingReorientation&&m.applyAxisAngle(new a(0,1,0),H)),b&&(I.copy(M).applyQuaternion(this.actor.quaternion),A.copy(I).divideScalar(o),this.mode===S.walking?(m.x=A.x+C.x,m.z=A.z+C.z):(m.x=A.x,m.z=A.z),l.lengthSq()>0)){const t=Math.sqrt(A.x*A.x+A.z*A.z);t>.4&&(d=t)}if(this.impulse.lengthSq()>.1){const t=Math.min(1,this.impulse.length()/5);m.x=r.lerp(m.x,this.impulse.x,t),m.z=r.lerp(m.z,this.impulse.z,t),m.y+=this.impulse.y;const e=Math.exp(-this.impulseDamping*o);this.impulse.x*=e,this.impulse.y>0?this.impulse.y+=o*this.getEffectiveGravity():this.impulse.y*=e,this.impulse.z*=e}else this.impulse.set(0,0,0);if(n.copy(m).normalize(),i.copy(m),m.length()>0||!this.isGrounded){if(w.copy(m).multiplyScalar(o),this.isGrounded&&this.mode===S.walking&&(this.rayTestResult.distance>t.offset()||(w.y=Math.max(0,C.y*o)),this.physicsSystem.getActorComputedMovement(this.actor,t,w,k),t.computedCollision(0,D),null!=D&&null!=D.normal1)){const e=(new s.Vector3).copy(D.normal1);Math.acos(e.dot(T))>t.maxSlopeClimbAngle()&&(w.y=.016*this.getEffectiveGravity()*.5)}v.copy(this.physicsSystem.getActorComputedMovement(this.actor,t,w,k))}else v.set(0,0,0);this.physicsSystem.setNextKinematicTranslation(this.actor,v);let P=function(t){if(t.numComputedCollisions()>0){const e=t.computedCollision(0);j.x=e.normal2.x,j.y=e.normal2.y,j.z=e.normal2.z;const i=j.angleTo(T);j.x=e.normal1.x,j.y=e.normal1.y,j.z=e.normal1.z;const o=j.angleTo(T);return!(i<100)&&o>t.minSlopeSlideAngle()}return!1}(t);this.isGrounded&&!P||this.mode!==S.falling&&(null==g?g=performance.now():performance.now()-g>100&&(this.mode=S.falling,this.velocity.copy(i))),this.isGrounded&&this.velocity.y<=0&&(this.mode,S.falling,this.mode=S.walking,this.velocity.y=0,g=null),this.mode,S.walking,this.horizontalSpeed=d})}applyImpulse(t){this.impulse.add(t)}debugDirection(){const t=new n(w,this.actor.position,1,65280);this.actor.object.parent.add(t),setTimeout(()=>{t.removeFromParent()},30);const e=new n(v,this.actor.position,1,16711680);this.actor.object.parent.add(e),setTimeout(()=>{e.removeFromParent()},30)}setRootMotionAction(t){const e=t?.getClip();if(e instanceof c){this.rootMotionAction=t,this.resetRootMotion=!0;const i=[];this.rootMotionInterpolant=e.motionTrack.InterpolantFactoryMethodSmooth(i);const o=s.PropertyBinding.parseTrackName(e.motionTrack.name);this.rootMotionRootBone=this.actor.object.getObjectByName(o.nodeName)}}getWallDirection(t,e){const i=t.clone().negate().cross(T);return i.dot(e)<0?i.negate():i}moveTo(t){this.actor.position.copy(t),this.physicsSystem.updateActorTransform(this.actor)}getEffectiveGravity(){return this.gravityOverride??this.physicsSystem.getGravity().y}checkGrounded(t){this.colliderHeight,this.colliderRadius;C.y=-.05,this.physicsSystem.rayTest(A.addVectors(this.actor.position,z.set(0,this.offset,0)),R.addVectors(this.actor.position,C),this.rayTestResult,{excludeActor:this.actor,excludeTriggers:!0})}get isGrounded(){return this.rayTestResult.hasHit||this.cc.computedGrounded()}createCollisionShape(){const t=new d(this.colliderHeight,this.colliderRadius);return t.offset.y=this.colliderRadius+this.colliderHeight/2+this.offset,t.collisionGroup=b,t}step(t){}performMovement(t){}arrowHelper(t,e,i){const o=new n(t.clone().normalize(),e,1,i);this.actor.object.parent.add(o),setTimeout(()=>{o.removeFromParent()},30)}};I=t([f({inEditor:!1}),e("design:paramtypes",[])],I);export{I as CharacterMovementComponent};const T=new a(0,1,0),j=new a;const A=new a,R=new a,z=new a(0,1,0),C=new a(0,-.1,0),D=(new a(0,-1,0),new i.CharacterCollision),G=new s.Quaternion,q=new s.Euler,F=new s.Vector3;/*
|
|
2
2
|
* Copyright (©) 2026 Hology Interactive AB. All rights reserved.
|
|
3
3
|
* See the LICENSE.md file for details.
|
|
4
4
|
*/
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { BaseActor } from "../..";
|
|
2
2
|
import { Texture } from 'three';
|
|
3
|
+
import { Vector3 } from 'three';
|
|
3
4
|
import * as THREE from 'three';
|
|
4
5
|
import { ActorComponent } from "../../actors/component.js";
|
|
6
|
+
import { ToneMapping } from '../../../../../../node_modules/@types/three/src/constants.js';
|
|
5
7
|
export type PostProcessingVolumeShape = {
|
|
6
8
|
kind: 'box';
|
|
7
9
|
box: THREE.Box3;
|
|
@@ -10,7 +12,7 @@ export type PostProcessingVolumeShape = {
|
|
|
10
12
|
sphere: THREE.Sphere;
|
|
11
13
|
};
|
|
12
14
|
export declare class PostProcessSettings extends ActorComponent {
|
|
13
|
-
tonemapMapping?:
|
|
15
|
+
tonemapMapping?: ToneMapping;
|
|
14
16
|
tonemapExposure?: number;
|
|
15
17
|
envTexture?: Texture;
|
|
16
18
|
envIntensity?: number;
|
|
@@ -25,6 +27,28 @@ export declare class PostProcessSettings extends ActorComponent {
|
|
|
25
27
|
depthMaxBlur?: number;
|
|
26
28
|
temperature?: number;
|
|
27
29
|
temperatureTint?: number;
|
|
30
|
+
globalSaturation?: Vector3;
|
|
31
|
+
globalContrast?: Vector3;
|
|
32
|
+
globalGamma?: Vector3;
|
|
33
|
+
globalGain?: Vector3;
|
|
34
|
+
globalOffset?: Vector3;
|
|
35
|
+
shadowsSaturation?: Vector3;
|
|
36
|
+
shadowsContrast?: Vector3;
|
|
37
|
+
shadowsGamma?: Vector3;
|
|
38
|
+
shadowsGain?: Vector3;
|
|
39
|
+
shadowsOffset?: Vector3;
|
|
40
|
+
shadowsMax?: number;
|
|
41
|
+
midtonesSaturation?: Vector3;
|
|
42
|
+
midtonesContrast?: Vector3;
|
|
43
|
+
midtonesGamma?: Vector3;
|
|
44
|
+
midtonesGain?: Vector3;
|
|
45
|
+
midtonesOffset?: Vector3;
|
|
46
|
+
highlightsSaturation?: Vector3;
|
|
47
|
+
highlightsContrast?: Vector3;
|
|
48
|
+
highlightsGamma?: Vector3;
|
|
49
|
+
highlightsGain?: Vector3;
|
|
50
|
+
highlightsOffset?: Vector3;
|
|
51
|
+
highlightsMin?: number;
|
|
28
52
|
lut?: Texture;
|
|
29
53
|
lutIntensity?: number;
|
|
30
54
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{__decorate as t,__metadata as
|
|
1
|
+
import{__decorate as t,__metadata as o}from"tslib";import{Actor as e,attach as i,BaseActor as p,Component as n,inject as r,Parameter as s}from"../..";import{Texture as a}from"three";import{tonemapMappings as d}from"../../../rendering/tone-mapping";import{OBB as l}from"three/examples/jsm/math/OBB.js";import{Vector3 as g}from"three";import*as y from"three";import{RenderingView as u}from"../../../rendering.js";import{TriggerVolumeMesh as h}from"./components/volume-editor-component.js";import{ActorComponent as m}from"../../actors/component.js";const b=[0,2],v=[-1,1];function f(t,o){return{optional:!0,type:g,group:t,label:o,range:b,precision:3,stepSize:.01,defaultValue:new g(1,1,1),editorInput:"colorGrade"}}function c(t,o){return{optional:!0,type:g,group:t,label:o,range:v,precision:3,stepSize:.01,defaultValue:new g(0,0,0),editorInput:"colorGrade"}}let w=class extends m{};t([s({options:d,optional:!0,group:"Tone Mapping",label:"Tone Mapping"}),o("design:type",Number)],w.prototype,"tonemapMapping",void 0),t([s({optional:!0,group:"Tone Mapping",label:"Tone Mapping Exposure",defaultValue:1}),o("design:type",Number)],w.prototype,"tonemapExposure",void 0),t([s({optional:!0}),o("design:type",a)],w.prototype,"envTexture",void 0),t([s({optional:!0}),o("design:type",Number)],w.prototype,"envIntensity",void 0),t([s({optional:!0,group:"Lens"}),o("design:type",Number)],w.prototype,"vignetteIntensity",void 0),t([s({optional:!0,group:"Color Correction"}),o("design:type",y.Color)],w.prototype,"colorTint",void 0),t([s({optional:!0,group:"Color Correction",defaultValue:0}),o("design:type",Number)],w.prototype,"colorTintIntensity",void 0),t([s({optional:!0,group:"Depth of Field"}),o("design:type",Number)],w.prototype,"depthFocus",void 0),t([s({optional:!0,group:"Depth of Field"}),o("design:type",Number)],w.prototype,"depthAperture",void 0),t([s({optional:!0,group:"Depth of Field"}),o("design:type",Number)],w.prototype,"depthMaxBlur",void 0),t([s({optional:!0,range:[1e3,4e4],precision:.1,group:"White Balance",defaultValue:6500}),o("design:type",Number)],w.prototype,"temperature",void 0),t([s({optional:!0,range:[-1,1],group:"White Balance",defaultValue:0}),o("design:type",Number)],w.prototype,"temperatureTint",void 0),t([s(f("Global","Saturation")),o("design:type",g)],w.prototype,"globalSaturation",void 0),t([s(f("Global","Contrast")),o("design:type",g)],w.prototype,"globalContrast",void 0),t([s(f("Global","Gamma")),o("design:type",g)],w.prototype,"globalGamma",void 0),t([s(f("Global","Gain")),o("design:type",g)],w.prototype,"globalGain",void 0),t([s(c("Global","Offset")),o("design:type",g)],w.prototype,"globalOffset",void 0),t([s(f("Shadows","Saturation")),o("design:type",g)],w.prototype,"shadowsSaturation",void 0),t([s(f("Shadows","Contrast")),o("design:type",g)],w.prototype,"shadowsContrast",void 0),t([s(f("Shadows","Gamma")),o("design:type",g)],w.prototype,"shadowsGamma",void 0),t([s(f("Shadows","Gain")),o("design:type",g)],w.prototype,"shadowsGain",void 0),t([s(c("Shadows","Offset")),o("design:type",g)],w.prototype,"shadowsOffset",void 0),t([s({optional:!0,group:"Shadows",label:"Max",range:[0,1],precision:3,stepSize:.01,defaultValue:.09}),o("design:type",Number)],w.prototype,"shadowsMax",void 0),t([s(f("Midtones","Saturation")),o("design:type",g)],w.prototype,"midtonesSaturation",void 0),t([s(f("Midtones","Contrast")),o("design:type",g)],w.prototype,"midtonesContrast",void 0),t([s(f("Midtones","Gamma")),o("design:type",g)],w.prototype,"midtonesGamma",void 0),t([s(f("Midtones","Gain")),o("design:type",g)],w.prototype,"midtonesGain",void 0),t([s(c("Midtones","Offset")),o("design:type",g)],w.prototype,"midtonesOffset",void 0),t([s(f("Highlights","Saturation")),o("design:type",g)],w.prototype,"highlightsSaturation",void 0),t([s(f("Highlights","Contrast")),o("design:type",g)],w.prototype,"highlightsContrast",void 0),t([s(f("Highlights","Gamma")),o("design:type",g)],w.prototype,"highlightsGamma",void 0),t([s(f("Highlights","Gain")),o("design:type",g)],w.prototype,"highlightsGain",void 0),t([s(c("Highlights","Offset")),o("design:type",g)],w.prototype,"highlightsOffset",void 0),t([s({optional:!0,group:"Highlights",label:"Min",range:[0,1],precision:3,stepSize:.01,defaultValue:.5}),o("design:type",Number)],w.prototype,"highlightsMin",void 0),t([s({label:"LUT",optional:!0,group:"LUT"}),o("design:type",a)],w.prototype,"lut",void 0),t([s({label:"LUT Intensity",range:[0,1],optional:!0,group:"LUT",defaultValue:0}),o("design:type",Number)],w.prototype,"lutIntensity",void 0),w=t([n()],w);export{w as PostProcessSettings};let G=class extends p{constructor(){super(),this.settings=i(w),this.bounded=!1,this.priority=0,this.blendWeight=1,this.blendRadius=0,this.box=new l(void 0,new y.Vector3(.5,.5,.5)),this.view=r(u),this.editorView=i(h),this.disposed.subscribe(()=>{this.view.removePostProcessVolume(this)})}onInit(){this.view.addPostProcessVolume(this),this.editorView.visible=this.bounded}containsPoint(t){return!this.bounded||(this.resetBox(),this.box.containsPoint(t))}distanceToPoint(t){return this.bounded?(this.resetBox(),this.box.clampPoint(t,S),S.distanceTo(t)):0}resetBox(){const t=this.box;t.center.set(0,0,0),t.halfSize.set(.5,.5,.5),t.rotation.copy(x.rotation),t.applyMatrix4(this.object.matrixWorld)}};t([s(),o("design:type",Boolean)],G.prototype,"bounded",void 0),t([s({precision:0}),o("design:type",Number)],G.prototype,"priority",void 0),t([s({range:[0,1]}),o("design:type",Number)],G.prototype,"blendWeight",void 0),t([s(),o("design:type",Number)],G.prototype,"blendRadius",void 0),G=t([e(),o("design:paramtypes",[])],G);export{G as PostProcessVolume};const x=new l,S=new g;/*
|
|
2
2
|
* Copyright (©) 2026 Hology Interactive AB. All rights reserved.
|
|
3
3
|
* See the LICENSE.md file for details.
|
|
4
4
|
*/
|
package/dist/gameplay/index.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ export { ActorFactory } from './actors/factory.js';
|
|
|
7
7
|
export { Actor, BaseActor } from './actors/actor.js';
|
|
8
8
|
export { Component, ActorComponent, type ComponentOptions, attach, Attach } from './actors/component.js';
|
|
9
9
|
export * from './services/world.js';
|
|
10
|
+
export * from './services/game-time-scheduler.js';
|
|
10
11
|
export * from './services/render.js';
|
|
11
12
|
export * from './services/physics/physics-system.js';
|
|
12
13
|
export * from './animation/anim-sm.js';
|
package/dist/gameplay/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import"reflect-metadata";export{Container as DIContainer}from"typedi";export*from"./initiate.js";export*from"./inject.js";export{Service,Inject}from"typedi";export{ActorFactory}from"./actors/factory.js";export{Actor,BaseActor}from"./actors/actor.js";export{Component,ActorComponent,attach,Attach}from"./actors/component.js";export*from"./services/world.js";export*from"./services/render.js";export*from"./services/physics/physics-system.js";export*from"./animation/anim-sm.js";export*from"./animation/root-motion.js";export*from"./services/asset-loader.js";export*from"./services/pointer-events.js";export{VisualEffect}from"../effects/vfx/vfx-param.js";export*from"../shader/parameter.js";export*from"./ai/index.js";export*from"../effects/vfx/index.js";export*from"./services/shader-provider.js";/*
|
|
1
|
+
import"reflect-metadata";export{Container as DIContainer}from"typedi";export*from"./initiate.js";export*from"./inject.js";export{Service,Inject}from"typedi";export{ActorFactory}from"./actors/factory.js";export{Actor,BaseActor}from"./actors/actor.js";export{Component,ActorComponent,attach,Attach}from"./actors/component.js";export*from"./services/world.js";export*from"./services/game-time-scheduler.js";export*from"./services/render.js";export*from"./services/physics/physics-system.js";export*from"./animation/anim-sm.js";export*from"./animation/root-motion.js";export*from"./services/asset-loader.js";export*from"./services/pointer-events.js";export{VisualEffect}from"../effects/vfx/vfx-param.js";export*from"../shader/parameter.js";export*from"./ai/index.js";export*from"../effects/vfx/index.js";export*from"./services/shader-provider.js";/*
|
|
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
|
-
import e from"typedi";import{loadScene as t}from"../scene/bootstrap.js";import{ActorFactory as s}from"./actors/factory.js";import{World as n}from"./services/world.js";import{SceneMaterializer as
|
|
1
|
+
import e from"typedi";import{loadScene as t}from"../scene/bootstrap.js";import{ActorFactory as s}from"./actors/factory.js";import{World as n}from"./services/world.js";import{SceneMaterializer as r}from"../scene/materializer.js";import{ViewController as o}from"./services/render.js";import{RenderingView as i}from"../rendering.js";import{PhysicsSystem as a}from"./services/physics/physics-system.js";import{MeshComponent as c}from"./actors/builtin/components/mesh-component.js";import{builtInComponents as d}from"./actors/builtin/components/index.js";import{activeContainerInstance as m}from"./actors/internal/container-map.js";import{InputService as l}from"./input/index.js";import{RuntimeAssetsService as p}from"../scene/runtime-asset-service.js";import{AssetResourceLoader as h}from"../scene/asset-resource-loader.js";import{AssetLoader as u}from"./services/asset-loader.js";import{polyfillClient as f}from"./polyfill.js";import{Subject as g}from"rxjs";import{PointerEvents as w}from"./services/pointer-events.js";import{RuntimeBundledBackendService as b}from"../scene/runtime-bundled-backend-service.js";import{Scene as j}from"three";import{ShaderProvider as v}from"./services/shader-provider.js";import{SceneDataService as S}from"../scene/scene-data-service.js";import{AssetsProvider as x}from"../scene/assets-provider.js";export function initiateGame(g,y){if(f(),0!=y.element.childNodes.length)return console.error("Can not initialize the game with a non-empty html element"),null;e.has(o);const I=e.of("default"),P=new HologyRuntime(I),D=new s(I,{inEditor:!1});var R;D.classes=y.actors,e.set(s,D),R=y.element,Object.assign(R.style,{position:"absolute",top:"0",left:"0",width:"100%",height:"100%",overflow:"hidden"});const A=new i(y.element,{enableXR:!0===y.xr?.enabled,maxPixelRatio:y.rendering?.maxPixelRatio,resolutionScale:y.rendering?.resolutionScale,msaa:y.rendering?.msaa,depthPrepass:{enabled:!0===y.rendering?.depthPrepass?.enabled},bloom:{enabled:!1!==y?.rendering?.bloom?.enabled},reflection:{enabled:!1!==y?.rendering?.reflection?.enabled},shadows:{cascadeUpdateIntervals:[22,135,250,500]}});A.renderer.shadowMap.enabled=y.rendering?.shadows?.enabled??!0,A.renderer.shadowMap.autoUpdate=y.rendering?.shadows?.autoUpdate??!0,A.renderer.debug.checkShaderErrors=!1,e.set(i,A);const E=new o(A);e.set(o,E);const G=new b,O=new p(G),z=new h;z.setDataDir(y.dataDir),z.initKtx2(A.renderer);const H=Object.entries(y.shaders).map(([e,t])=>({name:e,type:t})),M=Object.entries(y.actors).map(([e,t])=>({name:e,type:t})),T={...d,...y.components??{}},U=Object.entries(T).map(([e,t])=>({name:e,type:t})),W=new v(H);e.set(v,W);const k=new u(z,O,H);e.set(x,O),e.set(h,z),e.set(u,k);const F=new j,N=new r(F,new S,O,z,A,H,M,D,U);e.set(r,N);const _=e.get(n);return e.set(n,_),_.materializer=N,(async()=>{const s=e.get(a);if(await s.start(),P.isShutdown)return;if(await G.preloadData(),P.isShutdown)return;_.scene=F;const{scene:n,actors:r}=await t(A,y.sceneName,y.dataDir,y.shaders,y.actors,T,D,G,O,z,{detailTier:y.detailTier});_.scene=n,s.scene=_.scene;for(const e of F.children)_.scene.add(e);if(P.isShutdown)return void A.stop();e.import([c]);for(const e of r)_.addActor(e);s.addFromScene(n),console.log("Start compile shaders"),console.time("compile shaders"),await A.compileAsync(),console.timeEnd("compile shaders"),console.log("Finished compile shaders. Programs: ",A.renderer.info.programs?.length??0),console.log("Start init scene textures"),console.time("init scene textures"),A.initTextures(),console.timeEnd("init scene textures");const o=I.get(l);A.loop(e=>{o.update(e)}),P.status=5,P.shutdownStarted.subscribe(()=>{z.disposeAll()}),m.value=I,I.remove(g),I.set({id:g,type:g});const i=I.get(g);m.value=null,P.gameInstance=i,I.get(w).start(),i instanceof GameInstance&&await i.onStart(),P._resolver(!0)})(),P}export class GameInstance{onStart(){}onShutdown(){}}export function createHologyScene(){}export class HologyRuntime{constructor(e){this.containerInstance=e,this.status=0,this.isShutdown=!1,this.shutdownStarted=new g,this.ready=new Promise(e=>{this._resolver=e})}getWorld(){return this.containerInstance.get(n)}getService(e){return this.containerInstance.get(e)}shutdown(){this.isShutdown=!0;const e=this.shutdownStarted;e.next(),e.complete(),this.gameInstance instanceof GameInstance&&this.gameInstance.onShutdown(),this.containerInstance.get(l).stop();const t=this.containerInstance.get(i);t?.stop();const s=this.containerInstance.get(o);s.setMuted(!0),s.dispose();for(const e of this.getWorld().actors)this.getWorld().removeActor(e);this.containerInstance.get(a).stop(),this.containerInstance.get(r).dispose(),this.containerInstance.get(w).stop(),this.containerInstance.reset()}}/*
|
|
2
2
|
* Copyright (©) 2026 Hology Interactive AB. All rights reserved.
|
|
3
3
|
* See the LICENSE.md file for details.
|
|
4
4
|
*/
|