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
* HD Skins
* Capes
* Elytras
* Slim Arms
* Automatic model detection (Slim / Default)
@ -37,7 +38,10 @@ Three.js powered Minecraft skin viewer.
// Load a cape
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);
// Control objects with your mouse!

View File

@ -182,6 +182,13 @@
</tr>
</tbody>
</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 class="control-section">
@ -284,7 +291,8 @@
skinViewer.loadCape(null);
input.setCustomValidity("");
} else {
skinViewer.loadCape(url)
const selectedBackEquipment = document.querySelector('input[type="radio"][name="back_equipment"]:checked');
skinViewer.loadCape(url, { backEquipment: selectedBackEquipment.value })
.then(() => input.setCustomValidity(""))
.catch(e => {
input.setCustomValidity("Image can't be loaded.");
@ -363,6 +371,18 @@
document.getElementById("skin_url").addEventListener("change", () => reloadSkin());
document.getElementById("skin_model").addEventListener("change", () => reloadSkin());
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", () => {
skinViewer.dispose();
orbitControl.dispose();

View File

@ -12,7 +12,7 @@
<div id="rendered_imgs"></div>
<script src="../bundles/skinview3d.bundle.js"></script>
<script>
const textures = [
const configurations = [
{
skin: "img/1_8_texturemap_redux.png",
cape: null
@ -23,11 +23,12 @@
},
{
skin: "img/haka.png",
cape: null
cape: "img/mojang_cape.png"
},
{
skin: "img/hatsune_miku.png",
cape: "img/mojang_cape.png"
cape: "img/mojang_cape.png",
backEquipment: "elytra"
},
{
skin: "img/ironman_hd.png",
@ -54,8 +55,11 @@
skinViewer.camera.position.y = 22.0;
skinViewer.camera.position.z = 42.0;
for (const { skin, cape } of textures) {
await Promise.all([skinViewer.loadSkin(skin), skinViewer.loadCape(cape)]);
for (const config of configurations) {
await Promise.all([
skinViewer.loadSkin(config.skin),
skinViewer.loadCape(config.cape, { backEquipment: config.backEquipment })
]);
skinViewer.render();
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 {
readonly skin: SkinObject;
@ -369,4 +371,19 @@ export class PlayerObject extends Group {
this.elytra.visible = false;
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 { NearestFilter, PerspectiveCamera, Scene, Texture, Vector2, WebGLRenderer } from "three";
import { RootAnimation } from "./animation.js";
import { PlayerObject } from "./model.js";
import { BackEquipment, PlayerObject } from "./model.js";
export interface LoadOptions {
/**
@ -10,6 +10,14 @@ export interface LoadOptions {
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 {
width?: number;
height?: number;
@ -40,13 +48,6 @@ export interface SkinViewerOptions {
renderPaused?: boolean;
}
function toMakeVisible(options?: LoadOptions): boolean {
if (options && options.makeVisible === false) {
return false;
}
return true;
}
class SkinViewer {
readonly canvas: HTMLCanvasElement;
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.playerObject.skin.modelType = model;
if (toMakeVisible(options)) {
if (options.makeVisible !== false) {
this.playerObject.skin.visible = true;
}
}
protected capeLoaded(options?: LoadOptions): void {
protected capeLoaded(options: CapeLoadOptions = {}): void {
this.capeTexture.needsUpdate = true;
if (toMakeVisible(options)) {
this.playerObject.cape.visible = true;
if (options.makeVisible !== false) {
this.playerObject.backEquipment = options.backEquipment === undefined ? "cape" : options.backEquipment;
}
}
@ -138,7 +139,7 @@ class SkinViewer {
}
protected resetCape(): void {
this.playerObject.cape.visible = false;
this.playerObject.backEquipment = null;
}
private draw(): void {
@ -208,6 +209,6 @@ class SkinViewer {
this.setSize(this.width, newHeight);
}
}
interface SkinViewer extends SkinContainer<LoadOptions>, CapeContainer<LoadOptions> { }
interface SkinViewer extends SkinContainer<LoadOptions>, CapeContainer<CapeLoadOptions> { }
applyMixins(SkinViewer, [SkinContainer, CapeContainer]);
export { SkinViewer };