summaryrefslogtreecommitdiffstats
path: root/src/tacmap
diff options
context:
space:
mode:
authorJon Santmyer <jon@jonsantmyer.com>2026-05-10 13:29:56 -0400
committerJon Santmyer <jon@jonsantmyer.com>2026-05-10 13:29:56 -0400
commit7f63ec5c10eb7e8dd4edaabd1a6a437328911d39 (patch)
tree36bd3d75ebc0c25256413c21a7cb28c9296953f5 /src/tacmap
parentc9041e2e6fe59d6127bb1085b874e8e3cda8000e (diff)
downloadsystemic4x-7f63ec5c10eb7e8dd4edaabd1a6a437328911d39.tar.gz
systemic4x-7f63ec5c10eb7e8dd4edaabd1a6a437328911d39.tar.bz2
systemic4x-7f63ec5c10eb7e8dd4edaabd1a6a437328911d39.zip
fleets
Diffstat (limited to 'src/tacmap')
-rw-r--r--src/tacmap/body_render.rs32
-rw-r--r--src/tacmap/camera.rs10
-rw-r--r--src/tacmap/fleet_render.rs177
-rw-r--r--src/tacmap/orbit_render.rs70
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);
}
}
}