mirror of
https://github.com/Noratrieb/spaceship.git
synced 2026-01-14 08:25:06 +01:00
orbiting
This commit is contained in:
parent
aa1b983764
commit
2d63b5a054
5 changed files with 185 additions and 53 deletions
BIN
assets/2k_moon.png
Normal file
BIN
assets/2k_moon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.5 MiB |
4
assets/README.md
Normal file
4
assets/README.md
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
# credits
|
||||||
|
|
||||||
|
`2k_moon.png`: https://www.solarsystemscope.com/textures/
|
||||||
|
`thrusters_loop.ogg`: https://pixabay.com/sound-effects/thrusters-loopwav-14699/
|
||||||
|
|
@ -45,8 +45,10 @@
|
||||||
''-I"${pkgs.glib.dev}/include/glib-2.0"''
|
''-I"${pkgs.glib.dev}/include/glib-2.0"''
|
||||||
''-I${pkgs.glib.out}/lib/glib-2.0/include/''
|
''-I${pkgs.glib.out}/lib/glib-2.0/include/''
|
||||||
];
|
];
|
||||||
|
RUST_BACKTRACE = "1";
|
||||||
packages = (with pkgs; [
|
packages = (with pkgs; [
|
||||||
ffmpeg
|
ffmpeg
|
||||||
|
imagemagick
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
|
||||||
165
src/main.rs
165
src/main.rs
|
|
@ -1,10 +1,11 @@
|
||||||
mod forces;
|
mod forces;
|
||||||
|
mod orbit;
|
||||||
|
|
||||||
use bevy::{
|
use bevy::{
|
||||||
audio::PlaybackMode,
|
audio::PlaybackMode,
|
||||||
input::mouse::{MouseMotion, MouseWheel},
|
input::mouse::{MouseMotion, MouseWheel},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
window::{CursorGrabMode, PrimaryWindow},
|
window::PrimaryWindow,
|
||||||
};
|
};
|
||||||
use bevy_rapier3d::prelude::*;
|
use bevy_rapier3d::prelude::*;
|
||||||
use forces::ExternalForceSet;
|
use forces::ExternalForceSet;
|
||||||
|
|
@ -12,13 +13,10 @@ use forces::ExternalForceSet;
|
||||||
use crate::forces::update_external_forces;
|
use crate::forces::update_external_forces;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
dbg!(orbital::Orbit::from_pos_dir(42000.0, 0.0, 0.0, 3074.0));
|
|
||||||
return;
|
|
||||||
|
|
||||||
App::new()
|
App::new()
|
||||||
.add_plugins(DefaultPlugins)
|
.add_plugins(DefaultPlugins)
|
||||||
.add_plugins(RapierPhysicsPlugin::<NoUserData>::default())
|
.add_plugins(RapierPhysicsPlugin::<NoUserData>::default())
|
||||||
.add_plugins(RapierDebugRenderPlugin::default())
|
// .add_plugins(RapierDebugRenderPlugin::default())
|
||||||
.add_systems(Startup, setup)
|
.add_systems(Startup, setup)
|
||||||
.add_systems(
|
.add_systems(
|
||||||
Update,
|
Update,
|
||||||
|
|
@ -45,6 +43,7 @@ struct SpaceshipBundle {
|
||||||
thrusters: Thrusters,
|
thrusters: Thrusters,
|
||||||
thruster_force: ExternalForce,
|
thruster_force: ExternalForce,
|
||||||
forces: ExternalForceSet,
|
forces: ExternalForceSet,
|
||||||
|
light: PointLight,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
|
|
@ -58,7 +57,7 @@ struct Thrusters {
|
||||||
|
|
||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
struct GravityAttractor {
|
struct GravityAttractor {
|
||||||
mass: f32,
|
mass: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
|
|
@ -141,8 +140,6 @@ fn apply_gravity(
|
||||||
) {
|
) {
|
||||||
struct GravityForce;
|
struct GravityForce;
|
||||||
|
|
||||||
const G: f64 = 1.0;
|
|
||||||
|
|
||||||
let (mut ship_forces, ship_transform) = query.single_mut();
|
let (mut ship_forces, ship_transform) = query.single_mut();
|
||||||
|
|
||||||
for (gravity, body_transform) in &body_query {
|
for (gravity, body_transform) in &body_query {
|
||||||
|
|
@ -150,7 +147,7 @@ fn apply_gravity(
|
||||||
.translation
|
.translation
|
||||||
.distance(body_transform.translation) as f64;
|
.distance(body_transform.translation) as f64;
|
||||||
|
|
||||||
let fg = (G * (gravity.mass as f64)) / (distance * distance);
|
let fg = (orbit::G * (gravity.mass as f64)) / (distance * distance);
|
||||||
let direction = (body_transform.translation - ship_transform.translation).normalize();
|
let direction = (body_transform.translation - ship_transform.translation).normalize();
|
||||||
|
|
||||||
let fg = ExternalForce {
|
let fg = ExternalForce {
|
||||||
|
|
@ -162,15 +159,20 @@ fn apply_gravity(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
|
struct OrbitText;
|
||||||
|
|
||||||
fn debug_spaceship_orbit(
|
fn debug_spaceship_orbit(
|
||||||
query: Query<(&Transform, &Velocity), With<Spaceship>>,
|
query: Query<(&Transform, &Velocity), With<Spaceship>>,
|
||||||
body_query: Query<&Transform, (With<GravityAttractor>, Without<Spaceship>)>,
|
body_query: Query<(&Transform, &GravityAttractor), Without<Spaceship>>,
|
||||||
|
mut text_query: Query<&mut Text, With<OrbitText>>,
|
||||||
mut gizmos: Gizmos,
|
mut gizmos: Gizmos,
|
||||||
) {
|
) {
|
||||||
|
let mut text = text_query.single_mut();
|
||||||
let (ship_transform, &v) = query.single();
|
let (ship_transform, &v) = query.single();
|
||||||
|
|
||||||
let ship_pos = ship_transform.translation;
|
let ship_pos = ship_transform.translation;
|
||||||
let body_transform = body_query.single();
|
let (body_transform, body_gravity) = body_query.single();
|
||||||
let body_pos = body_transform.translation;
|
let body_pos = body_transform.translation;
|
||||||
|
|
||||||
let body_rotation = body_transform.rotation;
|
let body_rotation = body_transform.rotation;
|
||||||
|
|
@ -192,7 +194,6 @@ fn debug_spaceship_orbit(
|
||||||
|
|
||||||
let rotated_vel = orbital_plane_rot * velocity;
|
let rotated_vel = orbital_plane_rot * velocity;
|
||||||
let rotated_pos = orbital_plane_rot * translation;
|
let rotated_pos = orbital_plane_rot * translation;
|
||||||
dbg!((rotated_pos, rotated_vel));
|
|
||||||
|
|
||||||
gizmos.ray(body_pos, rotated_pos, Color::FUCHSIA);
|
gizmos.ray(body_pos, rotated_pos, Color::FUCHSIA);
|
||||||
gizmos.ray(
|
gizmos.ray(
|
||||||
|
|
@ -201,15 +202,17 @@ fn debug_spaceship_orbit(
|
||||||
Color::OLIVE,
|
Color::OLIVE,
|
||||||
);
|
);
|
||||||
|
|
||||||
if true && (rotated_pos.length() > 0.0 && rotated_vel.length() > 0.1 && rotated_vel.z > 0.1) {
|
let orbit = orbit::Orbit::from_pos_dir(
|
||||||
let orbit = orbital::Orbit::from_pos_dir(
|
body_gravity.mass.into(),
|
||||||
rotated_pos.x.into(),
|
rotated_pos.x.into(),
|
||||||
rotated_pos.z.into(),
|
rotated_pos.z.into(),
|
||||||
rotated_vel.x.into(),
|
rotated_vel.x.into(),
|
||||||
rotated_vel.z.into(),
|
rotated_vel.z.into(),
|
||||||
);
|
);
|
||||||
dbg!(orbit);
|
text.sections[1].value = format!("{:.2}", orbit.semi_major_axis);
|
||||||
}
|
text.sections[3].value = format!("{:.2}", orbit.apoapsis());
|
||||||
|
text.sections[5].value = format!("{:.2}", orbit.periapsis());
|
||||||
|
|
||||||
|
|
||||||
gizmos.ray_gradient(ship_pos, velocity, Color::RED, Color::GREEN);
|
gizmos.ray_gradient(ship_pos, velocity, Color::RED, Color::GREEN);
|
||||||
gizmos.ray_gradient(ship_pos, translation, Color::BLUE, Color::GREEN);
|
gizmos.ray_gradient(ship_pos, translation, Color::BLUE, Color::GREEN);
|
||||||
|
|
@ -253,10 +256,11 @@ fn orbit_camera(
|
||||||
|
|
||||||
/// set up a simple 3D scene
|
/// set up a simple 3D scene
|
||||||
fn setup(
|
fn setup(
|
||||||
mut windows: Query<&mut Window>,
|
// mut windows: Query<&mut Window>,
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
mut meshes: ResMut<Assets<Mesh>>,
|
mut meshes: ResMut<Assets<Mesh>>,
|
||||||
mut materials: ResMut<Assets<StandardMaterial>>,
|
mut materials: ResMut<Assets<StandardMaterial>>,
|
||||||
|
asset_server: Res<AssetServer>,
|
||||||
) {
|
) {
|
||||||
let mut rapier = RapierConfiguration::default();
|
let mut rapier = RapierConfiguration::default();
|
||||||
// We ain't a normal game, we do our own gravity.
|
// We ain't a normal game, we do our own gravity.
|
||||||
|
|
@ -266,23 +270,23 @@ fn setup(
|
||||||
commands.spawn(PlanetBundle::new(
|
commands.spawn(PlanetBundle::new(
|
||||||
&mut meshes,
|
&mut meshes,
|
||||||
&mut materials,
|
&mut materials,
|
||||||
|
&asset_server,
|
||||||
Transform::from_xyz(0.0, -100.0, 0.0),
|
Transform::from_xyz(0.0, -100.0, 0.0),
|
||||||
100.0,
|
10000.0,
|
||||||
5000.0,
|
));
|
||||||
Color::rgb(0.2, 0.8, 0.5),
|
|
||||||
|
commands.spawn(SpaceshipBundle::new(
|
||||||
|
&mut meshes,
|
||||||
|
&mut materials,
|
||||||
|
Vec3::new(0.0, 100.0, 0.0),
|
||||||
));
|
));
|
||||||
|
|
||||||
commands.spawn(SpaceshipBundle::new(&mut meshes, &mut materials));
|
|
||||||
// light
|
// light
|
||||||
commands.spawn(PointLightBundle {
|
commands.insert_resource(AmbientLight {
|
||||||
point_light: PointLight {
|
color: Color::WHITE,
|
||||||
intensity: 1500.0,
|
brightness: 0.1,
|
||||||
shadows_enabled: true,
|
|
||||||
..default()
|
|
||||||
},
|
|
||||||
transform: Transform::from_xyz(4.0, 8.0, 4.0),
|
|
||||||
..default()
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// camera
|
// camera
|
||||||
|
|
||||||
let camera_translation = Vec3::new(-2.0, 2.5, 5.0);
|
let camera_translation = Vec3::new(-2.0, 2.5, 5.0);
|
||||||
|
|
@ -297,13 +301,58 @@ fn setup(
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
|
|
||||||
let mut window = windows.single_mut();
|
commands.spawn((
|
||||||
|
TextBundle::from_sections([
|
||||||
|
TextSection::new(
|
||||||
|
"Semi Major Axis: ",
|
||||||
|
TextStyle {
|
||||||
|
font_size: 20.0,
|
||||||
|
color: Color::GRAY,
|
||||||
|
..default()
|
||||||
|
},
|
||||||
|
),
|
||||||
|
TextSection::from_style(TextStyle {
|
||||||
|
font_size: 20.0,
|
||||||
|
color: Color::GRAY,
|
||||||
|
..default()
|
||||||
|
}),
|
||||||
|
TextSection::new(
|
||||||
|
"\nApoapsis: ",
|
||||||
|
TextStyle {
|
||||||
|
font_size: 20.0,
|
||||||
|
color: Color::GRAY,
|
||||||
|
..default()
|
||||||
|
},
|
||||||
|
),
|
||||||
|
TextSection::from_style(TextStyle {
|
||||||
|
font_size: 20.0,
|
||||||
|
color: Color::GRAY,
|
||||||
|
..default()
|
||||||
|
}),
|
||||||
|
TextSection::new(
|
||||||
|
"\nPeriapsis: ",
|
||||||
|
TextStyle {
|
||||||
|
font_size: 20.0,
|
||||||
|
color: Color::GRAY,
|
||||||
|
..default()
|
||||||
|
},
|
||||||
|
),
|
||||||
|
TextSection::from_style(TextStyle {
|
||||||
|
font_size: 20.0,
|
||||||
|
color: Color::GRAY,
|
||||||
|
..default()
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
OrbitText,
|
||||||
|
));
|
||||||
|
|
||||||
|
// let mut window = windows.single_mut();
|
||||||
// window.cursor.visible = false;
|
// window.cursor.visible = false;
|
||||||
window.cursor.grab_mode = CursorGrabMode::Locked;
|
// window.cursor.grab_mode = CursorGrabMode::Locked;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SpaceshipBundle {
|
impl SpaceshipBundle {
|
||||||
fn new(meshes: &mut Assets<Mesh>, materials: &mut Assets<StandardMaterial>) -> Self {
|
fn new(meshes: &mut Assets<Mesh>, materials: &mut Assets<StandardMaterial>, pos: Vec3) -> Self {
|
||||||
let height = 4.0;
|
let height = 4.0;
|
||||||
let width = 0.5;
|
let width = 0.5;
|
||||||
|
|
||||||
|
|
@ -312,19 +361,11 @@ impl SpaceshipBundle {
|
||||||
model: PbrBundle {
|
model: PbrBundle {
|
||||||
mesh: meshes.add(Mesh::from(shape::Box::new(width, height, width))),
|
mesh: meshes.add(Mesh::from(shape::Box::new(width, height, width))),
|
||||||
material: materials.add(Color::rgb(0.8, 0.7, 0.6).into()),
|
material: materials.add(Color::rgb(0.8, 0.7, 0.6).into()),
|
||||||
transform: Transform::from_xyz(0.0, 3.0, 0.0).with_scale(Vec3 {
|
transform: Transform::from_translation(pos),
|
||||||
x: 1.0,
|
|
||||||
y: 1.0,
|
|
||||||
z: 1.0,
|
|
||||||
}),
|
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
vel: Velocity {
|
vel: Velocity {
|
||||||
linvel: Vec3 {
|
linvel: Vec3::ZERO,
|
||||||
x: 100.0,
|
|
||||||
y: 100.0,
|
|
||||||
z: 100.0,
|
|
||||||
},
|
|
||||||
angvel: Vec3::ZERO,
|
angvel: Vec3::ZERO,
|
||||||
},
|
},
|
||||||
body: RigidBody::Dynamic,
|
body: RigidBody::Dynamic,
|
||||||
|
|
@ -336,6 +377,11 @@ impl SpaceshipBundle {
|
||||||
torque: Vec3::ZERO,
|
torque: Vec3::ZERO,
|
||||||
},
|
},
|
||||||
forces: ExternalForceSet::default(),
|
forces: ExternalForceSet::default(),
|
||||||
|
light: PointLight {
|
||||||
|
intensity: 1500.0,
|
||||||
|
shadows_enabled: true,
|
||||||
|
..default()
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -351,26 +397,39 @@ impl PlanetBundle {
|
||||||
fn new(
|
fn new(
|
||||||
meshes: &mut Assets<Mesh>,
|
meshes: &mut Assets<Mesh>,
|
||||||
materials: &mut Assets<StandardMaterial>,
|
materials: &mut Assets<StandardMaterial>,
|
||||||
|
assert_server: &AssetServer,
|
||||||
position: Transform,
|
position: Transform,
|
||||||
radius: f32,
|
radius: f64,
|
||||||
mass: f32,
|
|
||||||
color: Color,
|
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
use std::f64::consts::PI;
|
||||||
|
|
||||||
|
let density = 2000.0 /* kg*m^3 */;
|
||||||
|
|
||||||
|
let mass = (4.0 / 3.0) * PI * radius * radius * radius * density;
|
||||||
|
|
||||||
|
let texture_handle = assert_server.load("2k_moon.png");
|
||||||
|
let material = materials.add(StandardMaterial {
|
||||||
|
base_color_texture: Some(texture_handle),
|
||||||
|
alpha_mode: AlphaMode::Blend,
|
||||||
|
unlit: false,
|
||||||
|
..default()
|
||||||
|
});
|
||||||
|
|
||||||
PlanetBundle {
|
PlanetBundle {
|
||||||
mesh: PbrBundle {
|
mesh: PbrBundle {
|
||||||
mesh: meshes.add(
|
mesh: meshes.add(
|
||||||
shape::UVSphere {
|
shape::UVSphere {
|
||||||
radius,
|
radius: radius as f32,
|
||||||
sectors: 100,
|
sectors: 100,
|
||||||
stacks: 100,
|
stacks: 100,
|
||||||
}
|
}
|
||||||
.into(),
|
.into(),
|
||||||
),
|
),
|
||||||
material: materials.add(color.into()),
|
material: material,
|
||||||
transform: position,
|
transform: position,
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
coll: Collider::ball(radius),
|
coll: Collider::ball(radius as f32),
|
||||||
gravity: GravityAttractor { mass },
|
gravity: GravityAttractor { mass },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
67
src/orbit.rs
Normal file
67
src/orbit.rs
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
use glam::DVec2;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct Orbit {
|
||||||
|
pub semi_major_axis: f64,
|
||||||
|
pub eccentricity: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const G: f64 = 6.6e-11;
|
||||||
|
|
||||||
|
impl Orbit {
|
||||||
|
pub fn from_pos_dir(m: f64, x: f64, y: f64, vx: f64, vy: f64) -> Orbit {
|
||||||
|
let v = DVec2::new(vx, vy);
|
||||||
|
let v = v.length();
|
||||||
|
|
||||||
|
let r_squared = x * x + y * y;
|
||||||
|
let r = r_squared.sqrt();
|
||||||
|
let theta = y.atan2(x);
|
||||||
|
let psi = vy.atan2(vx);
|
||||||
|
|
||||||
|
// https://phys.libretexts.org/Bookshelves/Astronomy__Cosmology/Celestial_Mechanics_(Tatum)/09%3A_The_Two_Body_Problem_in_Two_Dimensions/9.08%3A_Orbital_Elements_and_Velocity_Vector
|
||||||
|
|
||||||
|
// semi major axis, 9.5.31
|
||||||
|
// a = (GMr)/(2GM-v^2r)
|
||||||
|
let a = (G * m * r) / ((2.0 * G * m) - (v * v * r));
|
||||||
|
|
||||||
|
// eccentricity, 9.9.3
|
||||||
|
// rV sin(psi - theta) = sqrt(GMa(1-e^2))
|
||||||
|
let rvsin = r * v * (psi - theta).sin();
|
||||||
|
let gma = G * m * a;
|
||||||
|
|
||||||
|
let e = f64::sqrt((-(rvsin * rvsin - gma)) / gma);
|
||||||
|
|
||||||
|
Orbit {
|
||||||
|
semi_major_axis: a,
|
||||||
|
eccentricity: e,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn periapsis(&self) -> f64 {
|
||||||
|
self.semi_major_axis * (1.0 - self.eccentricity)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn apoapsis(&self) -> f64 {
|
||||||
|
self.semi_major_axis * (1.0 + self.eccentricity)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
#[test]
|
||||||
|
fn geostationary() {
|
||||||
|
let orbit = super::Orbit::from_pos_dir(5.972e24, 42000.0, 0.0, 0.0, 3074.0);
|
||||||
|
assert!(
|
||||||
|
(21000.0 - orbit.semi_major_axis) < 20.0,
|
||||||
|
"{} == {}",
|
||||||
|
21000.0,
|
||||||
|
orbit.semi_major_axis
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
(1.0 - orbit.eccentricity) < 0.1,
|
||||||
|
"{} == {}",
|
||||||
|
1.0,
|
||||||
|
orbit.eccentricity
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue