Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.6 KiB |
After Width: | Height: | Size: 689 B |
After Width: | Height: | Size: 589 B |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 278 KiB |
After Width: | Height: | Size: 109 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.4 KiB |
|
@ -4,13 +4,76 @@
|
|||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>skinview3d</title>
|
||||
<link href="https://fonts.googleapis.com/css?family=Archivo+Black" rel="stylesheet">
|
||||
<style>
|
||||
html,
|
||||
body {
|
||||
font-family: "Helvetica Neue", "Helvetica", "Arial", "sans-serif";
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 1.25em;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2 {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
input[type="number"] {
|
||||
width: 60px;
|
||||
}
|
||||
|
||||
.control {
|
||||
display: inline-block;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.control-section {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.control-section>h1,
|
||||
.control-section>h2 {
|
||||
margin-left: -10px;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
table td,
|
||||
table th {
|
||||
border: 1px black dashed;
|
||||
text-align: left;
|
||||
width: 50px;
|
||||
}
|
||||
|
||||
thead th {
|
||||
border-top: unset;
|
||||
}
|
||||
|
||||
tbody tr:last-child td,
|
||||
tbody tr:last-child th {
|
||||
border-bottom: unset;
|
||||
}
|
||||
|
||||
table th:first-child,
|
||||
table td:first-child {
|
||||
border-left: unset;
|
||||
}
|
||||
|
||||
table th:last-child,
|
||||
table td:last-child {
|
||||
border-right: unset;
|
||||
}
|
||||
|
||||
table td input[type="checkbox"] {
|
||||
vertical-align: middle;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background-color: #1e1e1e;
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
@ -19,22 +82,314 @@
|
|||
|
||||
<div id="skin_container"></div>
|
||||
|
||||
<script type="text/javascript" src="../bundles/skinview3d.bundle.js"></script>
|
||||
<div>
|
||||
|
||||
<button id="reset_all" type="button" class="control">Reset All</button>
|
||||
|
||||
<div class="control-section">
|
||||
<h1>Canvas Size</h1>
|
||||
<div class="control">
|
||||
<label for="canvas_width">Width:</label>
|
||||
<input id="canvas_width" type="number" value="300">
|
||||
</div>
|
||||
<div class="control">
|
||||
<label for="canvas_height">Height:</label>
|
||||
<input id="canvas_height" type="number" value="300">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-section">
|
||||
<h1>Animation</h1>
|
||||
<div class="control">
|
||||
<label for="global_animation_speed">Global Speed:</label>
|
||||
<input id="global_animation_speed" type="number" value="1" step="0.1">
|
||||
</div>
|
||||
<button id="animation_pause_resume" type="button" class="control">Pause / Resume</button>
|
||||
<div>
|
||||
<h2>Rotate</h2>
|
||||
<div class="control">
|
||||
<input id="rotate_animation" type="checkbox">
|
||||
<label for="rotate_animation">Enable</label>
|
||||
</div>
|
||||
<div class="control">
|
||||
<label for="rotate_animation_speed">Speed:</label>
|
||||
<input id="rotate_animation_speed" type="number" value="1" step="0.1">
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h2>Walk / Run</h2>
|
||||
<div class="control">
|
||||
<input type="radio" id="primary_animation_none" name="primary_animation" value="" checked>
|
||||
<label for="primary_animation_none">None</label>
|
||||
<input type="radio" id="primary_animation_walk" name="primary_animation" value="walk">
|
||||
<label for="primary_animation_walk">Walk</label>
|
||||
<input type="radio" id="primary_animation_run" name="primary_animation" value="run">
|
||||
<label for="primary_animation_run">Run</label>
|
||||
</div>
|
||||
<div class="control">
|
||||
<label for="primary_animation_speed">Speed:</label>
|
||||
<input id="primary_animation_speed" type="number" value="1" step="0.1">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-section">
|
||||
<h1>Mouse Control</h1>
|
||||
<div class="control">
|
||||
<input type="checkbox" id="control_rotate" checked>
|
||||
<label for="control_rotate">Enable Rotate</label>
|
||||
<input type="checkbox" id="control_zoom" checked>
|
||||
<label for="control_zoom">Enable Zoom</label>
|
||||
<input type="checkbox" id="control_pan">
|
||||
<label for="control_pan">Enable Pan</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-section">
|
||||
<h1>Skin Layers</h1>
|
||||
<table id="layers_table" class="control">
|
||||
<!-- table contents are generated using javascript -->
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="control-section">
|
||||
<h1>Textures</h1>
|
||||
<div>
|
||||
<div class="control">
|
||||
<label for="skin_url">Skin URL:</label>
|
||||
<input id="skin_url" type="text" value="img/1_8_texturemap_redux.png" placeholder="none"
|
||||
list="default_skins">
|
||||
<datalist id="default_skins">
|
||||
<option value="img/1_8_texturemap_redux.png">
|
||||
<option value="img/Hacksore.png">
|
||||
<option value="img/haka.png">
|
||||
<option value="img/hatsune_miku.png">
|
||||
<option value="img/ironman_hd.png">
|
||||
<option value="img/sethbling.png">
|
||||
</datalist>
|
||||
<input id="skin_url_upload" type="file" accept="image/*" style="display: none;">
|
||||
<button type="button" class="control"
|
||||
onclick="document.getElementById('skin_url_upload').click();">Browse...</button>
|
||||
</div>
|
||||
<div class="control">
|
||||
<label for="skin_model">Model:</label>
|
||||
<select id="skin_model" value="auto-detect">
|
||||
<option value="auto-detect">Auto detect</option>
|
||||
<option value="default">Default</option>
|
||||
<option value="slim">Slim</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="control">
|
||||
<label for="cape_url">Cape URL:</label>
|
||||
<input id="cape_url" type="text" value="" placeholder="none" list="default_capes">
|
||||
<datalist id="default_capes">
|
||||
<option value="">
|
||||
<option value="img/cape.png">
|
||||
<option value="img/mojang_cape.png">
|
||||
<option value="img/hd_cape.png">
|
||||
</datalist>
|
||||
<input id="cape_url_upload" type="file" accept="image/*" style="display: none;">
|
||||
<button type="button" class="control"
|
||||
onclick="document.getElementById('cape_url_upload').click();">Browse...</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<script src="../bundles/skinview3d.bundle.js"></script>
|
||||
<script>
|
||||
let skinViewer = new skinview3d.SkinViewer(document.getElementById("skin_container"), {
|
||||
width: 400,
|
||||
height: 300,
|
||||
skin: "./1_8_texturemap_redux.png"
|
||||
});
|
||||
const skinParts = ["head", "body", "rightArm", "leftArm", "rightLeg", "leftLeg"];
|
||||
const skinLayers = ["innerLayer", "outerLayer"];
|
||||
const availableAnimations = {
|
||||
walk: skinview3d.WalkingAnimation,
|
||||
run: skinview3d.RunningAnimation
|
||||
};
|
||||
|
||||
let control = new skinview3d.createOrbitControls(skinViewer);
|
||||
skinViewer.animations.add(skinview3d.WalkingAnimation);
|
||||
skinViewer.animations.speed = 1.5;
|
||||
let skinViewer;
|
||||
let oribitControl;
|
||||
let rotateAnimation;
|
||||
let primaryAnimation;
|
||||
|
||||
function reloadSkin() {
|
||||
const input = document.getElementById("skin_url");
|
||||
const url = input.value;
|
||||
if (url === "") {
|
||||
skinViewer.loadSkin(null);
|
||||
input.setCustomValidity("");
|
||||
} else {
|
||||
skinViewer.loadSkin(url, document.getElementById("skin_model").value)
|
||||
.then(() => input.setCustomValidity(""))
|
||||
.catch(e => {
|
||||
input.setCustomValidity("Image can't be loaded.");
|
||||
console.error(e);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function reloadCape() {
|
||||
const input = document.getElementById("cape_url");
|
||||
const url = input.value;
|
||||
if (url === "") {
|
||||
skinViewer.loadCape(null);
|
||||
input.setCustomValidity("");
|
||||
} else {
|
||||
skinViewer.loadCape(url)
|
||||
.then(() => input.setCustomValidity(""))
|
||||
.catch(e => {
|
||||
input.setCustomValidity("Image can't be loaded.");
|
||||
console.error(e);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function initializeLayersTable() {
|
||||
const table = document.getElementById("layers_table");
|
||||
const thead = document.createElement("thead");
|
||||
table.appendChild(thead);
|
||||
const rowHead = document.createElement("tr");
|
||||
rowHead.appendChild(document.createElement("th"));
|
||||
thead.appendChild(rowHead);
|
||||
for (const part of skinParts) {
|
||||
const cellHead = document.createElement("th");
|
||||
cellHead.innerText = part;
|
||||
rowHead.appendChild(cellHead);
|
||||
}
|
||||
const tbody = document.createElement("tbody");
|
||||
table.appendChild(tbody);
|
||||
for (const layer of skinLayers) {
|
||||
const row = document.createElement("tr");
|
||||
tbody.appendChild(row);
|
||||
const cellLayer = document.createElement("th");
|
||||
cellLayer.innerText = layer;
|
||||
row.appendChild(cellLayer);
|
||||
for (const part of skinParts) {
|
||||
const cell = document.createElement("td");
|
||||
row.appendChild(cell);
|
||||
const checkbox = document.createElement("input");
|
||||
checkbox.type = "checkbox";
|
||||
checkbox.dataset.layer = layer;
|
||||
checkbox.dataset.part = part;
|
||||
checkbox.checked = true;
|
||||
cell.appendChild(checkbox);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function initializeControls() {
|
||||
document.getElementById("canvas_width").addEventListener("change", e => skinViewer.width = e.target.value);
|
||||
document.getElementById("canvas_height").addEventListener("change", e => skinViewer.height = e.target.value);
|
||||
document.getElementById("global_animation_speed").addEventListener("change", e => skinViewer.animations.speed = e.target.value);
|
||||
document.getElementById("animation_pause_resume").addEventListener("click", () => skinViewer.animations.paused = !skinViewer.animations.paused);
|
||||
document.getElementById("rotate_animation").addEventListener("change", e => {
|
||||
if (e.target.checked && rotateAnimation === null) {
|
||||
rotateAnimation = skinViewer.animations.add(skinview3d.RotatingAnimation);
|
||||
rotateAnimation.speed = document.getElementById("rotate_animation_speed").value;
|
||||
} else if (!e.target.checked && rotateAnimation !== null) {
|
||||
rotateAnimation.resetAndRemove();
|
||||
rotateAnimation = null;
|
||||
}
|
||||
});
|
||||
document.getElementById("rotate_animation_speed").addEventListener("change", e => {
|
||||
if (rotateAnimation !== null) {
|
||||
rotateAnimation.speed = e.target.value;
|
||||
}
|
||||
});
|
||||
for (const el of document.querySelectorAll('input[type="radio"][name="primary_animation"]')) {
|
||||
el.addEventListener("change", e => {
|
||||
if (primaryAnimation !== null) {
|
||||
primaryAnimation.resetAndRemove();
|
||||
primaryAnimation = null;
|
||||
}
|
||||
if (e.target.value !== "") {
|
||||
primaryAnimation = skinViewer.animations.add(availableAnimations[e.target.value]);
|
||||
primaryAnimation.speed = document.getElementById("primary_animation_speed").value;
|
||||
}
|
||||
});
|
||||
}
|
||||
document.getElementById("primary_animation_speed").addEventListener("change", e => {
|
||||
if (primaryAnimation !== null) {
|
||||
primaryAnimation.speed = e.target.value;
|
||||
}
|
||||
});
|
||||
document.getElementById("control_rotate").addEventListener("change", e => oribitControl.enableRotate = e.target.checked);
|
||||
document.getElementById("control_zoom").addEventListener("change", e => oribitControl.enableZoom = e.target.checked);
|
||||
document.getElementById("control_pan").addEventListener("change", e => oribitControl.enablePan = e.target.checked);
|
||||
for (const part of skinParts) {
|
||||
for (const layer of skinLayers) {
|
||||
document.querySelector(`#layers_table input[type="checkbox"][data-part="${part}"][data-layer="${layer}"]`)
|
||||
.addEventListener("change", e => skinViewer.playerObject.skin[part][layer].visible = e.target.checked);
|
||||
}
|
||||
}
|
||||
const skinReader = new FileReader();
|
||||
skinReader.addEventListener("load", e => {
|
||||
document.getElementById("skin_url").value = skinReader.result;
|
||||
reloadSkin();
|
||||
});
|
||||
document.getElementById("skin_url_upload").addEventListener("change", e => {
|
||||
const file = e.target.files[0];
|
||||
if (file !== undefined) {
|
||||
skinReader.readAsDataURL(file);
|
||||
}
|
||||
});
|
||||
const capeReader = new FileReader();
|
||||
capeReader.addEventListener("load", e => {
|
||||
document.getElementById("cape_url").value = capeReader.result;
|
||||
reloadCape();
|
||||
});
|
||||
document.getElementById("cape_url_upload").addEventListener("change", e => {
|
||||
const file = e.target.files[0];
|
||||
if (file !== undefined) {
|
||||
capeReader.readAsDataURL(file);
|
||||
}
|
||||
});
|
||||
document.getElementById("skin_url").addEventListener("change", () => reloadSkin());
|
||||
document.getElementById("skin_model").addEventListener("change", () => reloadSkin());
|
||||
document.getElementById("cape_url").addEventListener("change", () => reloadCape());
|
||||
document.getElementById("reset_all").addEventListener("click", () => {
|
||||
skinViewer.dispose();
|
||||
initializeViewer();
|
||||
});
|
||||
}
|
||||
|
||||
function initializeViewer() {
|
||||
skinViewer = new skinview3d.SkinViewer(document.getElementById("skin_container"));
|
||||
oribitControl = skinview3d.createOrbitControls(skinViewer);
|
||||
rotateAnimation = null;
|
||||
primaryAnimation = null;
|
||||
|
||||
skinViewer.width = document.getElementById("canvas_width").value;
|
||||
skinViewer.height = document.getElementById("canvas_height").value;
|
||||
skinViewer.animations.speed = document.getElementById("global_animation_speed").value;
|
||||
if (document.getElementById("rotate_animation").checked) {
|
||||
rotateAnimation = skinViewer.animations.add(skinview3d.RotatingAnimation);
|
||||
rotateAnimation.speed = document.getElementById("rotate_animation_speed").value;
|
||||
}
|
||||
const primaryAnimationName = document.querySelector('input[type="radio"][name="primary_animation"]:checked').value;
|
||||
if (primaryAnimationName !== "") {
|
||||
primaryAnimation = skinViewer.animations.add(availableAnimations[primaryAnimationName]);
|
||||
primaryAnimation.speed = document.getElementById("primary_animation_speed").value;
|
||||
}
|
||||
oribitControl.enableRotate = document.getElementById("control_rotate").checked;
|
||||
oribitControl.enableZoom = document.getElementById("control_zoom").checked;
|
||||
oribitControl.enablePan = document.getElementById("control_pan").checked;
|
||||
for (const part of skinParts) {
|
||||
for (const layer of skinLayers) {
|
||||
skinViewer.playerObject.skin[part][layer].visible =
|
||||
document.querySelector(`#layers_table input[type="checkbox"][data-part="${part}"][data-layer="${layer}"]`).checked;
|
||||
}
|
||||
}
|
||||
reloadSkin();
|
||||
reloadCape();
|
||||
}
|
||||
|
||||
initializeLayersTable();
|
||||
initializeControls();
|
||||
initializeViewer();
|
||||
</script>
|
||||
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
|
|
@ -11,8 +11,8 @@
|
|||
"build": "npm run build:modules && npm run build:bundles",
|
||||
"test:lint": "eslint --ext .ts src",
|
||||
"test": "npm run test:lint",
|
||||
"dev:watch:modules": "tsc -w --declaration --sourceMap --outDir libs -p .",
|
||||
"dev:watch:bundles": "rollup -w -c",
|
||||
"dev:watch:modules": "tsc -w --preserveWatchOutput --declaration --sourceMap --outDir libs -p .",
|
||||
"dev:watch:bundles": "rollup -w --no-watch.clearScreen -c",
|
||||
"dev:serve": "ws",
|
||||
"dev": "npm-run-all --parallel dev:watch:modules dev:watch:bundles dev:serve",
|
||||
"prepublishOnly": "npm run clean && npm run build"
|
||||
|
|