diff --git a/package-lock.json b/package-lock.json index d261790..8467388 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,10 +9,12 @@ "version": "0.0.0", "dependencies": { "three": "^0.153.0", + "three-orbitcontrols-ts": "^0.1.2", "vue": "^3.3.4", "vue-facing-decorator": "^2.1.20" }, "devDependencies": { + "@types/three": "^0.152.1", "@vitejs/plugin-vue": "^4.2.3", "typescript": "^5.1.5", "vite": "^4.3.9", @@ -387,6 +389,37 @@ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" }, + "node_modules/@tweenjs/tween.js": { + "version": "18.6.4", + "resolved": "https://registry.npmjs.org/@tweenjs/tween.js/-/tween.js-18.6.4.tgz", + "integrity": "sha512-lB9lMjuqjtuJrx7/kOkqQBtllspPIN+96OvTCeJ2j5FEzinoAXTdAMFnDAQT1KVPRlnYfBrqxtqP66vDM40xxQ==", + "dev": true + }, + "node_modules/@types/stats.js": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@types/stats.js/-/stats.js-0.17.0.tgz", + "integrity": "sha512-9w+a7bR8PeB0dCT/HBULU2fMqf6BAzvKbxFboYhmDtDkKPiyXYbjoe2auwsXlEFI7CFNMF1dCv3dFH5Poy9R1w==", + "dev": true + }, + "node_modules/@types/three": { + "version": "0.152.1", + "resolved": "https://registry.npmjs.org/@types/three/-/three-0.152.1.tgz", + "integrity": "sha512-PMOCQnx9JRmq+2OUGTPoY9h1hTWD2L7/nmuW/SyNq1Vbq3Lwt3MNdl3wYSa4DvLTGv62NmIXD9jYdAOwohwJyw==", + "dev": true, + "dependencies": { + "@tweenjs/tween.js": "~18.6.4", + "@types/stats.js": "*", + "@types/webxr": "*", + "fflate": "~0.6.9", + "lil-gui": "~0.17.0" + } + }, + "node_modules/@types/webxr": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@types/webxr/-/webxr-0.5.2.tgz", + "integrity": "sha512-szL74BnIcok9m7QwYtVmQ+EdIKwbjPANudfuvDrAF8Cljg9MKUlIoc1w5tjj9PMpeSH3U1Xnx//czQybJ0EfSw==", + "dev": true + }, "node_modules/@vitejs/plugin-vue": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-4.2.3.tgz", @@ -631,6 +664,12 @@ "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==" }, + "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/fsevents": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", @@ -654,6 +693,12 @@ "he": "bin/he" } }, + "node_modules/lil-gui": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/lil-gui/-/lil-gui-0.17.0.tgz", + "integrity": "sha512-MVBHmgY+uEbmJNApAaPbtvNh1RCAeMnKym82SBjtp5rODTYKWtM+MXHCifLe2H2Ti1HuBGBtK/5SyG4ShQ3pUQ==", + "dev": true + }, "node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -791,6 +836,19 @@ "resolved": "https://registry.npmjs.org/three/-/three-0.153.0.tgz", "integrity": "sha512-OCP2/uQR6GcDpSLnJt/3a4mdS0kNWcbfUXIwLoEMgLzEUIVIYsSDwskpmOii/AkDM+BBwrl6+CKgrjX9+E2aWg==" }, + "node_modules/three-orbitcontrols-ts": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/three-orbitcontrols-ts/-/three-orbitcontrols-ts-0.1.2.tgz", + "integrity": "sha512-HG45dhJX4010lt/Ohk2d2K0kBaxCS6NLO3+wG9BDfMM5ddH7zMPuF3fhcn8vI4eqcSITtid0OoHEttHhjkIKEQ==", + "dependencies": { + "three": "^0.83.0" + } + }, + "node_modules/three-orbitcontrols-ts/node_modules/three": { + "version": "0.83.0", + "resolved": "https://registry.npmjs.org/three/-/three-0.83.0.tgz", + "integrity": "sha512-x9TqsmvhHG/Lw16Zi9zbJ0ho+kP8SgIfsz8dJYZbeWaFWoVwdXKolQQAftkUlpuKDys1+6SZIBHoA2QdoZKByQ==" + }, "node_modules/typescript": { "version": "5.1.5", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.5.tgz", diff --git a/package.json b/package.json index d2dd460..d7832c1 100644 --- a/package.json +++ b/package.json @@ -10,10 +10,12 @@ }, "dependencies": { "three": "^0.153.0", + "three-orbitcontrols-ts": "^0.1.2", "vue": "^3.3.4", "vue-facing-decorator": "^2.1.20" }, "devDependencies": { + "@types/three": "^0.152.1", "@vitejs/plugin-vue": "^4.2.3", "typescript": "^5.1.5", "vite": "^4.3.9", diff --git a/src/systems/Controls.ts b/src/systems/Controls.ts new file mode 100644 index 0000000..3ec1b49 --- /dev/null +++ b/src/systems/Controls.ts @@ -0,0 +1,35 @@ + +import { OrbitControls } from "three-orbitcontrols-ts"; +import { MathUtils, PerspectiveCamera } from "three"; + +class Controls { + private controls: OrbitControls; + + constructor(camera: PerspectiveCamera, canvas: HTMLElement) { + this.controls = new OrbitControls(camera, canvas); + + // Enable controls + this.controls.enabled = true; + this.controls.autoRotate = false; + + // Control limits + this.controls.minPolarAngle = MathUtils.degToRad(40); // Default + this.controls.maxPolarAngle = MathUtils.degToRad(75); + + // Smooth camera movement + this.controls.enableDamping = true; + + // Set maximum distance + this.controls.maxDistance = 250; + + // Update function for animation loop + this.controls.tick = () => this.controls.update(); + } + + public getControls(): OrbitControls { + return this.controls; + } +} + + +export { Controls }; diff --git a/src/systems/Loop.ts b/src/systems/Loop.ts new file mode 100644 index 0000000..d826505 --- /dev/null +++ b/src/systems/Loop.ts @@ -0,0 +1,38 @@ +import { Clock, PerspectiveCamera, Scene, Object3D, WebGLRenderer } from "three"; + +const clock = new Clock(); + +class Loop { + private camera: PerspectiveCamera; + private scene: Scene; + private renderer: WebGLRenderer; + private updatables: Object3D[]; + + constructor(camera: PerspectiveCamera, scene: Scene, renderer: WebGLRenderer) { + this.camera = camera; + this.scene = scene; + this.renderer = renderer; + this.updatables = []; + } + + public start(): void { + this.renderer.setAnimationLoop(() => { + this.tick(); + // render a frame + this.renderer.render(this.scene, this.camera); + }); + } + + public stop(): void { + this.renderer.setAnimationLoop(null); + } + + private tick(): void { + const delta = clock.getDelta(); + for (const object of this.updatables) { + object.tick(delta); + } + } +} + +export { Loop }; diff --git a/src/systems/Renderer.ts b/src/systems/Renderer.ts new file mode 100644 index 0000000..f78a4bb --- /dev/null +++ b/src/systems/Renderer.ts @@ -0,0 +1,22 @@ +import { WebGLRenderer, WebGLRendererParameters } from "three"; + +class Renderer { + private renderer: WebGLRenderer; + + constructor(parameters?: WebGLRendererParameters) { + this.renderer = new WebGLRenderer(parameters); + this.renderer.useLegacyLights = false; + } + + public get domElement(): HTMLCanvasElement { + return this.renderer.domElement; + } + + // Add more custom methods and properties as needed + + public render(scene: any, camera: any): void { + this.renderer.render(scene, camera); + } +} + +export { Renderer }; diff --git a/src/systems/Resizer.ts b/src/systems/Resizer.ts new file mode 100644 index 0000000..aa0446d --- /dev/null +++ b/src/systems/Resizer.ts @@ -0,0 +1,28 @@ + +class Resizer { + constructor(container: HTMLElement, camera: any, renderer: any) { + this.setSize(container, camera, renderer); + + window.addEventListener("resize", () => { + this.setSize(container, camera, renderer); + this.onResize(); + }); + } + + private setSize(camera: any, renderer: any): void { + const height = window.innerHeight; + const width = window.innerWidth - 50; + + camera.aspect = width / height; + camera.updateProjectionMatrix(); + + renderer.setSize(width, height); + renderer.setPixelRatio(window.devicePixelRatio); + } + + onResize(): void { + // Perform any custom actions on resize + } +} + +export { Resizer };