solar-sim/src/main.rs
2025-08-13 21:02:57 +09:00

178 lines
6.1 KiB
Rust

use bevy::{math::DVec3, prelude::*};
use bevy_panorbit_camera::{PanOrbitCamera, PanOrbitCameraPlugin};
// Unit wrapper types - all distances in AU
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct DistanceAu(pub f64);
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct PositionAu(pub DVec3);
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct VelocityAuPerDay(pub DVec3);
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct MassKg(pub f64);
#[derive(Clone, Debug, PartialEq)]
pub struct ObjectName(pub String);
// Component wrappers
#[derive(Component)]
struct Position(PositionAu);
#[derive(Component)]
struct Velocity(VelocityAuPerDay);
#[derive(Component)]
struct Mass(MassKg);
#[derive(Component)]
struct Radius(DistanceAu);
#[derive(Component)]
struct Name(ObjectName);
#[derive(Bundle)]
struct ObjectBundle {
name: Name,
position: Position,
mass: Mass,
radius: Radius,
velocity: Velocity,
}
pub struct SolarRenderingPlugin;
impl Plugin for SolarRenderingPlugin {
fn build(&self, app: &mut App) {
app.add_systems(Startup, setup_rendering)
.add_systems(
FixedPostUpdate,
(sync_radius_to_mesh, sync_position_to_transform),
);
}
}
fn sync_radius_to_mesh(
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
query: Query<(Entity, &Radius, Option<&Mesh3d>), Changed<Radius>>,
) {
for (entity, radius, existing_mesh) in query.iter() {
// Radius is already in AU, use directly for rendering
let render_radius = radius.0.0;
// Create or update sphere mesh
let sphere_mesh = meshes.add(Sphere::new(render_radius as f32));
let material = materials.add(StandardMaterial {
base_color: Color::WHITE,
..default()
});
if existing_mesh.is_none() {
// Add mesh and material components if they don't exist
commands
.entity(entity)
.insert((Mesh3d(sphere_mesh), MeshMaterial3d(material)));
} else {
// Update existing mesh
commands.entity(entity).insert(Mesh3d(sphere_mesh));
}
}
}
fn sync_position_to_transform(mut query: Query<(&Position, &mut Transform), Changed<Position>>) {
for (position, mut transform) in query.iter_mut() {
// Convert AU to rendering units (1 AU = 1 unit for simplicity)
transform.translation = position.0.0.as_vec3();
}
}
fn setup_rendering(mut commands: Commands) {
// Spawn camera with pan/orbit/zoom controls
// Place it at a good distance to view the solar system
commands.spawn((
Camera3d::default(),
Transform::from_translation(Vec3::new(2.0, 1.5, 2.0)),
PanOrbitCamera {
focus: Vec3::ZERO,
radius: Some(3.0),
is_upside_down: false,
..default()
},
));
// Spawn directional light from the sun's position
// This simulates sunlight illuminating the planets
commands.spawn((
DirectionalLight {
color: Color::WHITE,
illuminance: 10000.0, // Bright like the sun
shadows_enabled: true,
..default()
},
Transform::from_xyz(0.0, 0.0, 0.0).looking_at(Vec3::new(1.0, 0.0, 0.0), Vec3::Y),
));
}
fn setup_solar_system(mut commands: Commands) {
// Sun - From NASA JPL Horizons data (J2000.0 epoch)
// Physical properties: Mass = 1988410 x 10^24 kg, Radius = 695700 km = 0.00465 AU
commands.spawn(ObjectBundle {
name: Name(ObjectName("Sun".to_string())),
position: Position(PositionAu(DVec3::new(0.0, 0.0, 0.0))), // At barycenter
velocity: Velocity(VelocityAuPerDay(DVec3::new(0.0, 0.0, 0.0))), // Stationary relative to barycenter
mass: Mass(MassKg(1.988410e30)), // Solar mass in kg
radius: Radius(DistanceAu(0.00465)), // Solar radius in AU (695700 km / 149597870.691)
});
// Earth - From NASA JPL Horizons data (A.D. 2000-Jan-01 12:00:00.0000 TDB)
// Position and velocity vectors in ecliptic J2000.0 frame relative to Sun
// Mass = 5.97219 x 10^24 kg, Mean radius = 6371.01 km = 0.0000426 AU
commands.spawn(ObjectBundle {
name: Name(ObjectName("Earth".to_string())),
position: Position(PositionAu(DVec3::new(
-1.771350992727098e-1, // X = -0.177135 AU
9.672416867665306e-1, // Y = 0.967242 AU
-4.085281582511366e-6, // Z = -4.085e-6 AU
))),
velocity: Velocity(VelocityAuPerDay(DVec3::new(
-1.720762506872895e-2, // VX = -0.0172076 AU/day
-3.158782144324866e-3, // VY = -0.00315878 AU/day
1.049888594613343e-7, // VZ = 1.04989e-7 AU/day
))),
mass: Mass(MassKg(5.97219e24)), // Earth mass in kg
radius: Radius(DistanceAu(0.0000426)), // Mean radius in AU (6371.01 km / 149597870.691)
});
// Moon - From NASA JPL Horizons data (A.D. 2000-Jan-01 12:00:00.0000 TDB)
// Position and velocity vectors in ecliptic J2000.0 frame relative to Sun
// Mass = 7.349 x 10^22 kg, Mean radius = 1737.53 km = 0.0000116 AU
commands.spawn(ObjectBundle {
name: Name(ObjectName("Moon".to_string())),
position: Position(PositionAu(DVec3::new(
-1.790843809223965e-1, // X = -0.179084 AU
9.654035607264573e-1, // Y = 0.965404 AU
2.383726922995396e-4, // Z = 0.000238373 AU
))),
velocity: Velocity(VelocityAuPerDay(DVec3::new(
-1.683595459141215e-2, // VX = -0.0168360 AU/day
-3.580960720855671e-3, // VY = -0.00358096 AU/day
-6.540550604528720e-6, // VZ = -6.54055e-6 AU/day
))),
mass: Mass(MassKg(7.349e22)), // Moon mass in kg
radius: Radius(DistanceAu(0.0000116)), // Mean radius in AU (1737.53 km / 149597870.691)
});
}
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_plugins(PanOrbitCameraPlugin)
.add_plugins(SolarRenderingPlugin)
.add_systems(Startup, setup_solar_system)
.run();
}