diff --git a/TODO.md b/TODO.md index 788658364..36f72a50a 100644 --- a/TODO.md +++ b/TODO.md @@ -28,12 +28,12 @@ A step-by-step checklist for porting MatterControl's design features to a Vue + ## Phase 2: 3D Viewport Foundation ### three.js Setup -- [ ] Install three.js and `@types/three` -- [ ] Create `Viewport.vue` component with canvas element -- [ ] Initialize three.js Scene, Camera, and Renderer -- [ ] Add resize observer for responsive canvas -- [ ] Implement render loop with `requestAnimationFrame` -- [ ] Add basic lighting (ambient + two directional) +- [x] Install three.js and `@types/three` +- [x] Create `Viewport.vue` component with canvas element +- [x] Initialize three.js Scene, Camera, and Renderer +- [x] Add resize observer for responsive canvas +- [x] Implement render loop with `requestAnimationFrame` +- [x] Add basic lighting (ambient + two directional) ### Scene Helpers - [ ] Add ground grid helper (XZ plane) diff --git a/package.json b/package.json index 6ec062bd4..ff5a98df9 100644 --- a/package.json +++ b/package.json @@ -14,11 +14,13 @@ }, "dependencies": { "pinia": "^3.0.4", + "three": "^0.182.0", "vue": "^3.5.24" }, "devDependencies": { "@eslint/js": "^9.39.2", "@types/node": "^24.10.1", + "@types/three": "^0.182.0", "@vitejs/plugin-vue": "^6.0.1", "@vue/test-utils": "^2.4.6", "@vue/tsconfig": "^0.8.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 18ccbe4df..352e58c31 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,6 +11,9 @@ importers: pinia: specifier: ^3.0.4 version: 3.0.4(typescript@5.9.3)(vue@3.5.27(typescript@5.9.3)) + three: + specifier: ^0.182.0 + version: 0.182.0 vue: specifier: ^3.5.24 version: 3.5.27(typescript@5.9.3) @@ -21,6 +24,9 @@ importers: '@types/node': specifier: ^24.10.1 version: 24.10.9 + '@types/three': + specifier: ^0.182.0 + version: 0.182.0 '@vitejs/plugin-vue': specifier: ^6.0.1 version: 6.0.3(vite@7.3.1(@types/node@24.10.9))(vue@3.5.27(typescript@5.9.3)) @@ -80,6 +86,9 @@ packages: resolution: {integrity: sha512-0ZrskXVEHSWIqZM/sQZ4EV3jZJXRkio/WCxaqKZP1g//CEWEPSfeZFcms4XeKBCHU0ZKnIkdJeU/kF+eRp5lBg==} engines: {node: '>=6.9.0'} + '@dimforge/rapier3d-compat@0.12.0': + resolution: {integrity: sha512-uekIGetywIgopfD97oDL5PfeezkFpNhwlzlaEYNOA0N6ghdsOvh/HYjSMek5Q2O1PYvRSDFcqFVJl4r4ZBwOow==} + '@esbuild/aix-ppc64@0.27.2': resolution: {integrity: sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==} engines: {node: '>=18'} @@ -435,6 +444,9 @@ packages: '@standard-schema/spec@1.1.0': resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} + '@tweenjs/tween.js@23.1.3': + resolution: {integrity: sha512-vJmvvwFxYuGnF2axRtPYocag6Clbb5YS7kLL+SO/TeVFzHqDIWrNKYtcsPMibjDx9O+bu+psAy9NKfWklassUA==} + '@types/chai@5.2.3': resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} @@ -450,6 +462,15 @@ packages: '@types/node@24.10.9': resolution: {integrity: sha512-ne4A0IpG3+2ETuREInjPNhUGis1SFjv1d5asp8MzEAGtOZeTeHVDOYqOgqfhvseqg/iXty2hjBf1zAOb7RNiNw==} + '@types/stats.js@0.17.4': + resolution: {integrity: sha512-jIBvWWShCvlBqBNIZt0KAshWpvSjhkwkEu4ZUcASoAvhmrgAUI2t1dXrjSL4xXVLB4FznPrIsX3nKXFl/Dt4vA==} + + '@types/three@0.182.0': + resolution: {integrity: sha512-WByN9V3Sbwbe2OkWuSGyoqQO8Du6yhYaXtXLoA5FkKTUJorZ+yOHBZ35zUUPQXlAKABZmbYp5oAqpA4RBjtJ/Q==} + + '@types/webxr@0.5.24': + resolution: {integrity: sha512-h8fgEd/DpoS9CBrjEQXR+dIDraopAEfu4wYVNY2tEPwk60stPWhvZMf4Foo5FakuQ7HFZoa8WceaWFervK2Ovg==} + '@types/whatwg-mimetype@3.0.2': resolution: {integrity: sha512-c2AKvDT8ToxLIOUlN51gTiHXflsfIFisS4pO7pDPoKouJCESkhZnEy623gwP9laCy5lnLDAw1vAzu2vM2YLOrA==} @@ -615,6 +636,9 @@ packages: vue: optional: true + '@webgpu/types@0.1.69': + resolution: {integrity: sha512-RPmm6kgRbI8e98zSD3RVACvnuktIja5+yLgDAkTmxLr90BEwdTXRQWNLF3ETTTyH/8mKhznZuN5AveXYFEsMGQ==} + abbrev@2.0.0: resolution: {integrity: sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} @@ -854,6 +878,9 @@ packages: picomatch: optional: true + fflate@0.8.2: + resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==} + file-entry-cache@8.0.0: resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} engines: {node: '>=16.0.0'} @@ -984,6 +1011,9 @@ packages: magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + meshoptimizer@0.22.0: + resolution: {integrity: sha512-IebiK79sqIy+E4EgOr+CAw+Ke8hAspXKzBd0JdgEmPHiAwmvEj2S4h1rfvo+o/BnfEYd/jAOg5IeeIjzlzSnDg==} + minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} @@ -1181,6 +1211,9 @@ packages: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} + three@0.182.0: + resolution: {integrity: sha512-GbHabT+Irv+ihI1/f5kIIsZ+Ef9Sl5A1Y7imvS5RQjWgtTPfPnZ43JmlYI7NtCRDK9zir20lQpfg8/9Yd02OvQ==} + tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} @@ -1388,6 +1421,8 @@ snapshots: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.28.5 + '@dimforge/rapier3d-compat@0.12.0': {} + '@esbuild/aix-ppc64@0.27.2': optional: true @@ -1618,6 +1653,8 @@ snapshots: '@standard-schema/spec@1.1.0': {} + '@tweenjs/tween.js@23.1.3': {} + '@types/chai@5.2.3': dependencies: '@types/deep-eql': 4.0.2 @@ -1633,6 +1670,20 @@ snapshots: dependencies: undici-types: 7.16.0 + '@types/stats.js@0.17.4': {} + + '@types/three@0.182.0': + dependencies: + '@dimforge/rapier3d-compat': 0.12.0 + '@tweenjs/tween.js': 23.1.3 + '@types/stats.js': 0.17.4 + '@types/webxr': 0.5.24 + '@webgpu/types': 0.1.69 + fflate: 0.8.2 + meshoptimizer: 0.22.0 + + '@types/webxr@0.5.24': {} + '@types/whatwg-mimetype@3.0.2': {} '@types/ws@8.18.1': @@ -1879,6 +1930,8 @@ snapshots: typescript: 5.9.3 vue: 3.5.27(typescript@5.9.3) + '@webgpu/types@0.1.69': {} + abbrev@2.0.0: {} acorn-jsx@5.3.2(acorn@8.15.0): @@ -2120,6 +2173,8 @@ snapshots: optionalDependencies: picomatch: 4.0.3 + fflate@0.8.2: {} + file-entry-cache@8.0.0: dependencies: flat-cache: 4.0.1 @@ -2247,6 +2302,8 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 + meshoptimizer@0.22.0: {} + minimatch@3.1.2: dependencies: brace-expansion: 1.1.12 @@ -2434,6 +2491,8 @@ snapshots: dependencies: has-flag: 4.0.0 + three@0.182.0: {} + tinybench@2.9.0: {} tinyexec@1.0.2: {} diff --git a/src/App.vue b/src/App.vue index 58b0f21b1..3b51102f8 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,30 +1,16 @@ diff --git a/src/components/Viewport.vue b/src/components/Viewport.vue new file mode 100644 index 000000000..a82f01c5e --- /dev/null +++ b/src/components/Viewport.vue @@ -0,0 +1,124 @@ + + + + + diff --git a/src/style.css b/src/style.css index f69131543..28e960fc6 100644 --- a/src/style.css +++ b/src/style.css @@ -24,10 +24,7 @@ a:hover { body { margin: 0; - display: flex; - place-items: center; - min-width: 320px; - min-height: 100vh; + overflow: hidden; } h1 { @@ -59,10 +56,8 @@ button:focus-visible { } #app { - max-width: 1280px; - margin: 0 auto; - padding: 2rem; - text-align: center; + width: 100%; + height: 100vh; } @media (prefers-color-scheme: light) { diff --git a/vite.config.ts b/vite.config.ts index 033620ec7..416e6a81a 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,5 +1,4 @@ -/// -import { defineConfig } from 'vite' +import { defineConfig } from 'vitest/config' import vue from '@vitejs/plugin-vue' // https://vite.dev/config/