summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--assets/shaders/tacmap/body.wgsl (renamed from assets/shaders/tacbody.wgsl)33
-rw-r--r--assets/shaders/tacmap/grid.wgsl4
-rw-r--r--assets/shaders/tacmap/orbit.wgsl61
-rw-r--r--src/main.rs9
-rw-r--r--src/solar_system.rs22
-rw-r--r--src/tacmap.rs71
-rw-r--r--src/tacmap/camera.rs33
-rw-r--r--src/tacmap/render.rs268
-rw-r--r--src/ui.rs11
-rw-r--r--src/ui/camera_info.rs12
-rw-r--r--src/wgpuctx/pipeline.rs19
-rw-r--r--src/window.rs36
12 files changed, 437 insertions, 142 deletions
diff --git a/assets/shaders/tacbody.wgsl b/assets/shaders/tacmap/body.wgsl
index 62adcdd..7156150 100644
--- a/assets/shaders/tacbody.wgsl
+++ b/assets/shaders/tacmap/body.wgsl
@@ -1,10 +1,6 @@
-struct VertexInput {
- @location(0) position: vec3<f32>
-};
-
struct InstanceInput {
- @location(2) position: vec3<f32>,
- @location(3) radius: f32
+ @location(0) position: vec3<f32>,
+ @location(1) radius: f32
};
struct VertexOutput {
@@ -15,32 +11,43 @@ struct VertexOutput {
struct CameraUniform {
view: mat4x4<f32>,
proj: mat4x4<f32>,
+ pos: vec3<f32>,
scale: f32
};
@group(0) @binding(0)
var<uniform> camera: CameraUniform;
+const QUAD_VERTICES = array<vec3<f32>,6>(
+ vec3<f32>(-1.0, -1.0, 0.0),
+ vec3<f32>(-1.0, 1.0 , 0.0),
+ vec3<f32>(1.0, 1.0 , 0.0),
+ vec3<f32>(1.0, 1.0 , 0.0),
+ vec3<f32>(1.0, -1.0, 0.0),
+ vec3<f32>(-1.0, -1.0, 0.0),
+);
+
@vertex
fn vs_main(
- model: VertexInput,
+ @builtin(vertex_index) index: u32,
instance: InstanceInput
) -> VertexOutput {
var out: VertexOutput;
+ let model = QUAD_VERTICES[index];
var view = camera.view;
-
+
//Billboard the circle
let camera_right = vec3<f32>(view[0][0], view[1][0], view[2][0]);
let camera_up = vec3<f32>(view[0][1], view[1][1], view[2][1]);
- let model_pos = camera_right * model.position.x +
- camera_up * model.position.y;
+ let model_pos = camera_right * model.x +
+ camera_up * model.y;
let min_size = 0.025;
//Scale the world around the camera scale and translate about the camera's
//absolute (/target) position
- let instance_pos = (instance.position) * camera.scale;
+ let instance_pos = (instance.position - camera.pos) * camera.scale;
let view_proj = camera.proj * view;
@@ -50,12 +57,12 @@ fn vs_main(
let vertex_dist = length(vertex_view_pos - center_view_pos) / center_view_pos.w;
if vertex_dist < min_size {
out.clip_position = center_view_pos / center_view_pos.w;
- out.clip_position += camera.proj * vec4<f32>(model.position.xy * (min_size / 2.0), 0.0, 0.0);
+ out.clip_position += camera.proj * vec4<f32>(model.xy * (min_size / 2.0), 0.0, 0.0);
}else{
out.clip_position = vertex_view_pos;
}
- out.local_position = model.position;
+ out.local_position = model;
return out;
}
diff --git a/assets/shaders/tacmap/grid.wgsl b/assets/shaders/tacmap/grid.wgsl
index 625dc7f..822e957 100644
--- a/assets/shaders/tacmap/grid.wgsl
+++ b/assets/shaders/tacmap/grid.wgsl
@@ -7,6 +7,7 @@ struct VertexOutput {
struct CameraUniform {
view: mat4x4<f32>,
proj: mat4x4<f32>,
+ pos: vec3<f32>,
scale: f32
};
@@ -47,7 +48,6 @@ const cell_line_thickness: f32 = 0.001;
const subcell_line_thickness: f32 = 0.0001;
const cell_line_color: vec4<f32> = vec4<f32>(0.25, 0.25, 0.25, 0.5);
-const subcell_line_color: vec4<f32> = vec4<f32>(0.125, 0.125, 0.125, 0.5);
@fragment
fn fs_main(in: VertexOutput
@@ -79,7 +79,7 @@ fn fs_main(in: VertexOutput
var color = vec4<f32>(0.0);
if subcell_dist.x < sclt_real.x || subcell_dist.y < sclt_real.y
- { color = subcell_line_color * subcell_falloff; }
+ { color = cell_line_color * subcell_falloff; }
if cell_dist.x < clt_real.x || cell_dist.y < clt_real.y
{ color = cell_line_color; }
diff --git a/assets/shaders/tacmap/orbit.wgsl b/assets/shaders/tacmap/orbit.wgsl
new file mode 100644
index 0000000..d054a05
--- /dev/null
+++ b/assets/shaders/tacmap/orbit.wgsl
@@ -0,0 +1,61 @@
+struct VertexInput {
+ @builtin(vertex_index) index: u32,
+ @location(0) origin: u32,
+ @location(1) position: vec3<f32>,
+};
+
+struct VertexOutput {
+ @builtin(position) clip_position: vec4<f32>,
+};
+
+struct CameraUniform {
+ view: mat4x4<f32>,
+ proj: mat4x4<f32>,
+ pos: vec3<f32>,
+ scale: f32
+};
+
+@group(0) @binding(0)
+var<uniform> camera: CameraUniform;
+
+@group(1) @binding(0)
+var<storage> origins: array<array<f32,3>>;
+
+const POINT_NORMALS = array<vec2<f32>,4>(
+ vec2<f32>(0.0, -0.5),
+ vec2<f32>(0.0, 0.5),
+ vec2<f32>(0.5, -0.5),
+ vec2<f32>(0.5, 0.5)
+);
+
+@vertex
+fn vs_main(
+ model: VertexInput,
+) -> VertexOutput {
+ var out: VertexOutput;
+
+ let index = model.index % 4u;
+ let normal = POINT_NORMALS[index];
+ let origin = vec3<f32>(
+ origins[model.origin][0],
+ origins[model.origin][1],
+ origins[model.origin][2]);
+
+ let model_pos = (origin + model.position - camera.pos) * camera.scale;
+ let view_proj = camera.proj * camera.view;
+
+ //Scale the world around the camera scale and translate about the camera's
+ //absolute (/target) position
+
+ let point_view_pos = view_proj * vec4<f32>(model_pos, 1.0);
+ out.clip_position = point_view_pos;
+ out.clip_position += vec4<f32>(normal * 0.01, 0.0, 0.0) * point_view_pos.w;
+
+ return out;
+}
+
+@fragment
+fn fs_main(in: VertexOutput
+) -> @location(0) vec4<f32> {
+ return vec4<f32>(0.25, 1.0, 0.25, 1.0);
+}
diff --git a/src/main.rs b/src/main.rs
index d872cc1..7a7e3a1 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -64,7 +64,7 @@ impl GameState
pub fn new()
-> Self {
let timeman = TimeMan::new(0);
- let sol_system = match SolarSystem::new_from_known_star("sol")
+ let sol_system = match SolarSystem::new_from_known_star(0, "sol")
{
Ok(system) => system,
Err(e) => panic!("Unable to create sol system : {}", e)
@@ -139,9 +139,8 @@ impl ApplicationHandler for SystemicApp
self.last_render_time = now;
game_state.borrow_mut().update(delta_time);
- {
- window.update(game_state, delta_time);
- }
+ window.update(game_state, delta_time);
+
match window.render(game_state) {
Ok(_) => {}
Err(wgpu::SurfaceError::Outdated | wgpu::SurfaceError::Lost) => {
@@ -164,7 +163,7 @@ impl ApplicationHandler for SystemicApp
},
..
} => {
- window.keyboard_input(game_state, key_code, key_state);
+ window.keyboard_input(key_code, key_state);
}
_ => {}
}
diff --git a/src/solar_system.rs b/src/solar_system.rs
index c98a0e8..cf6a36a 100644
--- a/src/solar_system.rs
+++ b/src/solar_system.rs
@@ -1,6 +1,6 @@
use serde::{Deserialize};
use crate::{GameState, known_stars::{KNOWN_STARS, StarNotFoundError}, timeman::{Second, TimeMan}};
-use std::{cell::RefCell, error::Error};
+use std::{cell::RefCell, error::Error, f64::consts::PI};
const GRAVITATIONAL_CONSTANT: f64 = 6.67408e-20;
@@ -33,11 +33,13 @@ pub struct SerialOrbitalBody
pub struct OrbitalBody
{
body: SerialOrbitalBody,
+ id: BodyId,
position: Option<cgmath::Vector3<Kilometers>>
}
pub struct SolarSystem
{
+ id: SystemId,
name: String,
bodies: Vec<OrbitalBody>,
time: Option<Second>
@@ -46,6 +48,7 @@ pub struct SolarSystem
impl SolarSystem
{
pub fn new_from_csv(
+ id: SystemId,
data: &'static str)
-> Result<Self, Box<dyn Error>> {
let data_reader = stringreader::StringReader::new(data);
@@ -68,11 +71,13 @@ impl SolarSystem
bodies.push(OrbitalBody {
body: record,
+ id: bodies.len(),
position: None
});
}
Ok(Self {
+ id: id,
name: bodies[0].name().clone(),
bodies: bodies,
time: None
@@ -80,15 +85,18 @@ impl SolarSystem
}
pub fn new_from_known_star(
+ id: SystemId,
star: &'static str)
-> Result<Self, Box<dyn Error>> {
let star_csv = match KNOWN_STARS.get(star).copied() {
Some(csv) => csv,
None => return Err(Box::new(StarNotFoundError { star: star }))
};
- SolarSystem::new_from_csv(star_csv)
+ SolarSystem::new_from_csv(id, star_csv)
}
+ pub fn id(&self) -> SystemId { self.id }
+
pub fn name(&self) -> &String { &self.name }
pub fn bodies(&self)
@@ -140,18 +148,24 @@ impl SolarSystem
impl OrbitalBody
{
+ pub fn id(&self) -> BodyId { self.id }
pub fn name(&self) -> &String { &self.body.name }
pub fn radius(&self) -> f32 { self.body.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 }
- fn calculate_orbit_at(
+ pub fn orbital_period(&self) -> Second
+ { ((self.body.semi_major_axis.powf(3.0) / self.body.sgp).sqrt() * std::f64::consts::TAU) as u64 }
+
+ pub fn calculate_orbit_at(
&self,
time: Second)
-> cgmath::Vector3<f64>
{
if self.body.orbits.is_none() {
return cgmath::Vector3::<f64>::new(0.0, 0.0, 0.0);
- }
+}
let eccentricity = self.body.eccentricity;
diff --git a/src/tacmap.rs b/src/tacmap.rs
index abae50e..de2ddcd 100644
--- a/src/tacmap.rs
+++ b/src/tacmap.rs
@@ -16,6 +16,7 @@ use crate::GameState;
use crate::canvas::Canvas;
use crate::solar_system::SolarSystem;
use crate::solar_system::SystemId;
+use crate::timeman::TimeMan;
use crate::ui;
use crate::wgpuctx::RenderPassBuilder;
use crate::wgpuctx::SceneCtx;
@@ -30,8 +31,11 @@ pub struct TacticalMap
pmatrix: Projection,
camera_controller: CameraController,
- body_renderer: Option<(SystemId, BodyRenderer)>,
- grid_renderer: GridRenderer
+
+ system_for_render: Option<SystemId>,
+ body_renderer: BodyRenderer,
+ grid_renderer: GridRenderer,
+ orbit_renderer: OrbitRenderer
}
impl TacticalMap
@@ -69,8 +73,11 @@ impl TacticalMap
camera: camera,
pmatrix: projection,
camera_controller: CameraController::new(),
- body_renderer: None,
- grid_renderer: GridRenderer::new(wgpuctx)
+
+ system_for_render: None,
+ body_renderer: BodyRenderer::new(wgpuctx),
+ grid_renderer: GridRenderer::new(wgpuctx),
+ orbit_renderer: OrbitRenderer::new(wgpuctx),
}
}
@@ -86,7 +93,7 @@ impl TacticalMap
pub fn update(
&mut self,
- game_state: &RefCell<GameState>,
+ solar_system: &SolarSystem,
ui_state: &mut ui::State,
dt: Duration)
{
@@ -95,12 +102,11 @@ impl TacticalMap
ui_state.camera_info.camera_rot = Some(self.camera.get_rotation());
self.camera.set_target(ui_state.camera_info.target);
- self.camera_controller.update(&mut self.camera, game_state, ui_state, dt);
+ self.camera_controller.update(&mut self.camera, solar_system, ui_state, dt);
}
pub fn keyboard_input(
&mut self,
- game_state: &RefCell<GameState>,
key_code: KeyCode,
key_state: ElementState)
{
@@ -110,41 +116,27 @@ impl TacticalMap
pub fn draw(
&mut self,
wgpuctx: &WgpuCtx,
- game_state: &RefCell<GameState>,
- current_system: Option<SystemId>)
- -> Result<(), wgpu::SurfaceError>
+ solar_system: &SolarSystem,
+ timeman: &TimeMan,
+ ) -> Result<(), wgpu::SurfaceError>
{
- let game_state = game_state.borrow();
-
- let current_system_id = match current_system {
- Some(system) => system,
- None => return Ok(())
- };
- let solar_systems = game_state.solar_systems();
- let current_system = &solar_systems[current_system_id];
-
self.camera.update_buffer(wgpuctx, &self.pmatrix);
+
+ if self.system_for_render.is_none()
+ || self.system_for_render.unwrap() != solar_system.id() {
- let body_renderer = match &mut self.body_renderer {
- Some((id, render)) => {
- if *id != current_system_id {
- *id = current_system_id;
- render.mark_to_rebuild();
- }
- render
- },
- None => {
- let tmp = render::BodyRenderer::new(wgpuctx);
- self.body_renderer = Some((current_system_id, tmp));
- &mut self.body_renderer.as_mut().unwrap().1
- }
- };
+ self.body_renderer.mark_to_rebuild();
+ self.orbit_renderer.mark_to_rebuild();
+ }
+ self.system_for_render = Some(solar_system.id());
//Update buffers for the current system and time.
- body_renderer.rebuild(wgpuctx, current_system);
- match body_renderer.update(
- wgpuctx, current_system, &self.camera,
- game_state.timeman().seconds())
+ self.body_renderer.rebuild(wgpuctx, solar_system);
+ self.orbit_renderer.rebuild(wgpuctx, solar_system);
+
+ self.orbit_renderer.update(wgpuctx, solar_system, timeman.seconds());
+ match self.body_renderer.update(
+ wgpuctx, solar_system, timeman.seconds())
{
Ok(()) => {},
Err(e) => println!("Tactical map render update error: {}", e)
@@ -158,8 +150,9 @@ impl TacticalMap
{
let mut pass = pass_builder.build_from_scene(&mut scene);
- self.grid_renderer.render(wgpuctx, &mut pass, &self.camera);
- body_renderer.render(wgpuctx, &mut pass, &self.camera);
+ self.grid_renderer.render(&mut pass, &self.camera);
+ self.orbit_renderer.render(&mut pass, &self.camera);
+ self.body_renderer.render(&mut pass, &self.camera);
}
scene.submit(wgpuctx);
diff --git a/src/tacmap/camera.rs b/src/tacmap/camera.rs
index 895fa1e..ae9bcfa 100644
--- a/src/tacmap/camera.rs
+++ b/src/tacmap/camera.rs
@@ -3,7 +3,7 @@ use std::{cell::RefCell, time::Duration};
use cgmath::{EuclideanSpace, InnerSpace, Point2, Point3, Rad, Vector2, Vector3, Vector4, Zero, perspective};
use winit::{event::ElementState, keyboard::KeyCode};
-use crate::{GameState, solar_system::{self, Kilometers}, ui, wgpuctx::WgpuCtx};
+use crate::{GameState, solar_system::{self, Kilometers, SolarSystem}, ui, wgpuctx::WgpuCtx};
pub const OPENGL_TO_WGPU_MATRIX: cgmath::Matrix4<f32> = cgmath::Matrix4::from_cols(
Vector4::new(1.0, 0.0, 0.0, 0.0),
@@ -42,6 +42,7 @@ pub struct CameraUniform
{
view: [[f32;4];4],
proj: [[f32;4];4],
+ pos: [f32;3],
scale: f32
}
@@ -68,7 +69,7 @@ impl Camera
let buffer = wgpuctx.create_buffer(
&wgpu::BufferDescriptor {
label: Some("Camera buffer"),
- size: 144,
+ size: size_of::<CameraUniform>() as _,
usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST,
mapped_at_creation: false
}
@@ -77,7 +78,7 @@ impl Camera
let staging_buffer = wgpuctx.create_buffer(
&wgpu::wgt::BufferDescriptor {
label: Some("Camera staging buffer"),
- size: 144,
+ size: size_of::<CameraUniform>() as _,
usage: BufferUsages::COPY_SRC | BufferUsages::COPY_DST,
mapped_at_creation: false
}
@@ -212,6 +213,9 @@ impl Camera
CameraUniform {
view: (self.view_matrix()).into(),
proj: projection.projection_matrix().into(),
+ pos: [ self.abs_position.x as f32,
+ self.abs_position.y as f32,
+ self.abs_position.z as f32 ],
scale: self.scale
}
}
@@ -256,6 +260,9 @@ impl CameraController
target_radius: f32,
dt: Duration)
{
+ camera.scale *= 1.0 + ((self.scale_delta.y - self.scale_delta.x) * 0.1);
+ camera.scale = f32::max(1e-16, f32::min(1.0 / target_radius, camera.scale));
+
camera.abs_position = target;
let dt = dt.as_secs_f32();
@@ -294,6 +301,9 @@ impl CameraController
let dt = dt.as_secs_f32();
let speed = 1.0;
+ camera.scale *= 1.0 + ((self.scale_delta.y - self.scale_delta.x) * 0.1);
+ camera.scale = f32::max(1e-16, f32::min(1.0, camera.scale));
+
let (yaw_sin, yaw_cos) = camera.yaw.0.sin_cos();
let forward = Vector3::new(yaw_cos, 0.0, yaw_sin).normalize();
let right = Vector3::new(-yaw_sin, 0.0, yaw_cos).normalize();
@@ -313,26 +323,17 @@ impl CameraController
pub fn update(
&mut self,
camera: &mut Camera,
- game_state: &RefCell<GameState>,
+ solar_system: &SolarSystem,
ui_state: &mut ui::State,
dt: Duration)
{
- camera.scale *= 1.0 + ((self.scale_delta.y - self.scale_delta.x) * 0.1);
- camera.scale = f32::max(1e-16, f32::min(1e-6, camera.scale));
-
match ui_state.camera_info.target {
Some(body_id) => {
- let game_state = game_state.borrow();
- let solar_systems = game_state.solar_systems();
- let current_system = match ui_state.current_system {
- Some(id) => &solar_systems[id],
- None => return
-};
-
- let body = &current_system.bodies()[body_id];
+
+ let body = &solar_system.bodies()[body_id];
self.update_orbit(
camera,
- current_system.body_position(body).cast().unwrap(),
+ solar_system.body_position(body).cast().unwrap(),
(body.radius() * 2.0).max(1.0),
dt);
}
diff --git a/src/tacmap/render.rs b/src/tacmap/render.rs
index c1256ff..c2bd354 100644
--- a/src/tacmap/render.rs
+++ b/src/tacmap/render.rs
@@ -38,11 +38,9 @@ pub struct BodyRenderer
{
needs_rebuild: bool,
last_time: Option<Second>,
- last_pos: Option<cgmath::Vector3<Kilometers>>,
pipeline: wgpu::RenderPipeline,
- body_vertex_buffer: wgpu::Buffer,
body_instance_buffer: Option<(usize, wgpu::Buffer)>
}
@@ -51,34 +49,22 @@ impl BodyRenderer
pub fn new(
wgpuctx: &WgpuCtx)
-> Self {
- let quad_vertices = vertex::QUAD_VERTICES;
-
- let body_vertex_buffer = wgpuctx.create_buffer_init(
- &wgpu::util::BufferInitDescriptor {
- label: None,
- contents: bytemuck::cast_slice(quad_vertices),
- usage: wgpu::BufferUsages::VERTEX
- }
- );
-
let shader = wgpuctx.create_shader(
wgpu::include_wgsl!(concat!(
env!("CARGO_MANIFEST_DIR"),
- "/assets/shaders/tacbody.wgsl")
+ "/assets/shaders/tacmap/body.wgsl")
));
let render_pipeline = RenderPipelineBuilder::new(&shader)
.add_bindgroup(&Camera::bindgroup_layout(wgpuctx))
- .add_vertex_layout(Vertex::descr())
.add_vertex_layout(BodyInstanceRaw::descr())
+ .cull_mode(None)
.build(Some("Tactical map render pipeline"), wgpuctx);
Self {
needs_rebuild: true,
last_time: None,
- last_pos: None,
pipeline: render_pipeline,
- body_vertex_buffer,
body_instance_buffer: None
}
}
@@ -94,6 +80,7 @@ impl BodyRenderer
if self.body_instance_buffer.is_some() && !self.needs_rebuild {
return;
}
+ self.needs_rebuild = false;
match self.body_instance_buffer.as_mut() {
Some(buffer) => {
@@ -117,7 +104,6 @@ impl BodyRenderer
);
self.last_time = None;
- self.last_pos = None;
self.body_instance_buffer = Some((bodies.len(), buffer));
}
@@ -125,20 +111,16 @@ impl BodyRenderer
&mut self,
wgpuctx: &WgpuCtx,
solar_system: &SolarSystem,
- camera: &Camera,
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 }) {
- if self.last_pos.is_some_and(|last_pos| { last_pos == camera.get_abs_position() }) {
- return Ok(())
- }
+ return Ok(());
}
self.last_time = Some(time);
- self.last_pos = Some(camera.get_abs_position());
let (_, bodies_buffer) = match &self.body_instance_buffer {
Some(tuple) => tuple,
@@ -149,20 +131,18 @@ impl BodyRenderer
let body_instances = bodies.iter().map(|body| {
let position = solar_system.body_position(body);
BodyInstance {
- position: (position - self.last_pos.unwrap()),
+ position: position,
radius: body.radius()
}.raw()
}).collect::<Vec<_>>();
wgpuctx.queue().write_buffer(bodies_buffer, 0, bytemuck::cast_slice(&body_instances));
- //Update the canvas texture.
Ok(())
}
pub fn render(
&self,
- wgpuctx: &WgpuCtx,
pass: &mut RenderPass,
camera: &Camera)
{
@@ -174,8 +154,7 @@ impl BodyRenderer
pass.set_pipeline(&self.pipeline);
pass.set_bind_group(0, camera.bindgroup(), &[]);
- pass.set_vertex_buffer(0, self.body_vertex_buffer.slice(..));
- pass.set_vertex_buffer(1, bodies_buffer.slice(..));
+ pass.set_vertex_buffer(0, bodies_buffer.slice(..));
pass.draw(0..6, 0..num_bodies.clone() as _);
}
@@ -205,12 +184,12 @@ impl BodyInstanceRaw
attributes: &[
wgpu::VertexAttribute {
offset: 0,
- shader_location: 2,
+ shader_location: 0,
format: wgpu::VertexFormat::Float32x3
},
wgpu::VertexAttribute {
offset: mem::size_of::<[f32;3]>() as wgpu::BufferAddress,
- shader_location: 3,
+ shader_location: 1,
format: wgpu::VertexFormat::Float32
}
]
@@ -246,7 +225,6 @@ impl GridRenderer
pub fn render(
&self,
- wgpuctx: &WgpuCtx,
pass: &mut RenderPass,
camera: &Camera
)
@@ -256,4 +234,234 @@ 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 period_interval = period as f64 / 360.0;
+
+ let mut points = vec![
+ OrbitVertex::default();361*2];
+ for i in 0..361 {
+ 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();
+ }
+ let buffer = wgpuctx.create_buffer_init(
+ &wgpu::util::BufferInitDescriptor {
+ label: None,
+ usage: wgpu::BufferUsages::VERTEX,
+ contents: bytemuck::cast_slice(&points)
+ }
+ );
+ (361*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/ui.rs b/src/ui.rs
index f6f7721..42e5fa8 100644
--- a/src/ui.rs
+++ b/src/ui.rs
@@ -65,15 +65,14 @@ impl State
);
}
});
-
- let current_system = match state.current_system {
- Some(id) => &solar_systems[id],
- None => return
- };
});
ui.horizontal(|ui| {
- ui.label(format!("Time: {: <16}", TimeMan::format_duration(game_state.timeman().seconds())));
+ let time_str = TimeMan::format_duration(game_state.timeman().seconds());
+ ui.label(
+ egui::RichText::new(format!("Time: {: >16}", time_str))
+ .font(egui::FontId::monospace(12.0))
+ );
let button_seconds = [
1,
diff --git a/src/ui/camera_info.rs b/src/ui/camera_info.rs
index 35cd54b..ccd06c5 100644
--- a/src/ui/camera_info.rs
+++ b/src/ui/camera_info.rs
@@ -28,7 +28,9 @@ impl CameraWindowState
let current_system = &game_state.solar_systems()[ui_state.current_system.unwrap()];
- egui::Window::new("Camera Info").show(eguictx.context(), |ui| {
+ egui::Window::new("Debug Camera Info")
+ .title_bar(false)
+ .show(eguictx.context(), |ui| {
ui.vertical(|ui| {
let selected_body_label = match camera_state.target {
Some(id) => current_system.bodies()[id].name(),
@@ -50,14 +52,6 @@ impl CameraWindowState
});
ui.label(format!("Scale: {}", camera_state.camera_scale));
- match camera_state.camera_pos {
- Some(pos) => { ui.label(format!("Pos x:{:^10} y:{:^10} z:{:^10}",
- pos.x,
- pos.y,
- pos.z
- )); },
- None => {}
- }
match camera_state.camera_rot {
Some(rot) => {
ui.label(format!("pitch:{:^10} yaw:{:^10}",
diff --git a/src/wgpuctx/pipeline.rs b/src/wgpuctx/pipeline.rs
index 0811800..d698ac4 100644
--- a/src/wgpuctx/pipeline.rs
+++ b/src/wgpuctx/pipeline.rs
@@ -14,7 +14,8 @@ pub struct RenderPipelineBuilder<'a>
vertex_buffer_layouts: Vec<wgpu::VertexBufferLayout<'a>>,
- cull_mode: Option<wgpu::Face>
+ cull_mode: Option<wgpu::Face>,
+ primitive_topology: wgpu::PrimitiveTopology
}
impl<'a> RenderPipelineBuilder<'a>
@@ -34,7 +35,8 @@ impl<'a> RenderPipelineBuilder<'a>
vertex_buffer_layouts: Vec::new(),
- cull_mode: Some(wgpu::Face::Back)
+ cull_mode: Some(wgpu::Face::Back),
+ primitive_topology: wgpu::PrimitiveTopology::TriangleList
}
}
@@ -61,6 +63,13 @@ impl<'a> RenderPipelineBuilder<'a>
self.cull_mode = mode; self
}
+ pub fn primitive_topology(
+ mut self,
+ topology: wgpu::PrimitiveTopology
+ ) -> Self {
+ self.primitive_topology = topology; self
+ }
+
pub fn build(
self,
label: Option<&'static str>,
@@ -109,8 +118,10 @@ impl<'a> RenderPipelineBuilder<'a>
})],
}),
primitive: wgpu::PrimitiveState {
- topology: wgpu::PrimitiveTopology::TriangleList,
- strip_index_format: None,
+ topology: self.primitive_topology,
+ strip_index_format: if self.primitive_topology.is_strip() {
+ Some(wgpu::IndexFormat::Uint32) } else {
+ None },
front_face: wgpu::FrontFace::Ccw,
cull_mode: self.cull_mode,
polygon_mode: wgpu::PolygonMode::Fill,
diff --git a/src/window.rs b/src/window.rs
index 3b090ff..ce1b555 100644
--- a/src/window.rs
+++ b/src/window.rs
@@ -67,24 +67,27 @@ impl GameWindow
game_state: &RefCell<GameState>,
dt: Duration)
{
- {
- let mut game_state = game_state.borrow_mut();
- if self.ui_state.do_auto_tick {
- game_state.timeman.auto_tick = self.ui_state.auto_time;
- }else{
- game_state.timeman.auto_tick = None;
- }
+ let mut game_state = game_state.borrow_mut();
+ if self.ui_state.do_auto_tick {
+ game_state.timeman.auto_tick = self.ui_state.auto_time;
+ }else{
+ game_state.timeman.auto_tick = None;
}
- self.tactical_map.update(game_state, &mut self.ui_state, dt);
+
+ let current_system = match self.ui_state.current_system {
+ Some(id) => &game_state.solar_systems()[id],
+ None => { return; }
+ };
+
+ self.tactical_map.update(current_system, &mut self.ui_state, dt);
}
pub fn keyboard_input(
&mut self,
- game_state: &RefCell<GameState>,
key_code: KeyCode,
key_state: ElementState)
{
- self.tactical_map.keyboard_input(game_state, key_code, key_state);
+ self.tactical_map.keyboard_input(key_code, key_state);
}
pub fn render(
@@ -95,10 +98,15 @@ impl GameWindow
return Ok(());
}
- self.tactical_map.draw(
- &self.wgpuctx,
- game_state,
- self.ui_state.current_system)?;
+ if self.ui_state.current_system.is_some() {
+ let game_state = game_state.borrow();
+ let current_system = &game_state.solar_systems()[self.ui_state.current_system.unwrap()];
+
+ self.tactical_map.draw(
+ &self.wgpuctx,
+ current_system,
+ game_state.timeman())?;
+ }
let view = self.wgpuctx.prepare_surface(&wgpu::TextureViewDescriptor::default())?;
let mut scene = SceneCtx::from_view_default(&self.wgpuctx, &view, Some("Systemic window scene"));