diff --git a/README.md b/README.md index 81e6094..bf0bc10 100644 --- a/README.md +++ b/README.md @@ -28,28 +28,37 @@ Three.js powered Minecraft skin viewer. capeUrl: "img/cape.png" }); - // change the skin and cape - // skinViewer.skinUrl = "img/skin.png"; - // skinViewer.capeUrl = "img/cape.png"; + // Change the textures + skinViewer.skinUrl = "img/skin2.png"; + skinViewer.capeUrl = "img/cape2.png"; - // change the width and height - // skinViewer.width = 300; - // skinViewer.height = 400; + // Resize the skin viewer + skinViewer.width = 300; + skinViewer.height = 400; - // enable the mouse control feature - let control = new skinview3d.SkinControl(skinViewer); - - // disable the 'right click to play/pause' feature - // control.enableAnimationControl = false; + // Control objects with your mouse! + let control = skinview3d.createOrbitControls(skinViewer); + control.enableRotate = true; + control.enableZoom = false; + control.enablePan = false; skinViewer.animation = new skinview3d.CompositeAnimation(); - // add an animation - let walk = skinViewer.animation.add(skinview3d.WalkAnimation); + // Add an animation + let walk = skinViewer.animation.add(skinview3d.WalkingAnimation); + // Add another animation + let rotate = skinViewer.animation.add(skinview3d.RotatingAnimation); + // Remove an animation, stop walking dude + walk.remove(); + // And run for now! + let run = skinViewer.animation.add(skinview3d.RunningAnimation); - // set its speed and some others - walk.speed = 1.5; - // walk.paused = true; + // Set the speed of an animation + run.speed = 3; + // Pause single animation + run.paused = true; + // Pause all animations! + skinViewer.animationPaused = true; ``` diff --git a/src/animation.js b/src/animation.js index 7f53b04..bbacf6a 100644 --- a/src/animation.js +++ b/src/animation.js @@ -55,17 +55,73 @@ class CompositeAnimation { } } -let WalkAnimation = (player, time) => { +let WalkingAnimation = (player, time) => { let skin = player.skin; - let angleRot = time + Math.PI / 2; - // Leg Swing - skin.leftLeg.rotation.x = Math.cos(angleRot); - skin.rightLeg.rotation.x = Math.cos(angleRot + (Math.PI)); + // Multiply by animation's natural speed + time *= 8; - // Arm Swing - skin.leftArm.rotation.x = Math.cos(angleRot + (Math.PI)); - skin.rightArm.rotation.x = Math.cos(angleRot); + // Leg swing + skin.leftLeg.rotation.x = Math.sin(time) * 0.5; + skin.rightLeg.rotation.x = Math.sin(time + Math.PI) * 0.5; + + // Arm swing + skin.leftArm.rotation.x = Math.sin(time + Math.PI) * 0.5; + skin.rightArm.rotation.x = Math.sin(time) * 0.5; + let basicArmRotationZ = Math.PI * 0.02; + skin.leftArm.rotation.z = Math.cos(time) * 0.03 + basicArmRotationZ; + skin.rightArm.rotation.z = Math.cos(time + Math.PI) * 0.03 - basicArmRotationZ; + + // Head shaking with different frequency & amplitude + skin.head.rotation.y = Math.sin(time / 4) * 0.2; + skin.head.rotation.x = Math.sin(time / 5) * 0.1; + + // Always add an angle for cape around the x axis + let basicCapeRotationX = Math.PI * 0.06; + player.cape.rotation.x = Math.sin(time / 1.5) * 0.06 + basicCapeRotationX; }; -export { CompositeAnimation, WalkAnimation, invokeAnimation }; +let RunningAnimation = (player, time) => { + let skin = player.skin; + + time *= 15; + + // Leg swing with larger amplitude + skin.leftLeg.rotation.x = Math.cos(time + Math.PI) * 1.3; + skin.rightLeg.rotation.x = Math.cos(time) * 1.3; + + // Arm swing + skin.leftArm.rotation.x = Math.cos(time) * 1.5; + skin.rightArm.rotation.x = Math.cos(time + Math.PI) * 1.5; + let basicArmRotationZ = Math.PI * 0.1; + skin.leftArm.rotation.z = Math.cos(time) * 0.1 + basicArmRotationZ; + skin.rightArm.rotation.z = Math.cos(time + Math.PI) * 0.1 - basicArmRotationZ; + + // Jumping + player.position.y = Math.cos(time * 2); + // Dodging when running + player.position.x = Math.cos(time) * 0.15; + // Slightly tilting when running + player.rotation.z = Math.cos(time + Math.PI) * 0.01; + + // Apply higher swing frequency, lower amplitude, + // and greater basic rotation around x axis, + // to cape when running. + let basicCapeRotationX = Math.PI * 0.3; + player.cape.rotation.x = Math.sin(time * 2) * 0.1 + basicCapeRotationX; + + // What about head shaking? + // You shouldn't glance right and left when running dude :P +}; + +let RotatingAnimation = (player, time) => { + player.rotation.y = time; +}; + +export { + CompositeAnimation, + invokeAnimation, + WalkingAnimation, + RunningAnimation, + RotatingAnimation +}; diff --git a/src/orbit_controls.js b/src/orbit_controls.js index 08f8af2..633ee47 100644 --- a/src/orbit_controls.js +++ b/src/orbit_controls.js @@ -593,4 +593,17 @@ class OrbitControls extends THREE.EventDispatcher { } } -export { OrbitControls }; +function createOrbitControls(skinViewer) { + let control = new OrbitControls(skinViewer.camera, skinViewer.renderer.domElement); + + // default configuration + control.enablePan = false; + control.target = new THREE.Vector3(0, -12, 0); + control.minDistance = 10; + control.maxDistance = 256; + control.update(); + + return control; +} + +export { OrbitControls, createOrbitControls }; diff --git a/src/skinview3d.js b/src/skinview3d.js index b5d5c86..f4069d9 100644 --- a/src/skinview3d.js +++ b/src/skinview3d.js @@ -1,3 +1,10 @@ export { SkinObject, CapeObject, PlayerObject } from "./model"; -export { SkinViewer, SkinControl } from "./viewer"; -export { invokeAnimation, CompositeAnimation, WalkAnimation } from "./animation"; +export { SkinViewer } from "./viewer"; +export { OrbitControls, createOrbitControls } from "./orbit_controls"; +export { + invokeAnimation, + CompositeAnimation, + WalkingAnimation, + RunningAnimation, + RotatingAnimation +} from "./animation"; diff --git a/src/viewer.js b/src/viewer.js index 313ab53..964157a 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -1,6 +1,5 @@ import * as THREE from "three"; import { PlayerObject } from "./model"; -import { OrbitControls } from "./orbit_controls"; import { invokeAnimation } from "./animation"; function copyImage(context, sX, sY, w, h, dX, dY, flipHorizontal) { @@ -81,9 +80,10 @@ class SkinViewer { // scene this.scene = new THREE.Scene(); - this.camera = new THREE.PerspectiveCamera(75); + // Use smaller fov to avoid distortion + this.camera = new THREE.PerspectiveCamera(40); this.camera.position.y = -12; - this.camera.position.z = 30; + this.camera.position.z = 60; this.renderer = new THREE.WebGLRenderer({ angleRot: true, alpha: true, antialias: false }); this.renderer.setSize(300, 300); // default size @@ -227,31 +227,4 @@ class SkinViewer { } } -class SkinControl { - constructor(skinViewer) { - this.enableAnimationControl = true; - this.skinViewer = skinViewer; - - this.orbitControls = new OrbitControls(skinViewer.camera, skinViewer.renderer.domElement); - this.orbitControls.enablePan = false; - this.orbitControls.target = new THREE.Vector3(0, -12, 0); - this.orbitControls.minDistance = 10; - this.orbitControls.maxDistance = 256; - this.orbitControls.update(); - - this.animationPauseListener = e => { - if (this.enableAnimationControl) { - e.preventDefault(); - this.skinViewer.animationPaused = !this.skinViewer.animationPaused; - } - }; - this.skinViewer.domElement.addEventListener("contextmenu", this.animationPauseListener, false); - } - - dispose() { - this.skinViewer.domElement.removeEventListener("contextmenu", this.animationPauseListener, false); - this.orbitControls.dispose(); - } -} - -export { SkinViewer, SkinControl }; +export { SkinViewer }; diff --git a/types/animation.d.ts b/types/animation.d.ts index 8fa4d62..9b89563 100644 --- a/types/animation.d.ts +++ b/types/animation.d.ts @@ -28,4 +28,6 @@ export class CompositeAnimation implements IAnimation { public play(player: PlayerObject, time: number): void; } -export const WalkAnimation: AnimationFn; +export const WalkingAnimation: AnimationFn; +export const RunningAnimation: AnimationFn; +export const RotatingAnimation: AnimationFn; diff --git a/types/orbit_controls.d.ts b/types/orbit_controls.d.ts new file mode 100644 index 0000000..73cdcac --- /dev/null +++ b/types/orbit_controls.d.ts @@ -0,0 +1,57 @@ +import * as THREE from "three"; +import { SkinViewer } from "./viewer"; + +export class OrbitControls { + + public readonly object: THREE.Camera; + public readonly domElement: HTMLElement | HTMLDocument; + + public enabled: boolean; + public target: THREE.Vector3; + + public minDistance: number; + public maxDistance: number; + + public minZoom: number; + public maxZoom: number; + + public minPolarAngle: number; + public maxPolarAngle: number; + + public minAzimuthAngle: number; + public maxAzimuthAngle: number; + + public enableDamping: boolean; + public dampingFactor: number; + + public enableZoom: boolean; + public zoomSpeed: number; + + public enableRotate: boolean; + public rotateSpeed: number; + + public enablePan: boolean; + public keyPanSpeed: number; + + public autoRotate: boolean; + public autoRotateSpeed: number; + + public enableKeys: boolean; + public keys: { LEFT: number, UP: number, RIGHT: number, BOTTOM: number }; + + public mouseButtons: { ORBIT: THREE.MOUSE, ZOOM: THREE.MOUSE, PAN: THREE.MOUSE }; + + constructor(object: THREE.Camera, domElement?: HTMLElement); + + public getPolarAngle(): number; + public getAzimuthalAngle(): number; + + public saveState(): void; + public reset(): void; + + public update(): boolean; + + public dispose(): void; +} + +export function createOrbitControls(skinViewer: SkinViewer): OrbitControls; diff --git a/types/skinview3d.d.ts b/types/skinview3d.d.ts index 43581bc..befec93 100644 --- a/types/skinview3d.d.ts +++ b/types/skinview3d.d.ts @@ -1,3 +1,4 @@ export * from "./model"; export * from "./animation"; export * from "./viewer"; +export * from "./orbit_controls"; diff --git a/types/viewer.d.ts b/types/viewer.d.ts index f4bafd6..e6e64d0 100644 --- a/types/viewer.d.ts +++ b/types/viewer.d.ts @@ -34,8 +34,10 @@ export class SkinViewer { public dispose(): void; } -export class SkinControl { - public enableAnimationControl: boolean; +export class MouseControl { + public pan: boolean; + public zoom: boolean; + public rotation: boolean; public readonly skinViewer: SkinViewer; constructor(skinViewer: SkinViewer);