From 97d4590da477ff1f3d11fab2f798d67aea9759da Mon Sep 17 00:00:00 2001 From: Youwen Wu Date: Mon, 26 Feb 2024 14:24:02 -0800 Subject: [PATCH] feat: new camera control and scene implemented --- client/package-lock.json | 152 +++++++++++++++++- client/package.json | 2 + .../CameraControls/CameraControls.svelte | 106 ++++++++++++ .../CameraControls/CameraControls.svelte.d.ts | 16 ++ .../Visualization/CameraControls/Scene.svelte | 39 +++++ .../CameraControls/utils/cameraStore.ts | 6 + .../utils/useControlsContext.ts | 20 +++ .../Visualization/Visualization.svelte | 43 ++++- 8 files changed, 380 insertions(+), 4 deletions(-) create mode 100644 client/src/lib/Dashboard/Visualization/CameraControls/CameraControls.svelte create mode 100644 client/src/lib/Dashboard/Visualization/CameraControls/CameraControls.svelte.d.ts create mode 100644 client/src/lib/Dashboard/Visualization/CameraControls/Scene.svelte create mode 100644 client/src/lib/Dashboard/Visualization/CameraControls/utils/cameraStore.ts create mode 100644 client/src/lib/Dashboard/Visualization/CameraControls/utils/useControlsContext.ts diff --git a/client/package-lock.json b/client/package-lock.json index c3afd7d..218f778 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -11,12 +11,14 @@ "@fontsource/roboto": "^5.0.8", "@threlte/core": "^7.1.0", "@threlte/extras": "^8.8.0", + "camera-controls": "^2.8.3", "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", + "svelte-tweakpane-ui": "^1.2.1", "three": "^0.161.0" }, "devDependencies": { @@ -1035,6 +1037,11 @@ "integrity": "sha512-BRbo1fOtyVbhfLyuCWw6wAWp+U8UQle+ZXu84MYYWzYSEB28dyfnRBIE99eoG+qdAC0po6L2ScIEivcT07UaMA==", "dev": true }, + "node_modules/@tweakpane/core": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@tweakpane/core/-/core-2.0.3.tgz", + "integrity": "sha512-qHci4XA1Wngpwy8IzsLh5JEdscz8aDti/9YhyOaq01si+cgNDaZfwzTtXdn1+xTxSnCM+pW4Zb2/4eqn+K1ATw==" + }, "node_modules/@types/cookie": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", @@ -1300,6 +1307,14 @@ "node": ">= 6" } }, + "node_modules/camera-controls": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/camera-controls/-/camera-controls-2.8.3.tgz", + "integrity": "sha512-zFjqUR6onLkG+z1A6vAWfzovxZxWVSvp6e5t3lfZgfgPZtX3n74aykNAUaoRbq8Y3tOxadHkDjbfGDOP9hFf2w==", + "peerDependencies": { + "three": ">=0.126.1" + } + }, "node_modules/caniuse-lite": { "version": "1.0.30001588", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001588.tgz", @@ -1592,9 +1607,7 @@ "node_modules/esm-env": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.0.0.tgz", - "integrity": "sha512-Cf6VksWPsTuW01vU9Mk/3vRue91Zevka5SjyNf3nEpokFRuqt/KjUQoGAwq9qMmhpLTHmXzSIrFRw8zxWzmFBA==", - "dev": true, - "peer": true + "integrity": "sha512-Cf6VksWPsTuW01vU9Mk/3vRue91Zevka5SjyNf3nEpokFRuqt/KjUQoGAwq9qMmhpLTHmXzSIrFRw8zxWzmFBA==" }, "node_modules/estree-walker": { "version": "3.0.3", @@ -1604,6 +1617,19 @@ "@types/estree": "^1.0.0" } }, + "node_modules/fast-copy": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/fast-copy/-/fast-copy-3.0.1.tgz", + "integrity": "sha512-Knr7NOtK3HWRYGtHoJrjkaWepqT8thIVGAwt0p0aUs1zqkAzXZV4vo9fFNwyb5fcqK1GKYFYxldQdIDVKhUAfA==" + }, + "node_modules/fast-equals": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.0.1.tgz", + "integrity": "sha512-WF1Wi8PwwSY7/6Kx0vKXtw8RwuSGoM1bvDaJbu7MxDlR1vovZjIAKrnzyrThgAjm6JDTu0fVgWXDlMGspodfoQ==", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/fast-glob": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", @@ -2972,6 +2998,17 @@ "svelte": "^3.19.0 || ^4.0.0" } }, + "node_modules/svelte-local-storage-store": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/svelte-local-storage-store/-/svelte-local-storage-store-0.6.4.tgz", + "integrity": "sha512-45WoY2vSGPQM1sIQJ9jTkPPj20hYeqm+af6mUGRFSPP5WglZf36YYoZqwmZZ8Dt/2SU8lem+BTA8/Z/8TkqNLg==", + "engines": { + "node": ">=0.14" + }, + "peerDependencies": { + "svelte": "^3.48.0 || >4.0.0" + } + }, "node_modules/svelte-preprocess": { "version": "5.1.3", "resolved": "https://registry.npmjs.org/svelte-preprocess/-/svelte-preprocess-5.1.3.tgz", @@ -3035,6 +3072,115 @@ } } }, + "node_modules/svelte-tweakpane-ui": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/svelte-tweakpane-ui/-/svelte-tweakpane-ui-1.2.1.tgz", + "integrity": "sha512-F62AFvZiqhXa0E3HMpUdmdWWyus/C+nTBFrFd/vEhHP6dYOQ4EN9qwvjdybL90DZFTIwkZe3w4oSbEGn1muKkQ==", + "dependencies": { + "@0b5vr/tweakpane-plugin-profiler": "^0.4.1", + "@0b5vr/tweakpane-plugin-rotation": "^0.2.0", + "@kitschpatrol/tweakpane-image-plugin": "^2.0.0", + "@pangenerator/tweakpane-textarea-plugin": "^2.0.0", + "@tweakpane/core": "^2.0.3", + "@tweakpane/plugin-camerakit": "^0.3.0", + "@tweakpane/plugin-essentials": "^0.2.1", + "esm-env": "^1.0.0", + "fast-copy": "^3.0.1", + "fast-equals": "^5.0.1", + "nanoid": "^5.0.6", + "svelte-local-storage-store": "^0.6.4", + "tweakpane": "^4.0.3", + "tweakpane-plugin-waveform": "^1.0.0" + }, + "engines": { + "node": ">=18.0.0", + "pnpm": ">=8.0.0" + }, + "peerDependencies": { + "svelte": "^4.0.0" + } + }, + "node_modules/svelte-tweakpane-ui/node_modules/@0b5vr/tweakpane-plugin-profiler": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@0b5vr/tweakpane-plugin-profiler/-/tweakpane-plugin-profiler-0.4.1.tgz", + "integrity": "sha512-jgkPbT24eQ7isj8F7/IsbdqrwvBoWBmwjqxdP35smD2D6xsx+9viR57SKBxi9PxTZDEayicmCzBk++0PTqRnBg==", + "peerDependencies": { + "tweakpane": "^4.0.0" + } + }, + "node_modules/svelte-tweakpane-ui/node_modules/@0b5vr/tweakpane-plugin-rotation": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@0b5vr/tweakpane-plugin-rotation/-/tweakpane-plugin-rotation-0.2.0.tgz", + "integrity": "sha512-LK+84kNTusEepVwiKH6ib/Pd+5RxI3UC4rHxn5c14GO58QS49Hh0ft3hFXt/NDzYEST17Q9qg96BcpclhCzYYQ==", + "peerDependencies": { + "tweakpane": "^4.0.0" + } + }, + "node_modules/svelte-tweakpane-ui/node_modules/@kitschpatrol/tweakpane-image-plugin": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@kitschpatrol/tweakpane-image-plugin/-/tweakpane-image-plugin-2.0.0.tgz", + "integrity": "sha512-BzEZqIhD/dM7AW0Ebv+309L4k8ZZJ5fC9Zks4sozVK3FwJooviE6JzaFAuB7k0M5oX45Wyn59tQXdHafgsP3YA==", + "peerDependencies": { + "tweakpane": "^4.0.0" + } + }, + "node_modules/svelte-tweakpane-ui/node_modules/@pangenerator/tweakpane-textarea-plugin": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@pangenerator/tweakpane-textarea-plugin/-/tweakpane-textarea-plugin-2.0.0.tgz", + "integrity": "sha512-BERPuuyJYWvtJzXh4wtgYspza0ihigE2m4qs57ERKtWG59+lI2t/2TOXlwz7Xyx/QEIH25uO1g732YCljgKaUw==", + "peerDependencies": { + "tweakpane": "^4.0.0" + } + }, + "node_modules/svelte-tweakpane-ui/node_modules/@tweakpane/plugin-camerakit": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@tweakpane/plugin-camerakit/-/plugin-camerakit-0.3.0.tgz", + "integrity": "sha512-6UwgwDKU+oaAgXJ2D/pOoIpEAZts0RyeLmVzBJGs+VVNqSfkiHzL0i5XD+XnmSL2PaLXBne0dlz0bYOrjmeELw==", + "peerDependencies": { + "tweakpane": "^4.0.0-beta.2" + } + }, + "node_modules/svelte-tweakpane-ui/node_modules/@tweakpane/plugin-essentials": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@tweakpane/plugin-essentials/-/plugin-essentials-0.2.1.tgz", + "integrity": "sha512-VbFU1/uD+CJNFQdfLXUOLjeG5HyUZH97Ox9CxmyVetg1hqjVun3C83HAGFULyhKzl8tSgii8jr304r8QpdHwzQ==", + "peerDependencies": { + "tweakpane": "^4.0.0" + } + }, + "node_modules/svelte-tweakpane-ui/node_modules/nanoid": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.0.6.tgz", + "integrity": "sha512-rRq0eMHoGZxlvaFOUdK1Ev83Bd1IgzzR+WJ3IbDJ7QOSdAxYjlurSPqFs9s4lJg29RT6nPwizFtJhQS6V5xgiA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.js" + }, + "engines": { + "node": "^18 || >=20" + } + }, + "node_modules/svelte-tweakpane-ui/node_modules/tweakpane": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/tweakpane/-/tweakpane-4.0.3.tgz", + "integrity": "sha512-BlcWOAe8oe4c+k9pmLBARGdWB6MVZMszayekkixQXTgkxTaYoTUpHpwVEp+3HkoamZkomodpbBf0CkguIHTgLg==", + "funding": { + "url": "https://github.com/sponsors/cocopon" + } + }, + "node_modules/svelte-tweakpane-ui/node_modules/tweakpane-plugin-waveform": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/tweakpane-plugin-waveform/-/tweakpane-plugin-waveform-1.0.0.tgz", + "integrity": "sha512-fyTRe6Emt7YpgHC5iiTZgk6RHflNm5VIOAsl2+l3mm96+KE8I+7sNPeyADxKcfcQF23c7/R3La5WNhaHNyeJag==", + "peerDependencies": { + "tweakpane": "^4.0.0" + } + }, "node_modules/svelte-writable-derived": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/svelte-writable-derived/-/svelte-writable-derived-3.1.0.tgz", diff --git a/client/package.json b/client/package.json index 634fbe2..6c81629 100644 --- a/client/package.json +++ b/client/package.json @@ -31,12 +31,14 @@ "@fontsource/roboto": "^5.0.8", "@threlte/core": "^7.1.0", "@threlte/extras": "^8.8.0", + "camera-controls": "^2.8.3", "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", + "svelte-tweakpane-ui": "^1.2.1", "three": "^0.161.0" } } diff --git a/client/src/lib/Dashboard/Visualization/CameraControls/CameraControls.svelte b/client/src/lib/Dashboard/Visualization/CameraControls/CameraControls.svelte new file mode 100644 index 0000000..f6d3935 --- /dev/null +++ b/client/src/lib/Dashboard/Visualization/CameraControls/CameraControls.svelte @@ -0,0 +1,106 @@ + + + + + { + disableAutoRotate = true + }} + on:zoom={e => { + console.log('zoomstart', e) + }} + on:controlend={() => { + disableAutoRotate = false + }} + {...$$restProps} + bind:this={$forwardingComponent} +> + + diff --git a/client/src/lib/Dashboard/Visualization/CameraControls/CameraControls.svelte.d.ts b/client/src/lib/Dashboard/Visualization/CameraControls/CameraControls.svelte.d.ts new file mode 100644 index 0000000..6bc8f0e --- /dev/null +++ b/client/src/lib/Dashboard/Visualization/CameraControls/CameraControls.svelte.d.ts @@ -0,0 +1,16 @@ +import type { Events, Props, Slots } from '@threlte/core' +import CC from 'camera-controls' +import type { SvelteComponent } from 'svelte' + +export type CameraControlsProps = Props & { + autoRotate?: boolean + autoRotateSpeed?: number +} +export type CameraControlsEvents = Events +export type CameraControlsSlots = Slots + +export default class CameraControls extends SvelteComponent< + CameraControlsProps, + CameraControlsEvents, + CameraControlsSlots +> {} diff --git a/client/src/lib/Dashboard/Visualization/CameraControls/Scene.svelte b/client/src/lib/Dashboard/Visualization/CameraControls/Scene.svelte new file mode 100644 index 0000000..f1e1df2 --- /dev/null +++ b/client/src/lib/Dashboard/Visualization/CameraControls/Scene.svelte @@ -0,0 +1,39 @@ + + + { + ref.lookAt(0, 1, 0) + }} +> + { + $cameraControls = ref + }} + autoRotate + /> + + + + + { + $mesh = ref + }} +/> + + diff --git a/client/src/lib/Dashboard/Visualization/CameraControls/utils/cameraStore.ts b/client/src/lib/Dashboard/Visualization/CameraControls/utils/cameraStore.ts new file mode 100644 index 0000000..8bb6f23 --- /dev/null +++ b/client/src/lib/Dashboard/Visualization/CameraControls/utils/cameraStore.ts @@ -0,0 +1,6 @@ +import type CameraControls from 'camera-controls' +import { writable } from 'svelte/store' +import Hornet from '../../models/Hornet.svelte' + +export const cameraControls = writable() +export const mesh = writable() diff --git a/client/src/lib/Dashboard/Visualization/CameraControls/utils/useControlsContext.ts b/client/src/lib/Dashboard/Visualization/CameraControls/utils/useControlsContext.ts new file mode 100644 index 0000000..c8862b9 --- /dev/null +++ b/client/src/lib/Dashboard/Visualization/CameraControls/utils/useControlsContext.ts @@ -0,0 +1,20 @@ +import { useThrelteUserContext } from '@threlte/core' +import { writable, type Writable } from 'svelte/store' +import type { OrbitControls } from 'three/examples/jsm/controls/OrbitControls' + +type ControlsContext = { + orbitControls: Writable +} + +/** + * ### `useControlsContext` + * + * This hook is used to register the `OrbitControls` instance with the + * `ControlsContext`. We're using this context to enable and disable the + * controls when the user is interacting with the TransformControls. + */ +export const useControlsContext = (): ControlsContext => { + return useThrelteUserContext('threlte-controls', { + orbitControls: writable(undefined), + }) +} diff --git a/client/src/lib/Dashboard/Visualization/Visualization.svelte b/client/src/lib/Dashboard/Visualization/Visualization.svelte index 4ae23fe..6d6ea20 100644 --- a/client/src/lib/Dashboard/Visualization/Visualization.svelte +++ b/client/src/lib/Dashboard/Visualization/Visualization.svelte @@ -1,6 +1,47 @@