diff options
| author | Jon Santmyer <jon@jonsantmyer.com> | 2026-05-05 09:12:50 -0400 |
|---|---|---|
| committer | Jon Santmyer <jon@jonsantmyer.com> | 2026-05-05 09:12:50 -0400 |
| commit | be2cf936ca48f3d638c3ef01f4e338dfc904c5e3 (patch) | |
| tree | 9fbdbfbf3e0d427f3a6c4bd4c74901e421ef0ad6 /src/tacmap/orbit_render.rs | |
| parent | b35d6cf47ed154697fb45e10586206295148eb35 (diff) | |
| download | systemic4x-be2cf936ca48f3d638c3ef01f4e338dfc904c5e3.tar.gz systemic4x-be2cf936ca48f3d638c3ef01f4e338dfc904c5e3.tar.bz2 systemic4x-be2cf936ca48f3d638c3ef01f4e338dfc904c5e3.zip | |
seperate orbit logic from body
Diffstat (limited to 'src/tacmap/orbit_render.rs')
| -rw-r--r-- | src/tacmap/orbit_render.rs | 240 |
1 files changed, 240 insertions, 0 deletions
diff --git a/src/tacmap/orbit_render.rs b/src/tacmap/orbit_render.rs new file mode 100644 index 0000000..d79294e --- /dev/null +++ b/src/tacmap/orbit_render.rs @@ -0,0 +1,240 @@ +use super::*; + +use crate::solar_system::BodyId; +use crate::timeman::{SYSTEM_TICK_INTERVAL, Second}; +use crate::wgpuctx::{WgpuCtx, pipeline::RenderPipelineBuilder}; + +pub struct OrbitRenderer +{ + needs_rebuild: bool, + last_time: Option<Second>, + + pipeline: wgpu::RenderPipeline, + + orbit_vertex_buffers: Option<Vec<(BodyId, Option<wgpu::Buffer>)>>, + origin_buffer: Option<wgpu::Buffer>, + origin_bind_group: Option<wgpu::BindGroup>, + origin_bind_group_layout: wgpu::BindGroupLayout +} + +impl OrbitRenderer +{ + pub fn new( + wgpuctx: &WgpuCtx) + -> Self { + let shader = wgpuctx.create_shader( + wgpu::include_wgsl!(concat!( + env!("CARGO_MANIFEST_DIR"), + "/assets/shaders/tacmap/orbit.wgsl") + )); + + let origin_bind_group_layout = wgpuctx.device().create_bind_group_layout( + &wgpu::BindGroupLayoutDescriptor { + label: None, + entries: &[ + wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: wgpu::ShaderStages::VERTEX, + ty: wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Storage { read_only: true }, + has_dynamic_offset: false, + min_binding_size: None + }, + count: None + } + ] + } + ); + + let render_pipeline = RenderPipelineBuilder::new(&shader) + .add_bindgroup(&Camera::bindgroup_layout(wgpuctx)) + .add_bindgroup(&origin_bind_group_layout) + .add_vertex_layout(OrbitVertex::descr()) + .primitive_topology(wgpu::PrimitiveTopology::TriangleStrip) + .cull_mode(None) + .build(Some("Tactical map orbit render pipeline"), wgpuctx); + + Self { + needs_rebuild: true, + last_time: None, + pipeline: render_pipeline, + orbit_vertex_buffers: None, + origin_buffer: None, + origin_bind_group: None, + origin_bind_group_layout: origin_bind_group_layout + } + } + + pub fn mark_to_rebuild(&mut self) + { self.needs_rebuild = true; } + + pub fn rebuild( + &mut self, + wgpuctx: &WgpuCtx, + solar_system: &SolarSystem) + { + if self.orbit_vertex_buffers.is_some() && !self.needs_rebuild { + return; + } + self.needs_rebuild = false; + + match &self.orbit_vertex_buffers { + Some(buffers) => { + for buffer in buffers { + match &buffer.1 { + Some(v) => v.destroy(), + None => {} + } + } + self.orbit_vertex_buffers = None; + }, + None => {} + }; + + //From the solar system's bodies, calculate the orbits. + let bodies = solar_system.bodies(); + + match &self.origin_buffer { + Some(buffer) => { + buffer.destroy(); + } + None => {} + }; + let origin_buffer = wgpuctx.create_buffer( + &wgpu::BufferDescriptor { + label: None, + size: (std::mem::size_of::<[f32;3]>() * bodies.len()) as _, + usage: wgpu::BufferUsages::COPY_DST + | wgpu::BufferUsages::STORAGE, + mapped_at_creation: false + } + ); + + self.origin_bind_group = Some(wgpuctx.device().create_bind_group( + &wgpu::BindGroupDescriptor { + label: None, + layout: &self.origin_bind_group_layout, + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: origin_buffer.as_entire_binding() + } + ] + } + )); + self.origin_buffer = Some(origin_buffer); + + self.orbit_vertex_buffers = Some(bodies.iter().map(|body| { + if !body.does_orbit() { return (0, None); } + + let period = body.orbital_period(); + let num_points = ((period / (SYSTEM_TICK_INTERVAL)) as usize).clamp(180, 360*4); + 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( + (i as f64 * period_interval) as u64); + + points[i*2].origin_id = body.get_orbits().unwrap() as _; + points[i*2].position = [ + position.x as f32, + position.y as f32, + position.z as f32 + ]; + + points[(i*2)+1] = points[i*2].clone(); + } + points[num_points*2] = points[0].clone(); + points[num_points*2+1] = points[1].clone(); + + let buffer = wgpuctx.create_buffer_init( + &wgpu::util::BufferInitDescriptor { + label: None, + usage: wgpu::BufferUsages::VERTEX, + contents: bytemuck::cast_slice(&points) + } + ); + ((num_points+1)*2, Some(buffer)) + }).collect::<Vec<_>>()); + } + + pub fn update( + &mut self, + wgpuctx: &WgpuCtx, + solar_system: &SolarSystem, + time: Second) + { + if self.last_time.is_some_and(|t| { t == time }) { + return; + } + + self.last_time = Some(time); + let positions = solar_system.bodies().iter().map(|body| { + let pos = solar_system.body_position(body); + [ pos.x as f32, pos.y as f32, pos.z as f32 ] + }).collect::<Vec<_>>(); + + match &self.origin_buffer { + Some(buffer) => { + wgpuctx.queue().write_buffer(buffer, 0, bytemuck::cast_slice(&positions)); + }, + None => { return; } + }; + } + + pub fn render( + &self, + pass: &mut wgpu::RenderPass, + camera: &Camera) + { + let buffers = match &self.orbit_vertex_buffers { + Some(v) => v, + None => return + }; + + pass.set_pipeline(&self.pipeline); + pass.set_bind_group(0, camera.bindgroup(), &[]); + + match &self.origin_bind_group { + Some(bind_group) => { pass.set_bind_group(1, bind_group, &[]); }, + None => { return; } + } + + 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 => {} + } + } + } +} + +#[repr(C)] +#[derive(Copy, Clone, Default, Debug, bytemuck::Pod, bytemuck::Zeroable)] +pub struct OrbitVertex +{ + pub origin_id: u32, + pub position: [f32;3] +} + +impl OrbitVertex +{ + const ATTRIBS: [wgpu::VertexAttribute;2] = + wgpu::vertex_attr_array![0 => Uint32, 1 => 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::Vertex, + attributes: &Self::ATTRIBS + } + } +} |
