This commit is contained in:
Leon Liu 2025-08-14 06:37:14 +09:00
parent aa1ad4fbd7
commit 1e6d352e00

View File

@ -1,15 +1,15 @@
use bevy::{
core_pipeline::{bloom::Bloom, tonemapping::Tonemapping},
log::tracing_subscriber::field::debug,
math::DVec3,
prelude::*,
window::{WindowMode, WindowResolution},
window::WindowMode,
};
use bevy_inspector_egui::{bevy_egui::EguiPlugin, quick::WorldInspectorPlugin};
use bevy_panorbit_camera::{PanOrbitCamera, PanOrbitCameraPlugin};
// Scaling factor to convert AU to game units
// This makes the solar system larger so objects are visible at minimum camera zoom
// Neptune is approximately 30.1 astronomical units (AU) from the Sun
// Point light effective range is about 10 game units, so 10 game units = ~30 AU
const AU_TO_GAME_UNITS: f64 = 0.3;
// Unit wrapper types - all distances in AU
@ -160,7 +160,7 @@ fn setup_rendering(mut commands: Commands) {
..default()
}),
Tonemapping::TonyMcMapface,
Transform::from_translation(Vec3::new(2.0, 1.5, 2.0)),
Transform::from_translation(Vec3::new(0., 0., 10.0)),
Bloom::NATURAL,
PanOrbitCamera {
pan_sensitivity: 0.0, // Disable panning by setting sensitivity to 0
@ -233,7 +233,7 @@ fn update_label_positions(
};
for (mut node, label, mut text) in label_query.iter_mut() {
if let Ok((position, name, radius)) = objects_query.get(label.target_entity) {
if let Ok((position, name, _radius)) = objects_query.get(label.target_entity) {
let world_pos = position.0.0 * AU_TO_GAME_UNITS;
if let Ok(screen_pos) = camera.world_to_viewport(camera_transform, world_pos.as_vec3())
@ -341,6 +341,74 @@ fn setup_solar_system(mut commands: Commands) {
));
}
// High precision constants
const G_SI: f64 = 6.67430e-11; // Gravitational constant in m³/kg/s² (2018 CODATA value)
const AU_TO_M: f64 = 149597870691.0; // AU to meters (IAU 2012 definition, exact)
const DAY_TO_S: f64 = 86400.0; // Day to seconds (exact)
// Pre-calculated G in AU³/kg/day² units for optimization
// G_AU = G_SI * (day²/s²) / (AU³/m³)
// G_AU = G_SI * (DAY_TO_S²) / (AU_TO_M³)
const G_AU: f64 = G_SI * (DAY_TO_S * DAY_TO_S) / (AU_TO_M * AU_TO_M * AU_TO_M);
const STEPS: usize = 100;
// Time step calculation:
// FixedUpdate runs at 64fps, 1 game second = 7 days, 100 steps per FixedUpdate
// DT = (7 days / 64 fps) / 100 steps = 7 / (64 * 100) = 0.00109375 days per step
const DT: f64 = 7.0 / (64.0 * STEPS as f64); // Time step in days
fn n_body(mut query: Query<(&Mass, &mut Position, &mut Velocity)>) {
// Collect all bodies data (mass, position, velocity)
let mut bodies: Vec<(f64, DVec3, DVec3)> = query
.iter()
.map(|(mass, pos, vel)| (mass.0.0, pos.0.0, vel.0.0))
.collect();
// Perform integration steps
for _ in 0..STEPS {
// Calculate forces for all bodies
let mut forces: Vec<DVec3> = vec![DVec3::ZERO; bodies.len()];
for i in 0..bodies.len() {
for j in 0..bodies.len() {
if i != j {
let r_vec = bodies[j].1 - bodies[i].1; // Position difference in AU
let r_mag_au = r_vec.length();
if r_mag_au > 1e-10 {
// Avoid division by zero
// Calculate force directly in AU/day² units
let force_magnitude =
G_AU * bodies[i].0 * bodies[j].0 / (r_mag_au * r_mag_au);
let acceleration = force_magnitude / bodies[i].0; // F/m = a
let force_vec = r_vec.normalize() * acceleration;
forces[i] += force_vec;
}
}
}
}
// Update velocities and positions using Verlet integration
for i in 0..bodies.len() {
// Update velocity: v += a * dt
bodies[i].2 += forces[i] * DT;
// Update position: x += v * dt
let velocity = bodies[i].2;
bodies[i].1 += velocity * DT;
}
}
// Write back updated positions and velocities
for (i, (_mass, mut pos, mut vel)) in query.iter_mut().enumerate() {
if let Some(body) = bodies.get(i) {
pos.0.0 = body.1;
vel.0.0 = body.2;
}
}
}
fn main() {
App::new()
.add_plugins(DefaultPlugins.set(WindowPlugin {
@ -356,5 +424,6 @@ fn main() {
.add_plugins(WorldInspectorPlugin::new())
.add_plugins(SolarRenderingPlugin)
.add_systems(Startup, setup_solar_system)
.add_systems(FixedUpdate, n_body)
.run();
}