diff --git a/packages/client/src/components/feature/Controls/Controls.tsx b/packages/client/src/components/feature/Controls/Controls.tsx index f263fca..d35753f 100644 --- a/packages/client/src/components/feature/Controls/Controls.tsx +++ b/packages/client/src/components/feature/Controls/Controls.tsx @@ -2,12 +2,25 @@ import { useRef } from 'react'; import { OrbitControls } from '@react-three/drei'; import * as THREE from 'three'; import { useCameraStore } from 'store/useCameraStore'; -import { useFrame } from '@react-three/fiber'; +import { Camera, useFrame } from '@react-three/fiber'; import type { OrbitControls as OrbitControlsImpl } from 'three-stdlib'; +const setCameraPosition = ( + camera: Camera, + currentView: THREE.Vector3, + distance: number, +) => { + const direction = currentView + .clone() + .sub(camera.position) + .setLength(camera.position.distanceTo(currentView) - distance); + camera.position.add(direction); +}; + export default function Controls() { const controlsRef = useRef(null!); - const { currentView, setCurrentView, targetView } = useCameraStore(); + const { cameraToCurrentView, currentView, setCurrentView, targetView } = + useCameraStore(); useFrame((state, delta) => { const targetPosition = new THREE.Vector3(0, 0, 0); @@ -21,7 +34,7 @@ export default function Controls() { if (direction.length() > LENGTH_LIMIT) direction.setLength(LENGTH_LIMIT); setCurrentView(currentView.add(direction)); - state.camera.position.add(direction); + setCameraPosition(state.camera, currentView, cameraToCurrentView); controlsRef.current.target = currentView; } diff --git a/packages/client/src/components/feature/Galaxy/index.tsx b/packages/client/src/components/feature/Galaxy/index.tsx index b4a6018..1c0a905 100644 --- a/packages/client/src/components/feature/Galaxy/index.tsx +++ b/packages/client/src/components/feature/Galaxy/index.tsx @@ -1,7 +1,7 @@ import * as THREE from 'three'; import Star from '../Star'; import { getRandomInt, getGaussianRandomFloat } from '@utils/random'; -import { useRef } from 'react'; +import { useRef, useMemo } from 'react'; import { useFrame } from '@react-three/fiber'; import { ARMS, @@ -31,43 +31,46 @@ const getSpiralPositions = (offset: number) => { export default function Galaxy() { const galaxyRef = useRef(null!); - const stars = []; useFrame((_, delta) => (galaxyRef.current.rotation.y += delta / 100)); - for (let arm = 0; arm < ARMS; arm++) { + const stars = useMemo(() => { + const starList = []; + for (let arm = 0; arm < ARMS; arm++) { + for (let star = 0; star < STARS_NUM / (ARMS + 1); star++) { + const size = getRandomInt(STAR_MIN_SIZE, STAR_MAX_SIZE); + const position = getSpiralPositions((arm * 2 * Math.PI) / ARMS); + + starList.push( + , + ); + } + } + for (let star = 0; star < STARS_NUM / (ARMS + 1); star++) { const size = getRandomInt(STAR_MIN_SIZE, STAR_MAX_SIZE); - const position = getSpiralPositions((arm * 2 * Math.PI) / ARMS); + const position = new THREE.Vector3( + getGaussianRandomFloat(0, (ARMS_X_MEAN + ARMS_X_DIST) / 4), + getGaussianRandomFloat(0, GALAXY_THICKNESS * 2), + getGaussianRandomFloat(0, (ARMS_X_MEAN + ARMS_X_DIST) / 4), + ); - stars.push( + starList.push( , ); } - } - - for (let star = 0; star < STARS_NUM / (ARMS + 1); star++) { - const size = getRandomInt(STAR_MIN_SIZE, STAR_MAX_SIZE); - const position = new THREE.Vector3( - getGaussianRandomFloat(0, (ARMS_X_MEAN + ARMS_X_DIST) / 4), - getGaussianRandomFloat(0, GALAXY_THICKNESS * 2), - getGaussianRandomFloat(0, (ARMS_X_MEAN + ARMS_X_DIST) / 4), - ); - - stars.push( - , - ); - } + return starList; + }, []); return {stars}; } diff --git a/packages/client/src/components/feature/Screen/index.tsx b/packages/client/src/components/feature/Screen/index.tsx index e017c7d..3b60a34 100644 --- a/packages/client/src/components/feature/Screen/index.tsx +++ b/packages/client/src/components/feature/Screen/index.tsx @@ -5,6 +5,7 @@ import { EffectComposer, Bloom } from '@react-three/postprocessing'; import { useControls } from 'leva'; import { CAMERA_POSITION, CAMERA_ROTATION, CAMERA_FAR } from 'constants/camera'; import Controls from '../Controls/Controls.tsx'; +import { useCameraStore } from 'store/useCameraStore.ts'; export default function Screen() { const camera = { @@ -13,6 +14,8 @@ export default function Screen() { far: CAMERA_FAR, }; + const { cameraToCurrentView, setCameraToCurrentView } = useCameraStore(); + const { intensity, mipmapBlur, luminanceThreshold, luminanceSmoothing } = useControls('Bloom', { intensity: { value: 0.4, min: 0, max: 1.5, step: 0.01 }, @@ -23,7 +26,12 @@ export default function Screen() { return (
- + + setCameraToCurrentView(cameraToCurrentView + e.deltaY / 20) + } + > void; targetView: THREE.Mesh | null; setTargetView: (star: THREE.Mesh | null) => void; + cameraToCurrentView: number; + setCameraToCurrentView: (distance: number) => void; } export const useCameraStore = create()((set) => ({ @@ -13,4 +16,10 @@ export const useCameraStore = create()((set) => ({ setCurrentView: (position: THREE.Vector3) => set({ currentView: position }), targetView: null, setTargetView: (star: THREE.Mesh | null) => set({ targetView: star }), + cameraToCurrentView: CAMERA_MIN_DISTANCE, + setCameraToCurrentView: (distance: number) => + set({ + cameraToCurrentView: + distance > CAMERA_MIN_DISTANCE ? distance : CAMERA_MIN_DISTANCE, + }), }));