Minor reorganization

master
Mark 2024-01-17 10:17:18 -08:00
parent 246079b33a
commit 3561baa99c
Signed by: Mark
GPG Key ID: C6D63995FE72FD80
13 changed files with 369 additions and 290 deletions

View File

@ -44,7 +44,7 @@ impl<'a> Game {
ShipHandle { index: 0 }, ShipHandle { index: 0 },
FactionHandle { index: 0 }, FactionHandle { index: 0 },
ShipPersonality::Player, ShipPersonality::Player,
Point2::new(0.0, 0.0), Point2::new(0.0, 4000.0),
); );
let s = self.state.systemsim.get_ship_mut(&player).unwrap(); let s = self.state.systemsim.get_ship_mut(&player).unwrap();

View File

@ -28,6 +28,12 @@ fn anchor(
} else if anchor == 5u { // NE NE } else if anchor == 5u { // NE NE
trans += vec2(window_dim.x, window_dim.y) / 2.0; trans += vec2(window_dim.x, window_dim.y) / 2.0;
trans += vec2(-dim.x, -dim.y) / 2.0; trans += vec2(-dim.x, -dim.y) / 2.0;
} else if anchor == 6u { // C C
trans += vec2(0.0, 0.0) / 2.0;
trans += vec2(0.0, 0.0) / 2.0;
} else if anchor == 7u { // C NW
trans += vec2(0.0, 0.0) / 2.0;
trans += vec2(dim.x, -dim.y) / 2.0;
} else { // center / center as default, since it's the most visible variant. } else { // center / center as default, since it's the most visible variant.
trans += vec2(0.0, 0.0) / 2.0; trans += vec2(0.0, 0.0) / 2.0;
trans += vec2(0.0, 0.0) / 2.0; trans += vec2(0.0, 0.0) / 2.0;

View File

@ -1,78 +0,0 @@
use galactica_content::{Content, SystemHandle};
use galactica_playeragent::PlayerAgent;
use galactica_system::phys::{ParticleBuilder, PhysSim};
use galactica_util::timing::Timing;
use glyphon::{FontSystem, SwashCache, TextAtlas, TextRenderer};
use nalgebra::Vector2;
use std::rc::Rc;
use wgpu::BufferAddress;
use winit::window::Window;
use crate::{globaluniform::GlobalUniform, vertexbuffer::VertexBuffer};
/// Bundles parameters passed to a single call to GPUState::render
pub struct RenderInput<'a> {
/// Camera position, in world units
pub camera_pos: Vector2<f32>,
/// Player ship data
pub player: &'a PlayerAgent,
/// The system we're currently in
pub current_system: SystemHandle,
/// Height of screen, in world units
pub camera_zoom: f32,
/// The world state to render
pub systemsim: &'a PhysSim,
// TODO: handle overflow. is it a problem?
/// The current time, in seconds
pub current_time: f32,
/// Game content
pub ct: &'a Content,
/// Particles to spawn during this frame
pub particles: &'a Vec<ParticleBuilder>,
/// Time we spent in each part of the game loop
pub timing: Timing,
}
/// Renderer state. A reference to this struct is often passed to helper functions.
pub(crate) struct RenderState {
pub window: Window,
pub window_size: winit::dpi::PhysicalSize<u32>,
pub window_aspect: f32,
pub queue: wgpu::Queue,
pub global_uniform: GlobalUniform,
pub vertex_buffers: VertexBuffers,
pub text_font_system: FontSystem,
pub text_cache: SwashCache,
pub text_atlas: TextAtlas,
pub text_renderer: TextRenderer,
}
/// Vertex buffers
pub(crate) struct VertexBuffers {
// Keeps track of length of each buffer
// Most of these are reset on each frame.
//
// The exception is particle_counter, which
// is never reset, and loops back to zero once
// it exceeds buffer length
pub object_counter: BufferAddress,
pub ui_counter: BufferAddress,
pub particle_counter: BufferAddress,
pub radialbar_counter: BufferAddress,
pub object: Rc<VertexBuffer>,
pub starfield: Rc<VertexBuffer>,
pub ui: Rc<VertexBuffer>,
pub particle: Rc<VertexBuffer>,
pub radialbar: Rc<VertexBuffer>,
}

View File

@ -3,9 +3,7 @@ use galactica_content::Content;
use wgpu; use wgpu;
use winit; use winit;
use crate::{ use crate::{starfield::Starfield, texturearray::TextureArray, ui::UiManager, RenderState};
datastructs::RenderState, starfield::Starfield, texturearray::TextureArray, ui::UiManager,
};
/// GPUState is very big, so its methods have been split /// GPUState is very big, so its methods have been split
/// among the following files. /// among the following files.

View File

@ -1,28 +1,11 @@
use anyhow::Result; use anyhow::Result;
use galactica_content::Content; use galactica_content::Content;
use galactica_util::constants::{
OBJECT_SPRITE_INSTANCE_LIMIT, PARTICLE_SPRITE_INSTANCE_LIMIT, UI_SPRITE_INSTANCE_LIMIT,
};
use glyphon::{FontSystem, SwashCache, TextAtlas, TextRenderer}; use glyphon::{FontSystem, SwashCache, TextAtlas, TextRenderer};
use std::rc::Rc;
use crate::{ use crate::{
datastructs::{RenderState, VertexBuffers}, globaluniform::GlobalUniform, pipeline::PipelineBuilder, shaderprocessor::preprocess_shader,
globaluniform::GlobalUniform, starfield::Starfield, texturearray::TextureArray, ui::UiManager, GPUState, RenderState,
pipeline::PipelineBuilder, VertexBuffers,
shaderprocessor::preprocess_shader,
starfield::Starfield,
texturearray::TextureArray,
ui::UiManager,
vertexbuffer::{
consts::{SPRITE_INDICES, SPRITE_VERTICES},
types::{
ObjectInstance, ParticleInstance, RadialBarInstance, StarfieldInstance, TexturedVertex,
UiInstance,
},
VertexBuffer,
},
GPUState,
}; };
impl GPUState { impl GPUState {
@ -90,52 +73,7 @@ impl GPUState {
surface.configure(&device, &config); surface.configure(&device, &config);
} }
let vertex_buffers = VertexBuffers { let vertex_buffers = VertexBuffers::new(&device, ct);
object_counter: 0,
ui_counter: 0,
particle_counter: 0,
radialbar_counter: 0,
object: Rc::new(VertexBuffer::new::<TexturedVertex, ObjectInstance>(
"object",
&device,
Some(SPRITE_VERTICES),
Some(SPRITE_INDICES),
OBJECT_SPRITE_INSTANCE_LIMIT,
)),
starfield: Rc::new(VertexBuffer::new::<TexturedVertex, StarfieldInstance>(
"starfield",
&device,
Some(SPRITE_VERTICES),
Some(SPRITE_INDICES),
ct.get_config().starfield_instance_limit,
)),
ui: Rc::new(VertexBuffer::new::<TexturedVertex, UiInstance>(
"ui",
&device,
Some(SPRITE_VERTICES),
Some(SPRITE_INDICES),
UI_SPRITE_INSTANCE_LIMIT,
)),
particle: Rc::new(VertexBuffer::new::<TexturedVertex, ParticleInstance>(
"particle",
&device,
Some(SPRITE_VERTICES),
Some(SPRITE_INDICES),
PARTICLE_SPRITE_INSTANCE_LIMIT,
)),
radialbar: Rc::new(VertexBuffer::new::<TexturedVertex, RadialBarInstance>(
"radial bar",
&device,
Some(SPRITE_VERTICES),
Some(SPRITE_INDICES),
10,
)),
};
// Load uniforms // Load uniforms
let global_uniform = GlobalUniform::new(&device); let global_uniform = GlobalUniform::new(&device);
@ -197,7 +135,7 @@ impl GPUState {
)) ))
.set_format(config.format) .set_format(config.format)
.set_triangle(true) .set_triangle(true)
.set_vertex_buffer(&vertex_buffers.object) .set_vertex_buffer(vertex_buffers.get_object())
.set_bind_group_layouts(bind_group_layouts) .set_bind_group_layouts(bind_group_layouts)
.build(); .build();
@ -213,7 +151,7 @@ impl GPUState {
)) ))
.set_format(config.format) .set_format(config.format)
.set_triangle(true) .set_triangle(true)
.set_vertex_buffer(&vertex_buffers.starfield) .set_vertex_buffer(vertex_buffers.get_starfield())
.set_bind_group_layouts(bind_group_layouts) .set_bind_group_layouts(bind_group_layouts)
.build(); .build();
@ -225,7 +163,7 @@ impl GPUState {
)) ))
.set_format(config.format) .set_format(config.format)
.set_triangle(true) .set_triangle(true)
.set_vertex_buffer(&vertex_buffers.ui) .set_vertex_buffer(vertex_buffers.get_ui())
.set_bind_group_layouts(bind_group_layouts) .set_bind_group_layouts(bind_group_layouts)
.build(); .build();
@ -241,7 +179,7 @@ impl GPUState {
)) ))
.set_format(config.format) .set_format(config.format)
.set_triangle(true) .set_triangle(true)
.set_vertex_buffer(&vertex_buffers.particle) .set_vertex_buffer(vertex_buffers.get_particle())
.set_bind_group_layouts(bind_group_layouts) .set_bind_group_layouts(bind_group_layouts)
.build(); .build();
@ -257,7 +195,7 @@ impl GPUState {
)) ))
.set_format(config.format) .set_format(config.format)
.set_triangle(true) .set_triangle(true)
.set_vertex_buffer(&vertex_buffers.radialbar) .set_vertex_buffer(vertex_buffers.get_radialbar())
.set_bind_group_layouts(bind_group_layouts) .set_bind_group_layouts(bind_group_layouts)
.build(); .build();
@ -281,7 +219,7 @@ impl GPUState {
}; };
return Ok(Self { return Ok(Self {
ui: UiManager::new(&mut state), ui: UiManager::new(ct, &mut state),
device, device,
config, config,
surface, surface,

View File

@ -2,13 +2,11 @@
use bytemuck; use bytemuck;
use galactica_system::data::ShipState; use galactica_system::data::ShipState;
use galactica_util::{constants::OBJECT_SPRITE_INSTANCE_LIMIT, to_radians}; use galactica_util::to_radians;
use nalgebra::{Point2, Point3}; use nalgebra::{Point2, Point3};
use crate::{ use crate::{
globaluniform::ObjectData, globaluniform::ObjectData, vertexbuffer::types::ObjectInstance, GPUState, RenderInput,
vertexbuffer::{types::ObjectInstance, BufferObject},
GPUState, RenderInput,
}; };
impl GPUState { impl GPUState {
@ -68,7 +66,7 @@ impl GPUState {
continue; continue;
} }
let idx = self.state.vertex_buffers.object_counter; let idx = self.state.get_object_counter();
// Write this object's location data // Write this object's location data
self.state.queue.write_buffer( self.state.queue.write_buffer(
&self.state.global_uniform.object_buffer, &self.state.global_uniform.object_buffer,
@ -85,22 +83,11 @@ impl GPUState {
}]), }]),
); );
// Enforce buffer limit
if self.state.vertex_buffers.object_counter as u64 > OBJECT_SPRITE_INSTANCE_LIMIT {
// TODO: no panic, handle this better.
panic!("Sprite limit exceeded!")
}
// Push this object's instance // Push this object's instance
self.state.queue.write_buffer( self.state.push_object_buffer(ObjectInstance {
&self.state.vertex_buffers.object.instances,
ObjectInstance::SIZE * self.state.vertex_buffers.object_counter,
bytemuck::cast_slice(&[ObjectInstance {
sprite_index: ship_cnt.sprite.get_index(), sprite_index: ship_cnt.sprite.get_index(),
object_index: idx as u32, object_index: idx as u32,
}]), });
);
self.state.vertex_buffers.object_counter += 1;
let flare = ship.data.get_outfits().get_flare_sprite(state.ct); let flare = ship.data.get_outfits().get_flare_sprite(state.ct);
if { if {
@ -115,7 +102,7 @@ impl GPUState {
for engine_point in &ship_cnt.engines { for engine_point in &ship_cnt.engines {
self.state.queue.write_buffer( self.state.queue.write_buffer(
&self.state.global_uniform.object_buffer, &self.state.global_uniform.object_buffer,
ObjectData::SIZE * self.state.vertex_buffers.object_counter as u64, ObjectData::SIZE * self.state.get_object_counter() as u64,
bytemuck::cast_slice(&[ObjectData { bytemuck::cast_slice(&[ObjectData {
// Note that we adjust the y-coordinate for half-height, // Note that we adjust the y-coordinate for half-height,
// not the x-coordinate, even though our ships point east // not the x-coordinate, even though our ships point east
@ -135,23 +122,10 @@ impl GPUState {
}]), }]),
); );
// Enforce buffer limit self.state.push_object_buffer(ObjectInstance {
if self.state.vertex_buffers.object_counter as u64
> OBJECT_SPRITE_INSTANCE_LIMIT
{
// TODO: no panic, handle this better.
panic!("Sprite limit exceeded!")
}
self.state.queue.write_buffer(
&self.state.vertex_buffers.object.instances,
ObjectInstance::SIZE * self.state.vertex_buffers.object_counter,
bytemuck::cast_slice(&[ObjectInstance {
sprite_index: flare.unwrap().get_index(), sprite_index: flare.unwrap().get_index(),
object_index: self.state.vertex_buffers.object_counter as u32, object_index: self.state.get_object_counter() as u32,
}]), });
);
self.state.vertex_buffers.object_counter += 1;
} }
} }
} }
@ -191,7 +165,7 @@ impl GPUState {
continue; continue;
} }
let idx = self.state.vertex_buffers.object_counter; let idx = self.state.get_object_counter();
// Write this object's location data // Write this object's location data
self.state.queue.write_buffer( self.state.queue.write_buffer(
&self.state.global_uniform.object_buffer, &self.state.global_uniform.object_buffer,
@ -208,22 +182,11 @@ impl GPUState {
}]), }]),
); );
// Enforce buffer limit
if self.state.vertex_buffers.object_counter as u64 > OBJECT_SPRITE_INSTANCE_LIMIT {
// TODO: no panic, handle this better.
panic!("Sprite limit exceeded!")
}
// Push this object's instance // Push this object's instance
self.state.queue.write_buffer( self.state.push_object_buffer(ObjectInstance {
&self.state.vertex_buffers.object.instances,
ObjectInstance::SIZE * self.state.vertex_buffers.object_counter,
bytemuck::cast_slice(&[ObjectInstance {
sprite_index: proj_cnt.sprite.get_index(), sprite_index: proj_cnt.sprite.get_index(),
object_index: idx as u32, object_index: idx as u32,
}]), });
);
self.state.vertex_buffers.object_counter += 1;
} }
} }
@ -255,7 +218,7 @@ impl GPUState {
continue; continue;
} }
let idx = self.state.vertex_buffers.object_counter; let idx = self.state.get_object_counter();
// Write this object's location data // Write this object's location data
self.state.queue.write_buffer( self.state.queue.write_buffer(
&self.state.global_uniform.object_buffer, &self.state.global_uniform.object_buffer,
@ -272,22 +235,11 @@ impl GPUState {
}]), }]),
); );
// Enforce buffer limit
if self.state.vertex_buffers.object_counter as u64 > OBJECT_SPRITE_INSTANCE_LIMIT {
// TODO: no panic, handle this better.
panic!("Sprite limit exceeded!")
}
// Push this object's instance // Push this object's instance
self.state.queue.write_buffer( self.state.push_object_buffer(ObjectInstance {
&self.state.vertex_buffers.object.instances,
ObjectInstance::SIZE * self.state.vertex_buffers.object_counter,
bytemuck::cast_slice(&[ObjectInstance {
sprite_index: o.sprite.get_index(), sprite_index: o.sprite.get_index(),
object_index: idx as u32, object_index: idx as u32,
}]), });
);
self.state.vertex_buffers.object_counter += 1;
} }
} }
} }

View File

@ -8,7 +8,7 @@ use wgpu;
use crate::{ use crate::{
globaluniform::GlobalDataContent, globaluniform::GlobalDataContent,
vertexbuffer::{consts::SPRITE_INDICES, types::ParticleInstance, BufferObject}, vertexbuffer::{consts::SPRITE_INDICES, types::ParticleInstance},
RenderInput, RenderInput,
}; };
@ -45,11 +45,7 @@ impl super::GPUState {
timestamp_writes: None, timestamp_writes: None,
}); });
self.state.vertex_buffers.object_counter = 0; self.state.frame_reset();
self.state.vertex_buffers.ui_counter = 0;
self.state.vertex_buffers.radialbar_counter = 0;
// Don't reset particle counter, it's special
let s = input.ct.get_starfield_handle(); let s = input.ct.get_starfield_handle();
// Update global values // Update global values
@ -81,10 +77,7 @@ impl super::GPUState {
// Write all new particles to GPU buffer // Write all new particles to GPU buffer
for i in input.particles.iter() { for i in input.particles.iter() {
self.state.queue.write_buffer( self.state.push_particle_buffer(ParticleInstance {
&self.state.vertex_buffers.particle.instances,
ParticleInstance::SIZE * self.state.vertex_buffers.particle_counter,
bytemuck::cast_slice(&[ParticleInstance {
position: [i.pos.x, i.pos.y], position: [i.pos.x, i.pos.y],
velocity: i.velocity.into(), velocity: i.velocity.into(),
angle: i.angle, angle: i.angle,
@ -94,12 +87,7 @@ impl super::GPUState {
created: input.current_time, created: input.current_time,
expires: input.current_time + i.lifetime, expires: input.current_time + i.lifetime,
fade: i.fade, fade: i.fade,
}]), });
);
self.state.vertex_buffers.particle_counter += 1;
if self.state.vertex_buffers.particle_counter == PARTICLE_SPRITE_INSTANCE_LIMIT {
self.state.vertex_buffers.particle_counter = 0;
}
} }
// Create sprite instances // Create sprite instances
@ -109,10 +97,6 @@ impl super::GPUState {
let clip_ne = Point2::new(-self.state.window_aspect, 1.0) * input.camera_zoom; let clip_ne = Point2::new(-self.state.window_aspect, 1.0) * input.camera_zoom;
let clip_sw = Point2::new(self.state.window_aspect, -1.0) * input.camera_zoom; let clip_sw = Point2::new(self.state.window_aspect, -1.0) * input.camera_zoom;
// TODO: sorting. We don't need to sort ships, but we do need to sort system objects by z-level
// (which we don't yet draw)
// that should probably be done in iter_system().
// Order matters, it determines what is drawn on top. // Order matters, it determines what is drawn on top.
// The order inside ships and projectiles doesn't matter, // The order inside ships and projectiles doesn't matter,
// but ships should always be under projectiles. // but ships should always be under projectiles.
@ -129,31 +113,31 @@ impl super::GPUState {
// Starfield pipeline // Starfield pipeline
self.state self.state
.vertex_buffers .vertex_buffers
.starfield .get_starfield()
.set_in_pass(&mut render_pass); .set_in_pass(&mut render_pass);
render_pass.set_pipeline(&self.starfield_pipeline); render_pass.set_pipeline(&self.starfield_pipeline);
render_pass.draw_indexed( render_pass.draw_indexed(
0..SPRITE_INDICES.len() as u32, 0..SPRITE_INDICES.len() as u32,
0, 0,
0..self.starfield.instance_count, 0..self.state.get_starfield_counter(),
); );
// Sprite pipeline // Sprite pipeline
self.state self.state
.vertex_buffers .vertex_buffers
.object .get_object()
.set_in_pass(&mut render_pass); .set_in_pass(&mut render_pass);
render_pass.set_pipeline(&self.object_pipeline); render_pass.set_pipeline(&self.object_pipeline);
render_pass.draw_indexed( render_pass.draw_indexed(
0..SPRITE_INDICES.len() as u32, 0..SPRITE_INDICES.len() as u32,
0, 0,
0..self.state.vertex_buffers.object_counter as _, 0..self.state.get_object_counter(),
); );
// Particle pipeline // Particle pipeline
self.state self.state
.vertex_buffers .vertex_buffers
.particle .get_particle()
.set_in_pass(&mut render_pass); .set_in_pass(&mut render_pass);
render_pass.set_pipeline(&self.particle_pipeline); render_pass.set_pipeline(&self.particle_pipeline);
render_pass.draw_indexed( render_pass.draw_indexed(
@ -163,27 +147,31 @@ impl super::GPUState {
); );
// Ui pipeline // Ui pipeline
self.state.vertex_buffers.ui.set_in_pass(&mut render_pass); self.state
.vertex_buffers
.get_ui()
.set_in_pass(&mut render_pass);
render_pass.set_pipeline(&self.ui_pipeline); render_pass.set_pipeline(&self.ui_pipeline);
render_pass.draw_indexed( render_pass.draw_indexed(
0..SPRITE_INDICES.len() as u32, 0..SPRITE_INDICES.len() as u32,
0, 0,
0..self.state.vertex_buffers.ui_counter as _, 0..self.state.get_ui_counter(),
); );
// Radial progress bars // Radial progress bars
// TODO: do we need to do this every time? // TODO: do we need to do this every time?
self.state self.state
.vertex_buffers .vertex_buffers
.radialbar .get_radialbar()
.set_in_pass(&mut render_pass); .set_in_pass(&mut render_pass);
render_pass.set_pipeline(&self.radialbar_pipeline); render_pass.set_pipeline(&self.radialbar_pipeline);
render_pass.draw_indexed( render_pass.draw_indexed(
0..SPRITE_INDICES.len() as u32, 0..SPRITE_INDICES.len() as u32,
0, 0,
0..self.state.vertex_buffers.radialbar_counter as _, 0..self.state.get_radialbar_counter(),
); );
let textareas = self.ui.get_textareas(&input, &self.state);
self.state self.state
.text_renderer .text_renderer
.prepare( .prepare(
@ -195,7 +183,7 @@ impl super::GPUState {
width: self.state.window_size.width, width: self.state.window_size.width,
height: self.state.window_size.height, height: self.state.window_size.height,
}, },
self.ui.get_textareas(), textareas,
&mut self.state.text_cache, &mut self.state.text_cache,
) )
.unwrap(); .unwrap();

View File

@ -7,20 +7,24 @@
//! and the only one external code should interact with. //! and the only one external code should interact with.
//! (Excluding data structs, like [`ObjectSprite`]) //! (Excluding data structs, like [`ObjectSprite`])
mod anchoredposition;
mod datastructs;
mod globaluniform; mod globaluniform;
mod gpustate; mod gpustate;
mod pipeline; mod pipeline;
mod positionanchor;
mod renderinput;
mod renderstate;
mod shaderprocessor; mod shaderprocessor;
mod starfield; mod starfield;
mod texturearray; mod texturearray;
mod ui; mod ui;
mod vertexbuffer; mod vertexbuffer;
pub use anchoredposition::PositionAnchor; use renderstate::*;
pub use datastructs::RenderInput;
pub use gpustate::GPUState; pub use gpustate::GPUState;
pub use positionanchor::PositionAnchor;
pub use renderinput::RenderInput;
use nalgebra::Matrix4; use nalgebra::Matrix4;
/// Shader entry points /// Shader entry points

View File

@ -28,6 +28,14 @@ pub enum PositionAnchor {
/// Position of this sprite's ne corner, /// Position of this sprite's ne corner,
/// relative to the ne corner of the window. /// relative to the ne corner of the window.
NeNe, NeNe,
/// Position of this sprite's center,
/// relative to the center of the window.
CC,
/// Position of this sprite's NW corner,
/// relative to the center of the window.
CNw,
} }
// These offsets are implemented in wgsl shaders. // These offsets are implemented in wgsl shaders.
@ -42,6 +50,8 @@ impl PositionAnchor {
Self::NwSw => 3, Self::NwSw => 3,
Self::NwSe => 4, Self::NwSe => 4,
Self::NeNe => 5, Self::NeNe => 5,
Self::CC => 6,
Self::CNw => 7,
} }
} }
} }

