Refactor SkinViewer
This commit is contained in:
parent
2a0a030577
commit
e15ed531f1
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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",
|
||||||
|
|
|
||||||
|
|
@ -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": {
|
||||||
|
|
|
||||||
13
src/model.ts
13
src/model.ts
|
|
@ -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());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
132
src/viewer.ts
132
src/viewer.ts
|
|
@ -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 };
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue