Merge branch 'animation-refactor'
This commit is contained in:
commit
db1884e782
12
README.md
12
README.md
|
@ -42,23 +42,23 @@ Three.js powered Minecraft skin viewer.
|
||||||
control.enableZoom = false;
|
control.enableZoom = false;
|
||||||
control.enablePan = false;
|
control.enablePan = false;
|
||||||
|
|
||||||
skinViewer.animation = new skinview3d.CompositeAnimation();
|
|
||||||
|
|
||||||
// Add an animation
|
// Add an animation
|
||||||
let walk = skinViewer.animation.add(skinview3d.WalkingAnimation);
|
let walk = skinViewer.animations.add(skinview3d.WalkingAnimation);
|
||||||
// Add another animation
|
// Add another animation
|
||||||
let rotate = skinViewer.animation.add(skinview3d.RotatingAnimation);
|
let rotate = skinViewer.animations.add(skinview3d.RotatingAnimation);
|
||||||
// Remove an animation, stop walking dude
|
// Remove an animation, stop walking dude
|
||||||
walk.remove();
|
walk.remove();
|
||||||
|
// Remove the rotating animation, and make the player face forward
|
||||||
|
rotate.resetAndRemove();
|
||||||
// And run for now!
|
// And run for now!
|
||||||
let run = skinViewer.animation.add(skinview3d.RunningAnimation);
|
let run = skinViewer.animations.add(skinview3d.RunningAnimation);
|
||||||
|
|
||||||
// Set the speed of an animation
|
// Set the speed of an animation
|
||||||
run.speed = 3;
|
run.speed = 3;
|
||||||
// Pause single animation
|
// Pause single animation
|
||||||
run.paused = true;
|
run.paused = true;
|
||||||
// Pause all animations!
|
// Pause all animations!
|
||||||
skinViewer.animationPaused = true;
|
skinViewer.animations.paused = true;
|
||||||
</script>
|
</script>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -36,10 +36,8 @@
|
||||||
// skinViewer.playerObject.skin.slim = true;
|
// skinViewer.playerObject.skin.slim = true;
|
||||||
|
|
||||||
let control = new skinview3d.createOrbitControls(skinViewer);
|
let control = new skinview3d.createOrbitControls(skinViewer);
|
||||||
skinViewer.animation = new skinview3d.CompositeAnimation();
|
skinViewer.animations.add(skinview3d.WalkingAnimation);
|
||||||
|
skinViewer.animations.speed = 1.5;
|
||||||
let walk = skinViewer.animation.add(skinview3d.WalkingAnimation);
|
|
||||||
walk.speed = 1.5;
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
101
src/animation.ts
101
src/animation.ts
|
@ -1,4 +1,5 @@
|
||||||
import { PlayerObject } from "./model";
|
import { PlayerObject } from "./model";
|
||||||
|
import * as THREE from "three";
|
||||||
|
|
||||||
export interface IAnimation {
|
export interface IAnimation {
|
||||||
play(player: PlayerObject, time: number): void;
|
play(player: PlayerObject, time: number): void;
|
||||||
|
@ -19,55 +20,65 @@ export function invokeAnimation(animation: Animation, player: PlayerObject, time
|
||||||
|
|
||||||
// This interface is used to control animations
|
// This interface is used to control animations
|
||||||
export interface AnimationHandle {
|
export interface AnimationHandle {
|
||||||
paused: boolean;
|
|
||||||
speed: number;
|
speed: number;
|
||||||
|
paused: boolean;
|
||||||
|
progress: number;
|
||||||
readonly animation: Animation;
|
readonly animation: Animation;
|
||||||
|
|
||||||
reset(): void;
|
reset(): void;
|
||||||
remove(): void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class AnimationWrapper implements AnimationHandle, IAnimation {
|
export interface SubAnimationHandle extends AnimationHandle {
|
||||||
public paused = false;
|
remove(): void;
|
||||||
public speed: number = 1.0;
|
resetAndRemove(): void;
|
||||||
public readonly animation: Animation;
|
}
|
||||||
|
|
||||||
private _paused = false;
|
class AnimationWrapper implements SubAnimationHandle, IAnimation {
|
||||||
private _lastChange: number | null = null;
|
speed: number = 1.0;
|
||||||
private _speed: number = 1.0;
|
paused: boolean = false;
|
||||||
private _lastChangeX: number | null = null;
|
progress: number = 0;
|
||||||
|
readonly animation: Animation;
|
||||||
|
|
||||||
|
private lastTime: number = 0;
|
||||||
|
private started: boolean = false;
|
||||||
|
private toResetAndRemove: boolean = false;
|
||||||
|
|
||||||
constructor(animation: Animation) {
|
constructor(animation: Animation) {
|
||||||
this.animation = animation;
|
this.animation = animation;
|
||||||
}
|
}
|
||||||
|
|
||||||
play(player: PlayerObject, time: number) {
|
play(player: PlayerObject, time: number) {
|
||||||
if (this._lastChange === null) {
|
if (this.toResetAndRemove) {
|
||||||
this._lastChange = time;
|
invokeAnimation(this.animation, player, 0);
|
||||||
this._lastChangeX = 0;
|
this.remove();
|
||||||
} else if (this.paused !== this._paused || this.speed !== this._speed) {
|
return;
|
||||||
const dt = time - this._lastChange;
|
|
||||||
if (this._paused === false) {
|
|
||||||
this._lastChangeX! += dt * this._speed;
|
|
||||||
}
|
|
||||||
this._paused = this.paused;
|
|
||||||
this._speed = this.speed;
|
|
||||||
this._lastChange = time;
|
|
||||||
}
|
}
|
||||||
if (this.paused === false) {
|
|
||||||
const dt = time - this._lastChange;
|
let delta: number;
|
||||||
const x = this._lastChangeX! + this.speed * dt;
|
if (this.started) {
|
||||||
invokeAnimation(this.animation, player, x);
|
delta = time - this.lastTime;
|
||||||
|
} else {
|
||||||
|
delta = 0;
|
||||||
|
this.started = true;
|
||||||
}
|
}
|
||||||
|
this.lastTime = time;
|
||||||
|
if (!this.paused) {
|
||||||
|
this.progress += delta * this.speed;
|
||||||
|
}
|
||||||
|
invokeAnimation(this.animation, player, this.progress);
|
||||||
}
|
}
|
||||||
|
|
||||||
reset() {
|
reset() {
|
||||||
this._lastChange = null;
|
this.progress = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
remove() {
|
remove() {
|
||||||
// stub get's overriden
|
// stub get's overriden
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resetAndRemove() {
|
||||||
|
this.toResetAndRemove = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CompositeAnimation implements IAnimation {
|
export class CompositeAnimation implements IAnimation {
|
||||||
|
@ -76,7 +87,9 @@ export class CompositeAnimation implements IAnimation {
|
||||||
|
|
||||||
add(animation: Animation): AnimationHandle {
|
add(animation: Animation): AnimationHandle {
|
||||||
const handle = new AnimationWrapper(animation);
|
const handle = new AnimationWrapper(animation);
|
||||||
handle.remove = () => this.handles.delete(handle);
|
handle.remove = () => {
|
||||||
|
this.handles.delete(handle);
|
||||||
|
};
|
||||||
this.handles.add(handle);
|
this.handles.add(handle);
|
||||||
return handle;
|
return handle;
|
||||||
}
|
}
|
||||||
|
@ -86,6 +99,40 @@ export class CompositeAnimation implements IAnimation {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class RootAnimation extends CompositeAnimation implements AnimationHandle {
|
||||||
|
speed: number = 1.0;
|
||||||
|
progress: number = 0.0;
|
||||||
|
readonly clock: THREE.Clock = new THREE.Clock(true);
|
||||||
|
|
||||||
|
get animation() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
get paused() {
|
||||||
|
return !this.clock.running;
|
||||||
|
}
|
||||||
|
|
||||||
|
set paused(value) {
|
||||||
|
if (value) {
|
||||||
|
this.clock.stop();
|
||||||
|
} else {
|
||||||
|
this.clock.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
runAnimationLoop(player: PlayerObject): void {
|
||||||
|
if (this.handles.size === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.progress += this.clock.getDelta() * this.speed;
|
||||||
|
this.play(player, this.progress);
|
||||||
|
}
|
||||||
|
|
||||||
|
reset() {
|
||||||
|
this.progress = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const WalkingAnimation: Animation = (player, time) => {
|
export const WalkingAnimation: Animation = (player, time) => {
|
||||||
const skin = player.skin;
|
const skin = player.skin;
|
||||||
|
|
||||||
|
|
|
@ -20,8 +20,11 @@ export {
|
||||||
AnimationFn,
|
AnimationFn,
|
||||||
Animation,
|
Animation,
|
||||||
invokeAnimation,
|
invokeAnimation,
|
||||||
|
|
||||||
|
SubAnimationHandle,
|
||||||
AnimationHandle,
|
AnimationHandle,
|
||||||
CompositeAnimation,
|
CompositeAnimation,
|
||||||
|
RootAnimation,
|
||||||
|
|
||||||
WalkingAnimation,
|
WalkingAnimation,
|
||||||
RunningAnimation,
|
RunningAnimation,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import * as THREE from "three";
|
import * as THREE from "three";
|
||||||
import { PlayerObject } from "./model";
|
import { PlayerObject } from "./model";
|
||||||
import { invokeAnimation } from "./animation";
|
import { RootAnimation } from "./animation";
|
||||||
import { loadSkinToCanvas, loadCapeToCanvas, isSlimSkin } from "./utils";
|
import { loadSkinToCanvas, loadCapeToCanvas, isSlimSkin } from "./utils";
|
||||||
|
|
||||||
export interface SkinViewerOptions {
|
export interface SkinViewerOptions {
|
||||||
|
@ -16,10 +16,8 @@ export interface SkinViewerOptions {
|
||||||
export class SkinViewer {
|
export class SkinViewer {
|
||||||
|
|
||||||
public readonly domElement: Node;
|
public readonly domElement: Node;
|
||||||
public animation: Animation | null;
|
public readonly animations: RootAnimation = new RootAnimation();
|
||||||
public detectModel: boolean = true;
|
public detectModel: boolean = true;
|
||||||
public animationPaused: boolean = false;
|
|
||||||
public animationTime: number = 0;
|
|
||||||
public disposed: boolean = false;
|
public disposed: boolean = false;
|
||||||
|
|
||||||
public readonly skinImg: HTMLImageElement;
|
public readonly skinImg: HTMLImageElement;
|
||||||
|
@ -44,7 +42,6 @@ export class SkinViewer {
|
||||||
|
|
||||||
constructor(options: SkinViewerOptions) {
|
constructor(options: SkinViewerOptions) {
|
||||||
this.domElement = options.domElement;
|
this.domElement = options.domElement;
|
||||||
this.animation = options.animation || null;
|
|
||||||
if (options.detectModel === false) {
|
if (options.detectModel === false) {
|
||||||
this.detectModel = false;
|
this.detectModel = false;
|
||||||
}
|
}
|
||||||
|
@ -123,12 +120,7 @@ export class SkinViewer {
|
||||||
if (this.disposed || this._renderPaused) {
|
if (this.disposed || this._renderPaused) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!this.animationPaused) {
|
this.animations.runAnimationLoop(this.playerObject);
|
||||||
this.animationTime++;
|
|
||||||
if (this.animation) {
|
|
||||||
invokeAnimation(this.animation, this.playerObject, this.animationTime / 100.0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.renderer.render(this.scene, this.camera);
|
this.renderer.render(this.scene, this.camera);
|
||||||
window.requestAnimationFrame(() => this.draw());
|
window.requestAnimationFrame(() => this.draw());
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue