diff options
| author | Jon Santmyer <jon@jonsantmyer.com> | 2026-05-10 13:29:56 -0400 |
|---|---|---|
| committer | Jon Santmyer <jon@jonsantmyer.com> | 2026-05-10 13:29:56 -0400 |
| commit | 7f63ec5c10eb7e8dd4edaabd1a6a437328911d39 (patch) | |
| tree | 36bd3d75ebc0c25256413c21a7cb28c9296953f5 /src/tacmap | |
| parent | c9041e2e6fe59d6127bb1085b874e8e3cda8000e (diff) | |
| download | systemic4x-7f63ec5c10eb7e8dd4edaabd1a6a437328911d39.tar.gz systemic4x-7f63ec5c10eb7e8dd4edaabd1a6a437328911d39.tar.bz2 systemic4x-7f63ec5c10eb7e8dd4edaabd1a6a437328911d39.zip | |
fleets
Diffstat (limited to 'src/tacmap')
| -rw-r--r-- | src/tacmap/body_render.rs | 32 | ||||
| -rw-r--r-- | src/tacmap/camera.rs | 10 | ||||
| -rw-r--r-- | src/tacmap/fleet_render.rs | 177 | ||||
| -rw-r--r-- | src/tacmap/orbit_render.rs | 70 |
4 files changed, 224 insertions, 65 deletions
diff --git a/src/tacmap/body_render.rs b/src/tacmap/body_render.rs index 92ac13f..23a04c9 100644 --- a/src/tacmap/body_render.rs +++ b/src/tacmap/body_render.rs @@ -1,7 +1,8 @@ use std::error::Error; -use crate::solar_system::Kilometers; -use crate::timeman::{SYSTEM_TICK_INTERVAL, Second}; +use crate::solar_system::{Kilometers}; +use crate::solar_system::body::OrbitalBody; +use crate::timeman::{Second}; use crate::wgpuctx::{WgpuCtx, pipeline::RenderPipelineBuilder}; use super::*; @@ -9,7 +10,7 @@ use super::*; pub struct BodyRenderer { needs_rebuild: bool, - last_time: Option<Second>, + last_tick: Option<i64>, pipeline: wgpu::RenderPipeline, @@ -35,7 +36,7 @@ impl BodyRenderer Self { needs_rebuild: true, - last_time: None, + last_tick: None, pipeline: render_pipeline, body_instance_buffer: None } @@ -75,7 +76,7 @@ impl BodyRenderer } ); - self.last_time = None; + self.last_tick = None; self.body_instance_buffer = Some((bodies.len(), buffer)); } @@ -88,23 +89,12 @@ impl BodyRenderer { //If the last updated time is the same, we don't need to update //the positions of all the bodies. - if self.last_time.is_some_and(|last_time| { last_time == time }) { + let tick = time / OrbitalBody::TICK_DURATION; + if self.last_tick.is_some_and(|last_tick| { last_tick == tick }) { return Ok(()); } - //Round to the nearest tick - let tick_time = match self.last_time { - Some(last_time) => { - let this_tick = time / SYSTEM_TICK_INTERVAL; - let last_tick = last_time / SYSTEM_TICK_INTERVAL; - - if this_tick == last_tick { return Ok(()) } - this_tick * SYSTEM_TICK_INTERVAL - } - None => 0 - }; - - self.last_time = Some(time); + self.last_tick = Some(time); let (_, bodies_buffer) = match &self.body_instance_buffer { Some(tuple) => tuple, @@ -113,11 +103,11 @@ impl BodyRenderer let bodies = solar_system.bodies(); let body_instances = bodies.iter().map(|body| { - let position = body.relative_position(); + let position = body.relative_position(time); let origin = match body.get_orbit() { Some(orbit) => { let origin_body = &bodies[orbit.parent()]; - origin_body.relative_position() + origin_body.relative_position(time) }, None => cgmath::Vector3::new(0.0, 0.0, 0.0) }; diff --git a/src/tacmap/camera.rs b/src/tacmap/camera.rs index af163b8..1f2549b 100644 --- a/src/tacmap/camera.rs +++ b/src/tacmap/camera.rs @@ -6,6 +6,7 @@ use winit::keyboard::KeyCode; use crate::solar_system::body::OrbitalBody; use crate::solar_system::{self, Kilometers, SolarSystem}; +use crate::timeman::Second; use crate::ui; use crate::wgpuctx::WgpuCtx; @@ -268,6 +269,7 @@ impl CameraController camera: &mut Camera, solar_system: &SolarSystem, target: &OrbitalBody, + time: Second, dt: Duration) { let target_radius = (target.radius() * 2.0).max(1.0); @@ -277,11 +279,11 @@ impl CameraController 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 }); + camera.origin_position = parent.absolute_position(solar_system, time); + camera.rel_position = target.relative_position(time).map(|v| { v as f32 }); }, None => { - camera.origin_position = target.absolute_position(solar_system); + camera.origin_position = target.absolute_position(solar_system, time); camera.rel_position.set_zero(); } } @@ -330,6 +332,7 @@ impl CameraController camera: &mut Camera, solar_system: &SolarSystem, ui_state: &mut ui::State, + time: Second, dt: Duration) { match ui_state.camera_target { @@ -340,6 +343,7 @@ impl CameraController camera, solar_system, body, + time, dt); } None => self.update_pan(camera, dt) diff --git a/src/tacmap/fleet_render.rs b/src/tacmap/fleet_render.rs new file mode 100644 index 0000000..ba21318 --- /dev/null +++ b/src/tacmap/fleet_render.rs @@ -0,0 +1,177 @@ +use std::error::Error; + +use crate::fleet::FleetsManager; +use crate::solar_system::orbit::StaticOrbiter; +use crate::solar_system::{Kilometers}; +use crate::timeman::{Second}; +use crate::wgpuctx::{WgpuCtx, pipeline::RenderPipelineBuilder}; + +use super::*; + +pub struct FleetRenderer +{ + last_time: Option<Second>, + + pipeline: wgpu::RenderPipeline, + + fleet_instance_buffer: Option<(usize, wgpu::Buffer)> +} + +struct FleetInstance +{ + offset: cgmath::Vector3<Kilometers>, + origin: cgmath::Vector3<Kilometers>, + heading: cgmath::Vector2<cgmath::Rad<f32>> +} + +#[repr(C)] +#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] +struct FleetInstanceRaw +{ + rotmat: [[f32;3];3], + origin: [f32;3], + offset: [f32;3], +} + +impl FleetRenderer +{ + pub fn new( + wgpuctx: &WgpuCtx) + -> Self { + let shader = wgpuctx.create_shader( + wgpu::include_wgsl!(concat!( + env!("CARGO_MANIFEST_DIR"), + "/assets/shaders/tacmap/fleet.wgsl") + )); + + let render_pipeline = RenderPipelineBuilder::new(&shader) + .add_bindgroup(&Camera::bindgroup_layout(wgpuctx)) + .add_vertex_layout(FleetInstanceRaw::descr()) + .cull_mode(None) + .build(Some("Tactical map fleet render pipeline"), wgpuctx); + + Self { + last_time: None, + pipeline: render_pipeline, + fleet_instance_buffer: None + } + } + + pub fn update( + &mut self, + wgpuctx: &WgpuCtx, + fleets_manager: &FleetsManager, + solar_system: &SolarSystem, + time: Second) + -> Result<(), Box<dyn Error>> + { + //If the last updated time is the same, we don't need to update + //the positions of all the bodies. + if self.last_time.is_some_and(|last_time| { last_time == time }) { + return Ok(()); + } + + self.last_time = Some(time); + + if let Some((_, buffer)) = &self.fleet_instance_buffer { + buffer.destroy(); + self.fleet_instance_buffer = None; + } + + let fleets = solar_system.fleets(fleets_manager); + let fleets_instances = fleets.iter().filter_map(|id| { + let fleet = match fleets_manager.entry(*id) { + Some(fleet) => fleet, + None => { return None; } + }; + let heading = fleet.heading(); + let offset = fleet.offset_position(); + let origin = match fleet.orbit() { + Some(orbit) => { + let origin_body = solar_system.body(orbit.parent()); + origin_body.absolute_position(solar_system, time) + }, + None => cgmath::vec3(0.0, 0.0, 0.0) + }; + Some( + FleetInstance { + heading: *heading, + offset: *offset, + origin: origin + }.raw() + ) + }).collect::<Vec<_>>(); + let fleets_buffer = wgpuctx.create_buffer_init( + &wgpu::util::BufferInitDescriptor { + label: None, + contents: bytemuck::cast_slice(fleets_instances.as_slice()), + usage: wgpu::BufferUsages::VERTEX + }); + + self.fleet_instance_buffer = Some((fleets.len(), fleets_buffer)); + + Ok(()) + } + + pub fn render( + &self, + pass: &mut wgpu::RenderPass, + camera: &Camera) + { + let (num_fleets, fleets_buffer) = match &self.fleet_instance_buffer { + Some(tuple) => tuple, + None => return + }; + if *num_fleets == 0 { return; } + + pass.set_pipeline(&self.pipeline); + pass.set_bind_group(0, camera.bindgroup(), &[]); + + pass.set_vertex_buffer(0, fleets_buffer.slice(..)); + + pass.draw(0..6, 0..*num_fleets as _); + } +} // impl RenderState + +impl FleetInstance +{ + fn raw(&self) -> FleetInstanceRaw + { + FleetInstanceRaw { + rotmat: (cgmath::Matrix3::from_angle_x(self.heading.x) * + cgmath::Matrix3::from_angle_y(self.heading.y)) + .into(), + origin: [ + self.origin.x as f32, + self.origin.y as f32, + self.origin.z as f32 + ], + offset: [ + self.offset.x as f32, + self.offset.y as f32, + self.offset.z as f32 + ] + } + } +} // impl BodyInstance + +impl FleetInstanceRaw +{ + const ATTRIBS: [wgpu::VertexAttribute;5] = + wgpu::vertex_attr_array![ + 0 => Float32x3, 1 => Float32x3, 2 => Float32x3, + 3 => Float32x3, + 4 => Float32x3 + ]; + + pub fn descr() + -> wgpu::VertexBufferLayout<'static> { + use std::mem; + + wgpu::VertexBufferLayout { + array_stride: mem::size_of::<Self>() as wgpu::BufferAddress, + step_mode: wgpu::VertexStepMode::Instance, + attributes: &Self::ATTRIBS + } + } +} diff --git a/src/tacmap/orbit_render.rs b/src/tacmap/orbit_render.rs index c003366..7c0a089 100644 --- a/src/tacmap/orbit_render.rs +++ b/src/tacmap/orbit_render.rs @@ -3,17 +3,18 @@ use std::error::Error; use super::*; use crate::solar_system::body::{BodyId, OrbitalBody}; -use crate::timeman::{SYSTEM_TICK_INTERVAL, Second}; +use crate::solar_system::orbit::StaticOrbiter; +use crate::timeman::{self, Second}; use crate::wgpuctx::{WgpuCtx, pipeline::RenderPipelineBuilder}; pub struct OrbitRenderer { needs_rebuild: bool, - last_time: Option<Second>, + last_tick: Option<Second>, pipeline: wgpu::RenderPipeline, - orbit_vertex_buffers: Option<Vec<(BodyId, Option<wgpu::Buffer>)>>, + orbit_vertex_buffers: Option<Vec<(BodyId, wgpu::Buffer)>>, origin_buffer: Option<wgpu::Buffer>, origin_bind_group: Option<wgpu::BindGroup>, origin_bind_group_layout: wgpu::BindGroupLayout @@ -58,7 +59,7 @@ impl OrbitRenderer Self { needs_rebuild: true, - last_time: None, + last_tick: None, pipeline: render_pipeline, orbit_vertex_buffers: None, origin_buffer: None, @@ -70,28 +71,29 @@ impl OrbitRenderer pub fn mark_to_rebuild(&mut self) { self.needs_rebuild = true; } - fn create_orbit_buffer_for_body( + fn create_orbit_buffer<T: StaticOrbiter>( &self, wgpuctx: &WgpuCtx, - body: &OrbitalBody, + orbiter: &T, time: Second) - -> (usize, Option<wgpu::Buffer>) + -> Option<(usize, wgpu::Buffer)> { - let orbit = match body.get_orbit() { + let orbit = match orbiter.orbit() { Some(orbit) => orbit, - None => return (0, None) + None => { return None; } }; - let period = body.orbital_period(); + let period = orbit.period(orbiter); - let num_points = ((period / (SYSTEM_TICK_INTERVAL)) as usize).clamp(90, 360); + let num_points = ((period / timeman::HOUR) as usize).clamp(90, 360); let period_interval = period as f64 / num_points as f64; let mut points = vec![ OrbitVertex::default();(num_points+1)*2]; for i in 0..num_points { - let position = body.calculate_orbit_at( + let position = orbit.calculate_position_at( + orbiter, time + (i as f64 * period_interval) as Second); points[i*2].origin_id = orbit.parent() as _; @@ -113,7 +115,7 @@ impl OrbitRenderer contents: bytemuck::cast_slice(&points) } ); - ((num_points+1)*2, Some(buffer)) + Some(((num_points+1)*2, buffer)) } pub fn rebuild( @@ -129,10 +131,7 @@ impl OrbitRenderer match &self.orbit_vertex_buffers { Some(buffers) => { for buffer in buffers { - match &buffer.1 { - Some(v) => v.destroy(), - None => {} - } + buffer.1.destroy(); } self.orbit_vertex_buffers = None; }, @@ -171,6 +170,10 @@ impl OrbitRenderer } )); self.origin_buffer = Some(origin_buffer); + + self.orbit_vertex_buffers = Some(bodies.iter().filter_map(|body| { + self.create_orbit_buffer(wgpuctx, body, 0) + }).collect::<Vec<_>>()); } pub fn update( @@ -180,24 +183,14 @@ impl OrbitRenderer time: Second) -> Result<(), Box<dyn Error>> { - if self.last_time.is_some_and(|t| { t == time }) { + let tick = time / OrbitalBody::TICK_DURATION; + if self.last_tick.is_some_and(|t| { t == tick }) { return Ok(()); } - - let tick_time = match self.last_time { - Some(last_time) => { - let this_tick = time / SYSTEM_TICK_INTERVAL; - let last_tick = last_time / SYSTEM_TICK_INTERVAL; - - if this_tick == last_tick { return Ok(()) } - this_tick * SYSTEM_TICK_INTERVAL - } - None => 0 - }; - self.last_time = Some(time); + self.last_tick = Some(time); let positions = solar_system.bodies().iter().map(|body| { - let pos = solar_system.body_position(body); + let pos = solar_system.body_position(body, time); [ pos.x as f32, pos.y as f32, pos.z as f32 ] }).collect::<Vec<_>>(); @@ -208,10 +201,10 @@ impl OrbitRenderer None => { return Err(Box::new(NeedsRebuildError)); } }; - let bodies = solar_system.bodies(); - self.orbit_vertex_buffers = Some(bodies.iter().map(|body| { + //let bodies = solar_system.bodies(); + /*self.orbit_vertex_buffers = Some(bodies.iter().map(|body| { self.create_orbit_buffer_for_body(wgpuctx, body, tick_time) - }).collect::<Vec<_>>()); + }).collect::<Vec<_>>());*/ Ok(()) } @@ -234,13 +227,8 @@ impl OrbitRenderer } for (num_points, buffer) in buffers { - match &buffer { - Some(buffer) => { - pass.set_vertex_buffer(0, buffer.slice(..)); - pass.draw(0..(*num_points as u32), 0..1); - }, - None => {} - } + pass.set_vertex_buffer(0, buffer.slice(..)); + pass.draw(0..(*num_points as u32), 0..1); } } } |
