2018-01-14 13:46:20 +01:00
|
|
|
import * as THREE from "three";
|
2018-01-06 11:47:07 +01:00
|
|
|
import { PlayerObject } from "./model";
|
2018-01-06 15:01:12 +01:00
|
|
|
import { invokeAnimation } from "./animation";
|
2017-10-01 14:00:45 +02:00
|
|
|
|
|
|
|
function copyImage(context, sX, sY, w, h, dX, dY, flipHorizontal) {
|
|
|
|
let imgData = context.getImageData(sX, sY, w, h);
|
|
|
|
if (flipHorizontal) {
|
|
|
|
for (let y = 0; y < h; y++) {
|
|
|
|
for (let x = 0; x < (w / 2); x++) {
|
|
|
|
let index = (x + y * w) * 4;
|
|
|
|
let index2 = ((w - x - 1) + y * w) * 4;
|
|
|
|
let pA1 = imgData.data[index];
|
|
|
|
let pA2 = imgData.data[index + 1];
|
|
|
|
let pA3 = imgData.data[index + 2];
|
|
|
|
let pA4 = imgData.data[index + 3];
|
|
|
|
|
|
|
|
let pB1 = imgData.data[index2];
|
|
|
|
let pB2 = imgData.data[index2 + 1];
|
|
|
|
let pB3 = imgData.data[index2 + 2];
|
|
|
|
let pB4 = imgData.data[index2 + 3];
|
|
|
|
|
|
|
|
imgData.data[index] = pB1;
|
|
|
|
imgData.data[index + 1] = pB2;
|
|
|
|
imgData.data[index + 2] = pB3;
|
|
|
|
imgData.data[index + 3] = pB4;
|
|
|
|
|
|
|
|
imgData.data[index2] = pA1;
|
|
|
|
imgData.data[index2 + 1] = pA2;
|
|
|
|
imgData.data[index2 + 2] = pA3;
|
|
|
|
imgData.data[index2 + 3] = pA4;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
context.putImageData(imgData, dX, dY);
|
|
|
|
}
|
|
|
|
|
|
|
|
function convertSkinTo1_8(context, width) {
|
|
|
|
let scale = width / 64.0;
|
|
|
|
let copySkin = (context, sX, sY, w, h, dX, dY, flipHorizontal) => copyImage(context, sX * scale, sY * scale, w * scale, h * scale, dX * scale, dY * scale, flipHorizontal);
|
|
|
|
|
|
|
|
copySkin(context, 4, 16, 4, 4, 20, 48, true); // Top Leg
|
|
|
|
copySkin(context, 8, 16, 4, 4, 24, 48, true); // Bottom Leg
|
|
|
|
copySkin(context, 0, 20, 4, 12, 24, 52, true); // Outer Leg
|
|
|
|
copySkin(context, 4, 20, 4, 12, 20, 52, true); // Front Leg
|
|
|
|
copySkin(context, 8, 20, 4, 12, 16, 52, true); // Inner Leg
|
|
|
|
copySkin(context, 12, 20, 4, 12, 28, 52, true); // Back Leg
|
|
|
|
copySkin(context, 44, 16, 4, 4, 36, 48, true); // Top Arm
|
|
|
|
copySkin(context, 48, 16, 4, 4, 40, 48, true); // Bottom Arm
|
|
|
|
copySkin(context, 40, 20, 4, 12, 40, 52, true); // Outer Arm
|
|
|
|
copySkin(context, 44, 20, 4, 12, 36, 52, true); // Front Arm
|
|
|
|
copySkin(context, 48, 20, 4, 12, 32, 52, true); // Inner Arm
|
|
|
|
copySkin(context, 52, 20, 4, 12, 44, 52, true); // Back Arm
|
|
|
|
}
|
|
|
|
|
|
|
|
class SkinViewer {
|
|
|
|
constructor(options) {
|
|
|
|
this.domElement = options.domElement;
|
|
|
|
this.animation = options.animation || null;
|
|
|
|
this.animationPaused = false;
|
|
|
|
this.animationTime = 0;
|
|
|
|
this.disposed = false;
|
|
|
|
|
|
|
|
// texture
|
|
|
|
this.skinImg = new Image();
|
|
|
|
this.skinCanvas = document.createElement("canvas");
|
|
|
|
this.skinTexture = new THREE.Texture(this.skinCanvas);
|
|
|
|
this.skinTexture.magFilter = THREE.NearestFilter;
|
2018-04-21 13:10:36 +02:00
|
|
|
this.skinTexture.minFilter = THREE.NearestFilter;
|
2017-10-01 14:00:45 +02:00
|
|
|
|
|
|
|
this.capeImg = new Image();
|
|
|
|
this.capeCanvas = document.createElement("canvas");
|
|
|
|
this.capeTexture = new THREE.Texture(this.capeCanvas);
|
|
|
|
this.capeTexture.magFilter = THREE.NearestFilter;
|
2018-04-21 13:10:36 +02:00
|
|
|
this.capeTexture.minFilter = THREE.NearestFilter;
|
2017-10-01 14:00:45 +02:00
|
|
|
|
|
|
|
this.layer1Material = new THREE.MeshBasicMaterial({ map: this.skinTexture, side: THREE.FrontSide });
|
2018-04-15 05:36:34 +02:00
|
|
|
this.layer2Material = new THREE.MeshBasicMaterial({ map: this.skinTexture, transparent: true, opacity: 1, side: THREE.DoubleSide, alphaTest: 0.5 });
|
|
|
|
this.capeMaterial = new THREE.MeshBasicMaterial({ map: this.capeTexture, transparent: true, opacity: 1, side: THREE.DoubleSide, alphaTest: 0.5 });
|
2017-10-01 14:00:45 +02:00
|
|
|
|
|
|
|
// scene
|
|
|
|
this.scene = new THREE.Scene();
|
|
|
|
|
2018-02-11 12:21:33 +01:00
|
|
|
// Use smaller fov to avoid distortion
|
|
|
|
this.camera = new THREE.PerspectiveCamera(40);
|
2017-10-01 14:00:45 +02:00
|
|
|
this.camera.position.y = -12;
|
2018-02-11 12:21:33 +01:00
|
|
|
this.camera.position.z = 60;
|
2017-10-01 14:00:45 +02:00
|
|
|
|
|
|
|
this.renderer = new THREE.WebGLRenderer({ angleRot: true, alpha: true, antialias: false });
|
|
|
|
this.renderer.setSize(300, 300); // default size
|
|
|
|
this.renderer.context.getShaderInfoLog = () => ""; // shut firefox up
|
|
|
|
this.domElement.appendChild(this.renderer.domElement);
|
|
|
|
|
|
|
|
this.playerObject = new PlayerObject(options.slim === true, this.layer1Material, this.layer2Material, this.capeMaterial);
|
|
|
|
this.scene.add(this.playerObject);
|
|
|
|
|
|
|
|
// texture loading
|
2018-03-03 15:04:10 +01:00
|
|
|
this.skinImg.crossOrigin = "anonymous";
|
2018-01-06 11:47:07 +01:00
|
|
|
this.skinImg.onerror = () => console.error("Failed loading " + this.skinImg.src);
|
2017-10-01 14:00:45 +02:00
|
|
|
this.skinImg.onload = () => {
|
|
|
|
let isOldFormat = false;
|
|
|
|
if (this.skinImg.width !== this.skinImg.height) {
|
|
|
|
if (this.skinImg.width === 2 * this.skinImg.height) {
|
|
|
|
isOldFormat = true;
|
|
|
|
} else {
|
2018-01-06 11:47:07 +01:00
|
|
|
console.error("Bad skin size");
|
2017-10-01 14:00:45 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let skinContext = this.skinCanvas.getContext("2d");
|
|
|
|
if (isOldFormat) {
|
|
|
|
let width = this.skinImg.width;
|
|
|
|
this.skinCanvas.width = width;
|
|
|
|
this.skinCanvas.height = width;
|
|
|
|
skinContext.clearRect(0, 0, width, width);
|
|
|
|
skinContext.drawImage(this.skinImg, 0, 0, width, width / 2.0);
|
|
|
|
convertSkinTo1_8(skinContext, width);
|
|
|
|
} else {
|
|
|
|
this.skinCanvas.width = this.skinImg.width;
|
|
|
|
this.skinCanvas.height = this.skinImg.height;
|
|
|
|
skinContext.clearRect(0, 0, this.skinCanvas.width, this.skinCanvas.height);
|
|
|
|
skinContext.drawImage(this.skinImg, 0, 0, this.skinCanvas.width, this.skinCanvas.height);
|
|
|
|
}
|
|
|
|
|
|
|
|
this.skinTexture.needsUpdate = true;
|
|
|
|
this.layer1Material.needsUpdate = true;
|
|
|
|
this.layer2Material.needsUpdate = true;
|
|
|
|
|
|
|
|
this.playerObject.skin.visible = true;
|
|
|
|
};
|
|
|
|
|
2018-03-03 15:04:10 +01:00
|
|
|
this.capeImg.crossOrigin = "anonymous";
|
2018-01-06 11:47:07 +01:00
|
|
|
this.capeImg.onerror = () => console.error("Failed loading " + this.capeImg.src);
|
2017-10-01 14:00:45 +02:00
|
|
|
this.capeImg.onload = () => {
|
2018-02-09 03:47:08 +01:00
|
|
|
let isOldFormat = false;
|
2017-10-01 14:00:45 +02:00
|
|
|
if (this.capeImg.width !== 2 * this.capeImg.height) {
|
2018-02-09 03:47:08 +01:00
|
|
|
if (this.capeImg.width * 17 == this.capeImg.height * 22) {
|
|
|
|
// width/height = 22/17
|
|
|
|
isOldFormat = true;
|
|
|
|
} else {
|
|
|
|
console.error("Bad cape size");
|
|
|
|
return;
|
|
|
|
}
|
2017-10-01 14:00:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
let capeContext = this.capeCanvas.getContext("2d");
|
2018-02-09 03:47:08 +01:00
|
|
|
if (isOldFormat) {
|
|
|
|
let width = this.capeImg.width * 64 / 22;
|
|
|
|
this.capeCanvas.width = width;
|
|
|
|
this.capeCanvas.height = width / 2;
|
|
|
|
} else {
|
|
|
|
this.capeCanvas.width = this.capeImg.width;
|
|
|
|
this.capeCanvas.height = this.capeImg.height;
|
|
|
|
}
|
2017-10-01 14:00:45 +02:00
|
|
|
capeContext.clearRect(0, 0, this.capeCanvas.width, this.capeCanvas.height);
|
2018-02-09 03:47:08 +01:00
|
|
|
capeContext.drawImage(this.capeImg, 0, 0, this.capeImg.width, this.capeImg.height);
|
2017-10-01 14:00:45 +02:00
|
|
|
|
|
|
|
this.capeTexture.needsUpdate = true;
|
|
|
|
this.capeMaterial.needsUpdate = true;
|
|
|
|
|
|
|
|
this.playerObject.cape.visible = true;
|
|
|
|
};
|
|
|
|
|
|
|
|
if (options.skinUrl) this.skinUrl = options.skinUrl;
|
|
|
|
if (options.capeUrl) this.capeUrl = options.capeUrl;
|
|
|
|
if (options.width) this.width = options.width;
|
|
|
|
if (options.height) this.height = options.height;
|
|
|
|
|
|
|
|
let draw = () => {
|
|
|
|
if (this.disposed) return;
|
|
|
|
window.requestAnimationFrame(draw);
|
|
|
|
if (!this.animationPaused) {
|
|
|
|
this.animationTime++;
|
|
|
|
if (this.animation) {
|
2018-02-02 11:27:09 +01:00
|
|
|
invokeAnimation(this.animation, this.playerObject, this.animationTime / 100.0);
|
2017-10-01 14:00:45 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
this.renderer.render(this.scene, this.camera);
|
|
|
|
};
|
|
|
|
draw();
|
|
|
|
}
|
|
|
|
|
|
|
|
setSize(width, height) {
|
|
|
|
this.camera.aspect = width / height;
|
|
|
|
this.camera.updateProjectionMatrix();
|
|
|
|
this.renderer.setSize(width, height);
|
|
|
|
}
|
|
|
|
|
|
|
|
dispose() {
|
|
|
|
this.disposed = true;
|
|
|
|
this.domElement.removeChild(this.renderer.domElement);
|
|
|
|
this.renderer.dispose();
|
|
|
|
this.skinTexture.dispose();
|
|
|
|
this.capeTexture.dispose();
|
|
|
|
}
|
|
|
|
|
|
|
|
get skinUrl() {
|
|
|
|
return this.skinImg.src;
|
|
|
|
}
|
|
|
|
|
|
|
|
set skinUrl(url) {
|
|
|
|
this.skinImg.src = url;
|
|
|
|
}
|
|
|
|
|
|
|
|
get capeUrl() {
|
|
|
|
return this.capeImg.src;
|
|
|
|
}
|
|
|
|
|
|
|
|
set capeUrl(url) {
|
|
|
|
this.capeImg.src = url;
|
|
|
|
}
|
|
|
|
|
|
|
|
get width() {
|
|
|
|
return this.renderer.getSize().width;
|
|
|
|
}
|
|
|
|
|
|
|
|
set width(newWidth) {
|
|
|
|
this.setSize(newWidth, this.height);
|
|
|
|
}
|
|
|
|
|
|
|
|
get height() {
|
|
|
|
return this.renderer.getSize().height;
|
|
|
|
}
|
|
|
|
|
|
|
|
set height(newHeight) {
|
|
|
|
this.setSize(this.width, newHeight);
|
|
|
|
}
|
2017-10-02 15:29:41 +02:00
|
|
|
}
|
2017-10-01 14:00:45 +02:00
|
|
|
|
2018-02-12 02:46:07 +01:00
|
|
|
export { SkinViewer };
|