Merge remote-tracking branch 'upstream/master'

This commit is contained in:
James Harrison 2020-09-10 22:23:43 +01:00
commit 1e23944ce4
22 changed files with 986 additions and 242 deletions

View File

@ -1,6 +1,6 @@
name: CI name: CI
on: [push] on: [push, pull_request]
jobs: jobs:
test: test:
@ -9,8 +9,6 @@ jobs:
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: npm build & test - run: npm install
run: | - run: npm run build
npm install - run: npm test
npm run build
npm test

31
.github/workflows/deploy-ghpages.yaml vendored Normal file
View File

@ -0,0 +1,31 @@
name: Deploy to GitHub Pages
on:
push:
branches:
- master
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- run: npm install
- run: npm run build
- run: npm test
- name: Prepare files
run: |
cp -r examples public
cp -r bundles public/js
build_commit="${{ github.sha }}"
build_time=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
sed -Ez \
-e 's#\.\./bundles/skinview3d\.bundle\.js#js/skinview3d.bundle.js#g' \
-e 's#\{\{build_commit\}\}#'$build_commit'#g' \
-e 's#\{\{build_time\}\}#'$build_time'#g' \
-e 's#<!--%%deploy_only%%(.*)-->#\1#g' \
-i public/*.html
- uses: crazy-max/ghaction-github-pages@v2
with:
target_branch: gh-pages
build_dir: public
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

1
.gitignore vendored
View File

@ -62,3 +62,4 @@ libs/
_ignore/ _ignore/
.DS_Store .DS_Store
.rpt2_cache .rpt2_cache
public/

View File

@ -16,11 +16,12 @@ Three.js powered Minecraft skin viewer.
* Automatic model detection (Slim / Default) * Automatic model detection (Slim / Default)
# Usage # Usage
[Examples of using the viewer](https://bs-community.github.io/skinview3d/) [Example of using skinview3d](https://bs-community.github.io/skinview3d/)
```html ```html
<div id="skin_container"></div> <canvas id="skin_container"></canvas>
<script> <script>
let skinViewer = new skinview3d.SkinViewer(document.getElementById("skin_container"), { let skinViewer = new skinview3d.SkinViewer({
canvas: document.getElementById("skin_container"),
width: 300, width: 300,
height: 400, height: 400,
skin: "img/skin.png" skin: "img/skin.png"
@ -65,5 +66,22 @@ Three.js powered Minecraft skin viewer.
</script> </script>
``` ```
## Anti-aliasing
skinview3d supports FXAA (fast approximate anti-aliasing).
To enable it, you need to replace `SkinViewer` with `FXAASkinViewer`.
It's recommended to use an opaque background when FXAA is enabled,
as transparent background may look buggy.
```javascript
let skinViewer = new skinview3d.FXAASkinViewer({
// we do not use transparent background, so disable alpha to improve performance
alpha: false,
...
});
// set the background color
skinViewer.renderer.setClearColor(0x5a76f3);
```
# Build # Build
`npm run build` `npm run build`

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

BIN
examples/img/hacksore.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 689 B

BIN
examples/img/haka.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

BIN
examples/img/hd_cape.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 278 KiB

BIN
examples/img/ironman_hd.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 589 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
examples/img/sethbling.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

450
examples/index.html Executable file → Normal file
View File

@ -3,73 +3,439 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
<title>skinview3d</title> <title>skinview3d</title>
<link href="https://fonts.googleapis.com/css?family=Archivo+Black" rel="stylesheet">
<style> <style>
html,
body { body {
font-family: "Helvetica Neue", "Helvetica", "Arial", "sans-serif";
margin: 5px;
}
h1 {
font-size: 1.25em;
}
h2 {
font-size: 1em;
}
h1,
h2 {
margin-bottom: 0;
}
input[type="number"] {
max-width: 60px;
}
input[type="text"] {
box-sizing: border-box;
max-width: 250px;
width: calc(100% - 100px);
}
.control {
display: inline;
}
.control + .control {
margin-left: 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;
}
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; margin: 0;
padding: 0; width: 100%;
background-color: #1e1e1e;
} }
#skin_container { footer {
text-align: center; margin-top: 10px;
cursor: move; padding-top: 10px;
border-top: 1px grey solid;
} }
#skin_container > canvas { label {
background: white; white-space: nowrap;
filter: drop-shadow(-5px 5px 7px rgba(0, 0, 0, 0.4));
outline: none;
} }
#elytra, #animated { .control-section ul {
display: block; margin-top: 0;
margin: 1vh auto; padding-left: 20px;
font-size: 2rem;
} }
</style> </style>
</head> </head>
<body> <body>
<button id="elytra">Enable/Disable Elytra</button>
<button id="animated">Enable/Disable Animated</button> <canvas id="skin_container"></canvas>
<div id="skin_container"></div>
<script type="text/javascript" src="../bundles/skinview3d.bundle.js"></script> <div class="controls">
<button id="reset_all" type="button" class="control">Reset All</button>
<div class="control-section">
<h1>Canvas Size</h1>
<label class="control">Width: <input id="canvas_width" type="number" value="300"></label>
<label class="control">Height: <input id="canvas_height" type="number" value="300"></label>
</div>
<div class="control-section">
<h1>Animation</h1>
<label class="control">Global Speed: <input id="global_animation_speed" type="number" value="1" step="0.1"></label>
<button id="animation_pause_resume" type="button" class="control">Pause / Resume</button>
<div>
<h2>Rotate</h2>
<label class="control"><input id="rotate_animation" type="checkbox"> Enable</label>
<label class="control">Speed: <input id="rotate_animation_speed" type="number" value="1" step="0.1"></label>
</div>
<div>
<h2>Walk / Run</h2>
<div class="control">
<label><input type="radio" id="primary_animation_none" name="primary_animation" value="" checked> None</label>
<label><input type="radio" id="primary_animation_walk" name="primary_animation" value="walk"> Walk</label>
<label><input type="radio" id="primary_animation_run" name="primary_animation" value="run"> Run</label>
</div>
<label class="control">Speed: <input id="primary_animation_speed" type="number" value="1" step="0.1"></label>
</div>
</div>
<div class="control-section">
<h1>Mouse Control</h1>
<div class="control">
<label><input type="checkbox" id="control_rotate" checked> Enable Rotate</label>
<label><input type="checkbox" id="control_zoom" checked> Enable Zoom</label>
<label><input type="checkbox" id="control_pan"> Enable Pan</label>
</div>
</div>
<div class="control-section">
<h1>Skin Layers</h1>
<table id="layers_table">
<!-- table contents are generated using javascript -->
</table>
</div>
<div class="control-section">
<h1>Textures</h1>
<div>
<div class="control">
<label>Skin URL: <input id="skin_url" type="text" value="img/1_8_texturemap_redux.png" placeholder="none" list="default_skins"></label>
<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>
<label class="control">Model:
<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>
</label>
</div>
<div>
<div class="control">
<label>Cape URL: <input id="cape_url" type="text" value="" placeholder="none" list="default_capes"></label>
<datalist id="default_capes">
<option value="">
<option value="img/mojang_cape.png">
<option value="img/legacy_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 class="control-section">
<h1>Other examples</h1>
<ul>
<li><a href="offscreen-render.html">offscreen-render</a></li>
</ul>
</div>
</div>
<footer>
<div>
GitHub: <a href="https://github.com/bs-community/skinview3d">bs-community/skinview3d</a>
</div>
<!--%%deploy_only%%
<div>
Built from
commit <a href="https://github.com/bs-community/skinview3d/commit/{{build_commit}}">{{build_commit}}</a>
at <time datetime="{{build_time}}">{{build_time}}</time>
</div>
-->
</footer>
<script src="../bundles/skinview3d.bundle.js"></script>
<script> <script>
var animated = false; const skinParts = {
head: "head",
body: "body",
rightArm: "right arm",
leftArm: "left arm",
rightLeg: "right leg",
leftLeg: "left leg"
};
const skinLayers = {
innerLayer: "inner",
outerLayer: "outer"
};
const availableAnimations = {
walk: skinview3d.WalkingAnimation,
run: skinview3d.RunningAnimation
};
document.getElementById("elytra").addEventListener('click', function() { let skinViewer;
skinViewer.playerObject.cape.visible = !skinViewer.playerObject.cape.visible; let oribitControl;
skinViewer.playerObject.elytra.visible = !skinViewer.playerObject.elytra.visible let rotateAnimation;
}); let primaryAnimation;
document.getElementById("animated").addEventListener('click', function() { function reloadSkin() {
if(animated) { const input = document.getElementById("skin_url");
skinViewer.loadCustomCape("./cape.png"); const url = input.value;
animated = false; if (url === "") {
skinViewer.loadSkin(null);
input.setCustomValidity("");
} else { } else {
skinViewer.loadCustomCape("./animated.png"); skinViewer.loadSkin(url, document.getElementById("skin_model").value)
animated = true; .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, partName] of Object.entries(skinParts)) {
const cellHead = document.createElement("th");
cellHead.innerText = partName;
rowHead.appendChild(cellHead);
}
const tbody = document.createElement("tbody");
table.appendChild(tbody);
for (const [layer, layerName] of Object.entries(skinLayers)) {
const row = document.createElement("tr");
tbody.appendChild(row);
const cellLayer = document.createElement("th");
cellLayer.innerText = layerName;
row.appendChild(cellLayer);
for (const part of Object.keys(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 => {
let skinViewer = new skinview3d.SkinViewer(document.getElementById("skin_container"), { if (rotateAnimation !== null) {
width: 1400, rotateAnimation.speed = e.target.value;
height: 800, }
skin: "https://crafatar.com/skins/ba4161c03a42496c8ae07d13372f3371",
cape: "./cape.png",
ears: "./ears.png"
}); });
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 Object.keys(skinParts)) {
for (const layer of Object.keys(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();
});
}
let control = new skinview3d.createOrbitControls(skinViewer); function initializeViewer() {
control.enableZoom = false; skinViewer = new skinview3d.FXAASkinViewer({
control.enablePan = false; canvas: document.getElementById("skin_container"),
skinViewer.playerObject.rotation.y = 180 * Math.PI / 180; alpha: false
});
skinViewer.renderer.setClearColor(0x5a76f3);
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 Object.keys(skinParts)) {
for (const layer of Object.keys(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> </script>
</body> </body>
<script type="module" src="https://unpkg.com/stats.js@0.17.0/src/Stats.js" integrity="sha384-W71K+d2HbqXqQWSj3Vj4RDsIVvIV7lR8O6RArKAiqB39ntwLci0W08qOn4Z1n8sM" crossorigin="anonymous" async></script>
<script type="module" async>
import Stats from "https://unpkg.com/stats.js@0.17.0/src/Stats.js";
const stats = new Stats();
stats.dom.style.left = "";
stats.dom.style.right = "0";
document.body.appendChild(stats.dom);
function loop() {
stats.update();
requestAnimationFrame(loop)
}
requestAnimationFrame(loop);
</script>
</html> </html>

View File

@ -0,0 +1,74 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
<title>skinview3d / offscreen-render</title>
</head>
<body>
<div id="rendered_imgs"></div>
<script src="../bundles/skinview3d.bundle.js"></script>
<script>
const textures = [
{
skin: "img/1_8_texturemap_redux.png",
cape: null
},
{
skin: "img/hacksore.png",
cape: "img/legacy_cape.png"
},
{
skin: "img/haka.png",
cape: null
},
{
skin: "img/hatsune_miku.png",
cape: "img/mojang_cape.png"
},
{
skin: "img/ironman_hd.png",
cape: "img/hd_cape.png"
},
{
skin: "img/sethbling.png",
cape: null
}
];
(async function () {
const skinViewer = new skinview3d.FXAASkinViewer({
width: 200,
height: 300,
alpha: false,
renderPaused: true
});
skinViewer.renderer.setClearColor(0x5a76f3);
skinViewer.camera.rotation.x = -0.620;
skinViewer.camera.rotation.y = 0.534;
skinViewer.camera.rotation.z = 0.348;
skinViewer.camera.position.x = 30.5;
skinViewer.camera.position.y = 18.0;
skinViewer.camera.position.z = 42.0;
for (const { skin, cape } of textures) {
await Promise.all([skinViewer.loadSkin(skin), skinViewer.loadCape(cape)]);
skinViewer.render();
const image = skinViewer.canvas.toDataURL();
const imgElement = document.createElement("img");
imgElement.src = image;
imgElement.width = skinViewer.width;
imgElement.height = skinViewer.height;
document.getElementById("rendered_imgs").appendChild(imgElement);
}
skinViewer.dispose();
})();
</script>
</body>
</html>

346
package-lock.json generated
View File

@ -1,31 +1,31 @@
{ {
"name": "skinview3d", "name": "skinview3d",
"version": "2.0.0-alpha.5", "version": "2.0.0-alpha.8",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {
"@babel/code-frame": { "@babel/code-frame": {
"version": "7.8.3", "version": "7.10.4",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz",
"integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==",
"dev": true, "dev": true,
"requires": { "requires": {
"@babel/highlight": "^7.8.3" "@babel/highlight": "^7.10.4"
} }
}, },
"@babel/helper-validator-identifier": { "@babel/helper-validator-identifier": {
"version": "7.9.5", "version": "7.10.4",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.5.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz",
"integrity": "sha512-/8arLKUFq882w4tWGj9JYzRpAlZgiWUJ+dtteNTDqrRBz9Iguck9Rn3ykuBDoUwh2TO4tSAJlrxDUOXWklJe4g==", "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==",
"dev": true "dev": true
}, },
"@babel/highlight": { "@babel/highlight": {
"version": "7.9.0", "version": "7.10.4",
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.9.0.tgz", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz",
"integrity": "sha512-lJZPilxX7Op3Nv/2cvFdnlepPXDxi29wxteT57Q965oc5R9v86ztx0jfxVrTcBk8C2kcPkkDa2Z4T3ZsPPVWsQ==", "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==",
"dev": true, "dev": true,
"requires": { "requires": {
"@babel/helper-validator-identifier": "^7.9.0", "@babel/helper-validator-identifier": "^7.10.4",
"chalk": "^2.0.0", "chalk": "^2.0.0",
"js-tokens": "^4.0.0" "js-tokens": "^4.0.0"
} }
@ -40,15 +40,20 @@
} }
}, },
"@rollup/plugin-node-resolve": { "@rollup/plugin-node-resolve": {
<<<<<<< HEAD
"version": "8.4.0", "version": "8.4.0",
"resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-8.4.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-8.4.0.tgz",
"integrity": "sha512-LFqKdRLn0ShtQyf6SBYO69bGE1upV6wUhBX0vFOUnLAyzx5cwp8svA0eHUnu8+YU57XOkrMtfG63QOpQx25pHQ==", "integrity": "sha512-LFqKdRLn0ShtQyf6SBYO69bGE1upV6wUhBX0vFOUnLAyzx5cwp8svA0eHUnu8+YU57XOkrMtfG63QOpQx25pHQ==",
=======
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-9.0.0.tgz",
"integrity": "sha512-gPz+utFHLRrd41WMP13Jq5mqqzHL3OXrfj3/MkSyB6UBIcuNt9j60GCbarzMzdf1VHFpOxfQh/ez7wyadLMqkg==",
>>>>>>> upstream/master
"dev": true, "dev": true,
"requires": { "requires": {
"@rollup/pluginutils": "^3.1.0", "@rollup/pluginutils": "^3.1.0",
"@types/resolve": "1.17.1", "@types/resolve": "1.17.1",
"builtin-modules": "^3.1.0", "builtin-modules": "^3.1.0",
"deep-freeze": "^0.0.1",
"deepmerge": "^4.2.2", "deepmerge": "^4.2.2",
"is-module": "^1.0.0", "is-module": "^1.0.0",
"resolve": "^1.17.0" "resolve": "^1.17.0"
@ -117,9 +122,15 @@
"dev": true "dev": true
}, },
"@types/node": { "@types/node": {
<<<<<<< HEAD
"version": "14.0.22", "version": "14.0.22",
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.0.22.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-14.0.22.tgz",
"integrity": "sha512-emeGcJvdiZ4Z3ohbmw93E/64jRzUHAItSHt8nF7M4TGgQTiWqFVGB8KNpLGFmUHmHLvjvBgFwVlqNcq+VuGv9g==", "integrity": "sha512-emeGcJvdiZ4Z3ohbmw93E/64jRzUHAItSHt8nF7M4TGgQTiWqFVGB8KNpLGFmUHmHLvjvBgFwVlqNcq+VuGv9g==",
=======
"version": "14.6.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.6.0.tgz",
"integrity": "sha512-mikldZQitV94akrc4sCcSjtJfsTKt4p+e/s0AGscVA6XArQ9kFclP+ZiYUMnq987rc6QlYxXv/EivqlfSLxpKA==",
>>>>>>> upstream/master
"dev": true "dev": true
}, },
"@types/resolve": { "@types/resolve": {
@ -132,12 +143,21 @@
} }
}, },
"@typescript-eslint/eslint-plugin": { "@typescript-eslint/eslint-plugin": {
<<<<<<< HEAD
"version": "3.6.0", "version": "3.6.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-3.6.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-3.6.0.tgz",
"integrity": "sha512-ubHlHVt1lsPQB/CZdEov9XuOFhNG9YRC//kuiS1cMQI6Bs1SsqKrEmZnpgRwthGR09/kEDtr9MywlqXyyYd8GA==", "integrity": "sha512-ubHlHVt1lsPQB/CZdEov9XuOFhNG9YRC//kuiS1cMQI6Bs1SsqKrEmZnpgRwthGR09/kEDtr9MywlqXyyYd8GA==",
"dev": true, "dev": true,
"requires": { "requires": {
"@typescript-eslint/experimental-utils": "3.6.0", "@typescript-eslint/experimental-utils": "3.6.0",
=======
"version": "3.9.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-3.9.1.tgz",
"integrity": "sha512-XIr+Mfv7i4paEdBf0JFdIl9/tVxyj+rlilWIfZ97Be0lZ7hPvUbS5iHt9Glc8kRI53dsr0PcAEudbf8rO2wGgg==",
"dev": true,
"requires": {
"@typescript-eslint/experimental-utils": "3.9.1",
>>>>>>> upstream/master
"debug": "^4.1.1", "debug": "^4.1.1",
"functional-red-black-tree": "^1.0.1", "functional-red-black-tree": "^1.0.1",
"regexpp": "^3.0.0", "regexpp": "^3.0.0",
@ -146,6 +166,7 @@
} }
}, },
"@typescript-eslint/experimental-utils": { "@typescript-eslint/experimental-utils": {
<<<<<<< HEAD
"version": "3.6.0", "version": "3.6.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-3.6.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-3.6.0.tgz",
"integrity": "sha512-4Vdf2hvYMUnTdkCNZu+yYlFtL2v+N2R7JOynIOkFbPjf9o9wQvRwRkzUdWlFd2YiiUwJLbuuLnl5civNg5ykOQ==", "integrity": "sha512-4Vdf2hvYMUnTdkCNZu+yYlFtL2v+N2R7JOynIOkFbPjf9o9wQvRwRkzUdWlFd2YiiUwJLbuuLnl5civNg5ykOQ==",
@ -154,11 +175,22 @@
"@types/json-schema": "^7.0.3", "@types/json-schema": "^7.0.3",
"@typescript-eslint/types": "3.6.0", "@typescript-eslint/types": "3.6.0",
"@typescript-eslint/typescript-estree": "3.6.0", "@typescript-eslint/typescript-estree": "3.6.0",
=======
"version": "3.9.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-3.9.1.tgz",
"integrity": "sha512-lkiZ8iBBaYoyEKhCkkw4SAeatXyBq9Ece5bZXdLe1LWBUwTszGbmbiqmQbwWA8cSYDnjWXp9eDbXpf9Sn0hLAg==",
"dev": true,
"requires": {
"@types/json-schema": "^7.0.3",
"@typescript-eslint/types": "3.9.1",
"@typescript-eslint/typescript-estree": "3.9.1",
>>>>>>> upstream/master
"eslint-scope": "^5.0.0", "eslint-scope": "^5.0.0",
"eslint-utils": "^2.0.0" "eslint-utils": "^2.0.0"
} }
}, },
"@typescript-eslint/parser": { "@typescript-eslint/parser": {
<<<<<<< HEAD
"version": "3.6.0", "version": "3.6.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-3.6.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-3.6.0.tgz",
"integrity": "sha512-taghDxuLhbDAD1U5Fk8vF+MnR0yiFE9Z3v2/bYScFb0N1I9SK8eKHkdJl1DAD48OGFDMFTeOTX0z7g0W6SYUXw==", "integrity": "sha512-taghDxuLhbDAD1U5Fk8vF+MnR0yiFE9Z3v2/bYScFb0N1I9SK8eKHkdJl1DAD48OGFDMFTeOTX0z7g0W6SYUXw==",
@ -168,10 +200,22 @@
"@typescript-eslint/experimental-utils": "3.6.0", "@typescript-eslint/experimental-utils": "3.6.0",
"@typescript-eslint/types": "3.6.0", "@typescript-eslint/types": "3.6.0",
"@typescript-eslint/typescript-estree": "3.6.0", "@typescript-eslint/typescript-estree": "3.6.0",
=======
"version": "3.9.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-3.9.1.tgz",
"integrity": "sha512-y5QvPFUn4Vl4qM40lI+pNWhTcOWtpZAJ8pOEQ21fTTW4xTJkRplMjMRje7LYTXqVKKX9GJhcyweMz2+W1J5bMg==",
"dev": true,
"requires": {
"@types/eslint-visitor-keys": "^1.0.0",
"@typescript-eslint/experimental-utils": "3.9.1",
"@typescript-eslint/types": "3.9.1",
"@typescript-eslint/typescript-estree": "3.9.1",
>>>>>>> upstream/master
"eslint-visitor-keys": "^1.1.0" "eslint-visitor-keys": "^1.1.0"
} }
}, },
"@typescript-eslint/types": { "@typescript-eslint/types": {
<<<<<<< HEAD
"version": "3.6.0", "version": "3.6.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-3.6.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-3.6.0.tgz",
"integrity": "sha512-JwVj74ohUSt0ZPG+LZ7hb95fW8DFOqBuR6gE7qzq55KDI3BepqsCtHfBIoa0+Xi1AI7fq5nCu2VQL8z4eYftqg==", "integrity": "sha512-JwVj74ohUSt0ZPG+LZ7hb95fW8DFOqBuR6gE7qzq55KDI3BepqsCtHfBIoa0+Xi1AI7fq5nCu2VQL8z4eYftqg==",
@ -185,6 +229,21 @@
"requires": { "requires": {
"@typescript-eslint/types": "3.6.0", "@typescript-eslint/types": "3.6.0",
"@typescript-eslint/visitor-keys": "3.6.0", "@typescript-eslint/visitor-keys": "3.6.0",
=======
"version": "3.9.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-3.9.1.tgz",
"integrity": "sha512-15JcTlNQE1BsYy5NBhctnEhEoctjXOjOK+Q+rk8ugC+WXU9rAcS2BYhoh6X4rOaXJEpIYDl+p7ix+A5U0BqPTw==",
"dev": true
},
"@typescript-eslint/typescript-estree": {
"version": "3.9.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-3.9.1.tgz",
"integrity": "sha512-IqM0gfGxOmIKPhiHW/iyAEXwSVqMmR2wJ9uXHNdFpqVvPaQ3dWg302vW127sBpAiqM9SfHhyS40NKLsoMpN2KA==",
"dev": true,
"requires": {
"@typescript-eslint/types": "3.9.1",
"@typescript-eslint/visitor-keys": "3.9.1",
>>>>>>> upstream/master
"debug": "^4.1.1", "debug": "^4.1.1",
"glob": "^7.1.6", "glob": "^7.1.6",
"is-glob": "^4.0.1", "is-glob": "^4.0.1",
@ -194,21 +253,50 @@
} }
}, },
"@typescript-eslint/visitor-keys": { "@typescript-eslint/visitor-keys": {
<<<<<<< HEAD
"version": "3.6.0", "version": "3.6.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-3.6.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-3.6.0.tgz",
"integrity": "sha512-p1izllL2Ubwunite0ITjubuMQRBGgjdVYwyG7lXPX8GbrA6qF0uwSRz9MnXZaHMxID4948gX0Ez8v9tUDi/KfQ==", "integrity": "sha512-p1izllL2Ubwunite0ITjubuMQRBGgjdVYwyG7lXPX8GbrA6qF0uwSRz9MnXZaHMxID4948gX0Ez8v9tUDi/KfQ==",
=======
"version": "3.9.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-3.9.1.tgz",
"integrity": "sha512-zxdtUjeoSh+prCpogswMwVUJfEFmCOjdzK9rpNjNBfm6EyPt99x3RrJoBOGZO23FCt0WPKUCOL5mb/9D5LjdwQ==",
>>>>>>> upstream/master
"dev": true, "dev": true,
"requires": { "requires": {
"eslint-visitor-keys": "^1.1.0" "eslint-visitor-keys": "^1.1.0"
} }
}, },
"@yushijinhun/three-minifier-rollup": { <<<<<<< HEAD
"version": "0.1.7", =======
"resolved": "https://registry.npmjs.org/@yushijinhun/three-minifier-rollup/-/three-minifier-rollup-0.1.7.tgz", "@yushijinhun/three-minifier-common": {
"integrity": "sha512-GzlFkEzUqyI8X23guT7Y8R3KaCzCXdUNpWfSPiq7m/f3xUNhhclFJORWqurZdnSEnxWCiCMgNm/9GsLMd/N2Qw==", "version": "0.2.0-alpha.2",
"resolved": "https://registry.npmjs.org/@yushijinhun/three-minifier-common/-/three-minifier-common-0.2.0-alpha.2.tgz",
"integrity": "sha512-Y2NlRcyvEQmY5FlELV/Az7UElddTqKwZxA77OB0of5sj610pa2pM+IlwC20qgEXMaqfdULaENjYGi5EHPK0RSQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"@rollup/plugin-node-resolve": "^8.0.0", "acorn": "^8.0.1",
"acorn-walk": "^8.0.0",
"glsl-tokenizer": "^2.1.5"
},
"dependencies": {
"acorn": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.0.1.tgz",
"integrity": "sha512-dmKn4pqZ29iQl2Pvze1zTrps2luvls2PBY//neO2WJ0s10B3AxJXshN+Ph7B4GrhfGhHXrl4dnUwyNNXQcnWGQ==",
"dev": true
}
}
},
>>>>>>> upstream/master
"@yushijinhun/three-minifier-rollup": {
"version": "0.2.0-alpha.2",
"resolved": "https://registry.npmjs.org/@yushijinhun/three-minifier-rollup/-/three-minifier-rollup-0.2.0-alpha.2.tgz",
"integrity": "sha512-jB9efJwsX9M4QGx4/rA760H9G44tnGq6gIG/ZlP8PWKIBdw48JmRlk20LP1mCT0SH6CKGZcdE0DwR3At+ssGXg==",
"dev": true,
"requires": {
"@rollup/plugin-node-resolve": "^9.0.0",
"@yushijinhun/three-minifier-common": "^0.2.0-alpha.2",
"magic-string": "^0.25.7" "magic-string": "^0.25.7"
} }
}, },
@ -233,9 +321,9 @@
} }
}, },
"acorn": { "acorn": {
"version": "7.3.1", "version": "7.4.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-7.3.1.tgz", "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.0.tgz",
"integrity": "sha512-tLc0wSnatxAQHVHUapaHdz72pi9KUyHjq5KyHjGg9Y8Ifdc79pTh2XvI6I1/chZbnM7QtNKzh66ooDogPZSleA==", "integrity": "sha512-+G7P8jJmCHr+S+cLfQxygbWhXy+8YTVGzAkpEbcLo2mLoL7tij/VG41QSHACSf5QgYRhMZYHuNc6drJaO0Da+w==",
"dev": true "dev": true
}, },
"acorn-jsx": { "acorn-jsx": {
@ -244,19 +332,31 @@
"integrity": "sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ==", "integrity": "sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ==",
"dev": true "dev": true
}, },
"acorn-walk": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.0.0.tgz",
"integrity": "sha512-oZRad/3SMOI/pxbbmqyurIx7jHw1wZDcR9G44L8pUVFEomX/0dH89SrM1KaDXuv1NpzAXz6Op/Xu/Qd5XXzdEA==",
"dev": true
},
"agent-base": { "agent-base": {
"version": "6.0.0", "version": "6.0.1",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.0.tgz", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.1.tgz",
"integrity": "sha512-j1Q7cSCqN+AwrmDd+pzgqc0/NpC655x2bUf5ZjRIO77DcNBFmh+OgRNzF6OKdCC9RSCb19fGd99+bhXFdkRNqw==", "integrity": "sha512-01q25QQDwLSsyfhrKbn8yuur+JNw0H+0Y4JiGIKd3z9aYk/w/2kxD/Upc+t2ZBBSUNff50VjPsSW2YxM8QYKVg==",
"dev": true, "dev": true,
"requires": { "requires": {
"debug": "4" "debug": "4"
} }
}, },
"ajv": { "ajv": {
<<<<<<< HEAD
"version": "6.12.3", "version": "6.12.3",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.3.tgz", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.3.tgz",
"integrity": "sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA==", "integrity": "sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA==",
=======
"version": "6.12.4",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.4.tgz",
"integrity": "sha512-eienB2c9qVQs2KWexhkrdMLVDoIQCz5KSeLxwg9Lzk4DOfBtIK9PQwwufcsn1jjGuf9WZmqPMbGxOzfcuphJCQ==",
>>>>>>> upstream/master
"dev": true, "dev": true,
"requires": { "requires": {
"fast-deep-equal": "^3.1.1", "fast-deep-equal": "^3.1.1",
@ -643,12 +743,6 @@
"integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
"dev": true "dev": true
}, },
"deep-freeze": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/deep-freeze/-/deep-freeze-0.0.1.tgz",
"integrity": "sha1-OgsABd4YZygZ39OM0x+RF5yJPoQ=",
"dev": true
},
"deep-is": { "deep-is": {
"version": "0.1.3", "version": "0.1.3",
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
@ -776,9 +870,15 @@
"dev": true "dev": true
}, },
"eslint": { "eslint": {
<<<<<<< HEAD
"version": "7.4.0", "version": "7.4.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-7.4.0.tgz", "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.4.0.tgz",
"integrity": "sha512-gU+lxhlPHu45H3JkEGgYhWhkR9wLHHEXC9FbWFnTlEkbKyZKWgWRLgf61E8zWmBuI6g5xKBph9ltg3NtZMVF8g==", "integrity": "sha512-gU+lxhlPHu45H3JkEGgYhWhkR9wLHHEXC9FbWFnTlEkbKyZKWgWRLgf61E8zWmBuI6g5xKBph9ltg3NtZMVF8g==",
=======
"version": "7.7.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-7.7.0.tgz",
"integrity": "sha512-1KUxLzos0ZVsyL81PnRN335nDtQ8/vZUD6uMtWbF+5zDtjKcsklIi78XoE0MVL93QvWTu+E5y44VyyCsOMBrIg==",
>>>>>>> upstream/master
"dev": true, "dev": true,
"requires": { "requires": {
"@babel/code-frame": "^7.0.0", "@babel/code-frame": "^7.0.0",
@ -789,9 +889,9 @@
"doctrine": "^3.0.0", "doctrine": "^3.0.0",
"enquirer": "^2.3.5", "enquirer": "^2.3.5",
"eslint-scope": "^5.1.0", "eslint-scope": "^5.1.0",
"eslint-utils": "^2.0.0", "eslint-utils": "^2.1.0",
"eslint-visitor-keys": "^1.2.0", "eslint-visitor-keys": "^1.3.0",
"espree": "^7.1.0", "espree": "^7.2.0",
"esquery": "^1.2.0", "esquery": "^1.2.0",
"esutils": "^2.0.2", "esutils": "^2.0.2",
"file-entry-cache": "^5.0.1", "file-entry-cache": "^5.0.1",
@ -805,7 +905,7 @@
"js-yaml": "^3.13.1", "js-yaml": "^3.13.1",
"json-stable-stringify-without-jsonify": "^1.0.1", "json-stable-stringify-without-jsonify": "^1.0.1",
"levn": "^0.4.1", "levn": "^0.4.1",
"lodash": "^4.17.14", "lodash": "^4.17.19",
"minimatch": "^3.0.4", "minimatch": "^3.0.4",
"natural-compare": "^1.4.0", "natural-compare": "^1.4.0",
"optionator": "^0.9.1", "optionator": "^0.9.1",
@ -923,14 +1023,14 @@
"dev": true "dev": true
}, },
"espree": { "espree": {
"version": "7.1.0", "version": "7.2.0",
"resolved": "https://registry.npmjs.org/espree/-/espree-7.1.0.tgz", "resolved": "https://registry.npmjs.org/espree/-/espree-7.2.0.tgz",
"integrity": "sha512-dcorZSyfmm4WTuTnE5Y7MEN1DyoPYy1ZR783QW1FJoenn7RailyWFsq/UL6ZAAA7uXurN9FIpYyUs3OfiIW+Qw==", "integrity": "sha512-H+cQ3+3JYRMEIOl87e7QdHX70ocly5iW4+dttuR8iYSPr/hXKFb+7dBsZ7+u1adC4VrnPlTkv0+OwuPnDop19g==",
"dev": true, "dev": true,
"requires": { "requires": {
"acorn": "^7.2.0", "acorn": "^7.3.1",
"acorn-jsx": "^5.2.0", "acorn-jsx": "^5.2.0",
"eslint-visitor-keys": "^1.2.0" "eslint-visitor-keys": "^1.3.0"
} }
}, },
"esprima": { "esprima": {
@ -949,9 +1049,9 @@
}, },
"dependencies": { "dependencies": {
"estraverse": { "estraverse": {
"version": "5.1.0", "version": "5.2.0",
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.1.0.tgz", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz",
"integrity": "sha512-FyohXK+R0vE+y1nHLoBM7ZTyqRpqAlhdZHCWIWEviFLiGB8b04H6bQs8G+XTthacvT8VuwvteiP7RJSxMs8UEw==", "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==",
"dev": true "dev": true
} }
} }
@ -1124,6 +1224,15 @@
"type-fest": "^0.8.1" "type-fest": "^0.8.1"
} }
}, },
"glsl-tokenizer": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/glsl-tokenizer/-/glsl-tokenizer-2.1.5.tgz",
"integrity": "sha512-XSZEJ/i4dmz3Pmbnpsy3cKh7cotvFlBiZnDOwnj/05EwNp2XrhQ4XKJxT7/pDt4kp4YcpRSKz8eTV7S+mwV6MA==",
"dev": true,
"requires": {
"through2": "^0.6.3"
}
},
"graceful-fs": { "graceful-fs": {
"version": "4.1.11", "version": "4.1.11",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
@ -1351,11 +1460,12 @@
"dev": true "dev": true
}, },
"jest-worker": { "jest-worker": {
"version": "26.0.0", "version": "26.3.0",
"resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.0.0.tgz", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.3.0.tgz",
"integrity": "sha512-pPaYa2+JnwmiZjK9x7p9BoZht+47ecFCDFA/CJxspHzeDvQcfVBLWzCiWyo+EGrSiQMWZtCFo9iSvMZnAAo8vw==", "integrity": "sha512-Vmpn2F6IASefL+DVBhPzI2J9/GJUsqzomdeN+P+dK8/jKxbh8R3BtFnx3FIta7wYlPU62cpJMJQo4kuOowcMnw==",
"dev": true, "dev": true,
"requires": { "requires": {
"@types/node": "*",
"merge-stream": "^2.0.0", "merge-stream": "^2.0.0",
"supports-color": "^7.0.0" "supports-color": "^7.0.0"
} }
@ -1416,9 +1526,9 @@
} }
}, },
"koa": { "koa": {
"version": "2.12.1", "version": "2.13.0",
"resolved": "https://registry.npmjs.org/koa/-/koa-2.12.1.tgz", "resolved": "https://registry.npmjs.org/koa/-/koa-2.13.0.tgz",
"integrity": "sha512-NuYVKjnBxeEe19VljPO9yNcaKKVrMagcax3jjzZtOlxRY2nThWKQqgnI3Pr1OG7mFtvySoDRixoUWZIt6R9C3A==", "integrity": "sha512-i/XJVOfPw7npbMv67+bOeXr3gPqOAw6uh5wFyNs3QvJ47tUx3M3V9rIE0//WytY42MKz4l/MXKyGkQ2LQTfLUQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"accepts": "^1.3.5", "accepts": "^1.3.5",
@ -1591,26 +1701,14 @@
} }
}, },
"koa-send": { "koa-send": {
"version": "5.0.0", "version": "5.0.1",
"resolved": "https://registry.npmjs.org/koa-send/-/koa-send-5.0.0.tgz", "resolved": "https://registry.npmjs.org/koa-send/-/koa-send-5.0.1.tgz",
"integrity": "sha512-90ZotV7t0p3uN9sRwW2D484rAaKIsD8tAVtypw/aBU+ryfV+fR2xrcAwhI8Wl6WRkojLUs/cB9SBSCuIb+IanQ==", "integrity": "sha512-tmcyQ/wXXuxpDxyNXv5yNNkdAMdFRqwtegBXUaowiQzUKqJehttS0x2j0eOZDQAyloAth5w6wwBImnFzkUz3pQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"debug": "^3.1.0", "debug": "^4.1.1",
"http-errors": "^1.6.3", "http-errors": "^1.7.3",
"mz": "^2.7.0",
"resolve-path": "^1.4.0" "resolve-path": "^1.4.0"
},
"dependencies": {
"debug": {
"version": "3.2.6",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
"integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
"dev": true,
"requires": {
"ms": "^2.1.1"
}
}
} }
}, },
"koa-static": { "koa-static": {
@ -1691,9 +1789,15 @@
} }
}, },
"lodash": { "lodash": {
<<<<<<< HEAD
"version": "4.17.19", "version": "4.17.19",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz",
"integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==", "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==",
=======
"version": "4.17.20",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
"integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==",
>>>>>>> upstream/master
"dev": true "dev": true
}, },
"lodash.assignwith": { "lodash.assignwith": {
@ -2269,6 +2373,15 @@
"integrity": "sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ==", "integrity": "sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ==",
"dev": true "dev": true
}, },
"randombytes": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
"integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
"dev": true,
"requires": {
"safe-buffer": "^5.1.0"
}
},
"raw-body": { "raw-body": {
"version": "2.4.1", "version": "2.4.1",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.1.tgz", "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.1.tgz",
@ -2380,24 +2493,30 @@
} }
}, },
"rollup": { "rollup": {
<<<<<<< HEAD
"version": "2.21.0", "version": "2.21.0",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.21.0.tgz", "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.21.0.tgz",
"integrity": "sha512-BEGgy+wSzux7Ycq58pRiWEOBZaXRXTuvzl1gsm7gqmsAHxkWf9nyA5V2LN9fGSHhhDQd0/C13iRzSh4bbIpWZQ==", "integrity": "sha512-BEGgy+wSzux7Ycq58pRiWEOBZaXRXTuvzl1gsm7gqmsAHxkWf9nyA5V2LN9fGSHhhDQd0/C13iRzSh4bbIpWZQ==",
=======
"version": "2.26.3",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.26.3.tgz",
"integrity": "sha512-Mlt39/kL2rA9egcbQbaZV1SNVplGqYYhDDMcGgHPPE0tvM3R4GrB+IEdYy2QtTrdzMQx57ZcqDFf/KWWm8F+uw==",
>>>>>>> upstream/master
"dev": true, "dev": true,
"requires": { "requires": {
"fsevents": "~2.1.2" "fsevents": "~2.1.2"
} }
}, },
"rollup-plugin-terser": { "rollup-plugin-terser": {
"version": "6.1.0", "version": "7.0.0",
"resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-6.1.0.tgz", "resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-7.0.0.tgz",
"integrity": "sha512-4fB3M9nuoWxrwm39habpd4hvrbrde2W2GG4zEGPQg1YITNkM3Tqur5jSuXlWNzbv/2aMLJ+dZJaySc3GCD8oDw==", "integrity": "sha512-p/N3lLiFusCjYTLfVkoaiRTOGr5AESEaljMPH12MhOtoMkmTBhIAfuadrcWy4am1U0vU4WTxO9fi0K09O4CboQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"@babel/code-frame": "^7.8.3", "@babel/code-frame": "^7.10.4",
"jest-worker": "^26.0.0", "jest-worker": "^26.2.1",
"serialize-javascript": "^3.0.0", "serialize-javascript": "^4.0.0",
"terser": "^4.7.0" "terser": "^5.0.0"
} }
}, },
"safe-buffer": { "safe-buffer": {
@ -2419,10 +2538,13 @@
"dev": true "dev": true
}, },
"serialize-javascript": { "serialize-javascript": {
"version": "3.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-3.0.0.tgz", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz",
"integrity": "sha512-skZcHYw2vEX4bw90nAr2iTTsz6x2SrHEnfxgKYmZlvJYBEZrvbKtobJWlQ20zczKb3bsHHXXTYt48zBA7ni9cw==", "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==",
"dev": true "dev": true,
"requires": {
"randombytes": "^2.1.0"
}
}, },
"serve-index-75lb": { "serve-index-75lb": {
"version": "2.0.1", "version": "2.0.1",
@ -2508,9 +2630,15 @@
"dev": true "dev": true
}, },
"skinview-utils": { "skinview-utils": {
<<<<<<< HEAD
"version": "0.5.6", "version": "0.5.6",
"resolved": "https://registry.npmjs.org/skinview-utils/-/skinview-utils-0.5.6.tgz", "resolved": "https://registry.npmjs.org/skinview-utils/-/skinview-utils-0.5.6.tgz",
"integrity": "sha512-TkpkCkLHNmeltUrtmkMexS/6izDE6LD9VKrvEnKvAqByKcymiwfhFydzTCe1vsarnZK1VPtDbAvBh9/n6UkDLg==" "integrity": "sha512-TkpkCkLHNmeltUrtmkMexS/6izDE6LD9VKrvEnKvAqByKcymiwfhFydzTCe1vsarnZK1VPtDbAvBh9/n6UkDLg=="
=======
"version": "0.5.7",
"resolved": "https://registry.npmjs.org/skinview-utils/-/skinview-utils-0.5.7.tgz",
"integrity": "sha512-XhOuCzvGoHuYOymfuZ8IJklVgqkYfusuyQSZMWiE/kluh89ZtBjS3cwoHIff1+Z5mAXHoa+Vkr0wN3JHJvF2ag=="
>>>>>>> upstream/master
}, },
"slice-ansi": { "slice-ansi": {
"version": "2.1.0", "version": "2.1.0",
@ -2764,9 +2892,9 @@
} }
}, },
"terser": { "terser": {
"version": "4.7.0", "version": "5.1.0",
"resolved": "https://registry.npmjs.org/terser/-/terser-4.7.0.tgz", "resolved": "https://registry.npmjs.org/terser/-/terser-5.1.0.tgz",
"integrity": "sha512-Lfb0RiZcjRDXCC3OSHJpEkxJ9Qeqs6mp2v4jf2MHfy8vGERmVDuvjXdd/EnP5Deme5F2yBRBymKmKHCBg2echw==", "integrity": "sha512-pwC1Jbzahz1ZPU87NQ8B3g5pKbhyJSiHih4gLH6WZiPU8mmS1IlGbB0A2Nuvkj/LCNsgIKctg6GkYwWCeTvXZQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"commander": "^2.20.0", "commander": "^2.20.0",
@ -2781,9 +2909,9 @@
"dev": true "dev": true
}, },
"thenify": { "thenify": {
"version": "3.3.0", "version": "3.3.1",
"resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.0.tgz", "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
"integrity": "sha1-5p44obq+lpsBCCB5eLn2K4hgSDk=", "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==",
"dev": true, "dev": true,
"requires": { "requires": {
"any-promise": "^1.0.0" "any-promise": "^1.0.0"
@ -2799,9 +2927,9 @@
} }
}, },
"three": { "three": {
"version": "0.117.1", "version": "0.120.0",
"resolved": "https://registry.npmjs.org/three/-/three-0.117.1.tgz", "resolved": "https://registry.npmjs.org/three/-/three-0.120.0.tgz",
"integrity": "sha512-t4zeJhlNzUIj9+ub0l6nICVimSuRTZJOqvk3Rmlu+YGdTOJ49Wna8p7aumpkXJakJfITiybfpYE1XN1o1Z34UQ==" "integrity": "sha512-Swffpi3EAHWkmqC1MagKEzR5XgwkDiyeWI3M7vkGbBc0xhq2LcQmJj5DqBruLkrgcZQ+fM/+fSQBU1tDvggO4A=="
}, },
"through": { "through": {
"version": "2.3.8", "version": "2.3.8",
@ -2809,6 +2937,42 @@
"integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=",
"dev": true "dev": true
}, },
"through2": {
"version": "0.6.5",
"resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz",
"integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=",
"dev": true,
"requires": {
"readable-stream": ">=1.0.33-1 <1.1.0-0",
"xtend": ">=4.0.0 <4.1.0-0"
},
"dependencies": {
"isarray": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
"integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=",
"dev": true
},
"readable-stream": {
"version": "1.0.34",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz",
"integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=",
"dev": true,
"requires": {
"core-util-is": "~1.0.0",
"inherits": "~2.0.1",
"isarray": "0.0.1",
"string_decoder": "~0.10.x"
}
},
"string_decoder": {
"version": "0.10.31",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
"integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=",
"dev": true
}
}
},
"toidentifier": { "toidentifier": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
@ -2862,9 +3026,9 @@
} }
}, },
"typescript": { "typescript": {
"version": "3.9.6", "version": "3.9.7",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.6.tgz", "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.7.tgz",
"integrity": "sha512-Pspx3oKAPJtjNwE92YS05HQoY7z2SFyOpHo9MqJor3BXAGNaPUs83CuVp9VISFkSjyRfiTpmKuAYGJB7S7hOxw==", "integrity": "sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==",
"dev": true "dev": true
}, },
"typical": { "typical": {
@ -2976,6 +3140,12 @@
"mkdirp": "^0.5.1" "mkdirp": "^0.5.1"
} }
}, },
"xtend": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
"dev": true
},
"ylru": { "ylru": {
"version": "1.2.1", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/ylru/-/ylru-1.2.1.tgz", "resolved": "https://registry.npmjs.org/ylru/-/ylru-1.2.1.tgz",

View File

@ -1,6 +1,6 @@
{ {
"name": "skinview3d", "name": "skinview3d",
"version": "2.0.0-alpha.5", "version": "2.0.0-alpha.8",
"description": "Three.js powered Minecraft skin viewer", "description": "Three.js powered Minecraft skin viewer",
"main": "libs/skinview3d.js", "main": "libs/skinview3d.js",
"type": "module", "type": "module",
@ -11,10 +11,10 @@
"build": "npm run build:modules && npm run build:bundles", "build": "npm run build:modules && npm run build:bundles",
"test:lint": "eslint --ext .ts src", "test:lint": "eslint --ext .ts src",
"test": "npm run test:lint", "test": "npm run test:lint",
"dev:watch:modules": "tsc -w --declaration --sourceMap --outDir libs -p .", "dev:watch:modules": "tsc -w --preserveWatchOutput --declaration --sourceMap --outDir libs -p .",
"dev:watch:bundles": "rollup -w -c", "dev:watch:bundles": "rollup -w --no-watch.clearScreen -c",
"dev:serve": "ws", "dev:serve": "ws",
"dev": "npm-run-all --parallel dev:watch:modules dev:watch:bundles dev:serve", "dev": "npm-run-all --parallel dev:watch:bundles dev:serve",
"prepublishOnly": "npm run clean && npm run build" "prepublishOnly": "npm run clean && npm run build"
}, },
"repository": { "repository": {
@ -38,21 +38,21 @@
"bundles" "bundles"
], ],
"dependencies": { "dependencies": {
"skinview-utils": "^0.5.6", "skinview-utils": "^0.5.7",
"three": "^0.117.1" "three": "^0.120.0"
}, },
"devDependencies": { "devDependencies": {
"@rollup/plugin-node-resolve": "^8.4.0", "@rollup/plugin-node-resolve": "^9.0.0",
"@rollup/plugin-typescript": "^5.0.2", "@rollup/plugin-typescript": "^5.0.2",
"@typescript-eslint/eslint-plugin": "^3.6.0", "@typescript-eslint/eslint-plugin": "^3.9.1",
"@typescript-eslint/parser": "^3.6.0", "@typescript-eslint/parser": "^3.9.1",
"@yushijinhun/three-minifier-rollup": "^0.1.7", "@yushijinhun/three-minifier-rollup": "^0.2.0-alpha.2",
"eslint": "^7.4.0", "eslint": "^7.7.0",
"local-web-server": "^4.2.1", "local-web-server": "^4.2.1",
"npm-run-all": "^4.1.5", "npm-run-all": "^4.1.5",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"rollup": "^2.21.0", "rollup": "^2.26.3",
"rollup-plugin-terser": "^6.1.0", "rollup-plugin-terser": "^7.0.0",
"typescript": "^3.9.6" "typescript": "^3.9.7"
} }
} }

View File

@ -162,7 +162,7 @@ export const WalkingAnimation: Animation = (player, time) => {
export const RunningAnimation: Animation = (player, time) => { export const RunningAnimation: Animation = (player, time) => {
const skin = player.skin; const skin = player.skin;
time *= 15; time = time * 15 + Math.PI * 0.5;
// Leg swing with larger amplitude // Leg swing with larger amplitude
skin.leftLeg.rotation.x = Math.cos(time + Math.PI) * 1.3; skin.leftLeg.rotation.x = Math.cos(time + Math.PI) * 1.3;

45
src/fxaa.ts Normal file
View File

@ -0,0 +1,45 @@
import { EffectComposer } from "three/examples/jsm/postprocessing/EffectComposer.js";
import { RenderPass } from "three/examples/jsm/postprocessing/RenderPass.js";
import { ShaderPass } from "three/examples/jsm/postprocessing/ShaderPass.js";
import { FXAAShader } from "three/examples/jsm/shaders/FXAAShader.js";
import { SkinViewer, SkinViewerOptions } from "./viewer.js";
export class FXAASkinViewer extends SkinViewer {
readonly composer: EffectComposer;
readonly renderPass: RenderPass;
readonly fxaaPass: ShaderPass;
/**
* Note: FXAA doesn't work well with transparent backgrounds.
* It's recommended to use an opaque background and set `options.alpha` to false.
*/
constructor(options: SkinViewerOptions = {}) {
super(options);
this.composer = new EffectComposer(this.renderer);
this.renderPass = new RenderPass(this.scene, this.camera);
this.fxaaPass = new ShaderPass(FXAAShader);
this.composer.addPass(this.renderPass);
this.composer.addPass(this.fxaaPass);
this.updateComposerSize();
}
setSize(width: number, height: number): void {
super.setSize(width, height);
if (this.composer !== undefined) {
this.updateComposerSize();
}
}
private updateComposerSize(): void {
this.composer.setSize(this.width, this.height);
const pixelRatio = this.renderer.getPixelRatio();
this.composer.setPixelRatio(pixelRatio);
this.fxaaPass.material.uniforms["resolution"].value.x = 1 / (this.width * pixelRatio);
this.fxaaPass.material.uniforms["resolution"].value.y = 1 / (this.height * pixelRatio);
}
render(): void {
this.composer.render();
}
}

