summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main.rs13
-rw-r--r--src/solar_system.rs136
-rw-r--r--src/tacmap.rs7
-rw-r--r--src/tacmap/camera.rs150
-rw-r--r--src/tacmap/render.rs7
-rw-r--r--src/window/ui.rs40
6 files changed, 254 insertions, 99 deletions
diff --git a/src/main.rs b/src/main.rs
index b6efdcd..ae4233e 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -13,6 +13,7 @@ use std::cell::RefCell;
use std::thread;
use std::time::{Duration, Instant};
+use serde::de;
use winit::event::{KeyEvent, WindowEvent};
use winit::event_loop::{EventLoop, ActiveEventLoop, ControlFlow};
use winit::error::{EventLoopError};
@@ -82,6 +83,16 @@ impl GameState
pub fn timeman_mut(&mut self) -> &mut TimeMan
{ &mut self.timeman }
+
+ fn update(
+ &mut self,
+ dt: Duration)
+ {
+ self.timeman.update();
+ self.solar_systems.iter_mut().for_each(|solar_system| {
+ solar_system.update(self.timeman.seconds());
+ });
+ }
}
impl ApplicationHandler for SystemicApp
@@ -126,7 +137,7 @@ impl ApplicationHandler for SystemicApp
let delta_time = now - self.last_render_time;
self.last_render_time = now;
- game_state.borrow_mut().timeman.update();
+ game_state.borrow_mut().update(delta_time);
{
window.update(game_state, delta_time);
}
diff --git a/src/solar_system.rs b/src/solar_system.rs
index e8137f0..6730719 100644
--- a/src/solar_system.rs
+++ b/src/solar_system.rs
@@ -1,6 +1,7 @@
+use cgmath::Point3;
use serde::{Deserialize};
-use crate::{known_stars::{KNOWN_STARS, StarNotFoundError}, timeman::Second};
-use std::error::Error;
+use crate::{GameState, known_stars::{KNOWN_STARS, StarNotFoundError}, timeman::Second};
+use std::{cell::RefCell, error::Error};
const GRAVITATIONAL_CONSTANT: f64 = 6.67408e-11;
@@ -16,7 +17,7 @@ pub type SystemId = usize;
pub struct SerialOrbitalBody
{
name: String,
- orbits: BodyId,
+ orbits: Option<BodyId>,
mass: Kilograms,
radius: Kilometers,
@@ -33,13 +34,14 @@ pub struct SerialOrbitalBody
pub struct OrbitalBody
{
body: SerialOrbitalBody,
- position: Option<(Second, cgmath::Point3<Kilometers>)>
+ position: Option<cgmath::Point3<Kilometers>>
}
pub struct SolarSystem
{
name: String,
bodies: Vec<OrbitalBody>,
+ time: Option<Second>
}
impl SolarSystem
@@ -53,8 +55,16 @@ impl SolarSystem
let mut bodies = Vec::<OrbitalBody>::new();
for result in body_reader.deserialize() {
- let record: SerialOrbitalBody = result?;
+ let mut record: SerialOrbitalBody = result?;
+ match record.orbits {
+ Some(orbits) => {
+ if record.sgp == 0.0 {
+ record.sgp = bodies[orbits].body.mass + record.mass * GRAVITATIONAL_CONSTANT;
+ }
+ }
+ None => {}
+ }
println!("New body: {:?}", record);
bodies.push(OrbitalBody { body: record, position: None });
@@ -63,6 +73,7 @@ impl SolarSystem
Ok(Self {
name: bodies[0].name().clone(),
bodies: bodies,
+ time: None
})
}
@@ -83,79 +94,92 @@ impl SolarSystem
{
self.bodies.as_slice()
}
+
+ fn update_bodies(
+ &mut self,
+ time: Second)
+ {
+ self.bodies.iter_mut().for_each(|body| {
+ body.position = Some(body.calculate_orbit_at(time));
+ });
+ self.time = Some(time);
+ }
+
+ pub fn update(
+ &mut self,
+ time: Second)
+ {
+ match self.time {
+ Some(cache_time) => {
+ if cache_time == time { return; }
+ self.update_bodies(time);
+ }
+ None => {
+ self.update_bodies(time);
+ }
+ }
+ }
}
impl OrbitalBody
{
pub fn name(&self) -> &String { &self.body.name }
pub fn radius(&self) -> f32 { self.body.radius as f32 }
+ pub fn position(&self) -> Point3<f64> { self.position.unwrap() }
- pub fn position(&self, time: Second)
- -> Option<cgmath::Point3<Kilometers>>
+ fn calculate_orbit_at(
+ &self,
+ time: Second)
+ -> Point3<f64>
{
- match self.position {
- Some((cache_time, pos)) => {
- if time == cache_time {
- return Some(pos);
- }
- return None;
- },
- None => None
+ if self.body.orbits.is_none() {
+ return Point3::<f64>::new(0.0, 0.0, 0.0);
}
- }
- fn calculate_orbit(
- &self,
- time: i64)
- -> cgmath::Point3<Kilometers> {
- cgmath::Point3 { x: 0.0, y: 0.0, z: 0.0 }
- /*let arg_periapsis = self.long_periapsis - self.long_asc_node;
+ let eccentricity = self.body.eccentricity;
+ let long_asc_node = self.body.long_asc_node;
+ let arg_periaps = self.body.long_periapsis - long_asc_node;
- let mean_angular_motion: f64 = (
- self.sgp as f64 / (self.semi_major_axis as f64).powf(3.0)
- ).sqrt();
+ let sma_cubed = self.body.semi_major_axis.powf(3.0);
+ let mean_motion = (self.body.sgp / sma_cubed.abs()).sqrt();
- let mean_anomaly = (self.mean_long - self.long_periapsis) + (mean_angular_motion * time as f64) as f32;
+ let mean_anomaly_epoch = self.body.mean_long - self.body.long_periapsis;
+ let mean_anomaly = mean_anomaly_epoch + (mean_motion * time as f64);
- let mut eccentric_anomaly = mean_anomaly + self.eccentricity * mean_anomaly.sin();
+ //Find the eccentric anomaly via newton's method
+ let mut eccentric_anomaly = mean_anomaly;
for _ in 0..100 {
- let new_eccentric = eccentric_anomaly +
- (mean_anomaly - eccentric_anomaly + self.eccentricity * eccentric_anomaly.sin()) /
- (1.0 - self.eccentricity * eccentric_anomaly.cos());
- if (new_eccentric - eccentric_anomaly).abs() < 1e-6 {
- eccentric_anomaly = new_eccentric;
+ let (e_sin, e_cos) = eccentric_anomaly.sin_cos();
+ let new_anomaly = eccentric_anomaly -
+ (
+ (eccentric_anomaly - eccentricity * e_sin - mean_anomaly) /
+ (1.0 - eccentricity * e_cos)
+ );
+
+ let diff = eccentric_anomaly - new_anomaly;
+ eccentric_anomaly = new_anomaly;
+ if diff.abs() < 1e-6 {
break;
}
- eccentric_anomaly = new_eccentric;
}
- let beta = self.eccentricity / 1.0 + (1.0 - self.eccentricity * self.eccentricity).sqrt();
- let true_anomaly = eccentric_anomaly + 2.0 *
- ((beta * eccentric_anomaly.sin()) / (1.0 - beta * eccentric_anomaly.cos())).atan();
-
- let radius: f64 = self.semi_major_axis * (1.0 - self.eccentricity * eccentric_anomaly.cos()) as f64;
-
- let ohm_sin = self.long_asc_node.sin();
- let ohm_cos = self.long_asc_node.cos();
-
- let inc_sin = self.inclination.sin();
- let inc_cos = self.inclination.cos();
+ let true_anomaly = 2.0 * (
+ ((1.0 + eccentricity) / (1.0 - eccentricity)).sqrt()
+ * (eccentric_anomaly / 2.0).tan()
+ ).atan();
- let per_anom_sin = (arg_periapsis + true_anomaly).sin();
- let per_anom_cos = (arg_periapsis + true_anomaly).cos();
+ let vw = true_anomaly + arg_periaps;
+ let (vw_sin, vw_cos) = vw.sin_cos();
+ let (omega_sin, omega_cos) = long_asc_node.sin_cos();
+ let (i_sin, i_cos) = self.body.inclination.sin_cos();
- let x: f32 = (radius as f32) * (
- (ohm_cos * per_anom_cos) -
- (ohm_sin * per_anom_sin * inc_cos));
- let y: f32 = (radius as f32) * (
- (ohm_sin * per_anom_cos) +
- (ohm_cos * per_anom_sin * inc_cos));
- let z: f32 = (radius as f32) * (inc_sin * per_anom_sin);
+ let r = self.body.semi_major_axis * (1.0 - eccentricity * eccentric_anomaly.cos());
+ let x = r * (omega_cos * vw_cos - omega_sin * vw_sin * i_cos);
+ let y = r * i_sin * vw_sin;
+ let z = r * (omega_sin * vw_cos + omega_cos * vw_sin * i_cos);
- OrbitState {
- position: cgmath::Vector3::new(x, y, z)
- }*/
+ Point3::<f64>::new(x, y, z)
}
}
diff --git a/src/tacmap.rs b/src/tacmap.rs
index 89d21aa..b245f77 100644
--- a/src/tacmap.rs
+++ b/src/tacmap.rs
@@ -52,7 +52,7 @@ impl TacticalMap
let camera = Camera::new(
wgpuctx,
cgmath::Point3::<f32>::new(0.0, 0.0, 1.0),
- cgmath::Deg(-90.0),
+ cgmath::Deg(0.0),
cgmath::Rad(0.0));
let projection = Projection::new(
@@ -86,8 +86,11 @@ impl TacticalMap
ui_state: &mut GameWindowUiState,
dt: Duration)
{
- self.camera_controller.update(&mut self.camera, dt);
ui_state.camera_scale = self.camera.get_scale();
+ ui_state.camera_pos = Some(self.camera.get_position());
+ self.camera.set_target(ui_state.camera_target);
+
+ self.camera_controller.update(&mut self.camera, game_state, ui_state, dt);
}
pub fn keyboard_input(
diff --git a/src/tacmap/camera.rs b/src/tacmap/camera.rs
index 1a2787b..c6ee5c0 100644
--- a/src/tacmap/camera.rs
+++ b/src/tacmap/camera.rs
@@ -1,9 +1,9 @@
-use std::time::Duration;
+use std::{cell::RefCell, time::Duration};
-use cgmath::{InnerSpace, Point3, Rad, Vector2, Vector3, Vector4, Zero, perspective};
+use cgmath::{EuclideanSpace, InnerSpace, Point3, Rad, Vector2, Vector3, Vector4, Zero, perspective};
use winit::{event::ElementState, keyboard::KeyCode};
-use crate::{solar_system::BodyId, wgpuctx::WgpuCtx};
+use crate::{GameState, solar_system, wgpuctx::WgpuCtx, window::ui::GameWindowUiState};
pub const OPENGL_TO_WGPU_MATRIX: cgmath::Matrix4<f32> = cgmath::Matrix4::from_cols(
Vector4::new(1.0, 0.0, 0.0, 0.0),
@@ -14,12 +14,13 @@ pub const OPENGL_TO_WGPU_MATRIX: cgmath::Matrix4<f32> = cgmath::Matrix4::from_co
pub struct Camera
{
- position: Point3<f32>,
+ abs_position: Point3<f32>,
+ rel_position: Point3<f32>,
pitch: Rad<f32>,
yaw: Rad<f32>,
scale: f32,
- target: Option<BodyId>,
+ target: Option<solar_system::BodyId>,
buffer: wgpu::Buffer,
staging_buffer: wgpu::Buffer,
@@ -96,7 +97,8 @@ impl Camera
}
);
Self {
- position: position.into(),
+ abs_position: position.into(),
+ rel_position: Point3::new(0.0, 0.0, 0.0),
yaw: yaw.into(),
pitch: pitch.into(),
scale: 1.0,
@@ -110,6 +112,14 @@ impl Camera
pub fn get_scale(&self) -> f32
{ self.scale }
+ pub fn get_position(&self) -> Point3<f32>
+ {
+ Point3::new(
+ self.abs_position.x + self.rel_position.x,
+ self.abs_position.y + self.rel_position.y,
+ self.abs_position.z + self.rel_position.z)
+ }
+
pub fn bindgroup_layout(wgpuctx: &WgpuCtx)
-> wgpu::BindGroupLayout
{
@@ -152,22 +162,44 @@ impl Camera
wgpuctx.queue().write_buffer(&self.staging_buffer, 0, bytemuck::cast_slice(&[self.uniform(projection)]));
}
+ pub fn set_target(
+ &mut self,
+ target: Option<solar_system::BodyId>)
+ {
+ if target == self.target { return; }
+ if !target.is_some() || !self.target.is_some() {
+ self.abs_position = self.get_position();
+ self.rel_position = Point3::new(0.0, 0.0, 0.0);
+ }
+ self.target = target;
+ }
+
+ pub fn get_target(&self) -> Option<solar_system::BodyId>
+ { self.target }
+
pub fn view_matrix(
&self)
-> cgmath::Matrix4<f32> {
- let (yaw_sin, yaw_cos) = self.yaw.0.sin_cos();
- let (pitch_sin, pitch_cos) = self.pitch.0.sin_cos();
-
- cgmath::Matrix4::look_to_rh(
- self.position,
- Vector3::new(
- pitch_cos * yaw_cos,
- pitch_sin,
- pitch_cos * yaw_sin
- ).normalize(),
- Vector3::unit_y()
- )
+ let (yaw_sin, yaw_cos) = self.pitch.0.sin_cos();
+ let (pitch_sin, pitch_cos) = self.yaw.0.sin_cos();
+
+ if self.target.is_some() {
+ cgmath::Matrix4::look_at_rh(
+ self.get_position(),
+ self.abs_position,
+ Vector3::unit_y())
+ }else{
+ cgmath::Matrix4::look_to_rh(
+ self.get_position(),
+ Vector3::new(
+ pitch_cos * yaw_cos,
+ pitch_sin,
+ pitch_cos * yaw_sin
+ ).normalize(),
+ Vector3::unit_y()
+ )
+ }
}
pub fn uniform(
@@ -177,7 +209,7 @@ impl Camera
{
CameraUniform {
view: (self.view_matrix()).into(),
- proj: (OPENGL_TO_WGPU_MATRIX * projection.projection_matrix()).into(),
+ proj: projection.projection_matrix().into(),
scale: self.scale
}
}
@@ -215,30 +247,96 @@ impl CameraController
}
}
- pub fn update(
+ fn update_orbit(
&mut self,
camera: &mut Camera,
+ target: Point3<f32>,
+ min_radius: f32,
dt: Duration)
{
+ camera.abs_position = target * camera.scale;
+
let dt = dt.as_secs_f32();
let speed = 1.0;
- camera.pitch.0 += (self.rotation_pos_delta.x - self.rotation_neg_delta.x) * speed * dt;
- camera.yaw.0 += (self.rotation_pos_delta.y - self.rotation_neg_delta.y) * speed * dt;
+ let current_radius = camera.rel_position.to_vec().magnitude();
+
+ let polar_diff = (self.position_pos_delta.z - self.position_neg_delta.z) * speed * dt;
+ let azimuth_diff = (self.position_pos_delta.x - self.position_neg_delta.x) * speed * dt;
+ let dist_diff = (self.position_pos_delta.y - self.position_neg_delta.y) * speed * dt;
+
+ camera.pitch.0 += polar_diff;
+ let polar_cap = cgmath::Deg(179.999).0;
+ if camera.pitch.0 > polar_cap { camera.pitch.0 = polar_cap; }
+ if camera.pitch.0 < -polar_cap { camera.pitch.0 = -polar_cap; }
+
+ camera.yaw.0 += azimuth_diff;
+
+ let (az_sin, az_cos) = camera.yaw.0.sin_cos();
+ let (p_sin, p_cos) = camera.pitch.0.sin_cos();
+
+ let radius = f32::max(min_radius * camera.scale, current_radius + dist_diff);
+ camera.rel_position = Point3::new(
+ radius * p_cos * az_cos,
+ radius * p_sin,
+ radius * p_cos * az_sin
+ );
+ }
+
+ fn update_pan(
+ &mut self,
+ camera: &mut Camera,
+ dt: Duration)
+ {
+ let dt = dt.as_secs_f32();
+ let speed = 1.0;
let (yaw_sin, yaw_cos) = camera.yaw.0.sin_cos();
let forward = Vector3::new(yaw_cos, 0.0, yaw_sin).normalize();
let right = Vector3::new(-yaw_sin, 0.0, yaw_cos).normalize();
let up = Vector3::new(0.0, 1.0, 0.0);
- camera.position += forward * (self.position_pos_delta.z - self.position_neg_delta.z) * speed * dt;
- camera.position += right * (self.position_pos_delta.x - self.position_neg_delta.x) * speed * dt;
- camera.position += up * (self.position_pos_delta.y - self.position_neg_delta.y) * speed * dt;
+ camera.rel_position += forward * (self.position_pos_delta.z - self.position_neg_delta.z) * speed * dt;
+ camera.rel_position += right * (self.position_pos_delta.x - self.position_neg_delta.x) * speed * dt;
+ camera.rel_position += up * (self.position_pos_delta.y - self.position_neg_delta.y) * speed * dt;
+
+ camera.abs_position = camera.rel_position;
+
+ camera.pitch.0 += (self.rotation_pos_delta.x - self.rotation_neg_delta.x) * speed * dt;
+ camera.yaw.0 += (self.rotation_pos_delta.y - self.rotation_neg_delta.y) * speed * dt;
+ }
+
+ pub fn update(
+ &mut self,
+ camera: &mut Camera,
+ game_state: &RefCell<GameState>,
+ ui_state: &mut GameWindowUiState,
+ dt: Duration)
+ {
camera.scale *= 1.0 + ((self.scale_delta.y - self.scale_delta.x) * 0.1);
camera.scale = f32::max(1e-16, f32::min(1.0, camera.scale));
+
+ match ui_state.camera_target {
+ Some(body_id) => {
+ let game_state = game_state.borrow();
+ let solar_systems = game_state.solar_systems();
+ let current_system = match ui_state.current_system {
+ Some(id) => &solar_systems[id],
+ None => return
+};
+
+ let body = &current_system.bodies()[body_id];
+ self.update_orbit(
+ camera,
+ body.position().cast().unwrap(),
+ body.radius(),
+ dt);
+ }
+ None => self.update_pan(camera, dt)
+ }
}
-}
+} //impl CameraController
impl Projection {
pub fn new<
diff --git a/src/tacmap/render.rs b/src/tacmap/render.rs
index c22878e..0f6b409 100644
--- a/src/tacmap/render.rs
+++ b/src/tacmap/render.rs
@@ -142,12 +142,7 @@ impl BodyRenderer
let bodies = solar_system.bodies();
let body_instances = bodies.iter().map(|body| {
- let position = match body.position(time) {
- Some(pos) => pos,
- None => return BodyInstanceRaw {
- position: [0.0, 0.0, 0.0],
- radius: body.radius() }
- };
+ let position = body.position();
BodyInstance {
position: position,
radius: body.radius()
diff --git a/src/window/ui.rs b/src/window/ui.rs
index 4d13740..60cd795 100644
--- a/src/window/ui.rs
+++ b/src/window/ui.rs
@@ -9,6 +9,7 @@ pub struct GameWindowUiState
{
pub current_system: Option<solar_system::SystemId>,
pub camera_scale: f32,
+ pub camera_pos: Option<cgmath::Point3<f32>>,
pub camera_target: Option<solar_system::BodyId>,
pub auto_time: Option<Second>,
pub do_auto_tick: bool
@@ -39,22 +40,16 @@ impl GameWindowUiState
egui::TopBottomPanel::top("topbar").show(
eguictx.context(),
|ui| {
-
- ui.horizontal(|ui| {
- ui.menu_button("File", |ui| {
-
- });
- });
ui.horizontal(|ui| {
let solar_systems = game_state.solar_systems();
- let selected_label = match state.current_system {
+ let selected_system_label = match state.current_system {
Some(id) => solar_systems[id].name(),
None => ""
};
egui::ComboBox::from_label("Current System")
- .selected_text(selected_label)
+ .selected_text(selected_system_label)
.show_ui(ui, |ui| {
for (i, system) in solar_systems.iter().enumerate() {
@@ -65,6 +60,35 @@ impl GameWindowUiState
);
}
});
+
+ let current_system = match state.current_system {
+ Some(id) => &solar_systems[id],
+ None => return
+ };
+
+ let selected_body_label = match state.camera_target {
+ Some(id) => current_system.bodies()[id].name(),
+ None => ""
+ };
+
+ ui.separator();
+
+ egui::ComboBox::from_label("Camera Target")
+ .selected_text(selected_body_label)
+ .show_ui(ui, |ui| {
+
+ for (i, body) in current_system.bodies().iter().enumerate() {
+ ui.selectable_value(
+ &mut state.camera_target,
+ Some(i),
+ body.name());
+ }
+ });
+
+ match state.camera_pos {
+ Some(pos) => { ui.label(format!("Pos x{} y{} z{}", pos.x, pos.y, pos.z)); },
+ None => {}
+ }
});
ui.horizontal(|ui| {