@awayjs/graphics
Version:
AwayJS graphics classes
188 lines (150 loc) • 5.88 kB
text/typescript
import { Vector3D } from '@awayjs/core';
import { JointPose } from '../data/JointPose';
import { Skeleton } from '../data/Skeleton';
import { SkeletonPose } from '../data/SkeletonPose';
import { SkeletonClipNode } from '../nodes/SkeletonClipNode';
import { SkeletonAnimator } from '../SkeletonAnimator';
import { AnimationClipState } from './AnimationClipState';
import { ISkeletonAnimationState } from './ISkeletonAnimationState';
import { AnimatorBase } from '../AnimatorBase';
/**
*
*/
export class SkeletonClipState extends AnimationClipState implements ISkeletonAnimationState {
private _rootPos: Vector3D = new Vector3D();
private _frames: Array<SkeletonPose>;
private _skeletonClipNode: SkeletonClipNode;
private _skeletonPose: SkeletonPose = new SkeletonPose();
private _skeletonPoseDirty: boolean = true;
private _currentPose: SkeletonPose;
private _nextPose: SkeletonPose;
/**
* Returns the current skeleton pose frame of animation in the clip based on the internal playhead position.
*/
public get currentPose(): SkeletonPose {
if (this._pFramesDirty)
this._pUpdateFrames();
return this._currentPose;
}
/**
* Returns the next skeleton pose frame of animation in the clip based on the internal playhead position.
*/
public get nextPose(): SkeletonPose {
if (this._pFramesDirty)
this._pUpdateFrames();
return this._nextPose;
}
constructor(animator: AnimatorBase, skeletonClipNode: SkeletonClipNode) {
super(animator, skeletonClipNode);
this._skeletonClipNode = skeletonClipNode;
this._frames = this._skeletonClipNode.frames;
}
/**
* Returns the current skeleton pose of the animation in the clip based on the internal playhead position.
*/
public getSkeletonPose(skeleton: Skeleton): SkeletonPose {
if (this._skeletonPoseDirty)
this.updateSkeletonPose(skeleton);
return this._skeletonPose;
}
/**
* @inheritDoc
*/
public _pUpdateTime(time: number): void {
this._skeletonPoseDirty = true;
super._pUpdateTime(time);
}
/**
* @inheritDoc
*/
public _pUpdateFrames(): void {
super._pUpdateFrames();
this._currentPose = this._frames[this._pCurrentFrame];
if (this._skeletonClipNode.looping && this._pNextFrame >= this._skeletonClipNode.lastFrame) {
this._nextPose = this._frames[0];
(<SkeletonAnimator> this._pAnimator).dispatchCycleEvent();
} else
this._nextPose = this._frames[this._pNextFrame];
}
/**
* Updates the output skeleton pose of the node based on the internal playhead position.
*
* @param skeleton The skeleton used by the animator requesting the ouput pose.
*/
private updateSkeletonPose(skeleton: Skeleton): void {
this._skeletonPoseDirty = false;
if (!this._skeletonClipNode.totalDuration)
return;
if (this._pFramesDirty)
this._pUpdateFrames();
const currentPose: Array<JointPose> = this._currentPose.jointPoses;
const nextPose: Array<JointPose> = this._nextPose.jointPoses;
const numJoints: number = skeleton.numJoints;
let p1: Vector3D, p2: Vector3D;
let pose1: JointPose, pose2: JointPose;
const endPoses: Array<JointPose> = this._skeletonPose.jointPoses;
let endPose: JointPose;
let tr: Vector3D;
// :s
if (endPoses.length != numJoints)
endPoses.length = numJoints;
if ((numJoints != currentPose.length) || (numJoints != nextPose.length))
throw new Error('joint counts don\'t match!');
for (let i: number = 0; i < numJoints; ++i) {
endPose = endPoses[i];
if (endPose == null)
endPose = endPoses[i] = new JointPose();
pose1 = currentPose[i];
pose2 = nextPose[i];
p1 = pose1.translation;
p2 = pose2.translation;
if (this._skeletonClipNode.highQuality)
endPose.orientation.slerp(pose1.orientation, pose2.orientation, this._pBlendWeight);
else
endPose.orientation.lerp(pose1.orientation, pose2.orientation, this._pBlendWeight);
if (i > 0) {
tr = endPose.translation;
tr.x = p1.x + this._pBlendWeight * (p2.x - p1.x);
tr.y = p1.y + this._pBlendWeight * (p2.y - p1.y);
tr.z = p1.z + this._pBlendWeight * (p2.z - p1.z);
}
}
}
/**
* @inheritDoc
*/
public _pUpdatePositionDelta(): void {
this._pPositionDeltaDirty = false;
if (this._pFramesDirty)
this._pUpdateFrames();
let p1: Vector3D, p2: Vector3D, p3: Vector3D;
const totalDelta: Vector3D = this._skeletonClipNode.totalDelta;
// jumping back, need to reset position
if ((this._pTimeDir > 0 && this._pNextFrame < this._pOldFrame) || (this._pTimeDir < 0 && this._pNextFrame > this._pOldFrame)) {
this._rootPos.x -= totalDelta.x * this._pTimeDir;
this._rootPos.y -= totalDelta.y * this._pTimeDir;
this._rootPos.z -= totalDelta.z * this._pTimeDir;
}
const dx: number = this._rootPos.x;
const dy: number = this._rootPos.y;
const dz: number = this._rootPos.z;
if (this._skeletonClipNode.stitchFinalFrame && this._pNextFrame == this._skeletonClipNode.lastFrame) {
p1 = this._frames[0].jointPoses[0].translation;
p2 = this._frames[1].jointPoses[0].translation;
p3 = this._currentPose.jointPoses[0].translation;
this._rootPos.x = p3.x + p1.x + this._pBlendWeight * (p2.x - p1.x);
this._rootPos.y = p3.y + p1.y + this._pBlendWeight * (p2.y - p1.y);
this._rootPos.z = p3.z + p1.z + this._pBlendWeight * (p2.z - p1.z);
} else {
p1 = this._currentPose.jointPoses[0].translation;
p2 = this._frames[this._pNextFrame].jointPoses[0].translation; //cover the instances where we wrap the pose but still want the final frame translation values
this._rootPos.x = p1.x + this._pBlendWeight * (p2.x - p1.x);
this._rootPos.y = p1.y + this._pBlendWeight * (p2.y - p1.y);
this._rootPos.z = p1.z + this._pBlendWeight * (p2.z - p1.z);
}
this._pRootDelta.x = this._rootPos.x - dx;
this._pRootDelta.y = this._rootPos.y - dy;
this._pRootDelta.z = this._rootPos.z - dz;
this._pOldFrame = this._pNextFrame;
}
}