View File

@ -72,23 +72,29 @@ export class SkinObject extends Group {
constructor(texture: Texture) { constructor(texture: Texture) {
super(); super();
const layer1 = { const layer1Material = new MeshBasicMaterial({
map: texture, map: texture,
side: FrontSide side: FrontSide
}; });
const layer2 = { const layer2Material = new MeshBasicMaterial({
map: texture, map: texture,
side: DoubleSide, side: DoubleSide,
transparent: true, transparent: true,
opacity: 1, alphaTest: 1e-5
alphaTest: 0.5 });
}
const layer1Material = new MeshBasicMaterial(layer1); const layer1MaterialBiased = layer1Material.clone();
const layer2Material = new MeshBasicMaterial(layer2); layer1MaterialBiased.polygonOffset = true;
layer1MaterialBiased.polygonOffsetFactor = 1.0;
layer1MaterialBiased.polygonOffsetUnits = 1.0;
const layer2MaterialBiased = layer2Material.clone();
layer2MaterialBiased.polygonOffset = true;
layer2MaterialBiased.polygonOffsetFactor = 1.0;
layer2MaterialBiased.polygonOffsetUnits = 1.0;
// Head // Head
const headBox = new BoxGeometry(8, 8, 8, 0, 0, 0); const headBox = new BoxGeometry(8, 8, 8);
setVertices(headBox, setVertices(headBox,
toSkinVertices(8, 0, 16, 8), toSkinVertices(8, 0, 16, 8),
toSkinVertices(16, 0, 24, 8), toSkinVertices(16, 0, 24, 8),
@ -99,7 +105,7 @@ export class SkinObject extends Group {
); );
const headMesh = new Mesh(headBox, layer1Material); const headMesh = new Mesh(headBox, layer1Material);
const head2Box = new BoxGeometry(9, 9, 9, 0, 0, 0); const head2Box = new BoxGeometry(9, 9, 9);
setVertices(head2Box, setVertices(head2Box,
toSkinVertices(40, 0, 48, 8), toSkinVertices(40, 0, 48, 8),
toSkinVertices(48, 0, 56, 8), toSkinVertices(48, 0, 56, 8),
@ -117,7 +123,7 @@ export class SkinObject extends Group {
this.add(this.head); this.add(this.head);
// Body // Body
const bodyBox = new BoxGeometry(8, 12, 4, 0, 0, 0); const bodyBox = new BoxGeometry(8, 12, 4);
setVertices(bodyBox, setVertices(bodyBox,
toSkinVertices(20, 16, 28, 20), toSkinVertices(20, 16, 28, 20),
toSkinVertices(28, 16, 36, 20), toSkinVertices(28, 16, 36, 20),
@ -126,15 +132,9 @@ export class SkinObject extends Group {
toSkinVertices(28, 20, 32, 32), toSkinVertices(28, 20, 32, 32),
toSkinVertices(32, 20, 40, 32) toSkinVertices(32, 20, 40, 32)
); );
const bodyMesh = new Mesh(bodyBox, new MeshBasicMaterial({ const bodyMesh = new Mesh(bodyBox, layer1Material);
...layer1,
// this pulls bodyMesh towards the camera
// so body is given priority over others in z-fighting
polygonOffset: true,
polygonOffsetUnits: -1
}));
const body2Box = new BoxGeometry(9, 13.5, 4.5, 0, 0, 0); const body2Box = new BoxGeometry(9, 13.5, 4.5);
setVertices(body2Box, setVertices(body2Box,
toSkinVertices(20, 32, 28, 36), toSkinVertices(20, 32, 28, 36),
toSkinVertices(28, 32, 36, 36), toSkinVertices(28, 32, 36, 36),
@ -143,12 +143,7 @@ export class SkinObject extends Group {
toSkinVertices(28, 36, 32, 48), toSkinVertices(28, 36, 32, 48),
toSkinVertices(32, 36, 40, 48) toSkinVertices(32, 36, 40, 48)
); );
const body2Mesh = new Mesh(body2Box, new MeshBasicMaterial({ const body2Mesh = new Mesh(body2Box, layer2Material);
...layer2,
// same as above
polygonOffset: true,
polygonOffsetUnits: -1
}));
this.body = new BodyPart(bodyMesh, body2Mesh); this.body = new BodyPart(bodyMesh, body2Mesh);
this.body.name = "body"; this.body.name = "body";
@ -157,7 +152,7 @@ export class SkinObject extends Group {
this.add(this.body); this.add(this.body);
// Right Arm // Right Arm
const rightArmBox = new BoxGeometry(1, 1, 1, 0, 0, 0); // w/d/h is model-related const rightArmBox = new BoxGeometry();
const rightArmMesh = new Mesh(rightArmBox, layer1Material); const rightArmMesh = new Mesh(rightArmBox, layer1Material);
this.modelListeners.push(() => { this.modelListeners.push(() => {
rightArmMesh.scale.x = this.slim ? 3 : 4; rightArmMesh.scale.x = this.slim ? 3 : 4;
@ -186,8 +181,8 @@ export class SkinObject extends Group {
rightArmBox.elementsNeedUpdate = true; rightArmBox.elementsNeedUpdate = true;
}); });
const rightArm2Box = new BoxGeometry(1, 1, 1, 0, 0, 0); // w/d/h is model-related const rightArm2Box = new BoxGeometry();
const rightArm2Mesh = new Mesh(rightArm2Box, layer2Material); const rightArm2Mesh = new Mesh(rightArm2Box, layer2MaterialBiased);
rightArm2Mesh.renderOrder = 1; rightArm2Mesh.renderOrder = 1;
this.modelListeners.push(() => { this.modelListeners.push(() => {
rightArm2Mesh.scale.x = this.slim ? 3.375 : 4.5; rightArm2Mesh.scale.x = this.slim ? 3.375 : 4.5;
@ -230,7 +225,7 @@ export class SkinObject extends Group {
this.add(this.rightArm); this.add(this.rightArm);
// Left Arm // Left Arm
const leftArmBox = new BoxGeometry(1, 1, 1, 0, 0, 0); // w/d/h is model-related const leftArmBox = new BoxGeometry();
const leftArmMesh = new Mesh(leftArmBox, layer1Material); const leftArmMesh = new Mesh(leftArmBox, layer1Material);
this.modelListeners.push(() => { this.modelListeners.push(() => {
leftArmMesh.scale.x = this.slim ? 3 : 4; leftArmMesh.scale.x = this.slim ? 3 : 4;
@ -259,8 +254,8 @@ export class SkinObject extends Group {
leftArmBox.elementsNeedUpdate = true; leftArmBox.elementsNeedUpdate = true;
}); });
const leftArm2Box = new BoxGeometry(1, 1, 1, 0, 0, 0); // w/d/h is model-related const leftArm2Box = new BoxGeometry();
const leftArm2Mesh = new Mesh(leftArm2Box, layer2Material); const leftArm2Mesh = new Mesh(leftArm2Box, layer2MaterialBiased);
leftArm2Mesh.renderOrder = 1; leftArm2Mesh.renderOrder = 1;
this.modelListeners.push(() => { this.modelListeners.push(() => {
leftArm2Mesh.scale.x = this.slim ? 3.375 : 4.5; leftArm2Mesh.scale.x = this.slim ? 3.375 : 4.5;
@ -303,7 +298,7 @@ export class SkinObject extends Group {
this.add(this.leftArm); this.add(this.leftArm);
// Right Leg // Right Leg
const rightLegBox = new BoxGeometry(4, 12, 4, 0, 0, 0); const rightLegBox = new BoxGeometry(4, 12, 4);
setVertices(rightLegBox, setVertices(rightLegBox,
toSkinVertices(4, 16, 8, 20), toSkinVertices(4, 16, 8, 20),
toSkinVertices(8, 16, 12, 20), toSkinVertices(8, 16, 12, 20),
@ -312,9 +307,9 @@ export class SkinObject extends Group {
toSkinVertices(8, 20, 12, 32), toSkinVertices(8, 20, 12, 32),
toSkinVertices(12, 20, 16, 32) toSkinVertices(12, 20, 16, 32)
); );
const rightLegMesh = new Mesh(rightLegBox, layer1Material); const rightLegMesh = new Mesh(rightLegBox, layer1MaterialBiased);
const rightLeg2Box = new BoxGeometry(4.5, 13.5, 4.5, 0, 0, 0); const rightLeg2Box = new BoxGeometry(4.5, 13.5, 4.5);
setVertices(rightLeg2Box, setVertices(rightLeg2Box,
toSkinVertices(4, 32, 8, 36), toSkinVertices(4, 32, 8, 36),
toSkinVertices(8, 32, 12, 36), toSkinVertices(8, 32, 12, 36),
@ -323,7 +318,7 @@ export class SkinObject extends Group {
toSkinVertices(8, 36, 12, 48), toSkinVertices(8, 36, 12, 48),
toSkinVertices(12, 36, 16, 48) toSkinVertices(12, 36, 16, 48)
); );
const rightLeg2Mesh = new Mesh(rightLeg2Box, layer2Material); const rightLeg2Mesh = new Mesh(rightLeg2Box, layer2MaterialBiased);
rightLeg2Mesh.renderOrder = 1; rightLeg2Mesh.renderOrder = 1;
const rightLegPivot = new Group(); const rightLegPivot = new Group();
@ -338,7 +333,7 @@ export class SkinObject extends Group {
this.add(this.rightLeg); this.add(this.rightLeg);
// Left Leg // Left Leg
const leftLegBox = new BoxGeometry(4, 12, 4, 0, 0, 0); const leftLegBox = new BoxGeometry(4, 12, 4);
setVertices(leftLegBox, setVertices(leftLegBox,
toSkinVertices(20, 48, 24, 52), toSkinVertices(20, 48, 24, 52),
toSkinVertices(24, 48, 28, 52), toSkinVertices(24, 48, 28, 52),
@ -347,9 +342,9 @@ export class SkinObject extends Group {
toSkinVertices(24, 52, 28, 64), toSkinVertices(24, 52, 28, 64),
toSkinVertices(28, 52, 32, 64) toSkinVertices(28, 52, 32, 64)
); );
const leftLegMesh = new Mesh(leftLegBox, layer1Material); const leftLegMesh = new Mesh(leftLegBox, layer1MaterialBiased);
const leftLeg2Box = new BoxGeometry(4.5, 13.5, 4.5, 0, 0, 0); const leftLeg2Box = new BoxGeometry(4.5, 13.5, 4.5);
setVertices(leftLeg2Box, setVertices(leftLeg2Box,
toSkinVertices(4, 48, 8, 52), toSkinVertices(4, 48, 8, 52),
toSkinVertices(8, 48, 12, 52), toSkinVertices(8, 48, 12, 52),
@ -358,7 +353,7 @@ export class SkinObject extends Group {
toSkinVertices(8, 52, 12, 64), toSkinVertices(8, 52, 12, 64),
toSkinVertices(12, 52, 16, 64) toSkinVertices(12, 52, 16, 64)
); );
const leftLeg2Mesh = new Mesh(leftLeg2Box, layer2Material); const leftLeg2Mesh = new Mesh(leftLeg2Box, layer2MaterialBiased);
leftLeg2Mesh.renderOrder = 1; leftLeg2Mesh.renderOrder = 1;
const leftLegPivot = new Group(); const leftLegPivot = new Group();
@ -404,11 +399,16 @@ export class CapeObject extends Group {
constructor(texture: Texture) { constructor(texture: Texture) {
super(); super();
const capeMaterial = new MeshBasicMaterial({ map: texture, transparent: true, opacity: 1, side: DoubleSide, alphaTest: 0.5 }); const capeMaterial = new MeshBasicMaterial({
map: texture,
side: DoubleSide,
transparent: true,
alphaTest: 1e-5
});
// back = outside // back = outside
// front = inside // front = inside
const capeBox = new BoxGeometry(10, 16, 1, 0, 0, 0); const capeBox = new BoxGeometry(10, 16, 1);
setVertices(capeBox, setVertices(capeBox,
toCapeVertices(1, 0, 11, 1), toCapeVertices(1, 0, 11, 1),
toCapeVertices(11, 0, 21, 1), toCapeVertices(11, 0, 21, 1),

View File

@ -2,3 +2,4 @@ export * from "./model.js";
export * from "./viewer.js"; export * from "./viewer.js";
export * from "./orbit_controls.js"; export * from "./orbit_controls.js";
export * from "./animation.js"; export * from "./animation.js";
export * from "./fxaa.js";

View File

@ -5,7 +5,7 @@ import { PlayerObject } from "./model.js";
export type LoadOptions = { export type LoadOptions = {
/** /**
* Whether to make the object visible after the texture is loaded. (default: true) * Whether to make the object visible after the texture is loaded. Default is true.
*/ */
makeVisible?: boolean; makeVisible?: boolean;
} }
@ -16,6 +16,29 @@ export type SkinViewerOptions = {
skin?: RemoteImage | TextureSource; skin?: RemoteImage | TextureSource;
cape?: RemoteImage | TextureSource; cape?: RemoteImage | TextureSource;
ears?: RemoteImage | TextureSource; ears?: RemoteImage | TextureSource;
/**
* Whether the canvas contains an alpha buffer. Default is true.
* This option can be turned off if you use an opaque background.
*/
alpha?: boolean;
/**
* Render target.
* A new canvas is created if this parameter is unspecified.
*/
canvas?: HTMLCanvasElement;
/**
* Whether to preserve the buffers until manually cleared or overwritten. Default is false.
*/
preserveDrawingBuffer?: boolean;
/**
* The initial value of `SkinViewer.renderPaused`. Default is false.
* If this option is true, rendering and animation loops will not start.
*/
renderPaused?: boolean;
} }
function toMakeVisible(options?: LoadOptions): boolean { function toMakeVisible(options?: LoadOptions): boolean {
@ -26,16 +49,16 @@ function toMakeVisible(options?: LoadOptions): boolean {
} }
class SkinViewer { class SkinViewer {
readonly domElement: Node; readonly canvas: HTMLCanvasElement;
readonly scene: Scene; readonly scene: Scene;
readonly camera: PerspectiveCamera; readonly camera: PerspectiveCamera;
readonly renderer: WebGLRenderer; readonly renderer: WebGLRenderer;
readonly playerObject: PlayerObject; readonly playerObject: PlayerObject;
readonly animations: RootAnimation = new RootAnimation(); readonly animations: RootAnimation = new RootAnimation();
protected readonly skinCanvas: HTMLCanvasElement; readonly skinCanvas: HTMLCanvasElement;
protected readonly capeCanvas: HTMLCanvasElement; readonly capeCanvas: HTMLCanvasElement;
protected readonly earCanvas: HTMLCanvasElement; readonly earCanvas: HTMLCanvasElement;
private readonly skinTexture: Texture; private readonly skinTexture: Texture;
private readonly capeTexture: Texture; private readonly capeTexture: Texture;
private readonly earTexture: Texture; private readonly earTexture: Texture;
@ -50,8 +73,8 @@ class SkinViewer {
private _disposed: boolean = false; private _disposed: boolean = false;
private _renderPaused: boolean = false; private _renderPaused: boolean = false;
constructor(domElement: Node, options: SkinViewerOptions = {}) { constructor(options: SkinViewerOptions = {}) {
this.domElement = domElement; this.canvas = options.canvas === undefined ? document.createElement("canvas") : options.canvas;
// texture // texture
this.skinCanvas = document.createElement("canvas"); this.skinCanvas = document.createElement("canvas");
@ -85,8 +108,13 @@ class SkinViewer {
this.camera.position.y = -12; this.camera.position.y = -12;
this.camera.position.z = 60; this.camera.position.z = 60;
this.renderer = new WebGLRenderer({ alpha: true, logarithmicDepthBuffer: true }); this.renderer = new WebGLRenderer({
this.domElement.appendChild(this.renderer.domElement); canvas: this.canvas,
alpha: options.alpha !== false, // default: true
preserveDrawingBuffer: options.preserveDrawingBuffer === true // default: false
});
this.renderer.setPixelRatio(window.devicePixelRatio);
this.playerObject = new PlayerObject(this.skinTexture, this.capeTexture, this.earTexture); this.playerObject = new PlayerObject(this.skinTexture, this.capeTexture, this.earTexture);
this.playerObject.name = "player"; this.playerObject.name = "player";
@ -95,8 +123,6 @@ class SkinViewer {
this.playerObject.ears.visible = false; this.playerObject.ears.visible = false;
this.scene.add(this.playerObject); this.scene.add(this.playerObject);
window.requestAnimationFrame(() => this.draw());
if (options.skin !== undefined) { if (options.skin !== undefined) {
this.loadSkin(options.skin); this.loadSkin(options.skin);
} }
@ -112,6 +138,12 @@ class SkinViewer {
if (options.height !== undefined) { if (options.height !== undefined) {
this.height = options.height; this.height = options.height;
} }
if (options.renderPaused === true) {
this._renderPaused = true;
} else {
window.requestAnimationFrame(() => this.draw());
}
} }
protected skinLoaded(model: ModelType, options?: LoadOptions): void { protected skinLoaded(model: ModelType, options?: LoadOptions): void {
@ -153,12 +185,16 @@ class SkinViewer {
return; return;
} }
this.animations.runAnimationLoop(this.playerObject); this.animations.runAnimationLoop(this.playerObject);
this.doRender(); this.render();
this.animatedCape(); this.animatedCape();
window.requestAnimationFrame(() => this.draw()); window.requestAnimationFrame(() => this.draw());
} }
protected doRender(): void { /**
* Renders the scene to the canvas.
* This method does not change the animation progress.
*/
render(): void {
this.renderer.render(this.scene, this.camera); this.renderer.render(this.scene, this.camera);
} }
@ -170,7 +206,6 @@ class SkinViewer {
dispose(): void { dispose(): void {
this._disposed = true; this._disposed = true;
this.domElement.removeChild(this.renderer.domElement);
this.renderer.dispose(); this.renderer.dispose();
this.skinTexture.dispose(); this.skinTexture.dispose();
this.capeTexture.dispose(); this.capeTexture.dispose();
@ -180,6 +215,11 @@ class SkinViewer {
return this._disposed; return this._disposed;
} }
/**
* Whether rendering and animations are paused.
* Setting this property to true will stop both rendering and animation loops.
* Setting it back to false will resume them.
*/
get renderPaused(): boolean { get renderPaused(): boolean {
return this._renderPaused; return this._renderPaused;
} }