summaryrefslogtreecommitdiffstats
path: root/src/tacmap/body_render.rs
diff options
context:
space:
mode:
authorJon Santmyer <jon@jonsantmyer.com>2026-05-05 09:12:50 -0400
committerJon Santmyer <jon@jonsantmyer.com>2026-05-05 09:12:50 -0400
commitbe2cf936ca48f3d638c3ef01f4e338dfc904c5e3 (patch)
tree9fbdbfbf3e0d427f3a6c4bd4c74901e421ef0ad6 /src/tacmap/body_render.rs
parentb35d6cf47ed154697fb45e10586206295148eb35 (diff)
downloadsystemic4x-be2cf936ca48f3d638c3ef01f4e338dfc904c5e3.tar.gz
systemic4x-be2cf936ca48f3d638c3ef01f4e338dfc904c5e3.tar.bz2
systemic4x-be2cf936ca48f3d638c3ef01f4e338dfc904c5e3.zip
seperate orbit logic from body
Diffstat (limited to 'src/tacmap/body_render.rs')
-rw-r--r--src/tacmap/body_render.rs193
1 files changed, 193 insertions, 0 deletions
diff --git a/src/tacmap/body_render.rs b/src/tacmap/body_render.rs
new file mode 100644
index 0000000..8b32532
--- /dev/null
+++ b/src/tacmap/body_render.rs
@@ -0,0 +1,193 @@
+use std::error::Error;
+
+use crate::solar_system::Kilometers;
+use crate::timeman::Second;
+use crate::wgpuctx::{WgpuCtx, pipeline::RenderPipelineBuilder};
+
+use super::*;
+
+pub struct BodyRenderer
+{
+ needs_rebuild: bool,
+ last_time: Option<Second>,
+
+ pipeline: wgpu::RenderPipeline,
+
+ body_instance_buffer: Option<(usize, wgpu::Buffer)>
+}
+
+impl BodyRenderer
+{
+ pub fn new(
+ wgpuctx: &WgpuCtx)
+ -> Self {
+ let shader = wgpuctx.create_shader(
+ wgpu::include_wgsl!(concat!(
+ env!("CARGO_MANIFEST_DIR"),
+ "/assets/shaders/tacmap/body.wgsl")
+ ));
+
+ let render_pipeline = RenderPipelineBuilder::new(&shader)
+ .add_bindgroup(&Camera::bindgroup_layout(wgpuctx))
+ .add_vertex_layout(BodyInstanceRaw::descr())
+ .cull_mode(None)
+ .build(Some("Tactical map render pipeline"), wgpuctx);
+
+ Self {
+ needs_rebuild: true,
+ last_time: None,
+ pipeline: render_pipeline,
+ body_instance_buffer: None
+ }
+ }
+
+ pub fn mark_to_rebuild(&mut self)
+ { self.needs_rebuild = true; }
+
+ pub fn rebuild(
+ &mut self,
+ wgpuctx: &WgpuCtx,
+ solar_system: &SolarSystem)
+ {
+ if self.body_instance_buffer.is_some() && !self.needs_rebuild {
+ return;
+ }
+ self.needs_rebuild = false;
+
+ match self.body_instance_buffer.as_mut() {
+ Some(buffer) => {
+ buffer.1.destroy();
+ self.body_instance_buffer = None;
+ }
+ None => {}
+ }
+
+ let bodies = solar_system.bodies();
+ let buffer_len = bodies.len() * size_of::<BodyInstanceRaw>();
+
+ let buffer = wgpuctx.create_buffer(
+ &wgpu::BufferDescriptor {
+ label: Some("Tactical map bodies instance buffer"),
+ size: buffer_len as u64,
+ usage: wgpu::BufferUsages::COPY_DST |
+ wgpu::BufferUsages::VERTEX,
+ mapped_at_creation: false
+ }
+ );
+
+ self.last_time = None;
+ self.body_instance_buffer = Some((bodies.len(), buffer));
+ }
+
+ pub fn update(
+ &mut self,
+ wgpuctx: &WgpuCtx,
+ 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);
+
+ let (_, bodies_buffer) = match &self.body_instance_buffer {
+ Some(tuple) => tuple,
+ None => return Err(Box::new(NeedsRebuildError))
+ };
+
+ let bodies = solar_system.bodies();
+ let body_instances = bodies.iter().map(|body| {
+ let position = body.position();
+ let origin = match body.get_orbits() {
+ Some(origin_id) => {
+ let origin_body = &bodies[origin_id];
+ origin_body.position()
+ },
+ None => cgmath::Vector3::new(0.0, 0.0, 0.0)
+ };
+ BodyInstance {
+ position: position,
+ origin: origin,
+ radius: body.radius()
+ }.raw()
+ }).collect::<Vec<_>>();
+
+ wgpuctx.queue().write_buffer(bodies_buffer, 0, bytemuck::cast_slice(&body_instances));
+
+ Ok(())
+ }
+
+ pub fn render(
+ &self,
+ pass: &mut wgpu::RenderPass,
+ camera: &Camera)
+ {
+ let (num_bodies, bodies_buffer) = match &self.body_instance_buffer {
+ Some(tuple) => tuple,
+ None => return
+ };
+
+ pass.set_pipeline(&self.pipeline);
+ pass.set_bind_group(0, camera.bindgroup(), &[]);
+
+ pass.set_vertex_buffer(0, bodies_buffer.slice(..));
+
+ pass.draw(0..6, 0..num_bodies.clone() as _);
+ }
+} // impl RenderState
+
+struct BodyInstance
+{
+ position: cgmath::Vector3<Kilometers>,
+ origin: cgmath::Vector3<Kilometers>,
+ radius: f32
+}
+
+#[repr(C)]
+#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
+struct BodyInstanceRaw
+{
+ position: [f32;3],
+ origin: [f32;3],
+ radius: f32
+}
+
+impl BodyInstance
+{
+ fn raw(&self) -> BodyInstanceRaw
+ {
+ BodyInstanceRaw {
+ position: [
+ self.position.x as f32,
+ self.position.y as f32,
+ self.position.z as f32 ],
+ origin: [
+ self.origin.x as f32,
+ self.origin.y as f32,
+ self.origin.z as f32 ],
+ radius: self.radius
+ }
+ }
+} // impl BodyInstance
+
+impl BodyInstanceRaw
+{
+ const ATTRIBS: [wgpu::VertexAttribute;3] =
+ wgpu::vertex_attr_array![0 => Float32x3, 1 => Float32x3, 2 => Float32];
+
+ 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
+ }
+ }
+}
+