diff --git a/package-lock.json b/package-lock.json index 65364059..50a601db 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,7 @@ "devDependencies": { "@playcanvas/eslint-config": "^1.7.1", "@playcanvas/observer": "^1.4.0", - "@playcanvas/pcui": "^4.2.0", + "@playcanvas/pcui": "^4.3.0", "@rollup/plugin-alias": "^5.1.0", "@rollup/plugin-commonjs": "^25.0.7", "@rollup/plugin-image": "^3.0.3", @@ -29,13 +29,13 @@ "eslint": "^8.56.0", "fflate": "^0.8.2", "handlebars": "^4.7.8", - "playcanvas": "^1.68.2", + "playcanvas": "^1.69.2", "prop-types": "^15.8.1", "qrious": "^4.0.2", "react": "^18.2.0", "react-dom": "^18.2.0", "react-visibility-sensor": "^5.1.1", - "rollup": "^4.12.0", + "rollup": "^4.14.0", "rollup-plugin-copy": "^3.5.0", "rollup-plugin-sass": "^1.12.21", "serve": "^14.2.1", @@ -327,9 +327,9 @@ "dev": true }, "node_modules/@playcanvas/pcui": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@playcanvas/pcui/-/pcui-4.2.0.tgz", - "integrity": "sha512-3tBE3ZFma5GiBChbARD5T3Ye5oxTmfI5zttw4a4dNyx8tvVNKT42B5axg0TYUdc/3KNogYyamR7Pw/7OM0l7NQ==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@playcanvas/pcui/-/pcui-4.3.0.tgz", + "integrity": "sha512-nhyF+u55ws1FPA4A3EkD9p4ujK2HfGxuA6GRWJWe2A9tPyKRMGTNNjrubc/4GnmCxfZP7ux9QEd/a82MXSoU3g==", "dev": true, "dependencies": { "@playcanvas/observer": "^1.4.0" @@ -538,9 +538,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.12.0.tgz", - "integrity": "sha512-+ac02NL/2TCKRrJu2wffk1kZ+RyqxVUlbjSagNgPm94frxtr+XDL12E5Ll1enWskLrtrZ2r8L3wED1orIibV/w==", + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.14.0.tgz", + "integrity": "sha512-jwXtxYbRt1V+CdQSy6Z+uZti7JF5irRKF8hlKfEnF/xJpcNGuuiZMBvuoYM+x9sr9iWGnzrlM0+9hvQ1kgkf1w==", "cpu": [ "arm" ], @@ -551,9 +551,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.12.0.tgz", - "integrity": "sha512-OBqcX2BMe6nvjQ0Nyp7cC90cnumt8PXmO7Dp3gfAju/6YwG0Tj74z1vKrfRz7qAv23nBcYM8BCbhrsWqO7PzQQ==", + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.14.0.tgz", + "integrity": "sha512-fI9nduZhCccjzlsA/OuAwtFGWocxA4gqXGTLvOyiF8d+8o0fZUeSztixkYjcGq1fGZY3Tkq4yRvHPFxU+jdZ9Q==", "cpu": [ "arm64" ], @@ -564,9 +564,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.12.0.tgz", - "integrity": "sha512-X64tZd8dRE/QTrBIEs63kaOBG0b5GVEd3ccoLtyf6IdXtHdh8h+I56C2yC3PtC9Ucnv0CpNFJLqKFVgCYe0lOQ==", + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.14.0.tgz", + "integrity": "sha512-BcnSPRM76/cD2gQC+rQNGBN6GStBs2pl/FpweW8JYuz5J/IEa0Fr4AtrPv766DB/6b2MZ/AfSIOSGw3nEIP8SA==", "cpu": [ "arm64" ], @@ -577,9 +577,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.12.0.tgz", - "integrity": "sha512-cc71KUZoVbUJmGP2cOuiZ9HSOP14AzBAThn3OU+9LcA1+IUqswJyR1cAJj3Mg55HbjZP6OLAIscbQsQLrpgTOg==", + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.14.0.tgz", + "integrity": "sha512-LDyFB9GRolGN7XI6955aFeI3wCdCUszFWumWU0deHA8VpR3nWRrjG6GtGjBrQxQKFevnUTHKCfPR4IvrW3kCgQ==", "cpu": [ "x64" ], @@ -590,9 +590,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.12.0.tgz", - "integrity": "sha512-a6w/Y3hyyO6GlpKL2xJ4IOh/7d+APaqLYdMf86xnczU3nurFTaVN9s9jOXQg97BE4nYm/7Ga51rjec5nfRdrvA==", + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.14.0.tgz", + "integrity": "sha512-ygrGVhQP47mRh0AAD0zl6QqCbNsf0eTo+vgwkY6LunBcg0f2Jv365GXlDUECIyoXp1kKwL5WW6rsO429DBY/bA==", "cpu": [ "arm" ], @@ -603,9 +603,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.12.0.tgz", - "integrity": "sha512-0fZBq27b+D7Ar5CQMofVN8sggOVhEtzFUwOwPppQt0k+VR+7UHMZZY4y+64WJ06XOhBTKXtQB/Sv0NwQMXyNAA==", + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.14.0.tgz", + "integrity": "sha512-x+uJ6MAYRlHGe9wi4HQjxpaKHPM3d3JjqqCkeC5gpnnI6OWovLdXTpfa8trjxPLnWKyBsSi5kne+146GAxFt4A==", "cpu": [ "arm64" ], @@ -616,9 +616,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.12.0.tgz", - "integrity": "sha512-eTvzUS3hhhlgeAv6bfigekzWZjaEX9xP9HhxB0Dvrdbkk5w/b+1Sxct2ZuDxNJKzsRStSq1EaEkVSEe7A7ipgQ==", + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.14.0.tgz", + "integrity": "sha512-nrRw8ZTQKg6+Lttwqo6a2VxR9tOroa2m91XbdQ2sUUzHoedXlsyvY1fN4xWdqz8PKmf4orDwejxXHjh7YBGUCA==", "cpu": [ "arm64" ], @@ -628,10 +628,23 @@ "linux" ] }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.14.0.tgz", + "integrity": "sha512-xV0d5jDb4aFu84XKr+lcUJ9y3qpIWhttO3Qev97z8DKLXR62LC3cXT/bMZXrjLF9X+P5oSmJTzAhqwUbY96PnA==", + "cpu": [ + "ppc64le" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.12.0.tgz", - "integrity": "sha512-ix+qAB9qmrCRiaO71VFfY8rkiAZJL8zQRXveS27HS+pKdjwUfEhqo2+YF2oI+H/22Xsiski+qqwIBxVewLK7sw==", + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.14.0.tgz", + "integrity": "sha512-SDDhBQwZX6LPRoPYjAZWyL27LbcBo7WdBFWJi5PI9RPCzU8ijzkQn7tt8NXiXRiFMJCVpkuMkBf4OxSxVMizAw==", "cpu": [ "riscv64" ], @@ -641,6 +654,19 @@ "linux" ] }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.14.0.tgz", + "integrity": "sha512-RxB/qez8zIDshNJDufYlTT0ZTVut5eCpAZ3bdXDU9yTxBzui3KhbGjROK2OYTTor7alM7XBhssgoO3CZ0XD3qA==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, "node_modules/@rollup/rollup-linux-x64-gnu": { "version": "4.12.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.12.0.tgz", @@ -654,9 +680,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.12.0.tgz", - "integrity": "sha512-LfFdRhNnW0zdMvdCb5FNuWlls2WbbSridJvxOvYWgSBOYZtgBfW9UGNJG//rwMqTX1xQE9BAodvMH9tAusKDUw==", + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.14.0.tgz", + "integrity": "sha512-i0QwbHYfnOMYsBEyjxcwGu5SMIi9sImDVjDg087hpzXqhBSosxkE7gyIYFHgfFl4mr7RrXksIBZ4DoLoP4FhJg==", "cpu": [ "x64" ], @@ -667,9 +693,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.12.0.tgz", - "integrity": "sha512-JPDxovheWNp6d7AHCgsUlkuCKvtu3RB55iNEkaQcf0ttsDU/JZF+iQnYcQJSk/7PtT4mjjVG8N1kpwnI9SLYaw==", + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.14.0.tgz", + "integrity": "sha512-Fq52EYb0riNHLBTAcL0cun+rRwyZ10S9vKzhGKKgeD+XbwunszSY0rVMco5KbOsTlwovP2rTOkiII/fQ4ih/zQ==", "cpu": [ "arm64" ], @@ -680,9 +706,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.12.0.tgz", - "integrity": "sha512-fjtuvMWRGJn1oZacG8IPnzIV6GF2/XG+h71FKn76OYFqySXInJtseAqdprVTDTyqPxQOG9Exak5/E9Z3+EJ8ZA==", + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.14.0.tgz", + "integrity": "sha512-e/PBHxPdJ00O9p5Ui43+vixSgVf4NlLsmV6QneGERJ3lnjIua/kim6PRFe3iDueT1rQcgSkYP8ZBBXa/h4iPvw==", "cpu": [ "ia32" ], @@ -693,9 +719,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.12.0.tgz", - "integrity": "sha512-ZYmr5mS2wd4Dew/JjT0Fqi2NPB/ZhZ2VvPp7SmvPZb4Y1CG/LRcS6tcRo2cYU7zLK5A7cdbhWnnWmUjoI4qapg==", + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.14.0.tgz", + "integrity": "sha512-aGg7iToJjdklmxlUlJh/PaPNa4PmqHfyRMLunbL3eaMO0gp656+q1zOKkpJ/CVe9CryJv6tAN1HDoR8cNGzkag==", "cpu": [ "x64" ], @@ -3906,9 +3932,9 @@ } }, "node_modules/playcanvas": { - "version": "1.68.2", - "resolved": "https://registry.npmjs.org/playcanvas/-/playcanvas-1.68.2.tgz", - "integrity": "sha512-YodgrsT3ckkB2cLfQyW85oNK6j5zzEz/Alrl2VRaxTO/1EthzEHZKfb8qAXp1Gxi4fyzeuW3UBQvfdDY69zlZQ==", + "version": "1.69.2", + "resolved": "https://registry.npmjs.org/playcanvas/-/playcanvas-1.69.2.tgz", + "integrity": "sha512-UOjfvzkDHWHnyL9wSaFCYs04W/2owZoPS7pOZDKHneMlEE/OMg0bvdwYuWfy1mZRiGT00TFvqrP2Mla+BYgu9w==", "dev": true, "dependencies": { "@types/webxr": "^0.5.7", @@ -4232,9 +4258,9 @@ } }, "node_modules/rollup": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.12.0.tgz", - "integrity": "sha512-wz66wn4t1OHIJw3+XU7mJJQV/2NAfw5OAk6G6Hoo3zcvz/XOfQ52Vgi+AN4Uxoxi0KBBwk2g8zPrTDA4btSB/Q==", + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.14.0.tgz", + "integrity": "sha512-Qe7w62TyawbDzB4yt32R0+AbIo6m1/sqO7UPzFS8Z/ksL5mrfhA0v4CavfdmFav3D+ub4QeAgsGEe84DoWe/nQ==", "dev": true, "dependencies": { "@types/estree": "1.0.5" @@ -4247,19 +4273,21 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.12.0", - "@rollup/rollup-android-arm64": "4.12.0", - "@rollup/rollup-darwin-arm64": "4.12.0", - "@rollup/rollup-darwin-x64": "4.12.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.12.0", - "@rollup/rollup-linux-arm64-gnu": "4.12.0", - "@rollup/rollup-linux-arm64-musl": "4.12.0", - "@rollup/rollup-linux-riscv64-gnu": "4.12.0", - "@rollup/rollup-linux-x64-gnu": "4.12.0", - "@rollup/rollup-linux-x64-musl": "4.12.0", - "@rollup/rollup-win32-arm64-msvc": "4.12.0", - "@rollup/rollup-win32-ia32-msvc": "4.12.0", - "@rollup/rollup-win32-x64-msvc": "4.12.0", + "@rollup/rollup-android-arm-eabi": "4.14.0", + "@rollup/rollup-android-arm64": "4.14.0", + "@rollup/rollup-darwin-arm64": "4.14.0", + "@rollup/rollup-darwin-x64": "4.14.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.14.0", + "@rollup/rollup-linux-arm64-gnu": "4.14.0", + "@rollup/rollup-linux-arm64-musl": "4.14.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.14.0", + "@rollup/rollup-linux-riscv64-gnu": "4.14.0", + "@rollup/rollup-linux-s390x-gnu": "4.14.0", + "@rollup/rollup-linux-x64-gnu": "4.14.0", + "@rollup/rollup-linux-x64-musl": "4.14.0", + "@rollup/rollup-win32-arm64-msvc": "4.14.0", + "@rollup/rollup-win32-ia32-msvc": "4.14.0", + "@rollup/rollup-win32-x64-msvc": "4.14.0", "fsevents": "~2.3.2" } }, @@ -4363,6 +4391,19 @@ "node": ">=10" } }, + "node_modules/rollup/node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.14.0.tgz", + "integrity": "sha512-C6y6z2eCNCfhZxT9u+jAM2Fup89ZjiG5pIzZIDycs1IwESviLxwkQcFRGLjnDrP+PT+v5i4YFvlcfAs+LnreXg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -5631,9 +5672,9 @@ "dev": true }, "@playcanvas/pcui": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@playcanvas/pcui/-/pcui-4.2.0.tgz", - "integrity": "sha512-3tBE3ZFma5GiBChbARD5T3Ye5oxTmfI5zttw4a4dNyx8tvVNKT42B5axg0TYUdc/3KNogYyamR7Pw/7OM0l7NQ==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@playcanvas/pcui/-/pcui-4.3.0.tgz", + "integrity": "sha512-nhyF+u55ws1FPA4A3EkD9p4ujK2HfGxuA6GRWJWe2A9tPyKRMGTNNjrubc/4GnmCxfZP7ux9QEd/a82MXSoU3g==", "dev": true, "requires": { "@playcanvas/observer": "^1.4.0" @@ -5738,58 +5779,72 @@ } }, "@rollup/rollup-android-arm-eabi": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.12.0.tgz", - "integrity": "sha512-+ac02NL/2TCKRrJu2wffk1kZ+RyqxVUlbjSagNgPm94frxtr+XDL12E5Ll1enWskLrtrZ2r8L3wED1orIibV/w==", + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.14.0.tgz", + "integrity": "sha512-jwXtxYbRt1V+CdQSy6Z+uZti7JF5irRKF8hlKfEnF/xJpcNGuuiZMBvuoYM+x9sr9iWGnzrlM0+9hvQ1kgkf1w==", "dev": true, "optional": true }, "@rollup/rollup-android-arm64": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.12.0.tgz", - "integrity": "sha512-OBqcX2BMe6nvjQ0Nyp7cC90cnumt8PXmO7Dp3gfAju/6YwG0Tj74z1vKrfRz7qAv23nBcYM8BCbhrsWqO7PzQQ==", + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.14.0.tgz", + "integrity": "sha512-fI9nduZhCccjzlsA/OuAwtFGWocxA4gqXGTLvOyiF8d+8o0fZUeSztixkYjcGq1fGZY3Tkq4yRvHPFxU+jdZ9Q==", "dev": true, "optional": true }, "@rollup/rollup-darwin-arm64": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.12.0.tgz", - "integrity": "sha512-X64tZd8dRE/QTrBIEs63kaOBG0b5GVEd3ccoLtyf6IdXtHdh8h+I56C2yC3PtC9Ucnv0CpNFJLqKFVgCYe0lOQ==", + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.14.0.tgz", + "integrity": "sha512-BcnSPRM76/cD2gQC+rQNGBN6GStBs2pl/FpweW8JYuz5J/IEa0Fr4AtrPv766DB/6b2MZ/AfSIOSGw3nEIP8SA==", "dev": true, "optional": true }, "@rollup/rollup-darwin-x64": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.12.0.tgz", - "integrity": "sha512-cc71KUZoVbUJmGP2cOuiZ9HSOP14AzBAThn3OU+9LcA1+IUqswJyR1cAJj3Mg55HbjZP6OLAIscbQsQLrpgTOg==", + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.14.0.tgz", + "integrity": "sha512-LDyFB9GRolGN7XI6955aFeI3wCdCUszFWumWU0deHA8VpR3nWRrjG6GtGjBrQxQKFevnUTHKCfPR4IvrW3kCgQ==", "dev": true, "optional": true }, "@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.12.0.tgz", - "integrity": "sha512-a6w/Y3hyyO6GlpKL2xJ4IOh/7d+APaqLYdMf86xnczU3nurFTaVN9s9jOXQg97BE4nYm/7Ga51rjec5nfRdrvA==", + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.14.0.tgz", + "integrity": "sha512-ygrGVhQP47mRh0AAD0zl6QqCbNsf0eTo+vgwkY6LunBcg0f2Jv365GXlDUECIyoXp1kKwL5WW6rsO429DBY/bA==", "dev": true, "optional": true }, "@rollup/rollup-linux-arm64-gnu": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.12.0.tgz", - "integrity": "sha512-0fZBq27b+D7Ar5CQMofVN8sggOVhEtzFUwOwPppQt0k+VR+7UHMZZY4y+64WJ06XOhBTKXtQB/Sv0NwQMXyNAA==", + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.14.0.tgz", + "integrity": "sha512-x+uJ6MAYRlHGe9wi4HQjxpaKHPM3d3JjqqCkeC5gpnnI6OWovLdXTpfa8trjxPLnWKyBsSi5kne+146GAxFt4A==", "dev": true, "optional": true }, "@rollup/rollup-linux-arm64-musl": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.12.0.tgz", - "integrity": "sha512-eTvzUS3hhhlgeAv6bfigekzWZjaEX9xP9HhxB0Dvrdbkk5w/b+1Sxct2ZuDxNJKzsRStSq1EaEkVSEe7A7ipgQ==", + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.14.0.tgz", + "integrity": "sha512-nrRw8ZTQKg6+Lttwqo6a2VxR9tOroa2m91XbdQ2sUUzHoedXlsyvY1fN4xWdqz8PKmf4orDwejxXHjh7YBGUCA==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.14.0.tgz", + "integrity": "sha512-xV0d5jDb4aFu84XKr+lcUJ9y3qpIWhttO3Qev97z8DKLXR62LC3cXT/bMZXrjLF9X+P5oSmJTzAhqwUbY96PnA==", "dev": true, "optional": true }, "@rollup/rollup-linux-riscv64-gnu": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.12.0.tgz", - "integrity": "sha512-ix+qAB9qmrCRiaO71VFfY8rkiAZJL8zQRXveS27HS+pKdjwUfEhqo2+YF2oI+H/22Xsiski+qqwIBxVewLK7sw==", + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.14.0.tgz", + "integrity": "sha512-SDDhBQwZX6LPRoPYjAZWyL27LbcBo7WdBFWJi5PI9RPCzU8ijzkQn7tt8NXiXRiFMJCVpkuMkBf4OxSxVMizAw==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-s390x-gnu": { + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.14.0.tgz", + "integrity": "sha512-RxB/qez8zIDshNJDufYlTT0ZTVut5eCpAZ3bdXDU9yTxBzui3KhbGjROK2OYTTor7alM7XBhssgoO3CZ0XD3qA==", "dev": true, "optional": true }, @@ -5800,30 +5855,30 @@ "optional": true }, "@rollup/rollup-linux-x64-musl": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.12.0.tgz", - "integrity": "sha512-LfFdRhNnW0zdMvdCb5FNuWlls2WbbSridJvxOvYWgSBOYZtgBfW9UGNJG//rwMqTX1xQE9BAodvMH9tAusKDUw==", + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.14.0.tgz", + "integrity": "sha512-i0QwbHYfnOMYsBEyjxcwGu5SMIi9sImDVjDg087hpzXqhBSosxkE7gyIYFHgfFl4mr7RrXksIBZ4DoLoP4FhJg==", "dev": true, "optional": true }, "@rollup/rollup-win32-arm64-msvc": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.12.0.tgz", - "integrity": "sha512-JPDxovheWNp6d7AHCgsUlkuCKvtu3RB55iNEkaQcf0ttsDU/JZF+iQnYcQJSk/7PtT4mjjVG8N1kpwnI9SLYaw==", + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.14.0.tgz", + "integrity": "sha512-Fq52EYb0riNHLBTAcL0cun+rRwyZ10S9vKzhGKKgeD+XbwunszSY0rVMco5KbOsTlwovP2rTOkiII/fQ4ih/zQ==", "dev": true, "optional": true }, "@rollup/rollup-win32-ia32-msvc": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.12.0.tgz", - "integrity": "sha512-fjtuvMWRGJn1oZacG8IPnzIV6GF2/XG+h71FKn76OYFqySXInJtseAqdprVTDTyqPxQOG9Exak5/E9Z3+EJ8ZA==", + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.14.0.tgz", + "integrity": "sha512-e/PBHxPdJ00O9p5Ui43+vixSgVf4NlLsmV6QneGERJ3lnjIua/kim6PRFe3iDueT1rQcgSkYP8ZBBXa/h4iPvw==", "dev": true, "optional": true }, "@rollup/rollup-win32-x64-msvc": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.12.0.tgz", - "integrity": "sha512-ZYmr5mS2wd4Dew/JjT0Fqi2NPB/ZhZ2VvPp7SmvPZb4Y1CG/LRcS6tcRo2cYU7zLK5A7cdbhWnnWmUjoI4qapg==", + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.14.0.tgz", + "integrity": "sha512-aGg7iToJjdklmxlUlJh/PaPNa4PmqHfyRMLunbL3eaMO0gp656+q1zOKkpJ/CVe9CryJv6tAN1HDoR8cNGzkag==", "dev": true, "optional": true }, @@ -8169,9 +8224,9 @@ "dev": true }, "playcanvas": { - "version": "1.68.2", - "resolved": "https://registry.npmjs.org/playcanvas/-/playcanvas-1.68.2.tgz", - "integrity": "sha512-YodgrsT3ckkB2cLfQyW85oNK6j5zzEz/Alrl2VRaxTO/1EthzEHZKfb8qAXp1Gxi4fyzeuW3UBQvfdDY69zlZQ==", + "version": "1.69.2", + "resolved": "https://registry.npmjs.org/playcanvas/-/playcanvas-1.69.2.tgz", + "integrity": "sha512-UOjfvzkDHWHnyL9wSaFCYs04W/2owZoPS7pOZDKHneMlEE/OMg0bvdwYuWfy1mZRiGT00TFvqrP2Mla+BYgu9w==", "dev": true, "requires": { "@types/webxr": "^0.5.7", @@ -8414,26 +8469,37 @@ } }, "rollup": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.12.0.tgz", - "integrity": "sha512-wz66wn4t1OHIJw3+XU7mJJQV/2NAfw5OAk6G6Hoo3zcvz/XOfQ52Vgi+AN4Uxoxi0KBBwk2g8zPrTDA4btSB/Q==", - "dev": true, - "requires": { - "@rollup/rollup-android-arm-eabi": "4.12.0", - "@rollup/rollup-android-arm64": "4.12.0", - "@rollup/rollup-darwin-arm64": "4.12.0", - "@rollup/rollup-darwin-x64": "4.12.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.12.0", - "@rollup/rollup-linux-arm64-gnu": "4.12.0", - "@rollup/rollup-linux-arm64-musl": "4.12.0", - "@rollup/rollup-linux-riscv64-gnu": "4.12.0", - "@rollup/rollup-linux-x64-gnu": "4.12.0", - "@rollup/rollup-linux-x64-musl": "4.12.0", - "@rollup/rollup-win32-arm64-msvc": "4.12.0", - "@rollup/rollup-win32-ia32-msvc": "4.12.0", - "@rollup/rollup-win32-x64-msvc": "4.12.0", + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.14.0.tgz", + "integrity": "sha512-Qe7w62TyawbDzB4yt32R0+AbIo6m1/sqO7UPzFS8Z/ksL5mrfhA0v4CavfdmFav3D+ub4QeAgsGEe84DoWe/nQ==", + "dev": true, + "requires": { + "@rollup/rollup-android-arm-eabi": "4.14.0", + "@rollup/rollup-android-arm64": "4.14.0", + "@rollup/rollup-darwin-arm64": "4.14.0", + "@rollup/rollup-darwin-x64": "4.14.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.14.0", + "@rollup/rollup-linux-arm64-gnu": "4.14.0", + "@rollup/rollup-linux-arm64-musl": "4.14.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.14.0", + "@rollup/rollup-linux-riscv64-gnu": "4.14.0", + "@rollup/rollup-linux-s390x-gnu": "4.14.0", + "@rollup/rollup-linux-x64-gnu": "4.14.0", + "@rollup/rollup-linux-x64-musl": "4.14.0", + "@rollup/rollup-win32-arm64-msvc": "4.14.0", + "@rollup/rollup-win32-ia32-msvc": "4.14.0", + "@rollup/rollup-win32-x64-msvc": "4.14.0", "@types/estree": "1.0.5", "fsevents": "~2.3.2" + }, + "dependencies": { + "@rollup/rollup-linux-x64-gnu": { + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.14.0.tgz", + "integrity": "sha512-C6y6z2eCNCfhZxT9u+jAM2Fup89ZjiG5pIzZIDycs1IwESviLxwkQcFRGLjnDrP+PT+v5i4YFvlcfAs+LnreXg==", + "dev": true, + "optional": true + } } }, "rollup-plugin-copy": { diff --git a/package.json b/package.json index 32a4340a..98701531 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "devDependencies": { "@playcanvas/eslint-config": "^1.7.1", "@playcanvas/observer": "^1.4.0", - "@playcanvas/pcui": "^4.2.0", + "@playcanvas/pcui": "^4.3.0", "@rollup/plugin-alias": "^5.1.0", "@rollup/plugin-commonjs": "^25.0.7", "@rollup/plugin-image": "^3.0.3", @@ -67,13 +67,13 @@ "eslint": "^8.56.0", "fflate": "^0.8.2", "handlebars": "^4.7.8", - "playcanvas": "^1.68.2", + "playcanvas": "^1.69.2", "prop-types": "^15.8.1", "qrious": "^4.0.2", "react": "^18.2.0", "react-dom": "^18.2.0", "react-visibility-sensor": "^5.1.1", - "rollup": "^4.12.0", + "rollup": "^4.14.0", "rollup-plugin-copy": "^3.5.0", "rollup-plugin-sass": "^1.12.21", "serve": "^14.2.1", diff --git a/copy-and-watch.mjs b/plugins/copy-and-watch.mjs similarity index 95% rename from copy-and-watch.mjs rename to plugins/copy-and-watch.mjs index 65fe5b52..9a1ecb84 100644 --- a/copy-and-watch.mjs +++ b/plugins/copy-and-watch.mjs @@ -38,20 +38,20 @@ export default function copyAndWatch(config) { return { name: 'copy-and-watch', - async buildStart() { + buildStart() { resolvedConfig.targets.forEach((target) => { this.addWatchFile(target.src); }); }, - async generateBundle() { + generateBundle() { resolvedConfig.targets.forEach((target) => { const contents = fs.readFileSync(target.src); this.emitFile({ type: 'asset', fileName: target.dest, source: target.transform ? target.transform(contents, target.src) : contents - }) + }); }); } - } + }; } diff --git a/rollup.config.mjs b/rollup.config.mjs index 222f9777..e24edc6d 100644 --- a/rollup.config.mjs +++ b/rollup.config.mjs @@ -1,5 +1,5 @@ +// official rollup plugins import alias from '@rollup/plugin-alias'; -import copyAndWatch from "./copy-and-watch.mjs"; import image from '@rollup/plugin-image'; import commonjs from "@rollup/plugin-commonjs"; import Handlebars from 'handlebars'; @@ -11,6 +11,9 @@ import sass from 'rollup-plugin-sass'; import terser from '@rollup/plugin-terser'; import typescript from "@rollup/plugin-typescript"; +// custom plugins +import copyAndWatch from "./plugins/copy-and-watch.mjs"; + // prod is release build if (process.env.BUILD_TYPE === 'prod') { process.env.BUILD_TYPE = 'release'; @@ -25,24 +28,8 @@ const PCUI_DIR = path.resolve(process.env.PCUI_PATH || 'node_modules/@playcanvas const ENGINE_NAME = (BUILD_TYPE === 'debug') ? 'playcanvas.dbg.mjs' : 'playcanvas.mjs'; const ENGINE_PATH = path.resolve(ENGINE_DIR, 'build', ENGINE_NAME); -// define supported module overrides -const aliasEntries = { - 'playcanvas': ENGINE_PATH, - 'playcanvas-extras': EXTRAS_DIR, - 'pcui': PCUI_DIR -}; - -const tsCompilerOptions = { - baseUrl: '.', - paths: { - 'playcanvas': [ENGINE_DIR], - 'playcanvas-extras': [EXTRAS_DIR], - 'pcui': [PCUI_DIR] - } -}; - // compile mustache template -const compileMustache = (content, srcFilename) => { +const compileMustache = (content) => { return Handlebars.compile(content.toString('utf8'))({ hasPublicPath: !!process.env.PUBLIC_PATH, hasAnalyticsID: !!process.env.ANALYTICS_ID, @@ -53,11 +40,6 @@ const compileMustache = (content, srcFilename) => { }); }; -const replaceValues = { - 'process.env.NODE_ENV': JSON.stringify(BUILD_TYPE === 'release' ? 'production' : 'development'), - '__PUBLIC_PATH__': JSON.stringify(process.env.PUBLIC_PATH) -}; - export default { input: 'src/index.tsx', output: { @@ -74,7 +56,10 @@ export default { ] }), replace({ - values: replaceValues, + values: { + 'process.env.NODE_ENV': JSON.stringify(BUILD_TYPE === 'release' ? 'production' : 'development'), + '__PUBLIC_PATH__': JSON.stringify(process.env.PUBLIC_PATH) + }, preventAssignment: true }), sass({ @@ -82,12 +67,25 @@ export default { output: 'dist/style.css', outputStyle: 'compressed' }), - image({dom: true}), - alias({ entries: aliasEntries }), + image({ dom: true }), + alias({ + entries: { + 'playcanvas': ENGINE_PATH, + 'playcanvas-extras': EXTRAS_DIR, + 'pcui': PCUI_DIR + } + }), commonjs(), resolve(), typescript({ - compilerOptions: tsCompilerOptions + compilerOptions: { + baseUrl: '.', + paths: { + 'playcanvas': [ENGINE_DIR], + 'playcanvas-extras': [EXTRAS_DIR], + 'pcui': [PCUI_DIR] + } + } }), json(), (BUILD_TYPE !== 'debug') && terser() diff --git a/src/index.tsx b/src/index.tsx index 39c9a397..719ba2d6 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -2,7 +2,9 @@ import { basisInitialize, createGraphicsDevice, Vec3, - WasmModule + WasmModule, + version as engineVersion, + revision as engineRevision } from 'playcanvas'; import { Observer } from '@playcanvas/observer'; @@ -15,7 +17,6 @@ import './style.scss'; import { version as modelViewerVersion } from '../package.json'; import { version as pcuiVersion, revision as pcuiRevision } from 'pcui'; -import { version as engineVersion, revision as engineRevision } from 'playcanvas'; // Permit some additional properties to be set on the window declare global { @@ -64,7 +65,7 @@ const observerData: ObserverData = { }, skybox: { value: 'Paul Lobe Haus', - options: JSON.stringify(['None'].concat(skyboxes.map(s => s.label)).map(l => { return { v: l, t: l } })), + options: JSON.stringify(['None'].concat(skyboxes.map(s => s.label)).map(l => ({ v: l, t: l }))), exposure: 0, rotation: 0, background: 'Infinite Sphere', @@ -74,7 +75,7 @@ const observerData: ObserverData = { domeRadius: 20, domeOffset: 0.4, tripodOffset: 0.1 - }, + } }, light: { enabled: false, @@ -85,7 +86,7 @@ const observerData: ObserverData = { }, shadowCatcher: { enabled: false, - intensity: 0.4, + intensity: 0.4 }, debug: { renderMode: 'default', @@ -282,7 +283,7 @@ const main = () => { const url = decodeURIComponent(value); files.push({ url, filename: url }); break; - }; + } case 'cameraPosition': { const pos = value.split(',').map(Number); if (pos.length === 3) { diff --git a/src/png-exporter.js b/src/png-exporter.js deleted file mode 100644 index 6edcd3a9..00000000 --- a/src/png-exporter.js +++ /dev/null @@ -1,154 +0,0 @@ -import { RenderTarget } from 'playcanvas'; - -function PngExportWorker(href) { - const initLodepng = () => { - return new Promise((resolve, reject) => { - self.importScripts(`${href}static/lib/lodepng/lodepng.js`); - resolve(self.lodepng({ - locateFile: () => `${href}static/lib/lodepng/lodepng.wasm` - })); - }); - }; - - const compress = (lodepng, words, width, height) => { - const resultDataPtrPtr = lodepng._malloc(4); - const resultSizePtr = lodepng._malloc(4); - const imageData = lodepng._malloc(width * height * 4); - - // copy pixels into wasm memory - for (let y = 0; y < height; ++y) { - let soff = y * width; - let doff = imageData / 4 + (height - 1 - y) * width; - for (let x = 0; x < width; ++x) { - lodepng.HEAPU32[doff++] = words[soff++]; - } - } - - // invoke compress - lodepng._lodepng_encode32(resultDataPtrPtr, resultSizePtr, imageData, width, height); - - // read results - const result = lodepng.HEAPU8.slice(lodepng.HEAPU32[resultDataPtrPtr / 4], lodepng.HEAPU32[resultDataPtrPtr / 4] + lodepng.HEAPU32[resultSizePtr / 4]); - - lodepng._free(resultDataPtrPtr); - lodepng._free(resultSizePtr); - lodepng._free(imageData); - - return result; - }; - - const main = async () => { - const lodepng = await initLodepng(); - - self.onmessage = (message) => { - const data = message.data; - - // compress - const result = compress(lodepng, data.words, data.width, data.height); - - // return - self.postMessage({ result: result }, [result.buffer]); - }; - }; - - main(); -} - -const createWorker = () => { - const workerBlob = new Blob([`(${PngExportWorker.toString()})('${window.location.href.split('?')[0]}')\n\n`], { - type: 'application/javascript' - }); - return new Worker(URL.createObjectURL(workerBlob)); -} - -const readPixels = (renderTarget) => { - const device = renderTarget._device; - const data = new Uint8ClampedArray(renderTarget.width * renderTarget.height * 4); - device.setFramebuffer(renderTarget._glFrameBuffer); - device.gl.readPixels(0, 0, renderTarget.width, renderTarget.height, device.gl.RGBA, device.gl.UNSIGNED_BYTE, data); - return new Uint32Array(data.buffer); -}; - -const readTexturePixels = (texture, face) => { - const renderTarget = new RenderTarget({ colorBuffer: texture, depth: false, face: face }); - device.initRenderTarget(renderTarget); - const result = readPixels(renderTarget); - renderTarget.destroy(); - return result; -}; - -// download the data uri -const downloadFile = (filename, data) => { - const blob = new Blob([data], { type: "octet/stream" }); - const url = window.URL.createObjectURL(blob); - - const lnk = document.createElement('a'); - lnk.download = filename; - lnk.href = url; - - // create a "fake" click-event to trigger the download - if (document.createEvent) { - const e = document.createEvent("MouseEvents"); - e.initMouseEvent("click", true, true, window, - 0, 0, 0, 0, 0, false, false, false, - false, 0, null); - lnk.dispatchEvent(e); - } else if (lnk.fireEvent) { - lnk.fireEvent("onclick"); - } - - window.URL.revokeObjectURL(url); -}; - -class PngExporter { - constructor() { - let receiver; - - this.worker = createWorker(); - this.worker.addEventListener('message', (message) => { - receiver(message); - }); - - this.promiseFunc = (resolve, reject) => { - receiver = (message) => { - resolve(message.data.result); - receiver = null; - }; - }; - } - - async export(filename, words, width, height) { - const compress = (words, width, height) => { - this.worker.postMessage({ - words: words, - width: width, - height: height - }, [words.buffer]); - - return new Promise(this.promiseFunc); - }; - - downloadFile(filename, await compress(words, width, height)); - } - - async exportRenderTarget(filename, renderTarget) { - this.export(filename, readPixels(renderTarget), renderTarget.width, renderTarget.height); - } - - async exportTexture(filename, texture) { - if (texture.cubemap) { - const faceNames = ['posx', 'negx', 'posy', 'negy', 'posz', 'negz']; - const lastPoint = filename.lastIndexOf('.'); - const filenameBase = lastPoint === -1 ? filename : filename.substring(0, lastPoint); - for (let face = 0; face < 6; ++face) { - this.export(`${filenameBase}_${faceNames[face]}.png`, readTexturePixels(texture, face), texture.width, texture.height); - } - } else { - this.export(filename, readTexturePixels(texture, null), texture.width, texture.height); - } - } -} - -export { - PngExporter -}; diff --git a/src/png-exporter.ts b/src/png-exporter.ts new file mode 100644 index 00000000..133e8bad --- /dev/null +++ b/src/png-exporter.ts @@ -0,0 +1,103 @@ +class PngExporter { + static WORKER_STR = function (href: string) { + const initLodepng = () => { + return new Promise((resolve) => { + (self as any).importScripts(`${href}static/lib/lodepng/lodepng.js`); + resolve((self as any).lodepng({ + locateFile: () => `${href}static/lib/lodepng/lodepng.wasm` + })); + }); + }; + + const compress = (lodepng: any, words: any[], width: number, height: number): Uint8Array => { + const resultDataPtrPtr = lodepng._malloc(4); + const resultSizePtr = lodepng._malloc(4); + const imageData = lodepng._malloc(width * height * 4); + + // copy pixels into wasm memory + for (let y = 0; y < height; ++y) { + let soff = y * width; + let doff = imageData / 4 + (height - 1 - y) * width; + for (let x = 0; x < width; ++x) { + lodepng.HEAPU32[doff++] = words[soff++]; + } + } + + // invoke compress + lodepng._lodepng_encode32(resultDataPtrPtr, resultSizePtr, imageData, width, height); + + // read results + const result = lodepng.HEAPU8.slice(lodepng.HEAPU32[resultDataPtrPtr / 4], lodepng.HEAPU32[resultDataPtrPtr / 4] + lodepng.HEAPU32[resultSizePtr / 4]); + + lodepng._free(resultDataPtrPtr); + lodepng._free(resultSizePtr); + lodepng._free(imageData); + + return result; + }; + + const main = async () => { + const lodepng = await initLodepng(); + + self.onmessage = (message) => { + const data = message.data; + + // compress + const result = compress(lodepng, data.words, data.width, data.height); + + // return + self.postMessage({ result: result }, undefined, [result.buffer]); + }; + }; + + main(); + }.toString(); + + worker: Worker; + receiveCallback: (resolve: (result: Uint8Array) => void) => void; + + constructor() { + let receiver: (message: MessageEvent) => void = null; + + const workerBlob = new Blob([`(${PngExporter.WORKER_STR})('${window.location.href.split('?')[0]}')\n\n`], { + type: 'application/javascript' + }); + this.worker = new Worker(URL.createObjectURL(workerBlob)); + this.worker.addEventListener('message', (message) => { + receiver(message); + }); + + this.receiveCallback = (resolve) => { + receiver = (message) => { + resolve(message.data.result); + receiver = null; + }; + }; + } + + // download the data uri + _downloadFile(filename: string, data: any) { + const blob = new Blob([data], { type: "octet/stream" }); + const url = window.URL.createObjectURL(blob); + + const el = document.createElement('a'); + el.download = filename; + el.href = url; + el.click(); + + window.URL.revokeObjectURL(url); + } + + async export(filename: string, words: Uint32Array, width: number, height: number) { + this.worker.postMessage({ + words: words, + width: width, + height: height + }, [words.buffer]); + this._downloadFile(filename, await new Promise(this.receiveCallback)); + } +} + +export { + PngExporter +}; diff --git a/src/projective-skybox.ts b/src/projective-skybox.ts index 48ae8f9a..05b39ff4 100644 --- a/src/projective-skybox.ts +++ b/src/projective-skybox.ts @@ -4,9 +4,9 @@ import { Vec3 } from "playcanvas"; -const projectiveSkyboxVS = shaderChunks.skyboxVS.replace(' * cubeMapRotationMatrix', ''); +const projectiveSkyboxVS = (shaderChunks as any).skyboxVS.replace(' * cubeMapRotationMatrix', ''); -const projectiveSkyboxHDRPS = ` +const projectiveSkyboxHDRPS = /* glsl */` void intersectPlane(inout float t, vec3 pos, vec3 dir, vec4 plane) { float d = dot(dir, plane.xyz); if (d != 0.0) { @@ -67,8 +67,8 @@ void main(void) { } `; -shaderChunks.skyboxHDRPS = projectiveSkyboxHDRPS; -shaderChunks.skyboxVS = projectiveSkyboxVS; +(shaderChunks as any).skyboxHDRPS = projectiveSkyboxHDRPS; +(shaderChunks as any).skyboxVS = projectiveSkyboxVS; class ProjectiveSkybox { app: AppBase; diff --git a/src/shadow-catcher.ts b/src/shadow-catcher.ts index e9399e83..114655ce 100644 --- a/src/shadow-catcher.ts +++ b/src/shadow-catcher.ts @@ -3,7 +3,6 @@ import { CHUNKAPI_1_65, SHADOW_VSM16 as SHADOW_TYPE, SHADOWUPDATE_REALTIME as SHADOWUPDATE, - AppBase, BoundingBox, CameraComponent, diff --git a/src/ui/index.tsx b/src/ui/index.tsx index 50b8c6ab..a63e4598 100644 --- a/src/ui/index.tsx +++ b/src/ui/index.tsx @@ -12,7 +12,7 @@ import LoadControls from './load-controls'; import ErrorBox from './errors'; class App extends React.Component<{ observer: Observer }> { - state: ObserverData; + state: ObserverData = null; canvasRef: any; constructor(props: any) { diff --git a/src/ui/left-panel/index.tsx b/src/ui/left-panel/index.tsx index 9a11113f..34fce3d1 100644 --- a/src/ui/left-panel/index.tsx +++ b/src/ui/left-panel/index.tsx @@ -110,8 +110,7 @@ class HierarchyPanel extends React.Component <{ sceneData: ObserverData['scene'] } class DevicePanel extends React.Component <{ observerData: ObserverData, setProperty: SetProperty }> { - - shouldComponentUpdate(nextProps: Readonly<{ observerData: ObserverData, setProperty: SetProperty}>): boolean { + shouldComponentUpdate(nextProps: Readonly<{ observerData: ObserverData, setProperty: SetProperty}>): boolean { return JSON.stringify(nextProps.observerData.runtime) !== JSON.stringify(this.props.observerData.runtime) || nextProps.observerData.enableWebGPU !== this.props.observerData.enableWebGPU; } diff --git a/src/viewer.ts b/src/viewer.ts index e00ebde0..9747f4dd 100644 --- a/src/viewer.ts +++ b/src/viewer.ts @@ -68,7 +68,7 @@ import { DebugLines } from './debug-lines'; import { Multiframe } from './multiframe'; import { ReadDepth } from './read-depth'; import { OrbitCamera, OrbitCameraInputMouse, OrbitCameraInputTouch, OrbitCameraInputKeyboard } from './orbit-camera'; -import { PngExporter } from './png-exporter.js'; +import { PngExporter } from './png-exporter'; import { ProjectiveSkybox } from './projective-skybox'; import { ShadowCatcher } from './shadow-catcher'; import { XRObjectPlacementController } from './xr-mode'; @@ -809,7 +809,7 @@ class Viewer { case PRIMITIVE_TRISTRIP: primitiveCount += prim.count - 2; break; case PRIMITIVE_TRIFAN: primitiveCount += prim.count - 2; break; } - meshVRAM += mesh.vertexBuffer.numBytes + (mesh.indexBuffer?.numBytes ?? 0); + meshVRAM += mesh.vertexBuffer.numBytes + (mesh.indexBuffer?.[0]?.numBytes ?? 0); }); }); @@ -1853,12 +1853,6 @@ class Viewer { this.app.renderNextFrame = true; } } - - // to change samples at runtime execute in the debugger 'viewer.setSamples(5, false, 2, 0)' - setSamples(numSamples: number, jitter = false, size = 1, sigma = 0) { - this.multiframe.setSamples(numSamples, jitter, size, sigma); - this.renderNextFrame(); - } } export default Viewer; diff --git a/src/xr-mode.ts b/src/xr-mode.ts index 236f7b71..3538732f 100644 --- a/src/xr-mode.ts +++ b/src/xr-mode.ts @@ -16,8 +16,6 @@ import { GSplatComponent } from 'playcanvas'; -declare class XRObjectPlacementController { } - const vec = new Vec3(); const vec2 = new Vec3(); const translation = new Vec3(); @@ -26,148 +24,6 @@ const forward = new Vec3(); // modulo dealing with negative numbers const mod = (n: number, m: number) => ((n % m) + m) % m; -// create an invisible dom element for capturing pointer input -// rotate the model with two finger tap and twist -const createRotateInput = (controller: XRObjectPlacementController) => { - const touches: Map< - number, - { - start: {x: number; y: number}; - previous: {x: number; y: number}; - current: {x: number; y: number}; - } - > = new Map(); - let baseAngle = 0; - let angle = 0; - - const eventDefault = (e: PointerEvent) => { - e.preventDefault(); - e.stopPropagation(); - }; - - const onPointerDown = (e: PointerEvent) => { - eventDefault(e); - - touches.set(e.pointerId, { - start: { x: e.clientX, y: e.clientY }, - previous: { x: e.clientX, y: e.clientY }, - current: { x: e.clientX, y: e.clientY } - }); - - if (controller.rotating) { - if (touches.size === 1) { - controller.rotating = false; - } - } else { - controller.rotating = touches.size > 1; - } - }; - - const onPointerMove = (e: PointerEvent) => { - eventDefault(e); - - const touch = touches.get(e.pointerId); - if (touch) { - touch.previous.x = touch.current.x; - touch.previous.y = touch.current.y; - touch.current.x = e.clientX; - touch.current.y = e.clientY; - } - - if (touches.size === 2) { - const ids = Array.from(touches.keys()); - const a = touches.get(ids[0]); - const b = touches.get(ids[1]); - - const initialAngle = Math.atan2(b.start.y - a.start.y, b.start.x - a.start.x); - const currentAngle = Math.atan2(b.current.y - a.current.y, b.current.x - a.current.x); - angle = currentAngle - initialAngle; - - controller.events.fire('xr:rotate', ((baseAngle + angle) * -180) / Math.PI); - } - }; - - const onPointerUp = (e: PointerEvent) => { - eventDefault(e); - - if (touches.size === 2) { - baseAngle += angle; - } - - touches.delete(e.pointerId); - }; - - const dom = document.createElement('div'); - dom.style.position = 'fixed'; - dom.style.top = '0'; - dom.style.left = '0'; - dom.style.width = '100%'; - dom.style.height = '100%'; - dom.style.touchAction = 'none'; - dom.style.display = 'none'; - document.body.appendChild(dom); - - controller.events.on('xr:started', () => { - dom.style.display = 'block'; - dom.addEventListener('pointerdown', onPointerDown); - dom.addEventListener('pointermove', onPointerMove); - dom.addEventListener('pointerup', onPointerUp); - }); - - controller.events.on('xr:ended', () => { - dom.style.display = 'none'; - dom.removeEventListener('pointerdown', onPointerDown); - dom.removeEventListener('pointermove', onPointerMove); - dom.removeEventListener('pointerup', onPointerUp); - }); - - return dom; -}; - -// create a dom element and controller for launching and exiting xr mode -const createUI = (controller: XRObjectPlacementController) => { - const dom = document.createElement('img'); - dom.src = controller.options.startArImgSrc; - dom.style.position = 'fixed'; - dom.style.right = '20px'; - dom.style.top = '20px'; - dom.style.width = '36px'; - dom.style.height = '36px'; - dom.style.opacity = '100%'; - dom.style.display = 'none'; - document.body.appendChild(dom); - - // disable button during xr mode transitions - let enabled = true; - - controller.events.on('xr:available', (available: boolean) => { - dom.style.display = available ? 'block' : 'none'; - }); - - controller.events.on('xr:started', () => { - enabled = true; - dom.src = controller.options.stopArImgSrc; - controller.options.xr.domOverlay.root.appendChild(dom); - }); - - controller.events.on('xr:ended', () => { - enabled = true; - dom.src = controller.options.startArImgSrc; - document.body.appendChild(dom); - }); - - dom.addEventListener('click', () => { - if (enabled) { - enabled = false; - if (controller.active) { - controller.end(); - } else { - controller.start(); - } - } - }); -}; - type TweenValue = {[key: string]: number}; // helper tween class @@ -220,133 +76,6 @@ class Tween { } } -// register for callback events from the xr manager to smoothly transition and move the model -const createModelHandler = (controller: XRObjectPlacementController) => { - const xr = controller.options.xr; - const events = controller.events; - - const pos = new Tween({ x: 0, y: 0, z: 0 }); - const rot = new Tween({ x: 0, y: 0, z: 0 }); - const scale = new Tween({ scale: 1 }); - const lerpSpeed = 0.25; - - let hovering = true; - const hoverPos = new Vec3(); - - const bound = new BoundingBox(); - let meshInstances: MeshInstance[]; - - const updateBound = () => { - if (meshInstances.length) { - bound.copy(meshInstances[0].aabb); - for (let i = 1; i < meshInstances.length; ++i) { - bound.add(meshInstances[i].aabb); - } - } - }; - - events.on('xr:start', () => { - hovering = true; - - meshInstances = controller.options.content.findComponents('render') - .map((render: RenderComponent) => { - return render.meshInstances; - }) - .flat() - .concat(controller.options.content.findComponents('gsplat') - .map((gsplat: GSplatComponent) => { - return gsplat.instance.meshInstance; - }) - ); - - updateBound(); - - const halfExtents = bound.halfExtents; - hoverPos.set(0, -halfExtents.y, -halfExtents.length() * 4); - }); - - events.on('xr:initial-place', (position: Vec3) => { - const mat = xr.views.list[0]._viewInvMat; - mat.transformPoint(hoverPos, vec); - mat.getEulerAngles(vec2); - pos.goto({ x: vec.x, y: vec.y, z: vec.z }, 0); - rot.goto({ x: vec2.x, y: vec2.y, z: vec2.z }, 0); - scale.goto({ scale: 0.55 }, 0); - - rot.goto({ x: 0, y: 0, z: 0 }, lerpSpeed); - pos.goto({ x: position.x, y: position.y, z: position.z }, lerpSpeed); - hovering = false; - }); - - events.on('xr:place', (position: Vec3) => { - pos.goto({ x: position.x, y: position.y, z: position.z }, lerpSpeed); - }); - - events.on('xr:rotate', (angle: number) => { - angle = mod(angle, 360); - rot.goto({ x: 0, y: angle, z: 0 }, lerpSpeed); - // wrap source rotation to be within -180...180 degrees of target - rot.source.y = angle - 180 + mod(rot.source.y - angle + 180, 360); - }); - - events.on('xr:ended', () => { - controller.options.content.setLocalPosition(0, 0, 0); - controller.options.content.setLocalEulerAngles(0, 0, 0); - }); - - xr.app.on('frameupdate', (ms: number) => { - const dt = ms / 1000; - pos.update(dt); - rot.update(dt); - scale.update(dt); - }); - - xr.on('update', () => { - const xr = controller.options.xr; - - if (!xr.views.list.length) { - return; - } - - const mat = xr.views.list[0]._viewInvMat; - const contentRoot = controller.options.content; - - if (hovering) { - mat.transformPoint(hoverPos, vec); - mat.getEulerAngles(vec2); - - contentRoot.setLocalPosition(vec.x, vec.y, vec.z); - contentRoot.setLocalEulerAngles(vec2.x, vec2.y, vec2.z); - contentRoot.setLocalScale(1, 1, 1); - } else { - contentRoot.setLocalPosition(pos.value.x, pos.value.y, pos.value.z); - contentRoot.setLocalEulerAngles(rot.value.x, rot.value.y, rot.value.z); - contentRoot.setLocalScale(scale.value.scale, scale.value.scale, scale.value.scale); - } - - // calculate scene bounds - updateBound(); - - // update clipping planes - const boundCenter = bound.center; - const boundRadius = bound.halfExtents.length(); - - mat.getZ(forward); - mat.getTranslation(translation); - - vec.sub2(boundCenter, translation); - const dist = -vec.dot(forward); - - const far = dist + boundRadius; - const near = Math.max(0.0001, dist < boundRadius ? far / 1024 : dist - boundRadius); - - // @ts-ignore - xr._setClipPlanes(near / 1.5, far * 1.5); - - controller.events.fire('xr:update'); - }); -}; - interface XRObjectPlacementOptions { xr: XrManager; camera: Entity; @@ -369,14 +98,14 @@ class XRObjectPlacementController { const xr = options.xr; // create the rotation controller - xr.domOverlay.root = createRotateInput(this); + xr.domOverlay.root = this._createRotateInput(); // create dom if (this.options.showUI) { - createUI(this); + this._createUI(); } - createModelHandler(this); + this._createModelHandler(); // perform an asynchronous ray intersection test given a view-space ray // returns a handle used to cancel the hit test @@ -444,6 +173,275 @@ class XRObjectPlacementController { return this.options.xr.isAvailable(XRTYPE_AR); } + // create an invisible dom element for capturing pointer input + // rotate the model with two finger tap and twist + private _createRotateInput() { + const touches: Map< + number, + { + start: {x: number; y: number}; + previous: {x: number; y: number}; + current: {x: number; y: number}; + } + > = new Map(); + let baseAngle = 0; + let angle = 0; + + const eventDefault = (e: PointerEvent) => { + e.preventDefault(); + e.stopPropagation(); + }; + + const onPointerDown = (e: PointerEvent) => { + eventDefault(e); + + touches.set(e.pointerId, { + start: { x: e.clientX, y: e.clientY }, + previous: { x: e.clientX, y: e.clientY }, + current: { x: e.clientX, y: e.clientY } + }); + + if (this.rotating) { + if (touches.size === 1) { + this.rotating = false; + } + } else { + this.rotating = touches.size > 1; + } + }; + + const onPointerMove = (e: PointerEvent) => { + eventDefault(e); + + const touch = touches.get(e.pointerId); + if (touch) { + touch.previous.x = touch.current.x; + touch.previous.y = touch.current.y; + touch.current.x = e.clientX; + touch.current.y = e.clientY; + } + + if (touches.size === 2) { + const ids = Array.from(touches.keys()); + const a = touches.get(ids[0]); + const b = touches.get(ids[1]); + + const initialAngle = Math.atan2(b.start.y - a.start.y, b.start.x - a.start.x); + const currentAngle = Math.atan2(b.current.y - a.current.y, b.current.x - a.current.x); + angle = currentAngle - initialAngle; + + this.events.fire('xr:rotate', ((baseAngle + angle) * -180) / Math.PI); + } + }; + + const onPointerUp = (e: PointerEvent) => { + eventDefault(e); + + if (touches.size === 2) { + baseAngle += angle; + } + + touches.delete(e.pointerId); + }; + + const dom = document.createElement('div'); + dom.style.position = 'fixed'; + dom.style.top = '0'; + dom.style.left = '0'; + dom.style.width = '100%'; + dom.style.height = '100%'; + dom.style.touchAction = 'none'; + dom.style.display = 'none'; + document.body.appendChild(dom); + + this.events.on('xr:started', () => { + dom.style.display = 'block'; + dom.addEventListener('pointerdown', onPointerDown); + dom.addEventListener('pointermove', onPointerMove); + dom.addEventListener('pointerup', onPointerUp); + }); + + this.events.on('xr:ended', () => { + dom.style.display = 'none'; + dom.removeEventListener('pointerdown', onPointerDown); + dom.removeEventListener('pointermove', onPointerMove); + dom.removeEventListener('pointerup', onPointerUp); + }); + + return dom; + } + + // create a dom element and controller for launching and exiting xr mode + private _createUI() { + const dom = document.createElement('img'); + dom.src = this.options.startArImgSrc; + dom.style.position = 'fixed'; + dom.style.right = '20px'; + dom.style.top = '20px'; + dom.style.width = '36px'; + dom.style.height = '36px'; + dom.style.opacity = '100%'; + dom.style.display = 'none'; + document.body.appendChild(dom); + + // disable button during xr mode transitions + let enabled = true; + + this.events.on('xr:available', (available: boolean) => { + dom.style.display = available ? 'block' : 'none'; + }); + + this.events.on('xr:started', () => { + enabled = true; + dom.src = this.options.stopArImgSrc; + this.options.xr.domOverlay.root.appendChild(dom); + }); + + this.events.on('xr:ended', () => { + enabled = true; + dom.src = this.options.startArImgSrc; + document.body.appendChild(dom); + }); + + dom.addEventListener('click', () => { + if (enabled) { + enabled = false; + if (this.active) { + this.end(); + } else { + this.start(); + } + } + }); + } + + // register for callback events from the xr manager to smoothly transition and move the model + private _createModelHandler() { + const xr = this.options.xr; + const events = this.events; + + const pos = new Tween({ x: 0, y: 0, z: 0 }); + const rot = new Tween({ x: 0, y: 0, z: 0 }); + const scale = new Tween({ scale: 1 }); + const lerpSpeed = 0.25; + + let hovering = true; + const hoverPos = new Vec3(); + + const bound = new BoundingBox(); + let meshInstances: MeshInstance[]; + + const updateBound = () => { + if (meshInstances.length) { + bound.copy(meshInstances[0].aabb); + for (let i = 1; i < meshInstances.length; ++i) { + bound.add(meshInstances[i].aabb); + } + } + }; + + events.on('xr:start', () => { + hovering = true; + + meshInstances = this.options.content.findComponents('render') + .map((render: RenderComponent) => { + return render.meshInstances; + }) + .flat() + .concat(this.options.content.findComponents('gsplat') + .map((gsplat: GSplatComponent) => { + return gsplat.instance.meshInstance; + }) + ); + + updateBound(); + + const halfExtents = bound.halfExtents; + hoverPos.set(0, -halfExtents.y, -halfExtents.length() * 4); + }); + + events.on('xr:initial-place', (position: Vec3) => { + const mat = xr.views.list[0]._viewInvMat; + mat.transformPoint(hoverPos, vec); + mat.getEulerAngles(vec2); + pos.goto({ x: vec.x, y: vec.y, z: vec.z }, 0); + rot.goto({ x: vec2.x, y: vec2.y, z: vec2.z }, 0); + scale.goto({ scale: 0.55 }, 0); + + rot.goto({ x: 0, y: 0, z: 0 }, lerpSpeed); + pos.goto({ x: position.x, y: position.y, z: position.z }, lerpSpeed); + hovering = false; + }); + + events.on('xr:place', (position: Vec3) => { + pos.goto({ x: position.x, y: position.y, z: position.z }, lerpSpeed); + }); + + events.on('xr:rotate', (angle: number) => { + angle = mod(angle, 360); + rot.goto({ x: 0, y: angle, z: 0 }, lerpSpeed); + // wrap source rotation to be within -180...180 degrees of target + rot.source.y = angle - 180 + mod(rot.source.y - angle + 180, 360); + }); + + events.on('xr:ended', () => { + this.options.content.setLocalPosition(0, 0, 0); + this.options.content.setLocalEulerAngles(0, 0, 0); + }); + + xr.app.on('frameupdate', (ms: number) => { + const dt = ms / 1000; + pos.update(dt); + rot.update(dt); + scale.update(dt); + }); + + xr.on('update', () => { + const xr = this.options.xr; + + if (!xr.views.list.length) { + return; + } + + const mat = xr.views.list[0]._viewInvMat; + const contentRoot = this.options.content; + + if (hovering) { + mat.transformPoint(hoverPos, vec); + mat.getEulerAngles(vec2); + + contentRoot.setLocalPosition(vec.x, vec.y, vec.z); + contentRoot.setLocalEulerAngles(vec2.x, vec2.y, vec2.z); + contentRoot.setLocalScale(1, 1, 1); + } else { + contentRoot.setLocalPosition(pos.value.x, pos.value.y, pos.value.z); + contentRoot.setLocalEulerAngles(rot.value.x, rot.value.y, rot.value.z); + contentRoot.setLocalScale(scale.value.scale, scale.value.scale, scale.value.scale); + } + + // calculate scene bounds + updateBound(); + + // update clipping planes + const boundCenter = bound.center; + const boundRadius = bound.halfExtents.length(); + + mat.getZ(forward); + mat.getTranslation(translation); + + vec.sub2(boundCenter, translation); + const dist = -vec.dot(forward); + + const far = dist + boundRadius; + const near = Math.max(0.0001, dist < boundRadius ? far / 1024 : dist - boundRadius); + + // @ts-ignore + xr._setClipPlanes(near / 1.5, far * 1.5); + + this.events.fire('xr:update'); + }); + } + // request to start the xr session start() { if (!this.available || this.active) {