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>, mut materials: ResMut>, query: Query<(Entity, &Radius, Option<&Mesh3d>), Changed>, ) { 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>) { 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(); }