View File

@ -0,0 +1,36 @@
use galactica_content::{Content, SystemHandle};
use galactica_playeragent::PlayerAgent;
use galactica_system::phys::{ParticleBuilder, PhysSim};
use galactica_util::timing::Timing;
use nalgebra::Vector2;
/// Bundles parameters passed to a single call to GPUState::render
pub struct RenderInput<'a> {
/// Camera position, in world units
pub camera_pos: Vector2<f32>,
/// Player ship data
pub player: &'a PlayerAgent,
/// The system we're currently in
pub current_system: SystemHandle,
/// Height of screen, in world units
pub camera_zoom: f32,
/// The world state to render
pub systemsim: &'a PhysSim,
// TODO: handle overflow. is it a problem?
/// The current time, in seconds
pub current_time: f32,
/// Game content
pub ct: &'a Content,
/// Particles to spawn during this frame
pub particles: &'a Vec<ParticleBuilder>,
/// Time we spent in each part of the game loop
pub timing: Timing,
}

View File

@ -0,0 +1,237 @@
use galactica_content::Content;
use galactica_util::constants::{
OBJECT_SPRITE_INSTANCE_LIMIT, PARTICLE_SPRITE_INSTANCE_LIMIT, RADIALBAR_SPRITE_INSTANCE_LIMIT,
UI_SPRITE_INSTANCE_LIMIT,
};
use glyphon::{FontSystem, SwashCache, TextAtlas, TextRenderer};
use std::rc::Rc;
use wgpu::BufferAddress;
use winit::window::Window;
use crate::{
globaluniform::GlobalUniform,
vertexbuffer::{
consts::{SPRITE_INDICES, SPRITE_VERTICES},
types::{
ObjectInstance, ParticleInstance, RadialBarInstance, StarfieldInstance, TexturedVertex,
UiInstance,
},
BufferObject, VertexBuffer,
},
};
/// Vertex buffers
pub(crate) struct VertexBuffers {
// Keeps track of length of each buffer
// Most of these are reset on each frame.
//
// The exception is particle_counter, which
// is never reset, and loops back to zero once
// it exceeds buffer length
object_counter: BufferAddress,
ui_counter: BufferAddress,
particle_counter: BufferAddress,
radialbar_counter: BufferAddress,
starfield_counter: BufferAddress,
starfield_limit: BufferAddress,
object: Rc<VertexBuffer>,
starfield: Rc<VertexBuffer>,
ui: Rc<VertexBuffer>,
particle: Rc<VertexBuffer>,
radialbar: Rc<VertexBuffer>,
}
impl<'a> VertexBuffers {
pub fn new(device: &wgpu::Device, ct: &Content) -> Self {
Self {
object_counter: 0,
ui_counter: 0,
particle_counter: 0,
radialbar_counter: 0,
starfield_counter: 0,
starfield_limit: ct.get_config().starfield_instance_limit,
object: Rc::new(VertexBuffer::new::<TexturedVertex, ObjectInstance>(
"object",
&device,
Some(SPRITE_VERTICES),
Some(SPRITE_INDICES),
OBJECT_SPRITE_INSTANCE_LIMIT,
)),
starfield: Rc::new(VertexBuffer::new::<TexturedVertex, StarfieldInstance>(
"starfield",
&device,
Some(SPRITE_VERTICES),
Some(SPRITE_INDICES),
ct.get_config().starfield_instance_limit,
)),
ui: Rc::new(VertexBuffer::new::<TexturedVertex, UiInstance>(
"ui",
&device,
Some(SPRITE_VERTICES),
Some(SPRITE_INDICES),
UI_SPRITE_INSTANCE_LIMIT,
)),
particle: Rc::new(VertexBuffer::new::<TexturedVertex, ParticleInstance>(
"particle",
&device,
Some(SPRITE_VERTICES),
Some(SPRITE_INDICES),
PARTICLE_SPRITE_INSTANCE_LIMIT,
)),
radialbar: Rc::new(VertexBuffer::new::<TexturedVertex, RadialBarInstance>(
"radial bar",
&device,
Some(SPRITE_VERTICES),
Some(SPRITE_INDICES),
10,
)),
}
}
pub fn get_ui(&'a self) -> &'a Rc<VertexBuffer> {
&self.ui
}
pub fn get_object(&'a self) -> &'a Rc<VertexBuffer> {
&self.object
}
pub fn get_particle(&'a self) -> &'a Rc<VertexBuffer> {
&self.particle
}
pub fn get_radialbar(&'a self) -> &'a Rc<VertexBuffer> {
&self.radialbar
}
pub fn get_starfield(&'a self) -> &'a Rc<VertexBuffer> {
&self.starfield
}
}
/// Renderer state. A reference to this struct is often passed to helper functions.
pub(crate) struct RenderState {
pub window: Window,
pub window_size: winit::dpi::PhysicalSize<u32>,
pub window_aspect: f32,
pub queue: wgpu::Queue,
pub global_uniform: GlobalUniform,
pub vertex_buffers: VertexBuffers,
pub text_font_system: FontSystem,
pub text_cache: SwashCache,
pub text_atlas: TextAtlas,
pub text_renderer: TextRenderer,
}
impl RenderState {
/// Prepare this state for a new frame
pub fn frame_reset(&mut self) {
self.vertex_buffers.object_counter = 0;
self.vertex_buffers.ui_counter = 0;
self.vertex_buffers.radialbar_counter = 0
}
pub fn push_ui_buffer(&mut self, instance: UiInstance) {
// Enforce buffer limit
if self.vertex_buffers.ui_counter as u64 > UI_SPRITE_INSTANCE_LIMIT {
// TODO: no panic, handle this better.
panic!("UI limit exceeded!")
}
self.queue.write_buffer(
&self.vertex_buffers.ui.instances,
UiInstance::SIZE * self.vertex_buffers.ui_counter,
bytemuck::cast_slice(&[instance]),
);
self.vertex_buffers.ui_counter += 1;
}
pub fn get_ui_counter(&self) -> u32 {
self.vertex_buffers.ui_counter as u32
}
pub fn push_object_buffer(&mut self, instance: ObjectInstance) {
// Enforce buffer limit
if self.vertex_buffers.object_counter as u64 > OBJECT_SPRITE_INSTANCE_LIMIT {
// TODO: no panic, handle this better.
panic!("Object limit exceeded!")
}
self.queue.write_buffer(
&self.vertex_buffers.object.instances,
ObjectInstance::SIZE * self.vertex_buffers.object_counter,
bytemuck::cast_slice(&[instance]),
);
self.vertex_buffers.object_counter += 1;
}
pub fn get_object_counter(&self) -> u32 {
self.vertex_buffers.object_counter as u32
}
pub fn push_radialbar_buffer(&mut self, instance: RadialBarInstance) {
// Enforce buffer limit
if self.vertex_buffers.radialbar_counter as u64 > RADIALBAR_SPRITE_INSTANCE_LIMIT {
// TODO: no panic, handle this better.
panic!("Radialbar sprite limit exceeded!")
}
self.queue.write_buffer(
&self.vertex_buffers.radialbar.instances,
RadialBarInstance::SIZE * self.vertex_buffers.radialbar_counter,
bytemuck::cast_slice(&[instance]),
);
self.vertex_buffers.radialbar_counter += 1;
}
pub fn get_radialbar_counter(&self) -> u32 {
self.vertex_buffers.radialbar_counter as u32
}
pub fn reset_starfield_counter(&mut self) {
self.vertex_buffers.starfield_counter = 0;
}
pub fn push_starfield_buffer(&mut self, instance: StarfieldInstance) {
// Enforce buffer limit
// This should never happen, since starfield generator checks array size
if self.vertex_buffers.starfield_counter as u64 > self.vertex_buffers.starfield_limit {
panic!("Starfield sprite limit exceeded!")
}
self.queue.write_buffer(
&self.vertex_buffers.starfield.instances,
StarfieldInstance::SIZE * self.vertex_buffers.starfield_counter,
bytemuck::cast_slice(&[instance]),
);
self.vertex_buffers.starfield_counter += 1;
}
pub fn get_starfield_counter(&self) -> u32 {
self.vertex_buffers.starfield_counter as u32
}
pub fn push_particle_buffer(&mut self, instance: ParticleInstance) {
self.queue.write_buffer(
&self.vertex_buffers.particle.instances,
ParticleInstance::SIZE * self.vertex_buffers.particle_counter,
bytemuck::cast_slice(&[instance]),
);
self.vertex_buffers.particle_counter += 1;
if self.vertex_buffers.particle_counter == PARTICLE_SPRITE_INSTANCE_LIMIT {
self.vertex_buffers.particle_counter = 0;
}
}
//pub fn get_particle_counter(&self) -> u32 {
// self.vertex_buffers.particle_counter as u32
//}
}

View File

@ -2,10 +2,7 @@ use galactica_content::Content;
use nalgebra::{Point2, Point3, Vector2, Vector3}; use nalgebra::{Point2, Point3, Vector2, Vector3};
use rand::{self, Rng}; use rand::{self, Rng};
use crate::{ use crate::{vertexbuffer::types::StarfieldInstance, RenderState};
datastructs::RenderState,
vertexbuffer::{types::StarfieldInstance, BufferObject},
};
pub(crate) struct StarfieldStar { pub(crate) struct StarfieldStar {
/// Star coordinates, in world space. /// Star coordinates, in world space.
@ -23,15 +20,11 @@ pub(crate) struct StarfieldStar {
pub(crate) struct Starfield { pub(crate) struct Starfield {
stars: Vec<StarfieldStar>, stars: Vec<StarfieldStar>,
pub instance_count: u32,
} }
impl Starfield { impl Starfield {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self { stars: Vec::new() }
stars: Vec::new(),
instance_count: 0,
}
} }
pub fn regenerate(&mut self, ct: &Content) { pub fn regenerate(&mut self, ct: &Content) {
@ -97,24 +90,19 @@ impl Starfield {
} }
// Add all tiles to buffer // Add all tiles to buffer
self.instance_count = 0; // Keep track of buffer index state.reset_starfield_counter();
for x in (-nw_tile.x)..=nw_tile.x { for x in (-nw_tile.x)..=nw_tile.x {
for y in (-nw_tile.y)..=nw_tile.y { for y in (-nw_tile.y)..=nw_tile.y {
let offset = Vector3::new(sz * x as f32, sz * y as f32, 0.0); let offset = Vector3::new(sz * x as f32, sz * y as f32, 0.0);
for s in &self.stars { for s in &self.stars {
state.queue.write_buffer( // This shouldn't ever panic.
&state.vertex_buffers.starfield.instances, // instance count is guaranteed to stay within buffer limits,
StarfieldInstance::SIZE * self.instance_count as u64, // this is guaranteed by previous checks.
bytemuck::cast_slice(&[StarfieldInstance { state.push_starfield_buffer(StarfieldInstance {
position: (s.pos + offset).into(), position: (s.pos + offset).into(),
size: s.size, size: s.size,
tint: s.tint.into(), tint: s.tint.into(),
}]), });
);
self.instance_count += 1;
// instance_count is guaranteed to stay within buffer limits,
// this is guaranteed by previous checks.
} }
} }
} }

View File

@ -1,6 +1,6 @@
use glyphon::{Attrs, Buffer, Color, Family, Metrics, Shaping, TextArea, TextBounds}; use glyphon::{Attrs, Buffer, Color, Family, Metrics, Shaping, TextArea, TextBounds};
use crate::{datastructs::RenderState, RenderInput}; use crate::{RenderInput, RenderState};
pub(super) struct FpsIndicator { pub(super) struct FpsIndicator {
buffer: Buffer, buffer: Buffer,