Added ears

This commit is contained in:
James Harrison 2020-04-26 00:00:10 +01:00
parent ed22ce6953
commit 940f0a7083
7 changed files with 103 additions and 16 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

BIN
examples/cape.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
examples/ears.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 233 B

View File

@ -24,19 +24,14 @@
<script> <script>
let skinViewer = new skinview3d.SkinViewer({ let skinViewer = new skinview3d.SkinViewer({
domElement: document.getElementById("skin_container"), domElement: document.getElementById("skin_container"),
width: 900, width: 200,
height: 900, height: 400,
skinUrl: "./1_8_texturemap_redux.png" skinUrl: "./skin.png",
capeUrl: "./cape.png",
earUrl: "./ears.png"
}); });
// By default, the skin model is automatically detected. You can turn it off in this way:
// skinViewer.detectModel = false;
// skinViewer.playerObject.skin.slim = true;
let control = new skinview3d.createOrbitControls(skinViewer); let control = new skinview3d.createOrbitControls(skinViewer);
skinViewer.animations.add(skinview3d.WalkingAnimation);
skinViewer.animations.speed = 1.5;
</script> </script>

BIN
examples/skin.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -17,6 +17,10 @@ function toCapeVertices(x1: number, y1: number, x2: number, y2: number): Array<V
return toFaceVertices(x1, y1, x2, y2, 64.0, 32.0); return toFaceVertices(x1, y1, x2, y2, 64.0, 32.0);
} }
function toEarVertices(x1: number, y1: number, x2: number, y2: number): Array<Vector2> {
return toFaceVertices(x1, y1, x2, y2, 14.0, 7.0);
}
function setVertices(box: BoxGeometry, top: Array<Vector2>, bottom: Array<Vector2>, left: Array<Vector2>, front: Array<Vector2>, right: Array<Vector2>, back: Array<Vector2>): void { function setVertices(box: BoxGeometry, top: Array<Vector2>, bottom: Array<Vector2>, left: Array<Vector2>, front: Array<Vector2>, right: Array<Vector2>, back: Array<Vector2>): void {
box.faceVertexUvs[0] = []; box.faceVertexUvs[0] = [];
@ -416,12 +420,47 @@ export class CapeObject extends Group {
} }
} }
export class EarsObject extends Group {
readonly leftEar: Mesh;
readonly rightEar: Mesh;
constructor(texture: Texture) {
super();
const earMaterial = new MeshBasicMaterial({ map: texture, transparent: true, opacity: 1, side: DoubleSide, alphaTest: 0.5 });
// back = outside
// front = inside
const earBox = new BoxGeometry(6, 6, 1, 0, 0, 0);
//x1: number, y1: number, x2: number, y2: number
setVertices(earBox,
//from look at back
toEarVertices(1, 0, 7, 1), //top
toEarVertices(7, 0, 13, 1), //bottom
toEarVertices(0, 1, 1, 7), //right
toEarVertices(1, 1, 7, 7), //front
toEarVertices(0, 1, 1, 7), //left
toEarVertices(8, 1, 14, 7) //back
);
this.leftEar = new Mesh(earBox, earMaterial);
this.leftEar.position.x = -5.5;
this.add(this.leftEar);
this.rightEar = new Mesh(earBox, earMaterial);
this.rightEar.position.x = 5.5;
this.add(this.rightEar);
}
}
export class PlayerObject extends Group { export class PlayerObject extends Group {
readonly skin: SkinObject; readonly skin: SkinObject;
readonly cape: CapeObject; readonly cape: CapeObject;
readonly ears: EarsObject
constructor(skinTexture: Texture, capeTexture: Texture) { constructor(skinTexture: Texture, capeTexture: Texture, earTexture: Texture) {
super(); super();
this.skin = new SkinObject(skinTexture); this.skin = new SkinObject(skinTexture);
@ -432,7 +471,12 @@ export class PlayerObject extends Group {
this.cape.name = "cape"; this.cape.name = "cape";
this.cape.position.z = -2; this.cape.position.z = -2;
this.cape.position.y = -4; this.cape.position.y = -4;
this.cape.rotation.x = 25 * Math.PI / 180; this.cape.rotation.x = 10 * Math.PI / 180;
this.add(this.cape); this.add(this.cape);
this.ears = new EarsObject(earTexture);
this.ears.name = "ears";
this.ears.position.y = 5.5;
this.add(this.ears);
} }
} }

View File

@ -1,12 +1,13 @@
import { NearestFilter, PerspectiveCamera, Scene, Texture, Vector2, WebGLRenderer } from "three"; import { NearestFilter, PerspectiveCamera, Scene, Texture, Vector2, WebGLRenderer } from "three";
import { RootAnimation } from "./animation.js"; import { RootAnimation } from "./animation.js";
import { PlayerObject } from "./model.js"; import { PlayerObject } from "./model.js";
import { isSlimSkin, loadCapeToCanvas, loadSkinToCanvas } from "skinview-utils"; import { TextureCanvas, TextureSource, isSlimSkin, loadCapeToCanvas, loadSkinToCanvas } from "skinview-utils";
export interface SkinViewerOptions { export interface SkinViewerOptions {
domElement: Node; domElement: Node;
skinUrl?: string; skinUrl?: string;
capeUrl?: string; capeUrl?: string;
earUrl?: string;
width?: number; width?: number;
height?: number; height?: number;
detectModel?: boolean; detectModel?: boolean;
@ -26,6 +27,10 @@ export class SkinViewer {
public readonly capeCanvas: HTMLCanvasElement; public readonly capeCanvas: HTMLCanvasElement;
public readonly capeTexture: Texture; public readonly capeTexture: Texture;
public readonly earImg: HTMLImageElement;
public readonly earCanvas: HTMLCanvasElement;
public readonly earTexture: Texture;
public readonly scene: Scene; public readonly scene: Scene;
public readonly camera: PerspectiveCamera; public readonly camera: PerspectiveCamera;
public readonly renderer: WebGLRenderer; public readonly renderer: WebGLRenderer;
@ -36,6 +41,7 @@ export class SkinViewer {
private _renderPaused: boolean = false; private _renderPaused: boolean = false;
private _skinSet: boolean = false; private _skinSet: boolean = false;
private _capeSet: boolean = false; private _capeSet: boolean = false;
private _earSet: boolean = false;
constructor(options: SkinViewerOptions) { constructor(options: SkinViewerOptions) {
this.domElement = options.domElement; this.domElement = options.domElement;
@ -56,21 +62,28 @@ export class SkinViewer {
this.capeTexture.magFilter = NearestFilter; this.capeTexture.magFilter = NearestFilter;
this.capeTexture.minFilter = NearestFilter; this.capeTexture.minFilter = NearestFilter;
this.earImg = new Image();
this.earCanvas = document.createElement("canvas");
this.earTexture = new Texture(this.earCanvas);
this.earTexture.magFilter = NearestFilter;
this.earTexture.minFilter = NearestFilter;
// scene // scene
this.scene = new Scene(); this.scene = new Scene();
// Use smaller fov to avoid distortion // Use smaller fov to avoid distortion
this.camera = new PerspectiveCamera(40); this.camera = new PerspectiveCamera(40);
this.camera.position.y = -12; this.camera.position.y = 0;
this.camera.position.z = 60; this.camera.position.z = 65;
this.renderer = new WebGLRenderer({ alpha: true }); this.renderer = new WebGLRenderer({ alpha: true });
this.domElement.appendChild(this.renderer.domElement); this.domElement.appendChild(this.renderer.domElement);
this.playerObject = new PlayerObject(this.skinTexture, this.capeTexture); this.playerObject = new PlayerObject(this.skinTexture, this.capeTexture, this.earTexture);
this.playerObject.name = "player"; this.playerObject.name = "player";
this.playerObject.skin.visible = false; this.playerObject.skin.visible = false;
this.playerObject.cape.visible = false; this.playerObject.cape.visible = false;
this.playerObject.ears.visible = false;
this.scene.add(this.playerObject); this.scene.add(this.playerObject);
// texture loading // texture loading
@ -96,12 +109,24 @@ export class SkinViewer {
this.playerObject.cape.visible = true; this.playerObject.cape.visible = true;
}; };
this.earImg.crossOrigin = "anonymous";
this.earImg.onerror = (): void => console.error("Failed loading " + this.earImg.src);
this.earImg.onload = (): void => {
loadEarsToCanvas(this.earCanvas, this.earImg);
this.earTexture.needsUpdate = true;
this.playerObject.ears.visible = true;
};
if (options.skinUrl !== undefined) { if (options.skinUrl !== undefined) {
this.skinUrl = options.skinUrl; this.skinUrl = options.skinUrl;
} }
if (options.capeUrl !== undefined) { if (options.capeUrl !== undefined) {
this.capeUrl = options.capeUrl; this.capeUrl = options.capeUrl;
} }
if (options.earUrl !== undefined) {
this.earUrl = options.earUrl;
}
this.width = options.width === undefined ? 300 : options.width; this.width = options.width === undefined ? 300 : options.width;
this.height = options.height === undefined ? 300 : options.height; this.height = options.height === undefined ? 300 : options.height;
@ -179,6 +204,20 @@ export class SkinViewer {
} }
} }
get earUrl(): string | null {
return this._earSet ? this.earImg.src : null;
}
set earUrl(url: string | null) {
if (url === null) {
this._earSet = false;
this.playerObject.ears.visible = false;
} else {
this._earSet = true;
this.earImg.src = url;
}
}
get width(): number { get width(): number {
return this.renderer.getSize(new Vector2()).width; return this.renderer.getSize(new Vector2()).width;
} }
@ -195,3 +234,12 @@ export class SkinViewer {
this.setSize(this.width, newHeight); this.setSize(this.width, newHeight);
} }
} }
function loadEarsToCanvas(canvas: TextureCanvas, image: TextureSource): void {
canvas.width = 14;
canvas.height = 7;
const context = canvas.getContext("2d")!;
context.clearRect(0, 0, canvas.width, canvas.height);
context.drawImage(image, 0, 0, image.width, image.height);
}