245 lines
9.4 KiB
Rust
245 lines
9.4 KiB
Rust
// ▄████████▄ + ███ + ▄█████████ ███ +
|
|
// ███▀ ▀███ + + ███ ███▀ + ███ + +
|
|
// ███ + ███ ███ ███ █████████ ███ ███ ███ ███
|
|
// ███ +███ ███ ███ ███ ███▐██████ ███ ███ ███
|
|
// ███ + ███ ███+ ███ +███ ███ + ███ ███ + ███
|
|
// ███▄ ▄███ ███▄ ███ ███ + ███ + ███ ███▄ ███
|
|
// ▀████████▀ + ▀███████ ███▄ ███▄ ▀████ ▀███████
|
|
// + + + ███
|
|
// + ▀████████████████████████████████████████████████████▀
|
|
//
|
|
// This module manages the messy, impure parts of our universe.
|
|
|
|
use crate::prelude::*;
|
|
use std::time::SystemTime;
|
|
|
|
pub const OXYGEN_USE_KG_PER_S: f32 = 1e-5;
|
|
pub const OXY_S: f32 = OXYGEN_USE_KG_PER_S;
|
|
pub const OXY_M: f32 = OXYGEN_USE_KG_PER_S * 60.0;
|
|
pub const OXY_H: f32 = OXYGEN_USE_KG_PER_S * 60.0 * 60.0;
|
|
pub const OXY_D: f32 = OXYGEN_USE_KG_PER_S * 60.0 * 60.0 * 24.0;
|
|
pub const LIGHTYEAR2METER: f64 = 9_460_730_472_580_800.0;
|
|
pub const PARSEC2METER: f64 = 3.0857e16;
|
|
pub const DIST_JUPTER_SUN: f64 = 778479.0e6;
|
|
pub const EARTH_GRAVITY: f32 = 9.81;
|
|
pub const C: f64 = 299792458.0; // m/s
|
|
pub const G: f64 = 6.6743015e-11; // Gravitational constant in Nm²/kg²
|
|
|
|
pub const SOL_RADIUS: f64 = 696_300_000.0;
|
|
pub const JUPITER_RADIUS: f64 = 71_492_000.0;
|
|
pub const JUPITER_RING_RADIUS: f64 = 229_000_000.0;
|
|
pub const EARTH_RADIUS: f64 = 6_371_000.0;
|
|
|
|
pub const SOL_MASS: f64 = 1.9885e30;
|
|
pub const JUPITER_MASS: f64 = 1.8982e27;
|
|
pub const EARTH_MASS: f64 = 5.972168e24;
|
|
|
|
// Arbitrary offset to time epoch, to generate more interesting orbital arrangements:
|
|
pub const ORBIT_TIME_OFFSET: f64 = 614533234154.0;
|
|
|
|
// Each star's values: (x, y, z, magnitude, color index, distance, name)
|
|
pub const STARS: &[(f32, f32, f32, f32, f32, f32, &str)] = &include!("data/stars.in");
|
|
|
|
pub fn star_color_index_to_rgb(color_index: f32) -> (f32, f32, f32) {
|
|
let temperature =
|
|
4600.0 * ((1.0 / (0.92 * color_index + 1.7)) + (1.0 / (0.92 * color_index + 0.62)));
|
|
|
|
let (red, green, blue) = if temperature <= 6600.0 {
|
|
let red = 255.0;
|
|
let green = 99.4708025861 * (temperature / 100.0).ln() - 161.1195681661;
|
|
let blue = if temperature <= 2000.0 {
|
|
0.0
|
|
} else {
|
|
138.5177312231 * ((temperature / 100.0) - 10.0).ln() - 305.0447927307
|
|
};
|
|
(red, green, blue)
|
|
} else {
|
|
let red = 329.698727446 * ((temperature / 100.0 - 60.0).powf(-0.1332047592));
|
|
let green = 288.1221695283 * ((temperature / 100.0 - 60.0).powf(-0.0755148492));
|
|
let blue = 255.0;
|
|
(red, green, blue)
|
|
};
|
|
|
|
let clamp = |x: f32| -> f32 { (x / 255.0).max(0.0).min(1.0) };
|
|
|
|
return (clamp(red), clamp(green), clamp(blue));
|
|
}
|
|
|
|
fn smooth_edge(start: f32, end: f32, value: f32) -> f32 {
|
|
let x: f32 = (value - start) / (end - start);
|
|
return 4.0 * x * x * (1.0 - x * x);
|
|
}
|
|
|
|
pub fn ring_density(radius: f32) -> f32 {
|
|
// NOTE: Keep this in sync with assets/shaders/jupiters_rings.wgsl
|
|
// Input: distance to center of jupiter in million meters
|
|
// Output: relative brightness of the ring
|
|
let halo_inner: f32 = 92.0;
|
|
let halo_outer: f32 = 122.5;
|
|
let main_inner: f32 = 122.5;
|
|
let main_outer: f32 = 129.0;
|
|
let amalthea_inner: f32 = 129.0;
|
|
let amalthea_outer: f32 = 182.0;
|
|
let thebe_inner: f32 = 129.0;
|
|
let thebe_outer: f32 = 229.0;
|
|
let metis_notch_center: f32 = 128.0;
|
|
let metis_notch_width: f32 = 0.1;
|
|
|
|
let halo_brightness: f32 = 0.75;
|
|
let main_brightness: f32 = 1.0;
|
|
let almathea_brightness: f32 = 0.5;
|
|
let thebe_brightness: f32 = 0.5;
|
|
|
|
let mut density: f32 = 0.0;
|
|
|
|
if radius >= halo_inner && radius <= halo_outer {
|
|
density = halo_brightness * smooth_edge(halo_inner, halo_outer, radius);
|
|
} else if radius >= main_inner && radius <= main_outer {
|
|
let mut metis_notch_effect: f32 = 1.0;
|
|
if radius > metis_notch_center - metis_notch_width * 0.5
|
|
&& radius < metis_notch_center + metis_notch_width * 0.5
|
|
{
|
|
metis_notch_effect = 0.8
|
|
* (1.0
|
|
- smooth_edge(
|
|
metis_notch_center - metis_notch_width * 0.5,
|
|
metis_notch_center + metis_notch_width * 0.5,
|
|
radius,
|
|
));
|
|
}
|
|
density =
|
|
main_brightness * metis_notch_effect * smooth_edge(main_inner, main_outer, radius);
|
|
} else {
|
|
if radius >= amalthea_inner && radius <= amalthea_outer {
|
|
density = almathea_brightness * smooth_edge(amalthea_inner, amalthea_outer, radius);
|
|
}
|
|
if radius >= thebe_inner && radius <= thebe_outer {
|
|
density += thebe_brightness * smooth_edge(thebe_inner, thebe_outer, radius);
|
|
}
|
|
}
|
|
|
|
return density;
|
|
}
|
|
|
|
pub fn readable_distance(distance: f64) -> String {
|
|
let abs_distance = distance.abs();
|
|
if abs_distance > LIGHTYEAR2METER * 0.01 {
|
|
let lightyears = distance / LIGHTYEAR2METER;
|
|
return format!("{lightyears:.2} ly");
|
|
}
|
|
if abs_distance >= 1.0e10 {
|
|
let gigameters = distance * 1.0e-9;
|
|
return format!("{gigameters:.1}Gm");
|
|
}
|
|
if abs_distance >= 1.0e7 {
|
|
let megameters = distance * 1.0e-6;
|
|
return format!("{megameters:.1}Mm");
|
|
}
|
|
if abs_distance >= 1.0e4 {
|
|
let kilometers = distance * 1.0e-3;
|
|
return format!("{kilometers:.1}km");
|
|
}
|
|
return format!("{distance:.1}m");
|
|
}
|
|
|
|
pub fn readable_speed(speed: f64) -> String {
|
|
let abs = speed.abs();
|
|
if abs > C * 0.0005 {
|
|
let lightyears = abs / C;
|
|
return format!("{lightyears:.4} c");
|
|
} else {
|
|
let kmh = abs * 1.0e-3 * 3600.0;
|
|
return format!("{kmh:.0} km/h");
|
|
}
|
|
}
|
|
|
|
pub fn lorentz_factor(speed: f64) -> f64 {
|
|
(1.0 - (speed.powf(2.0) / C.powf(2.0))).powf(-0.5)
|
|
}
|
|
|
|
pub fn lorentz_factor_custom_c(speed: f64, c: f64) -> f64 {
|
|
(1.0 - (speed.powf(2.0) / c.powf(2.0))).powf(-0.5)
|
|
}
|
|
|
|
pub fn inverse_lorentz_factor(speed: f64) -> f64 {
|
|
(1.0 - (speed.powf(2.0) / C.powf(2.0))).sqrt()
|
|
}
|
|
|
|
pub fn inverse_lorentz_factor_custom_c(speed: f64, c: f64) -> f64 {
|
|
(1.0 - (speed.powf(2.0) / c.powf(2.0))).sqrt()
|
|
}
|
|
|
|
/// Calculates orbit duration in seconds, with given parameters, assuming circular orbit.
|
|
pub fn simple_orbital_period(mass: f64, distance: f64) -> f64 {
|
|
return 2.0 * PI * (distance.powf(3.0) / (G * mass)).sqrt();
|
|
}
|
|
|
|
/// Calculates the orbital velocity with given parameters, assuming prograde circular orbit.
|
|
pub fn orbital_velocity(coords: DVec3, mass: f64) -> DVec3 {
|
|
let r = coords.length();
|
|
let speed = (G * mass / r).sqrt();
|
|
|
|
// This generates a perpendicular orbital vector in the prograde direction
|
|
let perpendicular = DVec3::new(coords.z, 0.0, -coords.x).normalize();
|
|
|
|
return perpendicular * speed;
|
|
}
|
|
|
|
/// Calculates the acceleration towards a mass in m/s
|
|
pub fn gravitational_acceleration(coords: DVec3, mass: f64) -> DVec3 {
|
|
let r_squared = coords.length_squared();
|
|
let acceleration_magnitude = G * mass / r_squared;
|
|
return -acceleration_magnitude * (coords / r_squared.sqrt());
|
|
}
|
|
|
|
#[test]
|
|
fn test_gravitational_acceleration() {
|
|
let coords = DVec3::new(EARTH_RADIUS, 0.0, 0.0);
|
|
let mass = EARTH_MASS;
|
|
let g = gravitational_acceleration(coords, mass);
|
|
let g_rounded = (g * 10.0).round() / 10.0;
|
|
assert_eq!(g_rounded, DVec3::new(-9.8, 0.0, 0.0));
|
|
}
|
|
|
|
pub fn phase_dist_to_coords(phase_radians: f64, distance: f64) -> DVec3 {
|
|
return DVec3::new(
|
|
distance * phase_radians.cos(),
|
|
0.0,
|
|
distance * phase_radians.sin(),
|
|
);
|
|
}
|
|
|
|
pub fn pos_offset_for_orbiting_body(
|
|
orbit_distance: f64,
|
|
orbited_mass: Option<f64>,
|
|
phase_offset: Option<f64>,
|
|
) -> DVec3 {
|
|
let r = orbit_distance;
|
|
let mut phase_radians = 0.0f64;
|
|
if let Some(phase_offset) = phase_offset {
|
|
phase_radians += phase_offset
|
|
}
|
|
if let Some(mass) = orbited_mass {
|
|
if let Ok(epoch) = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) {
|
|
let orbital_period = simple_orbital_period(mass, r);
|
|
let now = epoch.as_secs_f64() + ORBIT_TIME_OFFSET;
|
|
phase_radians += PI * 2.0 * (now % orbital_period) / orbital_period;
|
|
} else {
|
|
eprintln!("WARNING: Can't determine current time in calculate_position_offset_for_orbiting_body().");
|
|
}
|
|
}
|
|
return phase_dist_to_coords(-phase_radians, r);
|
|
}
|
|
|
|
/// Assumes the "front" (as seen in blender) is pointing at the orbited mass
|
|
pub fn rotation_for_orbiting_body(orbit_distance: f64, mass: f64) -> f64 {
|
|
if let Ok(epoch) = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) {
|
|
let orbital_period = nature::simple_orbital_period(mass, orbit_distance);
|
|
let now = epoch.as_secs_f64() + ORBIT_TIME_OFFSET;
|
|
PI * 2.0 * (now % orbital_period) / orbital_period - PI * 0.5
|
|
} else {
|
|
eprintln!("WARNING: Can't determine current time in calculate_position_offset_for_orbiting_body().");
|
|
0.0
|
|
}
|
|
}
|