diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index b7c7d4b..719a2d3 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -1,6 +1,6 @@ name: CI -on: [push] +on: [push, pull_request] jobs: test: @@ -9,8 +9,6 @@ jobs: steps: - uses: actions/checkout@v2 - - name: npm build & test - run: | - npm install - npm run build - npm test + - run: npm install + - run: npm run build + - run: npm test diff --git a/.github/workflows/deploy-ghpages.yaml b/.github/workflows/deploy-ghpages.yaml new file mode 100644 index 0000000..983fd12 --- /dev/null +++ b/.github/workflows/deploy-ghpages.yaml @@ -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##\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 }} diff --git a/.gitignore b/.gitignore index ffea957..683c975 100644 --- a/.gitignore +++ b/.gitignore @@ -62,3 +62,4 @@ libs/ _ignore/ .DS_Store .rpt2_cache +public/ diff --git a/README.md b/README.md index 832f5b6..09176db 100644 --- a/README.md +++ b/README.md @@ -16,11 +16,12 @@ Three.js powered Minecraft skin viewer. * Automatic model detection (Slim / Default) # Usage -[Examples of using the viewer](https://bs-community.github.io/skinview3d/) +[Example of using skinview3d](https://bs-community.github.io/skinview3d/) ```html -
+ ``` +## 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 `npm run build` diff --git a/examples/img/1_8_texturemap_redux.png b/examples/img/1_8_texturemap_redux.png new file mode 100644 index 0000000..a206b32 Binary files /dev/null and b/examples/img/1_8_texturemap_redux.png differ diff --git a/examples/img/hacksore.png b/examples/img/hacksore.png new file mode 100644 index 0000000..72f254f Binary files /dev/null and b/examples/img/hacksore.png differ diff --git a/examples/img/haka.png b/examples/img/haka.png new file mode 100644 index 0000000..cfcbfc5 Binary files /dev/null and b/examples/img/haka.png differ diff --git a/examples/img/hatsune_miku.png b/examples/img/hatsune_miku.png new file mode 100644 index 0000000..d434946 Binary files /dev/null and b/examples/img/hatsune_miku.png differ diff --git a/examples/img/hd_cape.png b/examples/img/hd_cape.png new file mode 100644 index 0000000..81b20d3 Binary files /dev/null and b/examples/img/hd_cape.png differ diff --git a/examples/img/ironman_hd.png b/examples/img/ironman_hd.png new file mode 100644 index 0000000..bae9374 Binary files /dev/null and b/examples/img/ironman_hd.png differ diff --git a/examples/img/legacy_cape.png b/examples/img/legacy_cape.png new file mode 100644 index 0000000..01a8f65 Binary files /dev/null and b/examples/img/legacy_cape.png differ diff --git a/examples/img/mojang_cape.png b/examples/img/mojang_cape.png new file mode 100644 index 0000000..deac5c5 Binary files /dev/null and b/examples/img/mojang_cape.png differ diff --git a/examples/img/sethbling.png b/examples/img/sethbling.png new file mode 100644 index 0000000..db47a84 Binary files /dev/null and b/examples/img/sethbling.png differ diff --git a/examples/index.html b/examples/index.html old mode 100755 new mode 100644 index 0556c68..8256ceb --- a/examples/index.html +++ b/examples/index.html @@ -1,75 +1,441 @@ - - - - - - skinview3d - - - - - - - -
- - - - - - - + + + + + + + + skinview3d + + + + + + + +
+ + + +
+

Canvas Size

+ + +
+ +
+

Animation

+ + +
+

Rotate

+ + +
+
+

Walk / Run

+
+ + + +
+ +
+
+ +
+

Mouse Control

+
+ + + +
+
+ +
+

Skin Layers

+ + +
+
+ +
+

Textures

+
+
+ + + + + +
+ +
+
+
+ + + + + +
+
+
+ +
+

Other examples

+ +
+ +
+ + + + + + + + + + + + diff --git a/examples/offscreen-render.html b/examples/offscreen-render.html new file mode 100644 index 0000000..249aaad --- /dev/null +++ b/examples/offscreen-render.html @@ -0,0 +1,74 @@ + + + + + + + + skinview3d / offscreen-render + + + +
+ + + + + diff --git a/package-lock.json b/package-lock.json index 0f3337c..e1e6861 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,31 +1,31 @@ { "name": "skinview3d", - "version": "2.0.0-alpha.5", + "version": "2.0.0-alpha.8", "lockfileVersion": 1, "requires": true, "dependencies": { "@babel/code-frame": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", - "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", + "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", "dev": true, "requires": { - "@babel/highlight": "^7.8.3" + "@babel/highlight": "^7.10.4" } }, "@babel/helper-validator-identifier": { - "version": "7.9.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.5.tgz", - "integrity": "sha512-/8arLKUFq882w4tWGj9JYzRpAlZgiWUJ+dtteNTDqrRBz9Iguck9Rn3ykuBDoUwh2TO4tSAJlrxDUOXWklJe4g==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", + "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", "dev": true }, "@babel/highlight": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.9.0.tgz", - "integrity": "sha512-lJZPilxX7Op3Nv/2cvFdnlepPXDxi29wxteT57Q965oc5R9v86ztx0jfxVrTcBk8C2kcPkkDa2Z4T3ZsPPVWsQ==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", + "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.9.0", + "@babel/helper-validator-identifier": "^7.10.4", "chalk": "^2.0.0", "js-tokens": "^4.0.0" } @@ -40,15 +40,20 @@ } }, "@rollup/plugin-node-resolve": { +<<<<<<< HEAD "version": "8.4.0", "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-8.4.0.tgz", "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, "requires": { "@rollup/pluginutils": "^3.1.0", "@types/resolve": "1.17.1", "builtin-modules": "^3.1.0", - "deep-freeze": "^0.0.1", "deepmerge": "^4.2.2", "is-module": "^1.0.0", "resolve": "^1.17.0" @@ -117,9 +122,15 @@ "dev": true }, "@types/node": { +<<<<<<< HEAD "version": "14.0.22", "resolved": "https://registry.npmjs.org/@types/node/-/node-14.0.22.tgz", "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 }, "@types/resolve": { @@ -132,12 +143,21 @@ } }, "@typescript-eslint/eslint-plugin": { +<<<<<<< HEAD "version": "3.6.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-3.6.0.tgz", "integrity": "sha512-ubHlHVt1lsPQB/CZdEov9XuOFhNG9YRC//kuiS1cMQI6Bs1SsqKrEmZnpgRwthGR09/kEDtr9MywlqXyyYd8GA==", "dev": true, "requires": { "@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", "functional-red-black-tree": "^1.0.1", "regexpp": "^3.0.0", @@ -146,6 +166,7 @@ } }, "@typescript-eslint/experimental-utils": { +<<<<<<< HEAD "version": "3.6.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-3.6.0.tgz", "integrity": "sha512-4Vdf2hvYMUnTdkCNZu+yYlFtL2v+N2R7JOynIOkFbPjf9o9wQvRwRkzUdWlFd2YiiUwJLbuuLnl5civNg5ykOQ==", @@ -154,11 +175,22 @@ "@types/json-schema": "^7.0.3", "@typescript-eslint/types": "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-utils": "^2.0.0" } }, "@typescript-eslint/parser": { +<<<<<<< HEAD "version": "3.6.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-3.6.0.tgz", "integrity": "sha512-taghDxuLhbDAD1U5Fk8vF+MnR0yiFE9Z3v2/bYScFb0N1I9SK8eKHkdJl1DAD48OGFDMFTeOTX0z7g0W6SYUXw==", @@ -168,10 +200,22 @@ "@typescript-eslint/experimental-utils": "3.6.0", "@typescript-eslint/types": "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" } }, "@typescript-eslint/types": { +<<<<<<< HEAD "version": "3.6.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-3.6.0.tgz", "integrity": "sha512-JwVj74ohUSt0ZPG+LZ7hb95fW8DFOqBuR6gE7qzq55KDI3BepqsCtHfBIoa0+Xi1AI7fq5nCu2VQL8z4eYftqg==", @@ -185,6 +229,21 @@ "requires": { "@typescript-eslint/types": "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", "glob": "^7.1.6", "is-glob": "^4.0.1", @@ -194,21 +253,50 @@ } }, "@typescript-eslint/visitor-keys": { +<<<<<<< HEAD "version": "3.6.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-3.6.0.tgz", "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, "requires": { "eslint-visitor-keys": "^1.1.0" } }, - "@yushijinhun/three-minifier-rollup": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/@yushijinhun/three-minifier-rollup/-/three-minifier-rollup-0.1.7.tgz", - "integrity": "sha512-GzlFkEzUqyI8X23guT7Y8R3KaCzCXdUNpWfSPiq7m/f3xUNhhclFJORWqurZdnSEnxWCiCMgNm/9GsLMd/N2Qw==", +<<<<<<< HEAD +======= + "@yushijinhun/three-minifier-common": { + "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, "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" } }, @@ -233,9 +321,9 @@ } }, "acorn": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.3.1.tgz", - "integrity": "sha512-tLc0wSnatxAQHVHUapaHdz72pi9KUyHjq5KyHjGg9Y8Ifdc79pTh2XvI6I1/chZbnM7QtNKzh66ooDogPZSleA==", + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.0.tgz", + "integrity": "sha512-+G7P8jJmCHr+S+cLfQxygbWhXy+8YTVGzAkpEbcLo2mLoL7tij/VG41QSHACSf5QgYRhMZYHuNc6drJaO0Da+w==", "dev": true }, "acorn-jsx": { @@ -244,19 +332,31 @@ "integrity": "sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ==", "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": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.0.tgz", - "integrity": "sha512-j1Q7cSCqN+AwrmDd+pzgqc0/NpC655x2bUf5ZjRIO77DcNBFmh+OgRNzF6OKdCC9RSCb19fGd99+bhXFdkRNqw==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.1.tgz", + "integrity": "sha512-01q25QQDwLSsyfhrKbn8yuur+JNw0H+0Y4JiGIKd3z9aYk/w/2kxD/Upc+t2ZBBSUNff50VjPsSW2YxM8QYKVg==", "dev": true, "requires": { "debug": "4" } }, "ajv": { +<<<<<<< HEAD "version": "6.12.3", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.3.tgz", "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, "requires": { "fast-deep-equal": "^3.1.1", @@ -643,12 +743,6 @@ "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", "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": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", @@ -776,9 +870,15 @@ "dev": true }, "eslint": { +<<<<<<< HEAD "version": "7.4.0", "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.4.0.tgz", "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, "requires": { "@babel/code-frame": "^7.0.0", @@ -789,9 +889,9 @@ "doctrine": "^3.0.0", "enquirer": "^2.3.5", "eslint-scope": "^5.1.0", - "eslint-utils": "^2.0.0", - "eslint-visitor-keys": "^1.2.0", - "espree": "^7.1.0", + "eslint-utils": "^2.1.0", + "eslint-visitor-keys": "^1.3.0", + "espree": "^7.2.0", "esquery": "^1.2.0", "esutils": "^2.0.2", "file-entry-cache": "^5.0.1", @@ -805,7 +905,7 @@ "js-yaml": "^3.13.1", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", - "lodash": "^4.17.14", + "lodash": "^4.17.19", "minimatch": "^3.0.4", "natural-compare": "^1.4.0", "optionator": "^0.9.1", @@ -923,14 +1023,14 @@ "dev": true }, "espree": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-7.1.0.tgz", - "integrity": "sha512-dcorZSyfmm4WTuTnE5Y7MEN1DyoPYy1ZR783QW1FJoenn7RailyWFsq/UL6ZAAA7uXurN9FIpYyUs3OfiIW+Qw==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-7.2.0.tgz", + "integrity": "sha512-H+cQ3+3JYRMEIOl87e7QdHX70ocly5iW4+dttuR8iYSPr/hXKFb+7dBsZ7+u1adC4VrnPlTkv0+OwuPnDop19g==", "dev": true, "requires": { - "acorn": "^7.2.0", + "acorn": "^7.3.1", "acorn-jsx": "^5.2.0", - "eslint-visitor-keys": "^1.2.0" + "eslint-visitor-keys": "^1.3.0" } }, "esprima": { @@ -949,9 +1049,9 @@ }, "dependencies": { "estraverse": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.1.0.tgz", - "integrity": "sha512-FyohXK+R0vE+y1nHLoBM7ZTyqRpqAlhdZHCWIWEviFLiGB8b04H6bQs8G+XTthacvT8VuwvteiP7RJSxMs8UEw==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", "dev": true } } @@ -1124,6 +1224,15 @@ "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": { "version": "4.1.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", @@ -1351,11 +1460,12 @@ "dev": true }, "jest-worker": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.0.0.tgz", - "integrity": "sha512-pPaYa2+JnwmiZjK9x7p9BoZht+47ecFCDFA/CJxspHzeDvQcfVBLWzCiWyo+EGrSiQMWZtCFo9iSvMZnAAo8vw==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.3.0.tgz", + "integrity": "sha512-Vmpn2F6IASefL+DVBhPzI2J9/GJUsqzomdeN+P+dK8/jKxbh8R3BtFnx3FIta7wYlPU62cpJMJQo4kuOowcMnw==", "dev": true, "requires": { + "@types/node": "*", "merge-stream": "^2.0.0", "supports-color": "^7.0.0" } @@ -1416,9 +1526,9 @@ } }, "koa": { - "version": "2.12.1", - "resolved": "https://registry.npmjs.org/koa/-/koa-2.12.1.tgz", - "integrity": "sha512-NuYVKjnBxeEe19VljPO9yNcaKKVrMagcax3jjzZtOlxRY2nThWKQqgnI3Pr1OG7mFtvySoDRixoUWZIt6R9C3A==", + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/koa/-/koa-2.13.0.tgz", + "integrity": "sha512-i/XJVOfPw7npbMv67+bOeXr3gPqOAw6uh5wFyNs3QvJ47tUx3M3V9rIE0//WytY42MKz4l/MXKyGkQ2LQTfLUQ==", "dev": true, "requires": { "accepts": "^1.3.5", @@ -1591,26 +1701,14 @@ } }, "koa-send": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/koa-send/-/koa-send-5.0.0.tgz", - "integrity": "sha512-90ZotV7t0p3uN9sRwW2D484rAaKIsD8tAVtypw/aBU+ryfV+fR2xrcAwhI8Wl6WRkojLUs/cB9SBSCuIb+IanQ==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/koa-send/-/koa-send-5.0.1.tgz", + "integrity": "sha512-tmcyQ/wXXuxpDxyNXv5yNNkdAMdFRqwtegBXUaowiQzUKqJehttS0x2j0eOZDQAyloAth5w6wwBImnFzkUz3pQ==", "dev": true, "requires": { - "debug": "^3.1.0", - "http-errors": "^1.6.3", - "mz": "^2.7.0", + "debug": "^4.1.1", + "http-errors": "^1.7.3", "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": { @@ -1691,9 +1789,15 @@ } }, "lodash": { +<<<<<<< HEAD "version": "4.17.19", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz", "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 }, "lodash.assignwith": { @@ -2269,6 +2373,15 @@ "integrity": "sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ==", "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": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.1.tgz", @@ -2380,24 +2493,30 @@ } }, "rollup": { +<<<<<<< HEAD "version": "2.21.0", "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.21.0.tgz", "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, "requires": { "fsevents": "~2.1.2" } }, "rollup-plugin-terser": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-6.1.0.tgz", - "integrity": "sha512-4fB3M9nuoWxrwm39habpd4hvrbrde2W2GG4zEGPQg1YITNkM3Tqur5jSuXlWNzbv/2aMLJ+dZJaySc3GCD8oDw==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-7.0.0.tgz", + "integrity": "sha512-p/N3lLiFusCjYTLfVkoaiRTOGr5AESEaljMPH12MhOtoMkmTBhIAfuadrcWy4am1U0vU4WTxO9fi0K09O4CboQ==", "dev": true, "requires": { - "@babel/code-frame": "^7.8.3", - "jest-worker": "^26.0.0", - "serialize-javascript": "^3.0.0", - "terser": "^4.7.0" + "@babel/code-frame": "^7.10.4", + "jest-worker": "^26.2.1", + "serialize-javascript": "^4.0.0", + "terser": "^5.0.0" } }, "safe-buffer": { @@ -2419,10 +2538,13 @@ "dev": true }, "serialize-javascript": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-3.0.0.tgz", - "integrity": "sha512-skZcHYw2vEX4bw90nAr2iTTsz6x2SrHEnfxgKYmZlvJYBEZrvbKtobJWlQ20zczKb3bsHHXXTYt48zBA7ni9cw==", - "dev": true + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", + "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } }, "serve-index-75lb": { "version": "2.0.1", @@ -2508,9 +2630,15 @@ "dev": true }, "skinview-utils": { +<<<<<<< HEAD "version": "0.5.6", "resolved": "https://registry.npmjs.org/skinview-utils/-/skinview-utils-0.5.6.tgz", "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": { "version": "2.1.0", @@ -2764,9 +2892,9 @@ } }, "terser": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-4.7.0.tgz", - "integrity": "sha512-Lfb0RiZcjRDXCC3OSHJpEkxJ9Qeqs6mp2v4jf2MHfy8vGERmVDuvjXdd/EnP5Deme5F2yBRBymKmKHCBg2echw==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.1.0.tgz", + "integrity": "sha512-pwC1Jbzahz1ZPU87NQ8B3g5pKbhyJSiHih4gLH6WZiPU8mmS1IlGbB0A2Nuvkj/LCNsgIKctg6GkYwWCeTvXZQ==", "dev": true, "requires": { "commander": "^2.20.0", @@ -2781,9 +2909,9 @@ "dev": true }, "thenify": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.0.tgz", - "integrity": "sha1-5p44obq+lpsBCCB5eLn2K4hgSDk=", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", "dev": true, "requires": { "any-promise": "^1.0.0" @@ -2799,9 +2927,9 @@ } }, "three": { - "version": "0.117.1", - "resolved": "https://registry.npmjs.org/three/-/three-0.117.1.tgz", - "integrity": "sha512-t4zeJhlNzUIj9+ub0l6nICVimSuRTZJOqvk3Rmlu+YGdTOJ49Wna8p7aumpkXJakJfITiybfpYE1XN1o1Z34UQ==" + "version": "0.120.0", + "resolved": "https://registry.npmjs.org/three/-/three-0.120.0.tgz", + "integrity": "sha512-Swffpi3EAHWkmqC1MagKEzR5XgwkDiyeWI3M7vkGbBc0xhq2LcQmJj5DqBruLkrgcZQ+fM/+fSQBU1tDvggO4A==" }, "through": { "version": "2.3.8", @@ -2809,6 +2937,42 @@ "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", "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": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", @@ -2862,9 +3026,9 @@ } }, "typescript": { - "version": "3.9.6", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.6.tgz", - "integrity": "sha512-Pspx3oKAPJtjNwE92YS05HQoY7z2SFyOpHo9MqJor3BXAGNaPUs83CuVp9VISFkSjyRfiTpmKuAYGJB7S7hOxw==", + "version": "3.9.7", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.7.tgz", + "integrity": "sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==", "dev": true }, "typical": { @@ -2976,6 +3140,12 @@ "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": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ylru/-/ylru-1.2.1.tgz", diff --git a/package.json b/package.json index 189f81a..58a11d0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "skinview3d", - "version": "2.0.0-alpha.5", + "version": "2.0.0-alpha.8", "description": "Three.js powered Minecraft skin viewer", "main": "libs/skinview3d.js", "type": "module", @@ -11,10 +11,10 @@ "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", + "dev": "npm-run-all --parallel dev:watch:bundles dev:serve", "prepublishOnly": "npm run clean && npm run build" }, "repository": { @@ -38,21 +38,21 @@ "bundles" ], "dependencies": { - "skinview-utils": "^0.5.6", - "three": "^0.117.1" + "skinview-utils": "^0.5.7", + "three": "^0.120.0" }, "devDependencies": { - "@rollup/plugin-node-resolve": "^8.4.0", + "@rollup/plugin-node-resolve": "^9.0.0", "@rollup/plugin-typescript": "^5.0.2", - "@typescript-eslint/eslint-plugin": "^3.6.0", - "@typescript-eslint/parser": "^3.6.0", - "@yushijinhun/three-minifier-rollup": "^0.1.7", - "eslint": "^7.4.0", + "@typescript-eslint/eslint-plugin": "^3.9.1", + "@typescript-eslint/parser": "^3.9.1", + "@yushijinhun/three-minifier-rollup": "^0.2.0-alpha.2", + "eslint": "^7.7.0", "local-web-server": "^4.2.1", "npm-run-all": "^4.1.5", "rimraf": "^3.0.2", - "rollup": "^2.21.0", - "rollup-plugin-terser": "^6.1.0", - "typescript": "^3.9.6" + "rollup": "^2.26.3", + "rollup-plugin-terser": "^7.0.0", + "typescript": "^3.9.7" } } diff --git a/src/animation.ts b/src/animation.ts index 641927e..872a25d 100644 --- a/src/animation.ts +++ b/src/animation.ts @@ -162,7 +162,7 @@ export const WalkingAnimation: Animation = (player, time) => { export const RunningAnimation: Animation = (player, time) => { const skin = player.skin; - time *= 15; + time = time * 15 + Math.PI * 0.5; // Leg swing with larger amplitude skin.leftLeg.rotation.x = Math.cos(time + Math.PI) * 1.3; diff --git a/src/fxaa.ts b/src/fxaa.ts new file mode 100644 index 0000000..30fc2c0 --- /dev/null +++ b/src/fxaa.ts @@ -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(); + } +} diff --git a/src/model.ts b/src/model.ts index b38bc1f..675ca51 100644 --- a/src/model.ts +++ b/src/model.ts @@ -72,23 +72,29 @@ export class SkinObject extends Group { constructor(texture: Texture) { super(); - const layer1 = { + const layer1Material = new MeshBasicMaterial({ map: texture, side: FrontSide - }; - const layer2 = { + }); + const layer2Material = new MeshBasicMaterial({ map: texture, side: DoubleSide, transparent: true, - opacity: 1, - alphaTest: 0.5 - } + alphaTest: 1e-5 + }); - const layer1Material = new MeshBasicMaterial(layer1); - const layer2Material = new MeshBasicMaterial(layer2); + const layer1MaterialBiased = layer1Material.clone(); + 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 - const headBox = new BoxGeometry(8, 8, 8, 0, 0, 0); + const headBox = new BoxGeometry(8, 8, 8); setVertices(headBox, toSkinVertices(8, 0, 16, 8), toSkinVertices(16, 0, 24, 8), @@ -99,7 +105,7 @@ export class SkinObject extends Group { ); 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, toSkinVertices(40, 0, 48, 8), toSkinVertices(48, 0, 56, 8), @@ -117,7 +123,7 @@ export class SkinObject extends Group { this.add(this.head); // Body - const bodyBox = new BoxGeometry(8, 12, 4, 0, 0, 0); + const bodyBox = new BoxGeometry(8, 12, 4); setVertices(bodyBox, toSkinVertices(20, 16, 28, 20), toSkinVertices(28, 16, 36, 20), @@ -126,15 +132,9 @@ export class SkinObject extends Group { toSkinVertices(28, 20, 32, 32), toSkinVertices(32, 20, 40, 32) ); - const bodyMesh = new Mesh(bodyBox, new MeshBasicMaterial({ - ...layer1, - // this pulls bodyMesh towards the camera - // so body is given priority over others in z-fighting - polygonOffset: true, - polygonOffsetUnits: -1 - })); + const bodyMesh = new Mesh(bodyBox, layer1Material); - const body2Box = new BoxGeometry(9, 13.5, 4.5, 0, 0, 0); + const body2Box = new BoxGeometry(9, 13.5, 4.5); setVertices(body2Box, toSkinVertices(20, 32, 28, 36), toSkinVertices(28, 32, 36, 36), @@ -143,12 +143,7 @@ export class SkinObject extends Group { toSkinVertices(28, 36, 32, 48), toSkinVertices(32, 36, 40, 48) ); - const body2Mesh = new Mesh(body2Box, new MeshBasicMaterial({ - ...layer2, - // same as above - polygonOffset: true, - polygonOffsetUnits: -1 - })); + const body2Mesh = new Mesh(body2Box, layer2Material); this.body = new BodyPart(bodyMesh, body2Mesh); this.body.name = "body"; @@ -157,7 +152,7 @@ export class SkinObject extends Group { this.add(this.body); // 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); this.modelListeners.push(() => { rightArmMesh.scale.x = this.slim ? 3 : 4; @@ -186,8 +181,8 @@ export class SkinObject extends Group { rightArmBox.elementsNeedUpdate = true; }); - const rightArm2Box = new BoxGeometry(1, 1, 1, 0, 0, 0); // w/d/h is model-related - const rightArm2Mesh = new Mesh(rightArm2Box, layer2Material); + const rightArm2Box = new BoxGeometry(); + const rightArm2Mesh = new Mesh(rightArm2Box, layer2MaterialBiased); rightArm2Mesh.renderOrder = 1; this.modelListeners.push(() => { rightArm2Mesh.scale.x = this.slim ? 3.375 : 4.5; @@ -230,7 +225,7 @@ export class SkinObject extends Group { this.add(this.rightArm); // 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); this.modelListeners.push(() => { leftArmMesh.scale.x = this.slim ? 3 : 4; @@ -259,8 +254,8 @@ export class SkinObject extends Group { leftArmBox.elementsNeedUpdate = true; }); - const leftArm2Box = new BoxGeometry(1, 1, 1, 0, 0, 0); // w/d/h is model-related - const leftArm2Mesh = new Mesh(leftArm2Box, layer2Material); + const leftArm2Box = new BoxGeometry(); + const leftArm2Mesh = new Mesh(leftArm2Box, layer2MaterialBiased); leftArm2Mesh.renderOrder = 1; this.modelListeners.push(() => { leftArm2Mesh.scale.x = this.slim ? 3.375 : 4.5; @@ -303,7 +298,7 @@ export class SkinObject extends Group { this.add(this.leftArm); // Right Leg - const rightLegBox = new BoxGeometry(4, 12, 4, 0, 0, 0); + const rightLegBox = new BoxGeometry(4, 12, 4); setVertices(rightLegBox, toSkinVertices(4, 16, 8, 20), toSkinVertices(8, 16, 12, 20), @@ -312,9 +307,9 @@ export class SkinObject extends Group { toSkinVertices(8, 20, 12, 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, toSkinVertices(4, 32, 8, 36), toSkinVertices(8, 32, 12, 36), @@ -323,7 +318,7 @@ export class SkinObject extends Group { toSkinVertices(8, 36, 12, 48), toSkinVertices(12, 36, 16, 48) ); - const rightLeg2Mesh = new Mesh(rightLeg2Box, layer2Material); + const rightLeg2Mesh = new Mesh(rightLeg2Box, layer2MaterialBiased); rightLeg2Mesh.renderOrder = 1; const rightLegPivot = new Group(); @@ -338,7 +333,7 @@ export class SkinObject extends Group { this.add(this.rightLeg); // Left Leg - const leftLegBox = new BoxGeometry(4, 12, 4, 0, 0, 0); + const leftLegBox = new BoxGeometry(4, 12, 4); setVertices(leftLegBox, toSkinVertices(20, 48, 24, 52), toSkinVertices(24, 48, 28, 52), @@ -347,9 +342,9 @@ export class SkinObject extends Group { toSkinVertices(24, 52, 28, 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, toSkinVertices(4, 48, 8, 52), toSkinVertices(8, 48, 12, 52), @@ -358,7 +353,7 @@ export class SkinObject extends Group { toSkinVertices(8, 52, 12, 64), toSkinVertices(12, 52, 16, 64) ); - const leftLeg2Mesh = new Mesh(leftLeg2Box, layer2Material); + const leftLeg2Mesh = new Mesh(leftLeg2Box, layer2MaterialBiased); leftLeg2Mesh.renderOrder = 1; const leftLegPivot = new Group(); @@ -404,11 +399,16 @@ export class CapeObject extends Group { constructor(texture: Texture) { 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 // front = inside - const capeBox = new BoxGeometry(10, 16, 1, 0, 0, 0); + const capeBox = new BoxGeometry(10, 16, 1); setVertices(capeBox, toCapeVertices(1, 0, 11, 1), toCapeVertices(11, 0, 21, 1), diff --git a/src/skinview3d.ts b/src/skinview3d.ts index 366ed8e..2c75651 100644 --- a/src/skinview3d.ts +++ b/src/skinview3d.ts @@ -2,3 +2,4 @@ export * from "./model.js"; export * from "./viewer.js"; export * from "./orbit_controls.js"; export * from "./animation.js"; +export * from "./fxaa.js"; diff --git a/src/viewer.ts b/src/viewer.ts index cf3b9ad..0cf65a5 100644 --- a/src/viewer.ts +++ b/src/viewer.ts @@ -5,7 +5,7 @@ import { PlayerObject } from "./model.js"; 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; } @@ -16,6 +16,29 @@ export type SkinViewerOptions = { skin?: RemoteImage | TextureSource; cape?: 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 { @@ -26,16 +49,16 @@ function toMakeVisible(options?: LoadOptions): boolean { } class SkinViewer { - readonly domElement: Node; + readonly canvas: HTMLCanvasElement; readonly scene: Scene; readonly camera: PerspectiveCamera; readonly renderer: WebGLRenderer; readonly playerObject: PlayerObject; readonly animations: RootAnimation = new RootAnimation(); - protected readonly skinCanvas: HTMLCanvasElement; - protected readonly capeCanvas: HTMLCanvasElement; - protected readonly earCanvas: HTMLCanvasElement; + readonly skinCanvas: HTMLCanvasElement; + readonly capeCanvas: HTMLCanvasElement; + readonly earCanvas: HTMLCanvasElement; private readonly skinTexture: Texture; private readonly capeTexture: Texture; private readonly earTexture: Texture; @@ -50,8 +73,8 @@ class SkinViewer { private _disposed: boolean = false; private _renderPaused: boolean = false; - constructor(domElement: Node, options: SkinViewerOptions = {}) { - this.domElement = domElement; + constructor(options: SkinViewerOptions = {}) { + this.canvas = options.canvas === undefined ? document.createElement("canvas") : options.canvas; // texture this.skinCanvas = document.createElement("canvas"); @@ -85,8 +108,13 @@ class SkinViewer { this.camera.position.y = -12; this.camera.position.z = 60; - this.renderer = new WebGLRenderer({ alpha: true, logarithmicDepthBuffer: true }); - this.domElement.appendChild(this.renderer.domElement); + this.renderer = new WebGLRenderer({ + 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.name = "player"; @@ -95,8 +123,6 @@ class SkinViewer { this.playerObject.ears.visible = false; this.scene.add(this.playerObject); - window.requestAnimationFrame(() => this.draw()); - if (options.skin !== undefined) { this.loadSkin(options.skin); } @@ -112,6 +138,12 @@ class SkinViewer { if (options.height !== undefined) { this.height = options.height; } + + if (options.renderPaused === true) { + this._renderPaused = true; + } else { + window.requestAnimationFrame(() => this.draw()); + } } protected skinLoaded(model: ModelType, options?: LoadOptions): void { @@ -153,12 +185,16 @@ class SkinViewer { return; } this.animations.runAnimationLoop(this.playerObject); - this.doRender(); + this.render(); this.animatedCape(); 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); } @@ -170,7 +206,6 @@ class SkinViewer { dispose(): void { this._disposed = true; - this.domElement.removeChild(this.renderer.domElement); this.renderer.dispose(); this.skinTexture.dispose(); this.capeTexture.dispose(); @@ -180,6 +215,11 @@ class SkinViewer { 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 { return this._renderPaused; }