summaryrefslogtreecommitdiffstats
path: root/src/tacmap/orbit_render.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/tacmap/orbit_render.rs')
-rw-r--r--src/tacmap/orbit_render.rs240
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
+ }
+ }
+}