Added ears
This commit is contained in:
parent
ed22ce6953
commit
940f0a7083
Binary file not shown.
Before Width: | Height: | Size: 3.6 KiB |
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
Binary file not shown.
After Width: | Height: | Size: 233 B |
|
@ -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>
|
||||||
|
|
||||||
|
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 1.3 KiB |
48
src/model.ts
48
src/model.ts
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue