| import { WrapAroundEnding, ZeroCurvatureEnding, ZeroSlopeEnding, LoopPingPong, LoopOnce, LoopRepeat } from '../constants.js'; |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| function AnimationAction( mixer, clip, localRoot ) { |
|
|
| this._mixer = mixer; |
| this._clip = clip; |
| this._localRoot = localRoot || null; |
|
|
| var tracks = clip.tracks, |
| nTracks = tracks.length, |
| interpolants = new Array( nTracks ); |
|
|
| var interpolantSettings = { |
| endingStart: ZeroCurvatureEnding, |
| endingEnd: ZeroCurvatureEnding |
| }; |
|
|
| for ( var i = 0; i !== nTracks; ++ i ) { |
|
|
| var interpolant = tracks[ i ].createInterpolant( null ); |
| interpolants[ i ] = interpolant; |
| interpolant.settings = interpolantSettings; |
|
|
| } |
|
|
| this._interpolantSettings = interpolantSettings; |
|
|
| this._interpolants = interpolants; |
|
|
| |
| this._propertyBindings = new Array( nTracks ); |
|
|
| this._cacheIndex = null; |
| this._byClipCacheIndex = null; |
|
|
| this._timeScaleInterpolant = null; |
| this._weightInterpolant = null; |
|
|
| this.loop = LoopRepeat; |
| this._loopCount = - 1; |
|
|
| |
| |
| this._startTime = null; |
|
|
| |
| |
| this.time = 0; |
|
|
| this.timeScale = 1; |
| this._effectiveTimeScale = 1; |
|
|
| this.weight = 1; |
| this._effectiveWeight = 1; |
|
|
| this.repetitions = Infinity; |
|
|
| this.paused = false; |
| this.enabled = true; |
|
|
| this.clampWhenFinished = false; |
|
|
| this.zeroSlopeAtStart = true; |
| this.zeroSlopeAtEnd = true; |
|
|
| } |
|
|
| Object.assign( AnimationAction.prototype, { |
|
|
| |
|
|
| play: function () { |
|
|
| this._mixer._activateAction( this ); |
|
|
| return this; |
|
|
| }, |
|
|
| stop: function () { |
|
|
| this._mixer._deactivateAction( this ); |
|
|
| return this.reset(); |
|
|
| }, |
|
|
| reset: function () { |
|
|
| this.paused = false; |
| this.enabled = true; |
|
|
| this.time = 0; |
| this._loopCount = - 1; |
| this._startTime = null; |
|
|
| return this.stopFading().stopWarping(); |
|
|
| }, |
|
|
| isRunning: function () { |
|
|
| return this.enabled && ! this.paused && this.timeScale !== 0 && |
| this._startTime === null && this._mixer._isActiveAction( this ); |
|
|
| }, |
|
|
| |
| isScheduled: function () { |
|
|
| return this._mixer._isActiveAction( this ); |
|
|
| }, |
|
|
| startAt: function ( time ) { |
|
|
| this._startTime = time; |
|
|
| return this; |
|
|
| }, |
|
|
| setLoop: function ( mode, repetitions ) { |
|
|
| this.loop = mode; |
| this.repetitions = repetitions; |
|
|
| return this; |
|
|
| }, |
|
|
| |
|
|
| |
| |
| |
| setEffectiveWeight: function ( weight ) { |
|
|
| this.weight = weight; |
|
|
| |
| this._effectiveWeight = this.enabled ? weight : 0; |
|
|
| return this.stopFading(); |
|
|
| }, |
|
|
| |
| getEffectiveWeight: function () { |
|
|
| return this._effectiveWeight; |
|
|
| }, |
|
|
| fadeIn: function ( duration ) { |
|
|
| return this._scheduleFading( duration, 0, 1 ); |
|
|
| }, |
|
|
| fadeOut: function ( duration ) { |
|
|
| return this._scheduleFading( duration, 1, 0 ); |
|
|
| }, |
|
|
| crossFadeFrom: function ( fadeOutAction, duration, warp ) { |
|
|
| fadeOutAction.fadeOut( duration ); |
| this.fadeIn( duration ); |
|
|
| if ( warp ) { |
|
|
| var fadeInDuration = this._clip.duration, |
| fadeOutDuration = fadeOutAction._clip.duration, |
|
|
| startEndRatio = fadeOutDuration / fadeInDuration, |
| endStartRatio = fadeInDuration / fadeOutDuration; |
|
|
| fadeOutAction.warp( 1.0, startEndRatio, duration ); |
| this.warp( endStartRatio, 1.0, duration ); |
|
|
| } |
|
|
| return this; |
|
|
| }, |
|
|
| crossFadeTo: function ( fadeInAction, duration, warp ) { |
|
|
| return fadeInAction.crossFadeFrom( this, duration, warp ); |
|
|
| }, |
|
|
| stopFading: function () { |
|
|
| var weightInterpolant = this._weightInterpolant; |
|
|
| if ( weightInterpolant !== null ) { |
|
|
| this._weightInterpolant = null; |
| this._mixer._takeBackControlInterpolant( weightInterpolant ); |
|
|
| } |
|
|
| return this; |
|
|
| }, |
|
|
| |
|
|
| |
| |
| |
| setEffectiveTimeScale: function ( timeScale ) { |
|
|
| this.timeScale = timeScale; |
| this._effectiveTimeScale = this.paused ? 0 : timeScale; |
|
|
| return this.stopWarping(); |
|
|
| }, |
|
|
| |
| getEffectiveTimeScale: function () { |
|
|
| return this._effectiveTimeScale; |
|
|
| }, |
|
|
| setDuration: function ( duration ) { |
|
|
| this.timeScale = this._clip.duration / duration; |
|
|
| return this.stopWarping(); |
|
|
| }, |
|
|
| syncWith: function ( action ) { |
|
|
| this.time = action.time; |
| this.timeScale = action.timeScale; |
|
|
| return this.stopWarping(); |
|
|
| }, |
|
|
| halt: function ( duration ) { |
|
|
| return this.warp( this._effectiveTimeScale, 0, duration ); |
|
|
| }, |
|
|
| warp: function ( startTimeScale, endTimeScale, duration ) { |
|
|
| var mixer = this._mixer, now = mixer.time, |
| interpolant = this._timeScaleInterpolant, |
|
|
| timeScale = this.timeScale; |
|
|
| if ( interpolant === null ) { |
|
|
| interpolant = mixer._lendControlInterpolant(); |
| this._timeScaleInterpolant = interpolant; |
|
|
| } |
|
|
| var times = interpolant.parameterPositions, |
| values = interpolant.sampleValues; |
|
|
| times[ 0 ] = now; |
| times[ 1 ] = now + duration; |
|
|
| values[ 0 ] = startTimeScale / timeScale; |
| values[ 1 ] = endTimeScale / timeScale; |
|
|
| return this; |
|
|
| }, |
|
|
| stopWarping: function () { |
|
|
| var timeScaleInterpolant = this._timeScaleInterpolant; |
|
|
| if ( timeScaleInterpolant !== null ) { |
|
|
| this._timeScaleInterpolant = null; |
| this._mixer._takeBackControlInterpolant( timeScaleInterpolant ); |
|
|
| } |
|
|
| return this; |
|
|
| }, |
|
|
| |
|
|
| getMixer: function () { |
|
|
| return this._mixer; |
|
|
| }, |
|
|
| getClip: function () { |
|
|
| return this._clip; |
|
|
| }, |
|
|
| getRoot: function () { |
|
|
| return this._localRoot || this._mixer._root; |
|
|
| }, |
|
|
| |
|
|
| _update: function ( time, deltaTime, timeDirection, accuIndex ) { |
|
|
| |
|
|
| if ( ! this.enabled ) { |
|
|
| |
|
|
| this._updateWeight( time ); |
| return; |
|
|
| } |
|
|
| var startTime = this._startTime; |
|
|
| if ( startTime !== null ) { |
|
|
| |
|
|
| var timeRunning = ( time - startTime ) * timeDirection; |
| if ( timeRunning < 0 || timeDirection === 0 ) { |
|
|
| return; |
|
|
| } |
|
|
| |
|
|
| this._startTime = null; |
| deltaTime = timeDirection * timeRunning; |
|
|
| } |
|
|
| |
|
|
| deltaTime *= this._updateTimeScale( time ); |
| var clipTime = this._updateTime( deltaTime ); |
|
|
| |
| |
|
|
| var weight = this._updateWeight( time ); |
|
|
| if ( weight > 0 ) { |
|
|
| var interpolants = this._interpolants; |
| var propertyMixers = this._propertyBindings; |
|
|
| for ( var j = 0, m = interpolants.length; j !== m; ++ j ) { |
|
|
| interpolants[ j ].evaluate( clipTime ); |
| propertyMixers[ j ].accumulate( accuIndex, weight ); |
|
|
| } |
|
|
| } |
|
|
| }, |
|
|
| _updateWeight: function ( time ) { |
|
|
| var weight = 0; |
|
|
| if ( this.enabled ) { |
|
|
| weight = this.weight; |
| var interpolant = this._weightInterpolant; |
|
|
| if ( interpolant !== null ) { |
|
|
| var interpolantValue = interpolant.evaluate( time )[ 0 ]; |
|
|
| weight *= interpolantValue; |
|
|
| if ( time > interpolant.parameterPositions[ 1 ] ) { |
|
|
| this.stopFading(); |
|
|
| if ( interpolantValue === 0 ) { |
|
|
| |
| this.enabled = false; |
|
|
| } |
|
|
| } |
|
|
| } |
|
|
| } |
|
|
| this._effectiveWeight = weight; |
| return weight; |
|
|
| }, |
|
|
| _updateTimeScale: function ( time ) { |
|
|
| var timeScale = 0; |
|
|
| if ( ! this.paused ) { |
|
|
| timeScale = this.timeScale; |
|
|
| var interpolant = this._timeScaleInterpolant; |
|
|
| if ( interpolant !== null ) { |
|
|
| var interpolantValue = interpolant.evaluate( time )[ 0 ]; |
|
|
| timeScale *= interpolantValue; |
|
|
| if ( time > interpolant.parameterPositions[ 1 ] ) { |
|
|
| this.stopWarping(); |
|
|
| if ( timeScale === 0 ) { |
|
|
| |
| this.paused = true; |
|
|
| } else { |
|
|
| |
| this.timeScale = timeScale; |
|
|
| } |
|
|
| } |
|
|
| } |
|
|
| } |
|
|
| this._effectiveTimeScale = timeScale; |
| return timeScale; |
|
|
| }, |
|
|
| _updateTime: function ( deltaTime ) { |
|
|
| var time = this.time + deltaTime; |
| var duration = this._clip.duration; |
| var loop = this.loop; |
| var loopCount = this._loopCount; |
|
|
| var pingPong = ( loop === LoopPingPong ); |
|
|
| if ( deltaTime === 0 ) { |
|
|
| if ( loopCount === - 1 ) return time; |
|
|
| return ( pingPong && ( loopCount & 1 ) === 1 ) ? duration - time : time; |
|
|
| } |
|
|
| if ( loop === LoopOnce ) { |
|
|
| if ( loopCount === - 1 ) { |
|
|
| |
|
|
| this._loopCount = 0; |
| this._setEndings( true, true, false ); |
|
|
| } |
|
|
| handle_stop: { |
|
|
| if ( time >= duration ) { |
|
|
| time = duration; |
|
|
| } else if ( time < 0 ) { |
|
|
| time = 0; |
|
|
| } else break handle_stop; |
|
|
| if ( this.clampWhenFinished ) this.paused = true; |
| else this.enabled = false; |
|
|
| this._mixer.dispatchEvent( { |
| type: 'finished', action: this, |
| direction: deltaTime < 0 ? - 1 : 1 |
| } ); |
|
|
| } |
|
|
| } else { |
|
|
| if ( loopCount === - 1 ) { |
|
|
| |
|
|
| if ( deltaTime >= 0 ) { |
|
|
| loopCount = 0; |
|
|
| this._setEndings( true, this.repetitions === 0, pingPong ); |
|
|
| } else { |
|
|
| |
| |
| |
|
|
| this._setEndings( this.repetitions === 0, true, pingPong ); |
|
|
| } |
|
|
| } |
|
|
| if ( time >= duration || time < 0 ) { |
|
|
| |
|
|
| var loopDelta = Math.floor( time / duration ); |
| time -= duration * loopDelta; |
|
|
| loopCount += Math.abs( loopDelta ); |
|
|
| var pending = this.repetitions - loopCount; |
|
|
| if ( pending <= 0 ) { |
|
|
| |
|
|
| if ( this.clampWhenFinished ) this.paused = true; |
| else this.enabled = false; |
|
|
| time = deltaTime > 0 ? duration : 0; |
|
|
| this._mixer.dispatchEvent( { |
| type: 'finished', action: this, |
| direction: deltaTime > 0 ? 1 : - 1 |
| } ); |
|
|
| } else { |
|
|
| |
|
|
| if ( pending === 1 ) { |
|
|
| |
|
|
| var atStart = deltaTime < 0; |
| this._setEndings( atStart, ! atStart, pingPong ); |
|
|
| } else { |
|
|
| this._setEndings( false, false, pingPong ); |
|
|
| } |
|
|
| this._loopCount = loopCount; |
|
|
| this._mixer.dispatchEvent( { |
| type: 'loop', action: this, loopDelta: loopDelta |
| } ); |
|
|
| } |
|
|
| } |
|
|
| if ( pingPong && ( loopCount & 1 ) === 1 ) { |
|
|
| |
|
|
| this.time = time; |
| return duration - time; |
|
|
| } |
|
|
| } |
|
|
| this.time = time; |
| return time; |
|
|
| }, |
|
|
| _setEndings: function ( atStart, atEnd, pingPong ) { |
|
|
| var settings = this._interpolantSettings; |
|
|
| if ( pingPong ) { |
|
|
| settings.endingStart = ZeroSlopeEnding; |
| settings.endingEnd = ZeroSlopeEnding; |
|
|
| } else { |
|
|
| |
|
|
| if ( atStart ) { |
|
|
| settings.endingStart = this.zeroSlopeAtStart ? ZeroSlopeEnding : ZeroCurvatureEnding; |
|
|
| } else { |
|
|
| settings.endingStart = WrapAroundEnding; |
|
|
| } |
|
|
| if ( atEnd ) { |
|
|
| settings.endingEnd = this.zeroSlopeAtEnd ? ZeroSlopeEnding : ZeroCurvatureEnding; |
|
|
| } else { |
|
|
| settings.endingEnd = WrapAroundEnding; |
|
|
| } |
|
|
| } |
|
|
| }, |
|
|
| _scheduleFading: function ( duration, weightNow, weightThen ) { |
|
|
| var mixer = this._mixer, now = mixer.time, |
| interpolant = this._weightInterpolant; |
|
|
| if ( interpolant === null ) { |
|
|
| interpolant = mixer._lendControlInterpolant(); |
| this._weightInterpolant = interpolant; |
|
|
| } |
|
|
| var times = interpolant.parameterPositions, |
| values = interpolant.sampleValues; |
|
|
| times[ 0 ] = now; |
| values[ 0 ] = weightNow; |
| times[ 1 ] = now + duration; |
| values[ 1 ] = weightThen; |
|
|
| return this; |
|
|
| } |
|
|
| } ); |
|
|
|
|
| export { AnimationAction }; |
|
|