Add PlayerObject.backEquipment property

This commit is contained in:
Haowei Wen 2020-10-10 10:22:17 +08:00
parent 61572b6824
commit 61aa9753af
No known key found for this signature in database
GPG Key ID: 5BC167F73EA558E4
5 changed files with 68 additions and 22 deletions

View File

@ -12,6 +12,7 @@ Three.js powered Minecraft skin viewer.
* 1.8 Skins * 1.8 Skins
* HD Skins * HD Skins
* Capes * Capes
* Elytras
* Slim Arms * Slim Arms
* Automatic model detection (Slim / Default) * Automatic model detection (Slim / Default)
@ -37,7 +38,10 @@ Three.js powered Minecraft skin viewer.
// Load a cape // Load a cape
skinViewer.loadCape("img/cape.png"); skinViewer.loadCape("img/cape.png");
// Unload(hide) the cape // Load a elytra (from a cape texture)
skinViewer.loadCape("img/cape.png", { backEquipment: "elytra" });
// Unload(hide) the cape / elytra
skinViewer.loadCape(null); skinViewer.loadCape(null);
// Control objects with your mouse! // Control objects with your mouse!

View File

@ -182,6 +182,13 @@
</tr> </tr>
</tbody> </tbody>
</table> </table>
<div>
<h2>Back Equipment</h2>
<div class="control">
<label><input type="radio" id="back_equipment_cape" name="back_equipment" value="cape" checked> Cape</label>
<label><input type="radio" id="back_equipment_elytra" name="back_equipment" value="elytra"> Elytra</label>
</div>
</div>
</div> </div>
<div class="control-section"> <div class="control-section">
@ -284,7 +291,8 @@
skinViewer.loadCape(null); skinViewer.loadCape(null);
input.setCustomValidity(""); input.setCustomValidity("");
} else { } else {
skinViewer.loadCape(url) const selectedBackEquipment = document.querySelector('input[type="radio"][name="back_equipment"]:checked');
skinViewer.loadCape(url, { backEquipment: selectedBackEquipment.value })
.then(() => input.setCustomValidity("")) .then(() => input.setCustomValidity(""))
.catch(e => { .catch(e => {
input.setCustomValidity("Image can't be loaded."); input.setCustomValidity("Image can't be loaded.");
@ -363,6 +371,18 @@
document.getElementById("skin_url").addEventListener("change", () => reloadSkin()); document.getElementById("skin_url").addEventListener("change", () => reloadSkin());
document.getElementById("skin_model").addEventListener("change", () => reloadSkin()); document.getElementById("skin_model").addEventListener("change", () => reloadSkin());
document.getElementById("cape_url").addEventListener("change", () => reloadCape()); document.getElementById("cape_url").addEventListener("change", () => reloadCape());
for (const el of document.querySelectorAll('input[type="radio"][name="back_equipment"]')) {
el.addEventListener("change", e => {
if (skinViewer.playerObject.backEquipment === null) {
// cape texture hasn't been loaded yet
// this option will be processed on texture loading
} else {
skinViewer.playerObject.backEquipment = e.target.value;
}
});
}
document.getElementById("reset_all").addEventListener("click", () => { document.getElementById("reset_all").addEventListener("click", () => {
skinViewer.dispose(); skinViewer.dispose();
orbitControl.dispose(); orbitControl.dispose();

View File

@ -12,7 +12,7 @@
<div id="rendered_imgs"></div> <div id="rendered_imgs"></div>
<script src="../bundles/skinview3d.bundle.js"></script> <script src="../bundles/skinview3d.bundle.js"></script>
<script> <script>
const textures = [ const configurations = [
{ {
skin: "img/1_8_texturemap_redux.png", skin: "img/1_8_texturemap_redux.png",
cape: null cape: null
@ -23,11 +23,12 @@
}, },
{ {
skin: "img/haka.png", skin: "img/haka.png",
cape: null cape: "img/mojang_cape.png"
}, },
{ {
skin: "img/hatsune_miku.png", skin: "img/hatsune_miku.png",
cape: "img/mojang_cape.png" cape: "img/mojang_cape.png",
backEquipment: "elytra"
}, },
{ {
skin: "img/ironman_hd.png", skin: "img/ironman_hd.png",
@ -54,8 +55,11 @@
skinViewer.camera.position.y = 22.0; skinViewer.camera.position.y = 22.0;
skinViewer.camera.position.z = 42.0; skinViewer.camera.position.z = 42.0;
for (const { skin, cape } of textures) { for (const config of configurations) {
await Promise.all([skinViewer.loadSkin(skin), skinViewer.loadCape(cape)]); await Promise.all([
skinViewer.loadSkin(config.skin),
skinViewer.loadCape(config.cape, { backEquipment: config.backEquipment })
]);
skinViewer.render(); skinViewer.render();
const image = skinViewer.canvas.toDataURL(); const image = skinViewer.canvas.toDataURL();

View File

@ -343,6 +343,8 @@ export class ElytraObject extends Group {
} }
} }
export type BackEquipment = "cape" | "elytra";
export class PlayerObject extends Group { export class PlayerObject extends Group {
readonly skin: SkinObject; readonly skin: SkinObject;
@ -369,4 +371,19 @@ export class PlayerObject extends Group {
this.elytra.visible = false; this.elytra.visible = false;
this.add(this.elytra); this.add(this.elytra);
} }
get backEquipment(): BackEquipment | null {
if (this.cape.visible) {
return "cape";
} else if (this.elytra.visible) {
return "elytra";
} else {
return null;
}
}
set backEquipment(value: BackEquipment | null) {
this.cape.visible = value === "cape";
this.elytra.visible = value === "elytra";
}
} }

View File

@ -1,7 +1,7 @@
import { applyMixins, CapeContainer, ModelType, SkinContainer, RemoteImage, TextureSource } from "skinview-utils"; import { applyMixins, CapeContainer, ModelType, SkinContainer, RemoteImage, TextureSource } 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 { BackEquipment, PlayerObject } from "./model.js";
export interface LoadOptions { export interface LoadOptions {
/** /**
@ -10,6 +10,14 @@ export interface LoadOptions {
makeVisible?: boolean; makeVisible?: boolean;
} }
export interface CapeLoadOptions extends LoadOptions {
/**
* The equipment (cape or elytra) to show, defaults to "cape".
* If makeVisible is set to false, this option will have no effect.
*/
backEquipment?: BackEquipment;
}
export interface SkinViewerOptions { export interface SkinViewerOptions {
width?: number; width?: number;
height?: number; height?: number;
@ -40,13 +48,6 @@ export interface SkinViewerOptions {
renderPaused?: boolean; renderPaused?: boolean;
} }
function toMakeVisible(options?: LoadOptions): boolean {
if (options && options.makeVisible === false) {
return false;
}
return true;
}
class SkinViewer { class SkinViewer {
readonly canvas: HTMLCanvasElement; readonly canvas: HTMLCanvasElement;
readonly scene: Scene; readonly scene: Scene;
@ -118,18 +119,18 @@ class SkinViewer {
} }
} }
protected skinLoaded(model: ModelType, options?: LoadOptions): void { protected skinLoaded(model: ModelType, options: LoadOptions = {}): void {
this.skinTexture.needsUpdate = true; this.skinTexture.needsUpdate = true;
this.playerObject.skin.modelType = model; this.playerObject.skin.modelType = model;
if (toMakeVisible(options)) { if (options.makeVisible !== false) {
this.playerObject.skin.visible = true; this.playerObject.skin.visible = true;
} }
} }
protected capeLoaded(options?: LoadOptions): void { protected capeLoaded(options: CapeLoadOptions = {}): void {
this.capeTexture.needsUpdate = true; this.capeTexture.needsUpdate = true;
if (toMakeVisible(options)) { if (options.makeVisible !== false) {
this.playerObject.cape.visible = true; this.playerObject.backEquipment = options.backEquipment === undefined ? "cape" : options.backEquipment;
} }
} }
@ -138,7 +139,7 @@ class SkinViewer {
} }
protected resetCape(): void { protected resetCape(): void {
this.playerObject.cape.visible = false; this.playerObject.backEquipment = null;
} }
private draw(): void { private draw(): void {
@ -208,6 +209,6 @@ class SkinViewer {
this.setSize(this.width, newHeight); this.setSize(this.width, newHeight);
} }
} }
interface SkinViewer extends SkinContainer<LoadOptions>, CapeContainer<LoadOptions> { } interface SkinViewer extends SkinContainer<LoadOptions>, CapeContainer<CapeLoadOptions> { }
applyMixins(SkinViewer, [SkinContainer, CapeContainer]); applyMixins(SkinViewer, [SkinContainer, CapeContainer]);
export { SkinViewer }; export { SkinViewer };