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 | |
| parent | b35d6cf47ed154697fb45e10586206295148eb35 (diff) | |
| download | systemic4x-be2cf936ca48f3d638c3ef01f4e338dfc904c5e3.tar.gz systemic4x-be2cf936ca48f3d638c3ef01f4e338dfc904c5e3.tar.bz2 systemic4x-be2cf936ca48f3d638c3ef01f4e338dfc904c5e3.zip | |
seperate orbit logic from body
Diffstat (limited to 'src')
| -rw-r--r-- | src/solar_system.rs | 95 | ||||
| -rw-r--r-- | src/solar_system/orbit.rs | 29 | ||||
| -rw-r--r-- | src/solar_system/ship.rs | 4 | ||||
| -rw-r--r-- | src/tacmap.rs | 4 | ||||
| -rw-r--r-- | src/tacmap/body_render.rs | 193 | ||||
| -rw-r--r-- | src/tacmap/orbit_render.rs | 240 | ||||
| -rw-r--r-- | src/tacmap/render.rs | 420 | ||||
| -rw-r--r-- | src/window.rs | 3 |
8 files changed, 541 insertions, 447 deletions
diff --git a/src/solar_system.rs b/src/solar_system.rs index f4ca9ae..c53f045 100644 --- a/src/solar_system.rs +++ b/src/solar_system.rs @@ -1,5 +1,8 @@ +pub mod orbit; +pub mod ship; + use serde::{Deserialize}; -use crate::{known_stars::*, timeman::Second}; +use crate::{known_stars::*, solar_system::orbit::StaticOrbit, timeman::Second}; use std::error::Error; const GRAVITATIONAL_CONSTANT: f64 = 6.67408e-20; @@ -13,7 +16,7 @@ pub type BodyId = usize; pub type SystemId = usize; #[derive(Debug, Deserialize)] -pub struct SerialOrbitalBody +pub struct CSVOrbitalBody { name: String, orbits: Option<BodyId>, @@ -32,7 +35,12 @@ pub struct SerialOrbitalBody pub struct OrbitalBody { - body: SerialOrbitalBody, + name: String, + mass: Kilograms, + radius: Kilometers, + sgp: f64, + + orbit: Option<StaticOrbit>, position: Option<cgmath::Vector3<Kilometers>> } @@ -64,12 +72,12 @@ impl SolarSystem let mut bodies = Vec::<OrbitalBody>::new(); for result in body_reader.deserialize() { - let mut record: SerialOrbitalBody = result?; + let mut record: CSVOrbitalBody = result?; match record.orbits { Some(orbits) => { if record.sgp == 0.0 { - record.sgp = (bodies[orbits].body.mass + record.mass) * GRAVITATIONAL_CONSTANT; + record.sgp = (bodies[orbits].mass + record.mass) * GRAVITATIONAL_CONSTANT; } } None => {} @@ -78,7 +86,19 @@ impl SolarSystem println!("New body: {:?}", record); bodies.push(OrbitalBody { - body: record, + name: record.name, + mass: record.mass, + radius: record.radius, + sgp: record.sgp, + orbit: Some(StaticOrbit { + parent: record.orbits, + eccentricity: record.eccentricity, + inclination: record.inclination, + long_asc_node: record.long_asc_node, + long_periapsis: record.long_periapsis, + mean_long: record.mean_long, + semi_major_axis: record.semi_major_axis + }), position: None }); } @@ -116,11 +136,16 @@ impl SolarSystem body: &OrbitalBody) -> cgmath::Vector3<Kilometers> { - match body.body.orbits { - Some(parent) => { - let body_pos = body.position(); - let parent_pos = self.bodies[parent].position(); - body_pos + parent_pos + match &body.orbit { + Some(orbit) => { + match orbit.parent { + Some(parent) => { + let this_pos = body.position(); + let parent_pos = self.bodies[parent].position(); + this_pos + parent_pos + }, + None => body.position() + } }, None => body.position() } @@ -138,33 +163,53 @@ impl SolarSystem impl OrbitalBody { - pub fn name(&self) -> &String { &self.body.name } - pub fn radius(&self) -> f32 { self.body.radius as f32 } + pub fn name(&self) -> &String { &self.name } + pub fn radius(&self) -> f32 { self.radius as f32 } pub fn position(&self) -> cgmath::Vector3<f64> { self.position.unwrap() } - pub fn does_orbit(&self) -> bool { self.body.orbits.is_some() } - pub fn get_orbits(&self) -> Option<BodyId> { self.body.orbits } + pub fn does_orbit(&self) -> bool { + match &self.orbit { + Some(orbit) => orbit.parent.is_some(), + None => false + } + } + pub fn get_orbits(&self) -> Option<BodyId> { + match &self.orbit { + Some(orbit) => orbit.parent(), + None => None + } + } pub fn orbital_period(&self) -> Second - { ((self.body.semi_major_axis.powf(3.0) / self.body.sgp).sqrt() * std::f64::consts::TAU) as u64 } + { + match &self.orbit { + Some(v) => v.period(self), + None => return 0 + } + } pub fn calculate_orbit_at( &self, time: Second) -> cgmath::Vector3<f64> { - if self.body.orbits.is_none() { + let orbit = match &self.orbit { + Some(v) => v, + None => return cgmath::vec3(0.0, 0.0, 0.0) + }; + + if orbit.parent.is_none() { return cgmath::Vector3::<f64>::new(0.0, 0.0, 0.0); } - let eccentricity = self.body.eccentricity; + let eccentricity = orbit.eccentricity; - let long_asc_node = self.body.long_asc_node; - let arg_periaps = self.body.long_periapsis - long_asc_node; + let long_asc_node = orbit.long_asc_node; + let arg_periaps = orbit.long_periapsis - long_asc_node; - let sma_cubed = self.body.semi_major_axis.powf(3.0); - let mean_motion = (self.body.sgp / sma_cubed).sqrt(); + let sma_cubed = orbit.semi_major_axis.powf(3.0); + let mean_motion = (self.sgp / sma_cubed).sqrt(); - let mean_anomaly_epoch = self.body.mean_long - self.body.long_periapsis; + let mean_anomaly_epoch = orbit.mean_long - orbit.long_periapsis; let mean_anomaly = mean_anomaly_epoch + (mean_motion * time as f64); //Find the eccentric anomaly via newton's method @@ -192,9 +237,9 @@ impl OrbitalBody let vw = true_anomaly + arg_periaps; let (vw_sin, vw_cos) = vw.sin_cos(); let (omega_sin, omega_cos) = long_asc_node.sin_cos(); - let (i_sin, i_cos) = self.body.inclination.sin_cos(); + let (i_sin, i_cos) = orbit.inclination.sin_cos(); - let r = self.body.semi_major_axis * (1.0 - eccentricity * eccentric_anomaly.cos()); + let r = orbit.semi_major_axis * (1.0 - eccentricity * eccentric_anomaly.cos()); let x = r * (omega_cos * vw_cos - omega_sin * vw_sin * i_cos); let y = r * i_sin * vw_sin; let z = r * (omega_sin * vw_cos + omega_cos * vw_sin * i_cos); diff --git a/src/solar_system/orbit.rs b/src/solar_system/orbit.rs new file mode 100644 index 0000000..fe55e4f --- /dev/null +++ b/src/solar_system/orbit.rs @@ -0,0 +1,29 @@ +use crate::solar_system::{Angle, BodyId, Kilometers, OrbitalBody, Percentage}; +use crate::timeman::{Second}; + +pub struct StaticOrbit +{ + pub parent: Option<BodyId>, + pub eccentricity: Percentage, + pub inclination: Angle, + pub long_asc_node: Angle, + pub long_periapsis: Angle, + pub mean_long: Angle, + + pub semi_major_axis: Kilometers, +} + +impl StaticOrbit +{ + pub fn period( + &self, + this_body: &OrbitalBody) + -> Second + { + ((self.semi_major_axis.powf(3.0) / this_body.sgp).sqrt() * std::f64::consts::TAU) as u64 + } + + pub fn parent(&self) -> Option<BodyId> { + self.parent + } +} diff --git a/src/solar_system/ship.rs b/src/solar_system/ship.rs new file mode 100644 index 0000000..eb4cdf3 --- /dev/null +++ b/src/solar_system/ship.rs @@ -0,0 +1,4 @@ +pub struct Ship +{ + +} diff --git a/src/tacmap.rs b/src/tacmap.rs index 461d819..222363f 100644 --- a/src/tacmap.rs +++ b/src/tacmap.rs @@ -1,5 +1,7 @@ pub mod camera; pub mod render; +mod body_render; +mod orbit_render; use std::time::Duration; @@ -14,6 +16,8 @@ use crate::ui; use crate::wgpuctx::SceneCtx; use crate::wgpuctx::WgpuCtx; use render::*; +use body_render::*; +use orbit_render::*; use camera::*; pub struct TacticalMap 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 + } + } +} + 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 + } + } +} diff --git a/src/tacmap/render.rs b/src/tacmap/render.rs index e6944d0..fd93941 100644 --- a/src/tacmap/render.rs +++ b/src/tacmap/render.rs @@ -19,191 +19,6 @@ impl Display for NeedsRebuildError { impl Error for NeedsRebuildError {} -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 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 - } - } -} - pub struct GridRenderer { pipeline: wgpu::RenderPipeline, @@ -242,238 +57,3 @@ impl GridRenderer pass.draw(0..6, 0..1); } } //impl GridRenderer - -pub struct OrbitRenderer -{ - needs_rebuild: bool, - last_time: Option<Second>, - - pipeline: wgpu::RenderPipeline, - - orbit_vertex_buffers: Option<Vec<(usize, 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 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 - } - } -} diff --git a/src/window.rs b/src/window.rs index 73034ef..3461a53 100644 --- a/src/window.rs +++ b/src/window.rs @@ -6,8 +6,7 @@ use winit::event::{ElementState, WindowEvent}; use winit::keyboard::KeyCode; use crate::tacmap::TacticalMap; -use crate::{GameState, SystemicApp, ui}; -use crate::solar_system::{SolarSystem, SystemId}; +use crate::{GameState, ui}; use crate::wgpuctx::{RenderPassBuilder, SceneCtx, WgpuCtx}; use crate::eguictx::EguiCtx; |
