|
1 | 1 | import { useMemo } from 'react'; |
2 | | -import { OrbitControls } from '@react-three/drei'; |
| 2 | +import { OrbitControls, PerspectiveCamera } from '@react-three/drei'; |
3 | 3 | import { Canvas } from '@react-three/fiber'; |
| 4 | +import { useShallow } from 'zustand/react/shallow'; |
4 | 5 |
|
5 | | -import { VEHICLE_LABELS } from '@/domain/vehicle'; |
6 | | -import { PRESETS } from '@/schemas/spacecraft'; |
7 | | -import { configToSpacecraftProps, Spacecraft, TINTS } from '@/viewport3d/spacecraft'; |
| 6 | +import { PRESETS, type SpacecraftConfig } from '@/schemas/spacecraft'; |
| 7 | +import { loadedVector, useConfig } from '@/stores/configuration'; |
| 8 | +import type { Vec3 } from '@/viewport3d/types'; |
8 | 9 |
|
9 | | -import { CAMERA, DEMO_DEPUTY_OFFSET_RIC_M, ORBIT_CONTROLS } from './constants'; |
10 | | -import { ricToPosition } from './coordinates'; |
| 10 | +import CelestialIndicator from './CelestialIndicator'; |
| 11 | +import { CELESTIAL_EDGE_RATIO, CELESTIAL_TINTS, ORBIT_CONTROLS } from './constants'; |
11 | 12 | import Grid from './Grid'; |
12 | 13 | import RICAxes from './RICAxes'; |
| 14 | +import { computeCelestialDirsRic, computeDeputyPositionRicM, computeScale } from './scene'; |
| 15 | +import VehicleMesh from './VehicleMesh'; |
13 | 16 |
|
14 | | -const DEFAULT_CHIEF = { preset: 'Servicer 500kg' as const, ...PRESETS['Servicer 500kg'] }; |
15 | | -const DEFAULT_DEPUTY = { preset: 'Servicer 500kg' as const, ...PRESETS['Servicer 500kg'] }; |
| 17 | +const CHIEF_ORIGIN_RIC_M: Vec3 = [0, 0, 0]; |
| 18 | + |
| 19 | +const FALLBACK_CONFIG: SpacecraftConfig = { |
| 20 | + preset: 'Servicer 500kg', |
| 21 | + ...PRESETS['Servicer 500kg'], |
| 22 | +}; |
16 | 23 |
|
17 | 24 | export default function ProximityViewport() { |
18 | | - const chiefProps = useMemo(() => configToSpacecraftProps(DEFAULT_CHIEF, 'chief'), []); |
19 | | - const deputyProps = useMemo(() => configToSpacecraftProps(DEFAULT_DEPUTY, 'deputy'), []); |
20 | | - const deputyPosition = useMemo(() => ricToPosition(DEMO_DEPUTY_OFFSET_RIC_M), []); |
| 25 | + const { chiefConfig, deputyConfig, chiefState, deputyState } = useConfig( |
| 26 | + useShallow((s) => ({ |
| 27 | + chiefConfig: s.chiefConfig?.values ?? FALLBACK_CONFIG, |
| 28 | + deputyConfig: s.deputyConfig?.values ?? FALLBACK_CONFIG, |
| 29 | + chiefState: s.chiefState, |
| 30 | + deputyState: s.deputyState, |
| 31 | + })), |
| 32 | + ); |
| 33 | + |
| 34 | + const chiefVector = loadedVector(chiefState); |
| 35 | + const deputyVector = loadedVector(deputyState); |
| 36 | + |
| 37 | + const deputyRicM = useMemo( |
| 38 | + () => computeDeputyPositionRicM(chiefVector, deputyVector), |
| 39 | + [chiefVector, deputyVector], |
| 40 | + ); |
| 41 | + const scale = useMemo(() => computeScale(deputyRicM), [deputyRicM]); |
| 42 | + const celestials = useMemo(() => computeCelestialDirsRic(chiefVector), [chiefVector]); |
| 43 | + const celestialEdgeM = scale.extentM * CELESTIAL_EDGE_RATIO; |
21 | 44 |
|
22 | 45 | return ( |
23 | 46 | <div className="h-full w-full"> |
24 | | - <Canvas |
25 | | - camera={{ |
26 | | - position: CAMERA.position, |
27 | | - fov: CAMERA.fov, |
28 | | - near: CAMERA.near, |
29 | | - far: CAMERA.far, |
30 | | - }} |
31 | | - dpr={[1, 2]} |
32 | | - > |
| 47 | + <Canvas dpr={[1, 2]}> |
| 48 | + <PerspectiveCamera |
| 49 | + makeDefault |
| 50 | + fov={50} |
| 51 | + position={[0, 0, scale.cameraDistanceM]} |
| 52 | + near={scale.nearPlaneM} |
| 53 | + far={scale.farPlaneM} |
| 54 | + /> |
| 55 | + |
33 | 56 | <ambientLight intensity={0.35} /> |
34 | 57 | <hemisphereLight args={['#8aa0c0', '#1a1f2c', 0.4]} /> |
35 | | - <directionalLight position={[10, 5, 10]} intensity={1.0} /> |
36 | | - <Grid /> |
37 | | - <RICAxes /> |
38 | | - <Spacecraft {...chiefProps} label={VEHICLE_LABELS.chief} labelColor={TINTS.chief.accent} /> |
39 | | - <Spacecraft |
40 | | - {...deputyProps} |
41 | | - position={deputyPosition} |
42 | | - label={VEHICLE_LABELS.deputy} |
43 | | - labelColor={TINTS.deputy.accent} |
| 58 | + <directionalLight |
| 59 | + position={[scale.extentM, scale.extentM / 2, scale.extentM]} |
| 60 | + intensity={1.0} |
44 | 61 | /> |
| 62 | + |
| 63 | + <Grid sizeM={scale.extentM} sectionSizeM={scale.sectionM} /> |
| 64 | + <RICAxes /> |
| 65 | + |
| 66 | + {chiefVector && ( |
| 67 | + <VehicleMesh |
| 68 | + vehicle="chief" |
| 69 | + config={chiefConfig} |
| 70 | + positionRicM={CHIEF_ORIGIN_RIC_M} |
| 71 | + scale={scale.spacecraftScale} |
| 72 | + labelFontSizeM={scale.labelFontSizeM} |
| 73 | + /> |
| 74 | + )} |
| 75 | + {chiefVector && deputyVector && deputyRicM && ( |
| 76 | + <VehicleMesh |
| 77 | + vehicle="deputy" |
| 78 | + config={deputyConfig} |
| 79 | + positionRicM={deputyRicM} |
| 80 | + scale={scale.spacecraftScale} |
| 81 | + labelFontSizeM={scale.labelFontSizeM} |
| 82 | + /> |
| 83 | + )} |
| 84 | + |
| 85 | + {celestials.earth && ( |
| 86 | + <CelestialIndicator |
| 87 | + unitRic={celestials.earth} |
| 88 | + edgeM={celestialEdgeM} |
| 89 | + body="earth" |
| 90 | + color={CELESTIAL_TINTS.earth} |
| 91 | + /> |
| 92 | + )} |
| 93 | + {celestials.sun && ( |
| 94 | + <CelestialIndicator |
| 95 | + unitRic={celestials.sun} |
| 96 | + edgeM={celestialEdgeM} |
| 97 | + body="sun" |
| 98 | + color={CELESTIAL_TINTS.sun} |
| 99 | + /> |
| 100 | + )} |
| 101 | + {celestials.moon && ( |
| 102 | + <CelestialIndicator |
| 103 | + unitRic={celestials.moon} |
| 104 | + edgeM={celestialEdgeM} |
| 105 | + body="moon" |
| 106 | + color={CELESTIAL_TINTS.moon} |
| 107 | + /> |
| 108 | + )} |
| 109 | + |
45 | 110 | <OrbitControls |
46 | 111 | enablePan |
47 | 112 | enableDamping={ORBIT_CONTROLS.enableDamping} |
48 | | - minDistance={ORBIT_CONTROLS.minDistance} |
49 | | - maxDistance={ORBIT_CONTROLS.maxDistance} |
| 113 | + minDistance={scale.orbitMinM} |
| 114 | + maxDistance={scale.orbitMaxM} |
50 | 115 | /> |
51 | 116 | </Canvas> |
52 | 117 | </div> |
|
0 commit comments