Refactor SkinViewer

This commit is contained in:
yushijinhun 2020-01-27 03:39:29 +08:00
parent 2a0a030577
commit e15ed531f1
No known key found for this signature in database
GPG Key ID: 5BC167F73EA558E4
5 changed files with 51 additions and 103 deletions

View File

@ -9,3 +9,4 @@ extends:
rules: rules:
'@typescript-eslint/no-inferrable-types': off '@typescript-eslint/no-inferrable-types': off
'@typescript-eslint/interface-name-prefix': off '@typescript-eslint/interface-name-prefix': off
'@typescript-eslint/no-empty-interface': off

6
package-lock.json generated
View File

@ -2637,9 +2637,9 @@
"dev": true "dev": true
}, },
"skinview-utils": { "skinview-utils": {
"version": "0.2.0", "version": "0.3.0",
"resolved": "https://registry.npmjs.org/skinview-utils/-/skinview-utils-0.2.0.tgz", "resolved": "https://registry.npmjs.org/skinview-utils/-/skinview-utils-0.3.0.tgz",
"integrity": "sha512-MRcZf/u9Ksn1VbbBzjG50XAeKLMCPpQ2Onm+31nUcOw97XtS2sVBc4nNPwNGvUL5I7QTqfyr885gMxMkfp1klQ==" "integrity": "sha512-QolLWQoRGhqJUFN0sN/IAWQH+XhqPyd+10VdiZQZhrTqZGPNyR5Hh8RspGGzdSqThYJhm2L15+kty5DT3tGYjg=="
}, },
"slice-ansi": { "slice-ansi": {
"version": "2.1.0", "version": "2.1.0",

View File

@ -38,7 +38,7 @@
"bundles" "bundles"
], ],
"dependencies": { "dependencies": {
"skinview-utils": "^0.2.0", "skinview-utils": "^0.3.0",
"three": "^0.117.1" "three": "^0.117.1"
}, },
"devDependencies": { "devDependencies": {

View File

@ -1,3 +1,4 @@
import { ModelType } from "skinview-utils";
import { BoxGeometry, DoubleSide, FrontSide, Group, Mesh, MeshBasicMaterial, Object3D, Texture, Vector2 } from "three"; import { BoxGeometry, DoubleSide, FrontSide, Group, Mesh, MeshBasicMaterial, Object3D, Texture, Vector2 } from "three";
function toFaceVertices(x1: number, y1: number, x2: number, y2: number, w: number, h: number): Array<Vector2> { function toFaceVertices(x1: number, y1: number, x2: number, y2: number, w: number, h: number): Array<Vector2> {
@ -59,7 +60,7 @@ export class SkinObject extends Group {
readonly leftLeg: BodyPart; readonly leftLeg: BodyPart;
private modelListeners: Array<() => void> = []; // called when model(slim property) is changed private modelListeners: Array<() => void> = []; // called when model(slim property) is changed
private _slim = false; private slim = false;
constructor(texture: Texture) { constructor(texture: Texture) {
super(); super();
@ -364,15 +365,15 @@ export class SkinObject extends Group {
this.leftLeg.position.x = 2; this.leftLeg.position.x = 2;
this.add(this.leftLeg); this.add(this.leftLeg);
this.slim = false; this.modelType = "default";
} }
get slim(): boolean { get modelType(): ModelType {
return this._slim; return this.slim ? "slim" : "default";
} }
set slim(value) { set modelType(value: ModelType) {
this._slim = value; this.slim = value === "slim";
this.modelListeners.forEach(listener => listener()); this.modelListeners.forEach(listener => listener());
} }

View File

@ -1,56 +1,44 @@
import { applyMixins, CapeContainer, ModelType, SkinContainer } from "skinview-utils";
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";
export interface SkinViewerOptions { export type LoadOptions = {
domElement: Node; makeVisible?: boolean;
skinUrl?: string; };
capeUrl?: string;
width?: number; function toMakeVisible(options?: LoadOptions): boolean {
height?: number; if (options && options.makeVisible === false) {
detectModel?: boolean; return false;
}
return true;
} }
export class SkinViewer { class SkinViewer {
public readonly domElement: Node; readonly scene: Scene;
public readonly animations: RootAnimation = new RootAnimation(); readonly camera: PerspectiveCamera;
public detectModel: boolean = true; readonly renderer: WebGLRenderer;
readonly playerObject: PlayerObject;
readonly animations: RootAnimation = new RootAnimation();
public readonly skinImg: HTMLImageElement; protected readonly skinCanvas: HTMLCanvasElement;
public readonly skinCanvas: HTMLCanvasElement; protected readonly capeCanvas: HTMLCanvasElement;
public readonly skinTexture: Texture; private readonly skinTexture: Texture;
private readonly capeTexture: Texture;
public readonly capeImg: HTMLImageElement;
public readonly capeCanvas: HTMLCanvasElement;
public readonly capeTexture: Texture;
public readonly scene: Scene;
public readonly camera: PerspectiveCamera;
public readonly renderer: WebGLRenderer;
public readonly playerObject: PlayerObject;
private _disposed: boolean = false; private _disposed: boolean = false;
private _renderPaused: boolean = false; private _renderPaused: boolean = false;
private _skinSet: boolean = false; private _skinSet: boolean = false;
private _capeSet: boolean = false; private _capeSet: boolean = false;
constructor(options: SkinViewerOptions) { constructor(readonly domElement: Node) {
this.domElement = options.domElement;
if (options.detectModel === false) {
this.detectModel = false;
}
// texture // texture
this.skinImg = new Image();
this.skinCanvas = document.createElement("canvas"); this.skinCanvas = document.createElement("canvas");
this.skinTexture = new Texture(this.skinCanvas); this.skinTexture = new Texture(this.skinCanvas);
this.skinTexture.magFilter = NearestFilter; this.skinTexture.magFilter = NearestFilter;
this.skinTexture.minFilter = NearestFilter; this.skinTexture.minFilter = NearestFilter;
this.capeImg = new Image();
this.capeCanvas = document.createElement("canvas"); this.capeCanvas = document.createElement("canvas");
this.capeTexture = new Texture(this.capeCanvas); this.capeTexture = new Texture(this.capeCanvas);
this.capeTexture.magFilter = NearestFilter; this.capeTexture.magFilter = NearestFilter;
@ -73,41 +61,24 @@ export class SkinViewer {
this.playerObject.cape.visible = false; this.playerObject.cape.visible = false;
this.scene.add(this.playerObject); this.scene.add(this.playerObject);
// texture loading
this.skinImg.crossOrigin = "anonymous";
this.skinImg.onerror = (): void => console.error("Failed loading " + this.skinImg.src);
this.skinImg.onload = (): void => {
loadSkinToCanvas(this.skinCanvas, this.skinImg);
if (this.detectModel) {
this.playerObject.skin.slim = isSlimSkin(this.skinCanvas);
}
this.skinTexture.needsUpdate = true;
this.playerObject.skin.visible = true;
};
this.capeImg.crossOrigin = "anonymous";
this.capeImg.onerror = (): void => console.error("Failed loading " + this.capeImg.src);
this.capeImg.onload = (): void => {
loadCapeToCanvas(this.capeCanvas, this.capeImg);
this.capeTexture.needsUpdate = true;
this.playerObject.cape.visible = true;
};
if (options.skinUrl !== undefined) {
this.skinUrl = options.skinUrl;
}
if (options.capeUrl !== undefined) {
this.capeUrl = options.capeUrl;
}
this.width = options.width === undefined ? 300 : options.width;
this.height = options.height === undefined ? 300 : options.height;
window.requestAnimationFrame(() => this.draw()); window.requestAnimationFrame(() => this.draw());
} }
protected skinLoaded(model: ModelType, options?: LoadOptions): void {
this.skinTexture.needsUpdate = true;
this.playerObject.skin.modelType = model;
if (toMakeVisible(options)) {
this.playerObject.skin.visible = true;
}
}
protected capeLoaded(options?: LoadOptions): void {
this.capeTexture.needsUpdate = true;
if (toMakeVisible(options)) {
this.playerObject.cape.visible = true;
}
}
private draw(): void { private draw(): void {
if (this.disposed || this._renderPaused) { if (this.disposed || this._renderPaused) {
return; return;
@ -151,34 +122,6 @@ export class SkinViewer {
} }
} }
get skinUrl(): string | null {
return this._skinSet ? this.skinImg.src : null;
}
set skinUrl(url: string | null) {
if (url === null) {
this._skinSet = false;
this.playerObject.skin.visible = false;
} else {
this._skinSet = true;
this.skinImg.src = url;
}
}
get capeUrl(): string | null {
return this._capeSet ? this.capeImg.src : null;
}
set capeUrl(url: string | null) {
if (url === null) {
this._capeSet = false;
this.playerObject.cape.visible = false;
} else {
this._capeSet = true;
this.capeImg.src = url;
}
}
get width(): number { get width(): number {
return this.renderer.getSize(new Vector2()).width; return this.renderer.getSize(new Vector2()).width;
} }
@ -195,3 +138,6 @@ export class SkinViewer {
this.setSize(this.width, newHeight); this.setSize(this.width, newHeight);
} }
} }
interface SkinViewer extends SkinContainer<LoadOptions>, CapeContainer<LoadOptions> { }
applyMixins(SkinViewer, [SkinContainer, CapeContainer]);
export { SkinViewer };