diff --git a/client/package-lock.json b/client/package-lock.json index c4cca00..c3afd7d 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -9,13 +9,15 @@ "version": "0.0.0", "dependencies": { "@fontsource/roboto": "^5.0.8", + "@threlte/core": "^7.1.0", + "@threlte/extras": "^8.8.0", "howler": "^2.2.4", "material-icons": "^1.13.12", "material-symbols": "^0.15.0", "overlayscrollbars-svelte": "^0.5.3", "socket.io-client": "^4.7.4", "svelte-french-toast": "^1.2.0", - "svrollbar": "^0.12.0" + "three": "^0.161.0" }, "devDependencies": { "@svelte-plugins/tooltips": "^3.0.0", @@ -24,6 +26,7 @@ "@tauri-apps/cli": "^1.5.10", "@tsconfig/svelte": "^5.0.2", "@types/howler": "^2.2.11", + "@types/three": "^0.161.2", "autoprefixer": "^10.4.17", "postcss": "^8.4.35", "svelte": "^4.2.11", @@ -995,6 +998,37 @@ "node": ">= 10" } }, + "node_modules/@threlte/core": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@threlte/core/-/core-7.1.0.tgz", + "integrity": "sha512-Qshrrt1Bqu8B0rWuNtFqGJpbmxrFUBNYTHOJZ29Tiqy2kpmwYZfzVSPRoGUfJTqUtpfxGCrDU3S2etWnB0KsUA==", + "dependencies": { + "mitt": "^3.0.1" + }, + "peerDependencies": { + "svelte": ">=4", + "three": ">=0.133" + } + }, + "node_modules/@threlte/extras": { + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/@threlte/extras/-/extras-8.8.0.tgz", + "integrity": "sha512-NNGywihQlPGbDoWnr0ZHV59Gkg8nKM4kzdxPY0CVZ93pSxXpGdoua0mQBTaQbJ6w3Qeiz867va+4SQXeVRHs8w==", + "dependencies": { + "three-mesh-bvh": "^0.7.1", + "three-perf": "^1.0.10", + "troika-three-text": "^0.49.0" + }, + "peerDependencies": { + "svelte": ">=4", + "three": ">=0.133" + }, + "peerDependenciesMeta": { + "three-mesh-bvh": { + "optional": true + } + } + }, "node_modules/@tsconfig/svelte": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/@tsconfig/svelte/-/svelte-5.0.2.tgz", @@ -1025,6 +1059,30 @@ "integrity": "sha512-Sk/uYFOBAB7mb74XcpizmH0KOR2Pv3D2Hmrh1Dmy5BmK3MpdSa5kqZcg6EKBdklU0bFXX9gCfzvpnyUehrPIuA==", "dev": true }, + "node_modules/@types/stats.js": { + "version": "0.17.3", + "resolved": "https://registry.npmjs.org/@types/stats.js/-/stats.js-0.17.3.tgz", + "integrity": "sha512-pXNfAD3KHOdif9EQXZ9deK82HVNaXP5ZIF5RP2QG6OQFNTaY2YIetfrE9t528vEreGQvEPRDDc8muaoYeK0SxQ==", + "dev": true + }, + "node_modules/@types/three": { + "version": "0.161.2", + "resolved": "https://registry.npmjs.org/@types/three/-/three-0.161.2.tgz", + "integrity": "sha512-DazpZ+cIfBzbW/p0zm6G8CS03HBMd748A3R1ZOXHpqaXZLv2I5zNgQUrRG//UfJ6zYFp2cUoCQaOLaz8ubH07w==", + "dev": true, + "dependencies": { + "@types/stats.js": "*", + "@types/webxr": "*", + "fflate": "~0.6.10", + "meshoptimizer": "~0.18.1" + } + }, + "node_modules/@types/webxr": { + "version": "0.5.14", + "resolved": "https://registry.npmjs.org/@types/webxr/-/webxr-0.5.14.tgz", + "integrity": "sha512-UEMMm/Xn3DtEa+gpzUrOcDj+SJS1tk5YodjwOxcqStNhCfPcwgyC5Srg2ToVKyg2Fhq16Ffpb0UWUQHqoT9AMA==", + "dev": true + }, "node_modules/acorn": { "version": "8.11.3", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", @@ -1144,6 +1202,14 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, + "node_modules/bidi-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz", + "integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==", + "dependencies": { + "require-from-string": "^2.0.2" + } + }, "node_modules/binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -1563,6 +1629,12 @@ "reusify": "^1.0.4" } }, + "node_modules/fflate": { + "version": "0.6.10", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.6.10.tgz", + "integrity": "sha512-IQrh3lEPM93wVCEczc9SaAOvkmcoQn/G8Bo1e8ZPlY3X3bnAxWaBdvTdvM1hP62iZp0BXWDy4vTAy4fF0+Dlpg==", + "dev": true + }, "node_modules/fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -1922,6 +1994,12 @@ "node": ">= 8" } }, + "node_modules/meshoptimizer": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/meshoptimizer/-/meshoptimizer-0.18.1.tgz", + "integrity": "sha512-ZhoIoL7TNV4s5B6+rx5mC//fw8/POGyNxS/DZyCJeiZ12ScLfVwRE/GfsxwiTkMYYD5DmK2/JXnEVXqL4rF+Sw==", + "dev": true + }, "node_modules/micromatch": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", @@ -1974,6 +2052,11 @@ "node": ">=16 || 14 >=14.17" } }, + "node_modules/mitt": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", + "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==" + }, "node_modules/mkdirp": { "version": "0.5.6", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", @@ -2393,6 +2476,14 @@ "node": ">=8.10.0" } }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/resolve": { "version": "1.22.8", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", @@ -2955,11 +3046,6 @@ "svelte": "^3.2.1 || ^4.0.0-next.1" } }, - "node_modules/svrollbar": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/svrollbar/-/svrollbar-0.12.0.tgz", - "integrity": "sha512-okH0sz8bGtw+tgOfN1mpEtbveifxROcE3mbUMBJ1RQz8Q+1rVr+nVG7EAJ9b0G80cGDu7dskjAWuzj3iru0k5g==" - }, "node_modules/tailwindcss": { "version": "3.4.1", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.1.tgz", @@ -3030,6 +3116,58 @@ "node": ">=0.8" } }, + "node_modules/three": { + "version": "0.161.0", + "resolved": "https://registry.npmjs.org/three/-/three-0.161.0.tgz", + "integrity": "sha512-LC28VFtjbOyEu5b93K0bNRLw1rQlMJ85lilKsYj6dgTu+7i17W+JCCEbvrpmNHF1F3NAUqDSWq50UD7w9H2xQw==" + }, + "node_modules/three-mesh-bvh": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/three-mesh-bvh/-/three-mesh-bvh-0.7.3.tgz", + "integrity": "sha512-3W6KjzmupjfE89GuHPT31kxKWZ4YGZPEZJNysJpiOZfQRsBQQgmK7v/VJPpjG6syhAvTnY+5Fr77EvIkTLpGSw==", + "peerDependencies": { + "three": ">= 0.151.0" + } + }, + "node_modules/three-perf": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/three-perf/-/three-perf-1.0.10.tgz", + "integrity": "sha512-lCur/i8U6m0ysWYhQ1yFGWOZB0QA2oVsDsfynYd65HhXxLxJfiAt8OsXmpv9PnTLacfaZclBcZHUOB9QKk3eaw==", + "dependencies": { + "troika-three-text": "^0.47.2", + "tweakpane": "^3.1.10" + }, + "peerDependencies": { + "three": ">=0.151" + } + }, + "node_modules/three-perf/node_modules/troika-three-text": { + "version": "0.47.2", + "resolved": "https://registry.npmjs.org/troika-three-text/-/troika-three-text-0.47.2.tgz", + "integrity": "sha512-qylT0F+U7xGs+/PEf3ujBdJMYWbn0Qci0kLqI5BJG2kW1wdg4T1XSxneypnF05DxFqJhEzuaOR9S2SjiyknMng==", + "dependencies": { + "bidi-js": "^1.0.2", + "troika-three-utils": "^0.47.2", + "troika-worker-utils": "^0.47.2", + "webgl-sdf-generator": "1.1.1" + }, + "peerDependencies": { + "three": ">=0.125.0" + } + }, + "node_modules/three-perf/node_modules/troika-three-utils": { + "version": "0.47.2", + "resolved": "https://registry.npmjs.org/troika-three-utils/-/troika-three-utils-0.47.2.tgz", + "integrity": "sha512-/28plhCxfKtH7MSxEGx8e3b/OXU5A0xlwl+Sbdp0H8FXUHKZDoksduEKmjQayXYtxAyuUiCRunYIv/8Vi7aiyg==", + "peerDependencies": { + "three": ">=0.125.0" + } + }, + "node_modules/three-perf/node_modules/troika-worker-utils": { + "version": "0.47.2", + "resolved": "https://registry.npmjs.org/troika-worker-utils/-/troika-worker-utils-0.47.2.tgz", + "integrity": "sha512-mzss4MeyzUkYBppn4x5cdAqrhBHFEuVmMMgLMTyFV23x6GvQMyo+/R5E5Lsbrt7WSt5RfvewjcwD1DChRTA9lA==" + }, "node_modules/tiny-glob": { "version": "0.2.9", "resolved": "https://registry.npmjs.org/tiny-glob/-/tiny-glob-0.2.9.tgz", @@ -3063,6 +3201,33 @@ "node": ">=6" } }, + "node_modules/troika-three-text": { + "version": "0.49.0", + "resolved": "https://registry.npmjs.org/troika-three-text/-/troika-three-text-0.49.0.tgz", + "integrity": "sha512-sn9BNC6eIX8OO3iAkPwjecJ7Pn21Ve8P1UNFMNeQzXx759rrqS4i4pSZs7FLMYdWyCKVXBFGimBySFwRKLjq/Q==", + "dependencies": { + "bidi-js": "^1.0.2", + "troika-three-utils": "^0.49.0", + "troika-worker-utils": "^0.49.0", + "webgl-sdf-generator": "1.1.1" + }, + "peerDependencies": { + "three": ">=0.125.0" + } + }, + "node_modules/troika-three-utils": { + "version": "0.49.0", + "resolved": "https://registry.npmjs.org/troika-three-utils/-/troika-three-utils-0.49.0.tgz", + "integrity": "sha512-umitFL4cT+Fm/uONmaQEq4oZlyRHWwVClaS6ZrdcueRvwc2w+cpNQ47LlJKJswpqtMFWbEhOLy0TekmcPZOdYA==", + "peerDependencies": { + "three": ">=0.125.0" + } + }, + "node_modules/troika-worker-utils": { + "version": "0.49.0", + "resolved": "https://registry.npmjs.org/troika-worker-utils/-/troika-worker-utils-0.49.0.tgz", + "integrity": "sha512-1xZHoJrG0HFfCvT/iyN41DvI/nRykiBtHqFkGaGgJwq5iXfIZFBiPPEHFpPpgyKM3Oo5ITHXP5wM2TNQszYdVg==" + }, "node_modules/ts-interface-checker": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", @@ -3075,6 +3240,14 @@ "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", "dev": true }, + "node_modules/tweakpane": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/tweakpane/-/tweakpane-3.1.10.tgz", + "integrity": "sha512-rqwnl/pUa7+inhI2E9ayGTqqP0EPOOn/wVvSWjZsRbZUItzNShny7pzwL3hVlaN4m9t/aZhsP0aFQ9U5VVR2VQ==", + "funding": { + "url": "https://github.com/sponsors/cocopon" + } + }, "node_modules/typescript": { "version": "5.3.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", @@ -3193,6 +3366,11 @@ } } }, + "node_modules/webgl-sdf-generator": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/webgl-sdf-generator/-/webgl-sdf-generator-1.1.1.tgz", + "integrity": "sha512-9Z0JcMTFxeE+b2x1LJTdnaT8rT8aEp7MVxkNwoycNmJWwPdzoXzMh0BjJSh/AEFP+KPYZUli814h8bJZFIZ2jA==" + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/client/package.json b/client/package.json index 02b7cf9..634fbe2 100644 --- a/client/package.json +++ b/client/package.json @@ -17,6 +17,7 @@ "@tauri-apps/cli": "^1.5.10", "@tsconfig/svelte": "^5.0.2", "@types/howler": "^2.2.11", + "@types/three": "^0.161.2", "autoprefixer": "^10.4.17", "postcss": "^8.4.35", "svelte": "^4.2.11", @@ -28,12 +29,14 @@ }, "dependencies": { "@fontsource/roboto": "^5.0.8", + "@threlte/core": "^7.1.0", + "@threlte/extras": "^8.8.0", "howler": "^2.2.4", "material-icons": "^1.13.12", "material-symbols": "^0.15.0", "overlayscrollbars-svelte": "^0.5.3", "socket.io-client": "^4.7.4", "svelte-french-toast": "^1.2.0", - "svrollbar": "^0.12.0" + "three": "^0.161.0" } } diff --git a/client/public/static/app-icons/dev-tools.png b/client/public/static/app-icons/dev-tools.png new file mode 100644 index 0000000..7708218 Binary files /dev/null and b/client/public/static/app-icons/dev-tools.png differ diff --git a/client/src/App.svelte b/client/src/App.svelte index 78f8fd0..e534fa0 100644 --- a/client/src/App.svelte +++ b/client/src/App.svelte @@ -1,50 +1,51 @@
main { - font-family: "Roboto", sans-serif; + font-family: 'Roboto', sans-serif; } .infotainment-container { diff --git a/client/src/assets/models/license.txt b/client/src/assets/models/license.txt new file mode 100644 index 0000000..b48fe08 --- /dev/null +++ b/client/src/assets/models/license.txt @@ -0,0 +1,11 @@ +Model Information: +* title: Low Poly F/A-18 Hornet +* source: https://sketchfab.com/3d-models/low-poly-fa-18-hornet-9b48c88e91ba40fc8f518b616f44f714 +* author: cs09736 (https://sketchfab.com/cs09736) + +Model License: +* license type: CC-BY-SA-4.0 (http://creativecommons.org/licenses/by-sa/4.0/) +* requirements: Author must be credited. Modified versions must have the same license. Commercial use is allowed. + +If you use this 3D model in your project be sure to copy paste this credit wherever you share it: +This work is based on "Low Poly F/A-18 Hornet" (https://sketchfab.com/3d-models/low-poly-fa-18-hornet-9b48c88e91ba40fc8f518b616f44f714) by cs09736 (https://sketchfab.com/cs09736) licensed under CC-BY-SA-4.0 (http://creativecommons.org/licenses/by-sa/4.0/) \ No newline at end of file diff --git a/client/src/assets/models/scene.bin b/client/src/assets/models/scene.bin new file mode 100644 index 0000000..7e9d3e6 Binary files /dev/null and b/client/src/assets/models/scene.bin differ diff --git a/client/src/assets/models/scene.gltf b/client/src/assets/models/scene.gltf new file mode 100644 index 0000000..351145d --- /dev/null +++ b/client/src/assets/models/scene.gltf @@ -0,0 +1,754 @@ +{ + "accessors": [ + { + "bufferView": 2, + "componentType": 5126, + "count": 62, + "max": [ + 0.28601938486099243, + -0.03159094601869583, + 1.3521472215652466 + ], + "min": [ + -0.28601938486099243, + -0.3833470940589905, + -2.612701416015625 + ], + "type": "VEC3" + }, + { + "bufferView": 2, + "byteOffset": 744, + "componentType": 5126, + "count": 62, + "max": [ + 0.9978463053703308, + 0.9706209897994995, + 1.0 + ], + "min": [ + -0.9978463053703308, + -0.2406158298254013, + -1.0 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "componentType": 5126, + "count": 62, + "max": [ + 0.7755247354507446, + 1.0 + ], + "min": [ + 0.0, + 0.0 + ], + "type": "VEC2" + }, + { + "bufferView": 0, + "componentType": 5125, + "count": 90, + "type": "SCALAR" + }, + { + "bufferView": 2, + "byteOffset": 1488, + "componentType": 5126, + "count": 744, + "max": [ + 0.26639726758003235, + 0.11264356225728989, + -2.2082409858703613 + ], + "min": [ + -0.26639726758003235, + -0.13609451055526733, + -2.7548983097076416 + ], + "type": "VEC3" + }, + { + "bufferView": 2, + "byteOffset": 10416, + "componentType": 5126, + "count": 744, + "max": [ + 0.9659259915351868, + 0.9659258723258972, + 0.15793786942958832 + ], + "min": [ + -0.9659259915351868, + -0.9659259915351868, + -1.0 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 496, + "componentType": 5126, + "count": 744, + "max": [ + 0.48029109835624695, + 0.4802909791469574 + ], + "min": [ + 0.01970890536904335, + 0.019708896055817604 + ], + "type": "VEC2" + }, + { + "bufferView": 0, + "byteOffset": 360, + "componentType": 5125, + "count": 1356, + "type": "SCALAR" + }, + { + "bufferView": 2, + "byteOffset": 19344, + "componentType": 5126, + "count": 6762, + "max": [ + 1.8555290699005127, + 0.828994631767273, + 2.448202133178711 + ], + "min": [ + -1.8555290699005127, + -0.4696495532989502, + -2.894035577774048 + ], + "type": "VEC3" + }, + { + "bufferView": 2, + "byteOffset": 100488, + "componentType": 5126, + "count": 6762, + "max": [ + 1.0, + 0.9996607303619385, + 1.0 + ], + "min": [ + -1.0, + -1.0, + -1.0 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 6448, + "componentType": 5126, + "count": 6762, + "max": [ + 1.382123351097107, + 1.0000001192092896 + ], + "min": [ + 0.0, + -0.3820711672306061 + ], + "type": "VEC2" + }, + { + "bufferView": 0, + "byteOffset": 5784, + "componentType": 5125, + "count": 10506, + "type": "SCALAR" + }, + { + "bufferView": 2, + "byteOffset": 181632, + "componentType": 5126, + "count": 108, + "max": [ + 0.15389108657836914, + 0.40167421102523804, + 1.4429497718811035 + ], + "min": [ + -0.15389108657836914, + 0.1835811734199524, + 0.3292185962200165 + ], + "type": "VEC3" + }, + { + "bufferView": 2, + "byteOffset": 182928, + "componentType": 5126, + "count": 108, + "max": [ + 0.93450927734375, + 0.9493585228919983, + 0.32783323526382446 + ], + "min": [ + -0.93450927734375, + 0.34994035959243774, + -0.14981554448604584 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 60544, + "componentType": 5126, + "count": 108, + "max": [ + 0.75, + 0.7012776732444763 + ], + "min": [ + 0.0, + 0.0 + ], + "type": "VEC2" + }, + { + "bufferView": 0, + "byteOffset": 47808, + "componentType": 5125, + "count": 156, + "type": "SCALAR" + }, + { + "bufferView": 2, + "byteOffset": 184224, + "componentType": 5126, + "count": 458, + "max": [ + 0.25538086891174316, + 0.1017007827758789, + -2.4583163261413574 + ], + "min": [ + -0.25538086891174316, + -0.1249837875366211, + -3.5668210983276367 + ], + "type": "VEC3" + }, + { + "bufferView": 2, + "byteOffset": 189720, + "componentType": 5126, + "count": 458, + "max": [ + 0.9996814727783203, + 0.9996814727783203, + 0.6308417916297913 + ], + "min": [ + -0.9996814727783203, + -0.9996814727783203, + -0.6931174397468567 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 61408, + "componentType": 5126, + "count": 458, + "max": [ + 0.9998925924301147, + 0.6052115559577942 + ], + "min": [ + 0.0001073777093552053, + 0.0001073777093552053 + ], + "type": "VEC2" + }, + { + "bufferView": 0, + "byteOffset": 48432, + "componentType": 5125, + "count": 1896, + "type": "SCALAR" + }, + { + "bufferView": 2, + "byteOffset": 195216, + "componentType": 5126, + "count": 1993, + "max": [ + 0.457694411277771, + 0.019436508417129517, + 0.9355756044387817 + ], + "min": [ + -0.457694411277771, + -0.5843615531921387, + -1.0792063474655151 + ], + "type": "VEC3" + }, + { + "bufferView": 2, + "byteOffset": 219132, + "componentType": 5126, + "count": 1993, + "max": [ + 1.0, + 1.0, + 1.0 + ], + "min": [ + -1.0, + -1.0, + -1.0 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 65072, + "componentType": 5126, + "count": 1993, + "max": [ + 1.0, + 1.0 + ], + "min": [ + 0.0, + 0.0 + ], + "type": "VEC2" + }, + { + "bufferView": 0, + "byteOffset": 56016, + "componentType": 5125, + "count": 5028, + "type": "SCALAR" + }, + { + "bufferView": 2, + "byteOffset": 243048, + "componentType": 5126, + "count": 832, + "max": [ + 0.457694411277771, + -0.341027170419693, + 0.7706074714660645 + ], + "min": [ + -0.457694411277771, + -0.6231322884559631, + -1.1222631931304932 + ], + "type": "VEC3" + }, + { + "bufferView": 2, + "byteOffset": 253032, + "componentType": 5126, + "count": 832, + "max": [ + 1.0, + 0.9659259915351868, + 0.9659262299537659 + ], + "min": [ + -1.0, + -0.965925931930542, + -0.9659261107444763 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 81016, + "componentType": 5126, + "count": 832, + "max": [ + 1.0, + 1.0 + ], + "min": [ + 7.450580596923828e-08, + 0.5 + ], + "type": "VEC2" + }, + { + "bufferView": 0, + "byteOffset": 76128, + "componentType": 5125, + "count": 1440, + "type": "SCALAR" + } + ], + "asset": { + "extras": { + "author": "cs09736 (https://sketchfab.com/cs09736)", + "license": "CC-BY-SA-4.0 (http://creativecommons.org/licenses/by-sa/4.0/)", + "source": "https://sketchfab.com/3d-models/low-poly-fa-18-hornet-9b48c88e91ba40fc8f518b616f44f714", + "title": "Low Poly F/A-18 Hornet" + }, + "generator": "Sketchfab-12.68.0", + "version": "2.0" + }, + "bufferViews": [ + { + "buffer": 0, + "byteLength": 81888, + "name": "floatBufferViews", + "target": 34963 + }, + { + "buffer": 0, + "byteLength": 87672, + "byteOffset": 81888, + "byteStride": 8, + "name": "floatBufferViews", + "target": 34962 + }, + { + "buffer": 0, + "byteLength": 263016, + "byteOffset": 169560, + "byteStride": 12, + "name": "floatBufferViews", + "target": 34962 + } + ], + "buffers": [ + { + "byteLength": 432576, + "uri": "scene.bin" + } + ], + "materials": [ + { + "doubleSided": true, + "name": "clay", + "pbrMetallicRoughness": { + "baseColorFactor": [ + 0.8, + 0.8, + 0.8, + 1.0 + ], + "metallicFactor": 0.0, + "roughnessFactor": 0.4 + } + }, + { + "doubleSided": true, + "emissiveFactor": [ + 0.214041, + 0.000464395, + 0.0 + ], + "name": "engine_inside", + "pbrMetallicRoughness": { + "baseColorFactor": [ + 0.8, + 0.212337, + 0.0981736, + 1.0 + ], + "roughnessFactor": 0.742424 + } + }, + { + "doubleSided": true, + "name": "body_paint", + "pbrMetallicRoughness": { + "baseColorFactor": [ + 0.465996, + 0.488985, + 0.522522, + 1.0 + ], + "metallicFactor": 0.1, + "roughnessFactor": 0.5 + } + }, + { + "doubleSided": true, + "name": "glass", + "pbrMetallicRoughness": { + "baseColorFactor": [ + 0.360226, + 0.603827, + 0.476213, + 1.0 + ], + "metallicFactor": 0.7, + "roughnessFactor": 0.0 + } + }, + { + "doubleSided": true, + "emissiveFactor": [ + 1.0, + 0.0571504, + 0.00509488 + ], + "name": "after_burner", + "pbrMetallicRoughness": { + "baseColorFactor": [ + 0.4407909999999996, + 0.17297200000000015, + 0.08980889999999998, + 1.0 + ], + "metallicFactor": 0.0, + "roughnessFactor": 0.5 + } + }, + { + "doubleSided": true, + "name": "strut", + "pbrMetallicRoughness": { + "baseColorFactor": [ + 0.616618, + 0.6473, + 0.692071, + 1.0 + ], + "metallicFactor": 0.1, + "roughnessFactor": 0.5 + } + }, + { + "doubleSided": true, + "name": "tyre", + "pbrMetallicRoughness": { + "baseColorFactor": [ + 0.00424211, + 0.00424211, + 0.00424211, + 1.0 + ], + "metallicFactor": 0.0, + "roughnessFactor": 0.6 + } + } + ], + "meshes": [ + { + "name": "Object_0", + "primitives": [ + { + "attributes": { + "NORMAL": 1, + "POSITION": 0, + "TEXCOORD_0": 2 + }, + "indices": 3, + "material": 0, + "mode": 4 + } + ] + }, + { + "name": "Object_1", + "primitives": [ + { + "attributes": { + "NORMAL": 5, + "POSITION": 4, + "TEXCOORD_0": 6 + }, + "indices": 7, + "material": 1, + "mode": 4 + } + ] + }, + { + "name": "Object_2", + "primitives": [ + { + "attributes": { + "NORMAL": 9, + "POSITION": 8, + "TEXCOORD_0": 10 + }, + "indices": 11, + "material": 2, + "mode": 4 + } + ] + }, + { + "name": "Object_3", + "primitives": [ + { + "attributes": { + "NORMAL": 13, + "POSITION": 12, + "TEXCOORD_0": 14 + }, + "indices": 15, + "material": 3, + "mode": 4 + } + ] + }, + { + "name": "Object_4", + "primitives": [ + { + "attributes": { + "NORMAL": 17, + "POSITION": 16, + "TEXCOORD_0": 18 + }, + "indices": 19, + "material": 4, + "mode": 4 + } + ] + }, + { + "name": "Object_5", + "primitives": [ + { + "attributes": { + "NORMAL": 21, + "POSITION": 20, + "TEXCOORD_0": 22 + }, + "indices": 23, + "material": 5, + "mode": 4 + } + ] + }, + { + "name": "Object_6", + "primitives": [ + { + "attributes": { + "NORMAL": 25, + "POSITION": 24, + "TEXCOORD_0": 26 + }, + "indices": 27, + "material": 6, + "mode": 4 + } + ] + } + ], + "nodes": [ + { + "children": [ + 1 + ], + "matrix": [ + 2.0, + 0.0, + 0.0, + 0.0, + 0.0, + 4.440892098500626e-16, + -2.0, + 0.0, + 0.0, + 2.0, + 4.440892098500626e-16, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0 + ], + "name": "Sketchfab_model" + }, + { + "children": [ + 2 + ], + "name": "root" + }, + { + "children": [ + 3 + ], + "matrix": [ + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 2.220446049250313e-16, + 1.0, + 0.0, + 0.0, + -1.0, + 2.220446049250313e-16, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0 + ], + "name": "GLTF_SceneRootNode" + }, + { + "children": [ + 4, + 5, + 6, + 7, + 8, + 9, + 10 + ], + "name": "aircraft_0" + }, + { + "mesh": 0, + "name": "Object_4" + }, + { + "mesh": 1, + "name": "Object_5" + }, + { + "mesh": 2, + "name": "Object_6" + }, + { + "mesh": 3, + "name": "Object_7" + }, + { + "mesh": 4, + "name": "Object_8" + }, + { + "mesh": 5, + "name": "Object_9" + }, + { + "mesh": 6, + "name": "Object_10" + } + ], + "scene": 0, + "scenes": [ + { + "name": "Sketchfab_Scene", + "nodes": [ + 0 + ] + } + ] +} diff --git a/client/src/lib/Apps/DevTools/DevTools.svelte b/client/src/lib/Apps/DevTools/DevTools.svelte new file mode 100644 index 0000000..1f7ff99 --- /dev/null +++ b/client/src/lib/Apps/DevTools/DevTools.svelte @@ -0,0 +1,26 @@ + + + +

+ Developer Tools +

+

+ This is an app where developers can add buttons that trigger code to help + during development. +

+

+ Make sure the entry for this app is commented out in appList.ts before + building for production. +

+ +
+ + diff --git a/client/src/lib/Apps/appList.ts b/client/src/lib/Apps/appList.ts index ba45208..020a7c4 100644 --- a/client/src/lib/Apps/appList.ts +++ b/client/src/lib/Apps/appList.ts @@ -9,6 +9,7 @@ const resolveIconPath = (slug: keyof typeof appList, format: Format) => { import GBAEmulator from './GBAEmulator/GBAEmulator.svelte' import JsDoom from './JSDoom/JSDoom.svelte' +import DevTools from './DevTools/DevTools.svelte' interface AppList { [key: string]: { @@ -50,4 +51,10 @@ export const appList: AppList = { icon: resolveIconPath('settings', 'png'), external: false, }, + 'dev-tools': { + name: 'DevTools', + component: DevTools, + icon: resolveIconPath('dev-tools', 'png'), + external: false, + }, } diff --git a/client/src/lib/Dashboard/Dashboard.svelte b/client/src/lib/Dashboard/Dashboard.svelte index 56281eb..861e7a3 100644 --- a/client/src/lib/Dashboard/Dashboard.svelte +++ b/client/src/lib/Dashboard/Dashboard.svelte @@ -15,6 +15,7 @@ import Compass from './Compass.svelte' import { telemetryReadonlyStore } from '../stores/telemetryStore' import Bottom from './Bottom.svelte' + import Visualization from './Visualization/Visualization.svelte' $: speedResolved = Math.hypot( $telemetryReadonlyStore['chassis-x-speed'], @@ -36,6 +37,10 @@ +
+ +
+
+ import { createEventDispatcher, onDestroy } from 'svelte' + import { Camera, Vector2, Vector3, Quaternion } from 'three' + import { useThrelte, useParent, useTask } from '@threlte/core' + + export let object: Camera + export let rotateSpeed = 1.0 + + $: if (object) { + // console.log(object) + // object.position.y = 10 + // // Calculate the direction vector towards (0, 0, 0) + // const target = new Vector3(0, 0, 0) + // const direction = target.clone().sub(object.position).normalize() + // // Extract the forward direction from the object's current rotation matrix + // const currentDirection = new Vector3(0, 1, 0) + // currentDirection.applyQuaternion(object.quaternion) + // // Calculate the axis and angle to rotate the object + // const rotationAxis = currentDirection.clone().cross(direction).normalize() + // const rotationAngle = Math.acos(currentDirection.dot(direction)) + // // Rotate the object using rotateOnAxis() + // object.rotateOnAxis(rotationAxis, rotationAngle) + } + + export let idealOffset = { x: -0.5, y: 2, z: -3 } + export let idealLookAt = { x: 0, y: 1, z: 5 } + + const currentPosition = new Vector3() + const currentLookAt = new Vector3() + + let isOrbiting = false + let pointerDown = false + + const rotateStart = new Vector2() + const rotateEnd = new Vector2() + const rotateDelta = new Vector2() + + const axis = new Vector3(0, 1, 0) + const rotationQuat = new Quaternion() + + const { renderer, invalidate } = useThrelte() + + const domElement = renderer.domElement + const camera = useParent() + + const dispatch = createEventDispatcher() + + const isCamera = (p: any): p is Camera => { + return p.isCamera + } + + if (!isCamera($camera)) { + throw new Error( + 'Parent missing: need to be a child of a ' + ) + } + + // domElement.addEventListener('pointerdown', onPointerDown) + // domElement.addEventListener('pointermove', onPointerMove) + // domElement.addEventListener('pointerleave', onPointerLeave) + // domElement.addEventListener('pointerup', onPointerUp) + + onDestroy(() => { + // domElement.removeEventListener('pointerdown', onPointerDown) + // domElement.removeEventListener('pointermove', onPointerMove) + // domElement.removeEventListener('pointerleave', onPointerLeave) + // domElement.removeEventListener('pointerup', onPointerUp) + }) + + // This is basically your update function + useTask(delta => { + // the object's position is bound to the prop + if (!object) return + + // camera is based on character so we rotation character first + // rotationQuat.setFromAxisAngle(axis, -rotateDelta.x * rotateSpeed * delta) + // object.quaternion.multiply(rotationQuat) + + // then we calculate our ideal's + const offset = vectorFromObject(idealOffset) + const lookAt = vectorFromObject(idealLookAt) + + // and how far we should move towards them + const t = 1.0 - Math.pow(0.001, delta) + currentPosition.lerp(offset, t) + currentLookAt.lerp(lookAt, t) + + // then finally set the camera, a bit behind the model + $camera!.position.copy(currentPosition) + const behindOffset = currentPosition + .clone() + .normalize() + .multiplyScalar(8) + .setY(0.5) + + $camera!.position.copy(currentPosition).add(behindOffset) + + $camera!.lookAt(currentLookAt) + }) + + function onPointerMove(event: PointerEvent) { + const { x, y } = event + if (pointerDown && !isOrbiting) { + // calculate distance from init down + const distCheck = + Math.sqrt( + Math.pow(x - rotateStart.x, 2) + Math.pow(y - rotateStart.y, 2) + ) > 10 + if (distCheck) { + isOrbiting = true + } + } + if (!isOrbiting) return + + rotateEnd.set(x, y) + rotateDelta.subVectors(rotateEnd, rotateStart).multiplyScalar(rotateSpeed) + rotateStart.copy(rotateEnd) + + invalidate() + dispatch('change') + } + + function onPointerDown(event: PointerEvent) { + const { x, y } = event + rotateStart.set(x, y) + pointerDown = true + } + + function onPointerUp() { + rotateDelta.set(0, 0) + pointerDown = false + isOrbiting = false + } + + function onPointerLeave() { + rotateDelta.set(0, 0) + pointerDown = false + isOrbiting = false + } + + function vectorFromObject(vec: { x: number; y: number; z: number }) { + const { x, y, z } = vec + const ideal = new Vector3(x, y, z) + ideal.applyQuaternion(object.quaternion) + ideal.add( + new Vector3(object.position.x, object.position.y, object.position.z) + ) + return ideal + } + + function onKeyDown(event: KeyboardEvent) { + switch (event.key) { + case 'a': + rotateDelta.x = -2 * rotateSpeed + break + case 'd': + rotateDelta.x = 2 * rotateSpeed + break + default: + break + } + } + + function onKeyUp(event: KeyboardEvent) { + switch (event.key) { + case 'a': + rotateDelta.x = 0 + break + case 'd': + rotateDelta.x = 0 + break + default: + break + } + } + + + diff --git a/client/src/lib/Dashboard/Visualization/Scene.svelte b/client/src/lib/Dashboard/Visualization/Scene.svelte new file mode 100644 index 0000000..5f2d9ae --- /dev/null +++ b/client/src/lib/Dashboard/Visualization/Scene.svelte @@ -0,0 +1,141 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/client/src/lib/Dashboard/Visualization/Visualization.svelte b/client/src/lib/Dashboard/Visualization/Visualization.svelte new file mode 100644 index 0000000..4ae23fe --- /dev/null +++ b/client/src/lib/Dashboard/Visualization/Visualization.svelte @@ -0,0 +1,8 @@ + + + + + diff --git a/client/src/lib/Dashboard/Visualization/models/Hornet.svelte b/client/src/lib/Dashboard/Visualization/models/Hornet.svelte new file mode 100644 index 0000000..9e04738 --- /dev/null +++ b/client/src/lib/Dashboard/Visualization/models/Hornet.svelte @@ -0,0 +1,95 @@ + + + + + + {#await gltf} + + {:then gltf} + + + + + + + + + + + + {:catch error} + + {/await} + + + diff --git a/client/src/lib/Dashboard/Visualization/utils.ts b/client/src/lib/Dashboard/Visualization/utils.ts new file mode 100644 index 0000000..e69de29