cosmo/frontend/src/components/Scene.tsx

127 lines
4.0 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 { OrbitRenderer } from './OrbitRenderer';
import { Stars } from './Stars';
import { Constellations } from './Constellations';
import { Galaxies } from './Galaxies';
import { Nebulae } from './Nebulae';
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 natural celestial bodies (planets/dwarf planets/satellites/stars) from probes
const celestialBodies = bodies.filter((b) => b.type !== 'probe');
const probes = bodies.filter((b) => b.type === 'probe');
// Always show all probes (changed from previous behavior)
const visibleProbes = 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} allBodies={bodies} />
{/* 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 />
{/* Nebulae */}
<Nebulae />
{/* Distant galaxies */}
<Galaxies />
{/* Render all celestial bodies: planets, dwarf planets, satellites, and stars */}
{celestialBodies.map((body) => (
<CelestialBody key={body.id} body={body} allBodies={bodies} />
))}
{/* Unified orbit renderer for all celestial bodies (planets and dwarf planets) */}
<OrbitRenderer />
{/* Render visible probes with 3D models */}
{visibleProbes.map((body) => (
<Probe key={body.id} body={body} allBodies={bodies} />
))}
{/* 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>
);
}