import { AnimationStateEvent } from '../../events/AnimationStateEvent'; import { AnimationClipNodeBase } from '../nodes/AnimationClipNodeBase'; import { AnimatorBase } from '../AnimatorBase'; import { AnimationStateBase } from './AnimationStateBase'; /** * */ export class AnimationClipState extends AnimationStateBase { private _animationClipNode: AnimationClipNodeBase; private _animationStatePlaybackComplete: AnimationStateEvent; public _pBlendWeight: number; public _pCurrentFrame: number; public _pNextFrame: number; public _pOldFrame: number; public _pTimeDir: number; public _pFramesDirty: boolean = true; /** * Returns a fractional value between 0 and 1 representing the blending ratio of the current playhead position * between the current frame (0) and next frame (1) of the animation. * * @see #currentFrame * @see #nextFrame */ public get blendWeight(): number { if (this._pFramesDirty) this._pUpdateFrames(); return this._pBlendWeight; } /** * Returns the current frame of animation in the clip based on the internal playhead position. */ public get currentFrame(): number { if (this._pFramesDirty) this._pUpdateFrames(); return this._pCurrentFrame; } /** * Returns the next frame of animation in the clip based on the internal playhead position. */ public get nextFrame(): number { if (this._pFramesDirty) this._pUpdateFrames(); return this._pNextFrame; } constructor(animator: AnimatorBase, animationClipNode: AnimationClipNodeBase) { super(animator, animationClipNode); this._animationClipNode = animationClipNode; } /** * @inheritDoc */ public update(time: number): void { if (!this._animationClipNode.looping) { if (time > this._pStartTime + this._animationClipNode.totalDuration) time = this._pStartTime + this._animationClipNode.totalDuration; else if (time < this._pStartTime) time = this._pStartTime; } if (this._pTime == time - this._pStartTime) return; this._pUpdateTime(time); } /** * @inheritDoc */ public phase(value: number): void { const time: number = value * this._animationClipNode.totalDuration + this._pStartTime; if (this._pTime == time - this._pStartTime) return; this._pUpdateTime(time); } /** * @inheritDoc */ public _pUpdateTime(time: number): void { this._pFramesDirty = true; this._pTimeDir = (time - this._pStartTime > this._pTime) ? 1 : -1; super._pUpdateTime(time); } /** * Updates the nodes internal playhead to determine the current and next animation frame, and the blendWeight between the two. * * @see #currentFrame * @see #nextFrame * @see #blendWeight */ public _pUpdateFrames(): void { this._pFramesDirty = false; const looping: boolean = this._animationClipNode.looping; const totalDuration: number = this._animationClipNode.totalDuration; const lastFrame: number = this._animationClipNode.lastFrame; let time: number = this._pTime; //trace("time", time, totalDuration) if (looping && (time >= totalDuration || time < 0)) { time %= totalDuration; if (time < 0) time += totalDuration; } if (!looping && time >= totalDuration) { this.notifyPlaybackComplete(); this._pCurrentFrame = lastFrame; this._pNextFrame = lastFrame; this._pBlendWeight = 0; } else if (!looping && time <= 0) { this._pCurrentFrame = 0; this._pNextFrame = 0; this._pBlendWeight = 0; } else if (this._animationClipNode.fixedFrameRate) { const t: number = time / totalDuration * lastFrame; this._pCurrentFrame = Math.floor(t); this._pBlendWeight = t - this._pCurrentFrame; this._pNextFrame = this._pCurrentFrame + 1; } else { this._pCurrentFrame = 0; this._pNextFrame = 0; let dur: number = 0, frameTime: number; const durations: Array = this._animationClipNode.durations; do { frameTime = dur; dur += durations[this._pNextFrame]; this._pCurrentFrame = this._pNextFrame++; } while (time > dur); if (this._pCurrentFrame == lastFrame) { this._pCurrentFrame = 0; this._pNextFrame = 1; } this._pBlendWeight = (time - frameTime) / durations[this._pCurrentFrame]; } } private notifyPlaybackComplete(): void { if (this._animationStatePlaybackComplete == null) this._animationStatePlaybackComplete = new AnimationStateEvent(AnimationStateEvent.PLAYBACK_COMPLETE, this._pAnimator, this, this._animationClipNode); this._animationClipNode.dispatchEvent(this._animationStatePlaybackComplete); } }