134 lines
4.3 KiB
TypeScript
134 lines
4.3 KiB
TypeScript
/**
|
|
* Main 3D Scene component
|
|
*/
|
|
import { Canvas } from '@react-three/fiber';
|
|
import { OrbitControls, Stars as BackgroundStars } from '@react-three/drei';
|
|
import { useMemo } from 'react';
|
|
import { CelestialBody } from './CelestialBody';
|
|
import { Probe } from './Probe';
|
|
import { CameraController } from './CameraController';
|
|
import { Trajectory } from './Trajectory';
|
|
import { Orbit } from './Orbit';
|
|
import { Stars } from './Stars';
|
|
import { Constellations } from './Constellations';
|
|
import { Galaxies } from './Galaxies';
|
|
import { scalePosition } from '../utils/scaleDistance';
|
|
import type { CelestialBody as CelestialBodyType, Position } from '../types';
|
|
|
|
interface SceneProps {
|
|
bodies: CelestialBodyType[];
|
|
selectedBody: CelestialBodyType | null;
|
|
trajectoryPositions?: Position[];
|
|
}
|
|
|
|
export function Scene({ bodies, selectedBody, trajectoryPositions = [] }: SceneProps) {
|
|
// Separate planets/stars from probes
|
|
const planets = bodies.filter((b) => b.type !== 'probe');
|
|
const probes = bodies.filter((b) => b.type === 'probe');
|
|
|
|
// Filter probes to display based on focus
|
|
const visibleProbes = selectedBody?.type === 'probe'
|
|
? probes.filter((p) => p.id === selectedBody.id) // Only show focused probe
|
|
: []; // In overview mode, hide all probes
|
|
|
|
// Calculate target position for OrbitControls
|
|
const controlsTarget = useMemo(() => {
|
|
if (selectedBody) {
|
|
const pos = selectedBody.positions[0];
|
|
const scaledPos = scalePosition(pos.x, pos.y, pos.z);
|
|
return [scaledPos.x, scaledPos.z, scaledPos.y] as [number, number, number];
|
|
}
|
|
return [0, 0, 0] as [number, number, number];
|
|
}, [selectedBody]);
|
|
|
|
return (
|
|
<div className="w-full h-full bg-black">
|
|
<Canvas
|
|
camera={{
|
|
position: [50, 40, 50], // Angled view of the ecliptic plane
|
|
fov: 70, // Wider field of view
|
|
}}
|
|
gl={{
|
|
alpha: false,
|
|
antialias: true,
|
|
preserveDrawingBuffer: false,
|
|
}}
|
|
onCreated={({ gl, camera }) => {
|
|
gl.sortObjects = true; // Enable object sorting by renderOrder
|
|
camera.lookAt(0, 0, 0); // Look at the Sun (center)
|
|
}}
|
|
>
|
|
{/* Camera controller for smooth transitions */}
|
|
<CameraController focusTarget={selectedBody} />
|
|
|
|
{/* Increase ambient light to see textures better */}
|
|
<ambientLight intensity={0.5} />
|
|
|
|
{/* Additional directional light to illuminate planets */}
|
|
<directionalLight position={[10, 10, 5]} intensity={0.3} />
|
|
|
|
{/* Stars background (procedural) */}
|
|
<BackgroundStars
|
|
radius={300}
|
|
depth={60}
|
|
count={5000}
|
|
factor={7}
|
|
saturation={0}
|
|
fade={true}
|
|
/>
|
|
|
|
{/* Nearby stars (real data) */}
|
|
<Stars />
|
|
|
|
{/* Major constellations */}
|
|
<Constellations />
|
|
|
|
{/* Distant galaxies */}
|
|
<Galaxies />
|
|
|
|
{/* Render planets and stars */}
|
|
{planets.map((body) => (
|
|
<CelestialBody key={body.id} body={body} />
|
|
))}
|
|
|
|
{/* Render planet orbits */}
|
|
{planets.map((body) => {
|
|
const pos = body.positions[0];
|
|
const distance = Math.sqrt(pos.x ** 2 + pos.y ** 2 + pos.z ** 2);
|
|
// Only render orbits for planets (not Sun or Moon)
|
|
// Moon is too close to Earth, skip its orbit
|
|
if (body.type === 'planet' && distance > 0.1 && body.name !== 'Moon') {
|
|
return <Orbit key={`orbit-${body.id}`} distance={distance} color="#ffffff" lineWidth={1} />;
|
|
}
|
|
return null;
|
|
})}
|
|
|
|
{/* Render visible probes with 3D models */}
|
|
{visibleProbes.map((body) => (
|
|
<Probe key={body.id} body={body} />
|
|
))}
|
|
|
|
{/* Render trajectory for selected probe */}
|
|
{selectedBody?.type === 'probe' && trajectoryPositions.length > 1 && (
|
|
<Trajectory
|
|
positions={trajectoryPositions}
|
|
color="#00ffff"
|
|
lineWidth={3}
|
|
/>
|
|
)}
|
|
|
|
{/* Camera controls */}
|
|
<OrbitControls
|
|
enablePan={true}
|
|
enableZoom={true}
|
|
enableRotate={true}
|
|
minDistance={2}
|
|
maxDistance={500}
|
|
target={controlsTarget}
|
|
enabled={true} // Always enabled
|
|
/>
|
|
</Canvas>
|
|
</div>
|
|
);
|
|
}
|