diff options
Diffstat (limited to 'src/tacmap/body_render.rs')
| -rw-r--r-- | src/tacmap/body_render.rs | 193 |
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 + } + } +} + |
