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, pipeline: wgpu::RenderPipeline, fleet_instance_buffer: Option<(usize, wgpu::Buffer)> } struct FleetInstance { offset: cgmath::Vector3, origin: cgmath::Vector3, heading: cgmath::Vector2> } #[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> { //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::>(); 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::() as wgpu::BufferAddress, step_mode: wgpu::VertexStepMode::Instance, attributes: &Self::ATTRIBS } } }