From c9041e2e6fe59d6127bb1085b874e8e3cda8000e Mon Sep 17 00:00:00 2001 From: Jon Santmyer Date: Thu, 7 May 2026 10:12:11 -0400 Subject: more intense position-relative rendering to fix some precision problems --- assets/shaders/tacmap/body.wgsl | 20 ++++---- assets/shaders/tacmap/grid.wgsl | 3 +- assets/shaders/tacmap/orbit.wgsl | 10 ++-- src/solar_system/orbit.rs | 2 + src/tacmap.rs | 9 ++-- src/tacmap/camera.rs | 100 ++++++++++++++++++++++----------------- src/ui.rs | 25 ++++------ src/ui/bodies_window.rs | 41 +++++++++++++--- src/ui/camera_info.rs | 58 ----------------------- src/window.rs | 5 +- 10 files changed, 123 insertions(+), 150 deletions(-) delete mode 100644 src/ui/camera_info.rs diff --git a/assets/shaders/tacmap/body.wgsl b/assets/shaders/tacmap/body.wgsl index e91c841..0cfd5f9 100644 --- a/assets/shaders/tacmap/body.wgsl +++ b/assets/shaders/tacmap/body.wgsl @@ -6,14 +6,16 @@ struct InstanceInput { struct VertexOutput { @builtin(position) clip_position: vec4, - @location(0) local_position: vec3 + @location(0) local_position: vec3, + @location(1) distance: f32 }; struct CameraUniform { view: mat4x4, proj: mat4x4, - pos: vec3, - scale: f32 + abs_pos: vec3, + rel_pos: vec3, + scale: f32, }; @group(0) @binding(0) @@ -49,18 +51,18 @@ fn vs_main( //Scale the world around the camera scale and translate about the camera's //absolute (/target) position let relative_pos = instance.position; - let origin_pos = (instance.origin - camera.pos); + let origin_pos = instance.origin - camera.abs_pos; - if all(relative_pos != origin_pos) { + /*if all(relative_pos != origin_pos) { if length(relative_pos) < 0.01 / camera.scale { if length(relative_pos) != 0.0 { out.clip_position = vec4(0.0, 0.0, 0.0, 1.0); return out; } } - } + }*/ - let instance_pos = (relative_pos + origin_pos) * camera.scale; + let instance_pos = (relative_pos + origin_pos - camera.rel_pos) * camera.scale; let view_proj = camera.proj * view; @@ -76,6 +78,7 @@ fn vs_main( } out.local_position = model; + out.distance = length(instance_pos); return out; } @@ -88,5 +91,6 @@ fn fs_main(in: VertexOutput if point_dist > 1.0 { alpha = 0.0; } - return vec4(1.0, 1.0, 1.0, alpha); + let color = vec3(1.0, 1.0, 1.0) / clamp(in.distance, 1.0, 1.5); + return vec4(color, alpha); } diff --git a/assets/shaders/tacmap/grid.wgsl b/assets/shaders/tacmap/grid.wgsl index 822e957..1d2c4dc 100644 --- a/assets/shaders/tacmap/grid.wgsl +++ b/assets/shaders/tacmap/grid.wgsl @@ -7,7 +7,8 @@ struct VertexOutput { struct CameraUniform { view: mat4x4, proj: mat4x4, - pos: vec3, + abs_pos: vec3, + rel_pos: vec3, scale: f32 }; diff --git a/assets/shaders/tacmap/orbit.wgsl b/assets/shaders/tacmap/orbit.wgsl index 901200f..a890fd8 100644 --- a/assets/shaders/tacmap/orbit.wgsl +++ b/assets/shaders/tacmap/orbit.wgsl @@ -11,7 +11,8 @@ struct VertexOutput { struct CameraUniform { view: mat4x4, proj: mat4x4, - pos: vec3, + abs_pos: vec3, + rel_pos: vec3, scale: f32 }; @@ -37,13 +38,10 @@ fn vs_main( origins[model.origin][1], origins[model.origin][2]); - let model_pos = ((origin - camera.pos) + model.position) * camera.scale; - let origin_pos = (origin - camera.pos) * camera.scale; + let model_pos = ((origin - camera.abs_pos - camera.rel_pos) + model.position) * camera.scale; + let origin_pos = (origin - camera.abs_pos - camera.rel_pos) * camera.scale; let view = camera.view; - let camera_right = vec3(view[0][0], view[1][0], view[2][0]); - let camera_up = vec3(view[0][1], view[1][1], view[2][1]); - let camera_forward = vec3(view[0][2], view[1][2], view[2][2]); let orbit_normal = normalize(model.position); var normal = orbit_normal; diff --git a/src/solar_system/orbit.rs b/src/solar_system/orbit.rs index 1800127..3a1d129 100644 --- a/src/solar_system/orbit.rs +++ b/src/solar_system/orbit.rs @@ -63,6 +63,8 @@ impl StaticOrbit self.parent } + pub fn sma(&self) -> Kilometers { self.semi_major_axis } + pub fn calculate_position_at( &self, sgp: f64, diff --git a/src/tacmap.rs b/src/tacmap.rs index b093fc1..cd9d878 100644 --- a/src/tacmap.rs +++ b/src/tacmap.rs @@ -80,10 +80,7 @@ impl TacticalMap ui_state: &mut ui::State, dt: Duration) { - ui_state.camera_info.camera_scale = self.camera.get_scale(); - ui_state.camera_info.camera_pos = Some(self.camera.get_abs_position()); - ui_state.camera_info.camera_rot = Some(self.camera.get_rotation()); - self.camera.set_target(ui_state.camera_info.target); + self.camera.set_target(ui_state.camera_target); self.camera_controller.update(&mut self.camera, solar_system, ui_state, dt); } @@ -164,7 +161,7 @@ impl TacticalMap let scaled_radius = (screen_size.y * body.radius() * self.camera.get_scale()).max(16.0); let world_pos = solar_system.body_position(body); - let local_pos = world_pos - self.camera.get_abs_position(); + let local_pos = world_pos - self.camera.get_combined_position(); let origin_pos = match body.get_orbit() { Some(orbit) => { @@ -172,7 +169,7 @@ impl TacticalMap }, None => cgmath::vec3(0.0, 0.0, 0.0) }; - let origin_local_pos = origin_pos - self.camera.get_abs_position(); + let origin_local_pos = origin_pos - self.camera.get_origin_position(); let scaled_pos = local_pos * self.camera.get_scale() as f64; let scaled_origin_pos = origin_local_pos * self.camera.get_scale() as f64; diff --git a/src/tacmap/camera.rs b/src/tacmap/camera.rs index 57856e2..af163b8 100644 --- a/src/tacmap/camera.rs +++ b/src/tacmap/camera.rs @@ -1,16 +1,17 @@ use std::time::Duration; -use cgmath::{InnerSpace, Point3, Rad, Vector2, Vector3, perspective}; +use cgmath::{InnerSpace, Point3, Rad, Vector2, Vector3, Zero, perspective}; use winit::event::ElementState; use winit::keyboard::KeyCode; +use crate::solar_system::body::OrbitalBody; use crate::solar_system::{self, Kilometers, SolarSystem}; use crate::ui; use crate::wgpuctx::WgpuCtx; pub struct Camera { - abs_position: Vector3, + origin_position: Vector3, rel_position: Vector3, pitch: Rad, yaw: Rad, @@ -38,8 +39,10 @@ pub struct CameraUniform { view: [[f32;4];4], proj: [[f32;4];4], - pos: [f32;3], - scale: f32 + origin_pos: [f32;3], + _pad0: u32, + rel_pos: [f32;3], + scale: f32, } pub struct Projection @@ -94,7 +97,7 @@ impl Camera } ); Self { - abs_position: position.into(), + origin_position: position.into(), rel_position: Vector3::new(0.0, 0.0, 0.0), yaw: yaw.into(), pitch: pitch.into(), @@ -109,12 +112,12 @@ impl Camera pub fn get_scale(&self) -> f32 { self.scale } - pub fn get_abs_position(&self) -> Vector3 - { self.abs_position } + pub fn get_origin_position(&self) -> Vector3 + { self.origin_position } pub fn get_combined_position(&self) -> Vector3 { - self.abs_position + self.rel_position.map(|v| { v as f64 }) + self.origin_position + self.rel_position.map(|v| { v as f64 }) } pub fn get_rotation(&self) -> Vector2> @@ -170,7 +173,7 @@ impl Camera { if target == self.target { return; } if !target.is_some() || !self.target.is_some() { - self.abs_position = self.get_combined_position(); + self.origin_position = self.get_combined_position(); self.rel_position = Vector3::new(0.0, 0.0, 0.0); } self.target = target; @@ -180,21 +183,25 @@ impl Camera &self) -> cgmath::Matrix4 { - let (yaw_sin, yaw_cos) = self.pitch.0.sin_cos(); - let (pitch_sin, pitch_cos) = self.yaw.0.sin_cos(); + let (yaw_sin, yaw_cos) = self.yaw.0.sin_cos(); + let (pitch_sin, pitch_cos) = self.pitch.0.sin_cos(); + + let eye_pos = cgmath::point3( + pitch_cos * yaw_cos, + pitch_sin, + pitch_cos * yaw_sin + ); if self.target.is_some() { cgmath::Matrix4::look_at_rh( - Point3::new(self.rel_position.x, self.rel_position.y, self.rel_position.z), - Point3::new(0.0, 0.0, 0.0), + eye_pos, + cgmath::point3(0.0, 0.0, 0.0), Vector3::unit_y()) }else{ cgmath::Matrix4::look_to_rh( - Point3::new(0.0, 0.0, 0.0), - Vector3::new( - pitch_cos * yaw_cos, - pitch_sin, - pitch_cos * yaw_sin + cgmath::point3(0.0, 0.0, 0.0), + cgmath::vec3( + eye_pos.x, eye_pos.y, eye_pos.z ).normalize(), Vector3::unit_y() ) @@ -209,10 +216,17 @@ impl Camera CameraUniform { view: (self.view_matrix()).into(), proj: projection.projection_matrix().into(), - pos: [ self.abs_position.x as f32, - self.abs_position.y as f32, - self.abs_position.z as f32 ], - scale: self.scale + origin_pos: [ + self.origin_position.x as f32, + self.origin_position.y as f32, + self.origin_position.z as f32 ], + rel_pos: [ + self.rel_position.x as f32, + self.rel_position.y as f32, + self.rel_position.z as f32 ], + scale: self.scale, + + _pad0: 0 } } } //impl Camera @@ -252,14 +266,25 @@ impl CameraController fn update_orbit( &mut self, camera: &mut Camera, - target: Vector3, - target_radius: f32, + solar_system: &SolarSystem, + target: &OrbitalBody, dt: Duration) { + let target_radius = (target.radius() * 2.0).max(1.0); camera.scale *= 1.0 + ((self.scale_delta.y - self.scale_delta.x) * 0.1); camera.scale = f32::max(1e-16, f32::min(1.0 / target_radius, camera.scale)); - camera.abs_position = target; + match target.get_orbit() { + Some(orbit) => { + let parent = solar_system.body(orbit.parent()); + camera.origin_position = parent.absolute_position(solar_system); + camera.rel_position = target.relative_position().map(|v| { v as f32 }); + }, + None => { + camera.origin_position = target.absolute_position(solar_system); + camera.rel_position.set_zero(); + } + } let dt = dt.as_secs_f32(); let speed = 1.0; @@ -274,16 +299,6 @@ impl CameraController { camera.pitch.0 = polar_final; } 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 = 1.0; - camera.rel_position = Vector3::new( - radius * p_cos * az_cos, - radius * p_sin, - radius * p_cos * az_sin - ); } fn update_pan( @@ -302,12 +317,9 @@ impl CameraController let right = Vector3::new(-yaw_sin, 0.0, yaw_cos).normalize(); let up = Vector3::new(0.0, 1.0, 0.0); - camera.abs_position += (forward * (self.position_pos_delta.z - self.position_neg_delta.z) * speed * dt) - .map(|v| { v as f64 } ); - camera.abs_position += (right * (self.position_pos_delta.x - self.position_neg_delta.x) * speed * dt) - .map(|v| { v as f64 }); - camera.abs_position += (up * (self.position_pos_delta.y - self.position_neg_delta.y) * speed * dt) - .map(|v| { v as f64 }); + 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.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; @@ -320,14 +332,14 @@ impl CameraController ui_state: &mut ui::State, dt: Duration) { - match ui_state.camera_info.target { + match ui_state.camera_target { Some(body_id) => { let body = &solar_system.bodies()[body_id]; self.update_orbit( camera, - solar_system.body_position(body).cast().unwrap(), - (body.radius() * 2.0).max(1.0), + solar_system, + body, dt); } None => self.update_pan(camera, dt) diff --git a/src/ui.rs b/src/ui.rs index 6a48f2d..235481d 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -1,22 +1,21 @@ pub mod topbar; -pub mod camera_info; pub mod bodies_window; pub mod fleet_window; use std::{borrow::Borrow, cell::RefCell}; - -use crate::{GameState, eguictx::EguiCtx, ui::{bodies_window::BodiesWindowState, camera_info::CameraWindowState, fleet_window::FleetWindowState, topbar::TopBarState}}; - -mod ui { - -} +use crate::GameState; +use crate::eguictx::EguiCtx; +use crate::solar_system::body::BodyId; +use crate::ui::bodies_window::BodiesWindowState; +use crate::ui::fleet_window::FleetWindowState; +use crate::ui::topbar::TopBarState; #[derive(Default, Clone)] pub struct State { + pub camera_target: Option, pub topbar_sate: TopBarState, - pub camera_info: CameraWindowState, pub bodies_window: BodiesWindowState, pub fleet_window: FleetWindowState @@ -45,17 +44,11 @@ impl State None => return }; - CameraWindowState::render( - self, - &game_state, - eguictx); - if self.topbar_sate.bodies_window_visible { let bodies_window_action = self.bodies_window.render(current_system, eguictx); - - if let Some(body) = bodies_window_action.focus_body { - self.camera_info.target = Some(body); + if bodies_window_action.focus_body.is_some() { + self.camera_target = bodies_window_action.focus_body; } } diff --git a/src/ui/bodies_window.rs b/src/ui/bodies_window.rs index 6300f5f..ece605b 100644 --- a/src/ui/bodies_window.rs +++ b/src/ui/bodies_window.rs @@ -4,6 +4,7 @@ use crate::eguictx::EguiCtx; use crate::ntree::{NTree, NTreeNode}; use crate::solar_system::body::{BodyId, OrbitalBody}; use crate::solar_system::{SolarSystem, SystemId}; +use crate::timeman::TimeMan; #[derive(Default, Clone)] @@ -159,13 +160,39 @@ impl BodiesWindowState }; ui.separator(); - egui::Frame::canvas(ui.style()) - .show(ui, |ui| { - ui.set_width(200.0); - ui.vertical_centered(|ui| { - ui.label( - egui::RichText::new(selected_body.name()) - .heading()); + ui.vertical(|ui| { + egui::Frame::canvas(ui.style()) + .show(ui, |ui| { + ui.set_width(200.0); + ui.vertical_centered(|ui| { + ui.label( + egui::RichText::new(selected_body.name()) + .heading()); + ui.separator(); + }); + + ui.vertical(|ui| { + ui.vertical_centered(|ui| { + ui.label("Physical Properties"); + }); + ui.label(format!("Mass: {:.4E} kg", selected_body.mass())); + ui.label(format!("Radius: {} km", selected_body.radius())); + + if let Some(orbit) = selected_body.get_orbit() { + ui.vertical_centered(|ui| { + ui.label("Orbital Properties"); + }); + ui.label(format!("Orbiting {}", + star_system.body(orbit.parent()).name() + )); + ui.label(format!("Period: {}", + TimeMan::format_duration(orbit.period(selected_body)) + )); + ui.label(format!("Semi-major Axis: {:.4E} km", + orbit.sma() + )); + } + }); }); ui.horizontal(|ui| { diff --git a/src/ui/camera_info.rs b/src/ui/camera_info.rs deleted file mode 100644 index 5332370..0000000 --- a/src/ui/camera_info.rs +++ /dev/null @@ -1,58 +0,0 @@ -use std::cell::RefCell; - -use crate::{GameState, eguictx::EguiCtx, solar_system, ui}; - - -#[derive(Default, Clone)] -pub struct CameraWindowState -{ - pub camera_scale: f32, - pub camera_pos: Option>, - pub camera_rot: Option>>, - pub target: Option, -} - -impl CameraWindowState -{ - pub fn render( - ui_state: &mut ui::State, - game_state: &GameState, - eguictx: &EguiCtx) - { - let topbar_state = &ui_state.topbar_sate; - if topbar_state.current_system.is_none() { - return; - } - - let camera_state = &mut ui_state.camera_info; - - let current_system = &game_state.solar_systems()[topbar_state.current_system.unwrap()]; - - egui::Window::new("Debug Camera Info") - .title_bar(false) - .show(eguictx.context(), |ui| { - ui.vertical(|ui| { - let selected_body_label = match camera_state.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 camera_state.target, - Some(i), - body.name()); - } - }); - - ui.label(format!("Scale: {}", camera_state.camera_scale)); - }); - }); - } -} diff --git a/src/window.rs b/src/window.rs index 3461a53..d895040 100644 --- a/src/window.rs +++ b/src/window.rs @@ -40,14 +40,11 @@ impl GameWindow let tacmap = TacticalMap::new(&wgpuctx); let ui_state = ui::State{ + camera_target: Some(0), topbar_sate: ui::topbar::TopBarState { current_system : Some(0), ..Default::default() }, - camera_info: ui::camera_info::CameraWindowState { - target: Some(0), - ..Default::default() - }, ..Default::default() }; -- cgit v1.2.3