From abd41af202e74ffde9afcf3f8d1d992ee01855cb Mon Sep 17 00:00:00 2001 From: Mark Date: Mon, 25 Dec 2023 11:17:08 -0800 Subject: [PATCH] Cleanup --- src/consts.rs | 21 +++ src/game/camera.rs | 13 ++ src/{ => game}/doodad.rs | 2 +- src/game/game.rs | 84 +++++++++ src/{ => game}/inputstatus.rs | 0 src/game/mod.rs | 13 ++ src/{ => game}/ship.rs | 4 +- src/{ => game}/system.rs | 16 +- src/main.rs | 174 +----------------- src/render/consts.rs | 18 ++ src/render/gpustate.rs | 37 ++-- src/render/mod.rs | 5 +- src/render/pipeline.rs | 8 +- src/render/shaders/sprite.wgsl | 20 +- src/render/shaders/starfield.wgsl | 20 +- src/render/sprite.rs | 33 ++++ src/render/texturearray/array.rs | 9 +- src/render/texturearray/loader.rs | 11 +- .../vertexbuffer/{data.rs => consts.rs} | 0 src/render/vertexbuffer/mod.rs | 2 +- 20 files changed, 258 insertions(+), 232 deletions(-) create mode 100644 src/consts.rs create mode 100644 src/game/camera.rs rename src/{ => game}/doodad.rs (81%) create mode 100644 src/game/game.rs rename src/{ => game}/inputstatus.rs (100%) create mode 100644 src/game/mod.rs rename src/{ => game}/ship.rs (86%) rename src/{ => game}/system.rs (80%) create mode 100644 src/render/consts.rs create mode 100644 src/render/sprite.rs rename src/render/vertexbuffer/{data.rs => consts.rs} (100%) diff --git a/src/consts.rs b/src/consts.rs new file mode 100644 index 0000000..8a959d1 --- /dev/null +++ b/src/consts.rs @@ -0,0 +1,21 @@ +use crate::physics::Pfloat; + +pub const ZOOM_MIN: Pfloat = 200.0; +pub const ZOOM_MAX: Pfloat = 2000.0; + +// Z-axis range for starfield stars +pub const STARFIELD_PARALLAX_MIN: f32 = 100.0; +pub const STARFIELD_PARALLAX_MAX: f32 = 200.0; +// Size of a square starfield tile, in game units. +// A tile of size PARALLAX_MAX * screen-size-in-game-units +// will completely cover a (square) screen. This depends on zoom! +// +// Use a value smaller than zoom_max for debug. +pub const STARFIELD_SIZE: u64 = STARFIELD_PARALLAX_MAX as u64 * ZOOM_MAX as u64; +// Average number of stars per game unit +pub const STARFIELD_DENSITY: f64 = 0.01; +// Number of stars in one starfield tile +// Must fit inside an i32 +pub const STARFIELD_COUNT: u64 = (STARFIELD_SIZE as f64 * STARFIELD_DENSITY) as u64; + +pub const CONTENT_ROOT: &'static str = "./content"; diff --git a/src/game/camera.rs b/src/game/camera.rs new file mode 100644 index 0000000..617e2ba --- /dev/null +++ b/src/game/camera.rs @@ -0,0 +1,13 @@ +use cgmath::Point2; + +use crate::physics::Pfloat; + +#[derive(Debug, Clone, Copy)] +pub struct Camera { + /// Camera center + pub pos: Point2, + + /// Camera zoom + /// (How many game units tall is the viewport?) + pub zoom: Pfloat, +} diff --git a/src/doodad.rs b/src/game/doodad.rs similarity index 81% rename from src/doodad.rs rename to src/game/doodad.rs index 7db2435..bfc3df1 100644 --- a/src/doodad.rs +++ b/src/game/doodad.rs @@ -1,6 +1,6 @@ use cgmath::{Deg, Point2}; -use crate::{physics::Pfloat, render::SpriteTexture, Sprite, Spriteable}; +use crate::{physics::Pfloat, render::Sprite, render::SpriteTexture, render::Spriteable}; pub struct Doodad { pub sprite: SpriteTexture, diff --git a/src/game/game.rs b/src/game/game.rs new file mode 100644 index 0000000..3f0c42b --- /dev/null +++ b/src/game/game.rs @@ -0,0 +1,84 @@ +use cgmath::Deg; +use std::time::Instant; +use winit::event::{ElementState, MouseButton, MouseScrollDelta, TouchPhase, VirtualKeyCode}; + +use super::{ship::ShipKind, Camera, InputStatus, Ship, System}; +use crate::{consts, content::Content, physics::Pfloat, render::Sprite, render::Spriteable}; + +pub struct Game { + pub input: InputStatus, + pub last_update: Instant, + pub player: Ship, + pub system: System, + pub camera: Camera, +} + +impl Game { + pub fn new(ct: Content) -> Self { + Game { + last_update: Instant::now(), + input: InputStatus::new(), + player: Ship::new(ShipKind::Gypsum, (0.0, 0.0).into()), + camera: Camera { + pos: (0.0, 0.0).into(), + zoom: 500.0, + }, + system: System::new(&ct.systems[0]), + } + } + + pub fn process_key(&mut self, state: &ElementState, key: &VirtualKeyCode) { + self.input.process_key(state, key) + } + + pub fn process_click(&mut self, state: &ElementState, key: &MouseButton) { + self.input.process_click(state, key) + } + + pub fn process_scroll(&mut self, delta: &MouseScrollDelta, phase: &TouchPhase) { + self.input.process_scroll(delta, phase) + } + + pub fn update(&mut self) { + let t: Pfloat = self.last_update.elapsed().as_secs_f32(); + + if self.input.key_thrust { + self.player.body.thrust(50.0 * t); + } + + if self.input.key_right { + self.player.body.rot(Deg(35.0) * t); + } + + if self.input.key_left { + self.player.body.rot(Deg(-35.0) * t); + } + + if self.input.v_scroll != 0.0 { + self.camera.zoom = + (self.camera.zoom + self.input.v_scroll).clamp(consts::ZOOM_MIN, consts::ZOOM_MAX); + self.input.v_scroll = 0.0; + } + + self.player.body.tick(t); + self.camera.pos = self.player.body.pos; + + self.last_update = Instant::now(); + } + + pub fn get_sprites(&self) -> Vec { + let mut sprites: Vec = Vec::new(); + + sprites.append(&mut self.system.get_sprites()); + sprites.push(self.player.get_sprite()); + + // Make sure sprites are drawn in the correct order + // (note the reversed a, b in the comparator) + // + // TODO: use a gpu depth buffer with parallax as z-coordinate? + // Might be overkill. + sprites.sort_by(|a, b| b.parallax.total_cmp(&a.parallax)); + + return sprites; + } +} diff --git a/src/inputstatus.rs b/src/game/inputstatus.rs similarity index 100% rename from src/inputstatus.rs rename to src/game/inputstatus.rs diff --git a/src/game/mod.rs b/src/game/mod.rs new file mode 100644 index 0000000..c3f5290 --- /dev/null +++ b/src/game/mod.rs @@ -0,0 +1,13 @@ +mod camera; +mod doodad; +mod game; +mod inputstatus; +mod ship; +mod system; + +pub use camera::Camera; +pub use doodad::Doodad; +pub use game::Game; +pub use inputstatus::InputStatus; +pub use ship::Ship; +pub use system::System; diff --git a/src/ship.rs b/src/game/ship.rs similarity index 86% rename from src/ship.rs rename to src/game/ship.rs index 695d0b4..ef6cf63 100644 --- a/src/ship.rs +++ b/src/game/ship.rs @@ -1,6 +1,8 @@ use cgmath::Point2; -use crate::{physics::Pfloat, physics::PhysBody, render::SpriteTexture, Sprite, Spriteable}; +use crate::{ + physics::Pfloat, physics::PhysBody, render::Sprite, render::SpriteTexture, render::Spriteable, +}; pub enum ShipKind { Gypsum, diff --git a/src/system.rs b/src/game/system.rs similarity index 80% rename from src/system.rs rename to src/game/system.rs index 31623f8..e7c8641 100644 --- a/src/system.rs +++ b/src/game/system.rs @@ -1,10 +1,11 @@ -use crate::{ - content, physics::Pfloat, render::SpriteTexture, Doodad, Sprite, Spriteable, STARFIELD_COUNT, - STARFIELD_PARALLAX_MAX, STARFIELD_PARALLAX_MIN, STARFIELD_SIZE, -}; use cgmath::{Point2, Vector2}; use rand::{self, Rng}; +use super::Doodad; +use crate::{ + consts, content, physics::Pfloat, render::Sprite, render::SpriteTexture, render::Spriteable, +}; + pub struct StarfieldStar { /// Star coordinates, in world space. /// These are relative to the center of a starfield tile. @@ -28,17 +29,18 @@ pub struct System { impl System { pub fn new(ct: &content::system::System) -> Self { let mut rng = rand::thread_rng(); - let sz = STARFIELD_SIZE as f32 / 2.0; + let sz = consts::STARFIELD_SIZE as f32 / 2.0; let mut s = System { name: ct.name.clone(), bodies: Vec::new(), - starfield: (0..STARFIELD_COUNT) + starfield: (0..consts::STARFIELD_COUNT) .map(|_| StarfieldStar { pos: Point2 { x: rng.gen_range(-sz..=sz), y: rng.gen_range(-sz..=sz), }, - parallax: rng.gen_range(STARFIELD_PARALLAX_MIN..STARFIELD_PARALLAX_MAX), + parallax: rng + .gen_range(consts::STARFIELD_PARALLAX_MIN..consts::STARFIELD_PARALLAX_MAX), size: rng.gen_range(0.2..0.8), // TODO: configurable tint: Vector2 { x: rng.gen_range(0.0..=1.0), diff --git a/src/main.rs b/src/main.rs index 0db8080..57d4959 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,174 +1,29 @@ +mod consts; mod content; -mod doodad; -mod inputstatus; +mod game; mod physics; mod render; -mod ship; -mod system; use anyhow::Result; -use cgmath::{Deg, Point2}; -use content::Content; -use std::time::Instant; use winit::{ - event::{ - ElementState, Event, KeyboardInput, MouseButton, MouseScrollDelta, TouchPhase, - VirtualKeyCode, WindowEvent, - }, + event::{Event, KeyboardInput, WindowEvent}, event_loop::{ControlFlow, EventLoop}, window::WindowBuilder, }; -use crate::{ - doodad::Doodad, - inputstatus::InputStatus, - physics::Pfloat, - render::{GPUState, SpriteTexture}, - ship::{Ship, ShipKind}, - system::System, -}; +fn main() -> Result<()> { + let content = content::load_content_dir(consts::CONTENT_ROOT)?; + let game = game::Game::new(content); -pub const ZOOM_MIN: Pfloat = 200.0; -pub const ZOOM_MAX: Pfloat = 2000.0; - -// Z-axis range for starfield stars -pub const STARFIELD_PARALLAX_MIN: f32 = 100.0; -pub const STARFIELD_PARALLAX_MAX: f32 = 200.0; -// Size of a square starfield tile, in game units. -// A tile of size PARALLAX_MAX * screen-size-in-game-units -// will completely cover a (square) screen. This depends on zoom! -// -// Use a value smaller than zoom_max for debug. -pub const STARFIELD_SIZE: u64 = STARFIELD_PARALLAX_MAX as u64 * ZOOM_MAX as u64; -// Average number of stars per game unit -pub const STARFIELD_DENSITY: f64 = 0.01; -// Number of stars in one starfield tile -// Must fit inside an i32 -pub const STARFIELD_COUNT: u64 = (STARFIELD_SIZE as f64 * STARFIELD_DENSITY) as u64; - -#[derive(Debug, Clone, Copy)] -struct Camera { - /// Camera center - pos: Point2, - - /// Camera zoom - /// (How many game units tall is the viewport?) - zoom: Pfloat, + pollster::block_on(run(game))?; + return Ok(()); } -trait Spriteable { - fn get_sprite(&self) -> Sprite; -} - -struct Sprite { - /// Name of the sprite to draw - texture: SpriteTexture, - - /// This object's position, in world coordinates. - pos: Point2, - - /// The size of this sprite, - /// given as height in world units. - size: Pfloat, - - /// Scale factor. - /// if this is 1, sprite height is exactly self.size. - scale: Pfloat, - - /// This sprite's rotation - /// (relative to north, measured ccw) - angle: Deg, - - /// Parallax factor. - /// More positive => farther away - /// More negative => closer - /// Zero: no parallax. - parallax: Pfloat, -} - -struct Game { - input: InputStatus, - last_update: Instant, - player: Ship, - system: System, - camera: Camera, -} - -impl Game { - fn new(ct: Content) -> Self { - Game { - last_update: Instant::now(), - input: InputStatus::new(), - player: Ship::new(ShipKind::Gypsum, (0.0, 0.0).into()), - camera: Camera { - pos: (0.0, 0.0).into(), - zoom: 500.0, - }, - system: System::new(&ct.systems[0]), - } - } - - fn process_key(&mut self, state: &ElementState, key: &VirtualKeyCode) { - self.input.process_key(state, key) - } - - fn process_click(&mut self, state: &ElementState, key: &MouseButton) { - self.input.process_click(state, key) - } - - fn process_scroll(&mut self, delta: &MouseScrollDelta, phase: &TouchPhase) { - self.input.process_scroll(delta, phase) - } - - fn update(&mut self) { - let t: Pfloat = self.last_update.elapsed().as_secs_f32(); - - if self.input.key_thrust { - self.player.body.thrust(50.0 * t); - } - - if self.input.key_right { - self.player.body.rot(Deg(35.0) * t); - } - - if self.input.key_left { - self.player.body.rot(Deg(-35.0) * t); - } - - if self.input.v_scroll != 0.0 { - self.camera.zoom = (self.camera.zoom + self.input.v_scroll).clamp(ZOOM_MIN, ZOOM_MAX); - self.input.v_scroll = 0.0; - } - - println!("{:?}", self.player.body.pos); - self.player.body.tick(t); - self.camera.pos = self.player.body.pos; - - self.last_update = Instant::now(); - } - - fn get_sprites(&self) -> Vec { - let mut sprites: Vec = Vec::new(); - - sprites.append(&mut self.system.get_sprites()); - sprites.push(self.player.get_sprite()); - - // Make sure sprites are drawn in the correct order - // (note the reversed a, b in the comparator) - // - // TODO: use a gpu depth buffer with parallax as z-coordinate? - // Might be overkill. - sprites.sort_by(|a, b| b.parallax.total_cmp(&a.parallax)); - - return sprites; - } -} - -async fn run(mut game: Game) -> Result<()> { +async fn run(mut game: game::Game) -> Result<()> { let event_loop = EventLoop::new(); let window = WindowBuilder::new().build(&event_loop).unwrap(); - let mut gpu = GPUState::new(window).await?; + let mut gpu = render::GPUState::new(window).await?; gpu.update_starfield_buffer(&game); @@ -229,12 +84,3 @@ async fn run(mut game: Game) -> Result<()> { } }); } - -fn main() -> Result<()> { - let content = content::load_content_dir("./content")?; - println!("{:?}", content); - let game = Game::new(content); - - pollster::block_on(run(game))?; - return Ok(()); -} diff --git a/src/render/consts.rs b/src/render/consts.rs new file mode 100644 index 0000000..9a3affb --- /dev/null +++ b/src/render/consts.rs @@ -0,0 +1,18 @@ +use crate::consts; + +// We can draw at most this many sprites on the screen. +// TODO: compile-time option +pub const SPRITE_INSTANCE_LIMIT: u64 = 100; + +// Must be small enough to fit in an i32 +pub const STARFIELD_INSTANCE_LIMIT: u64 = consts::STARFIELD_COUNT * 24; + +/// Texture index file name +pub const TEXTURE_INDEX_NAME: &'static str = "_index.toml"; + +/// Texture index dir +pub const TEXTURE_INDEX_PATH: &'static str = "./assets"; + +/// Shader entry points +pub const SHADER_MAIN_VERTEX: &'static str = "vertex_main"; +pub const SHADER_MAIN_FRAGMENT: &'static str = "fragment_main"; diff --git a/src/render/gpustate.rs b/src/render/gpustate.rs index 3e22cd8..11cc71a 100644 --- a/src/render/gpustate.rs +++ b/src/render/gpustate.rs @@ -5,18 +5,18 @@ use std::{iter, rc::Rc}; use wgpu; use winit::{self, dpi::PhysicalSize, window::Window}; -use crate::{Game, STARFIELD_COUNT, STARFIELD_PARALLAX_MIN, STARFIELD_SIZE, ZOOM_MAX, ZOOM_MIN}; - use super::{ + consts::{SPRITE_INSTANCE_LIMIT, STARFIELD_INSTANCE_LIMIT}, globaldata::{GlobalData, GlobalDataContent}, pipeline::PipelineBuilder, texturearray::TextureArray, vertexbuffer::{ - data::{SPRITE_INDICES, SPRITE_VERTICES}, + consts::{SPRITE_INDICES, SPRITE_VERTICES}, types::{SpriteInstance, StarfieldInstance, TexturedVertex}, VertexBuffer, }, }; +use crate::{consts, game::Game}; pub struct GPUState { device: wgpu::Device, @@ -43,13 +43,6 @@ struct VertexBuffers { } impl GPUState { - // We can draw at most this many sprites on the screen. - // TODO: compile-time option - pub const SPRITE_INSTANCE_LIMIT: u64 = 100; - - // Must be small enough to fit in an i32 - pub const STARFIELD_INSTANCE_LIMIT: u64 = STARFIELD_COUNT * 24; - pub async fn new(window: Window) -> Result { let window_size = window.inner_size(); let window_aspect = window_size.width as f32 / window_size.height as f32; @@ -119,7 +112,7 @@ impl GPUState { &device, Some(SPRITE_VERTICES), Some(SPRITE_INDICES), - Self::SPRITE_INSTANCE_LIMIT, + SPRITE_INSTANCE_LIMIT, )), starfield: Rc::new(VertexBuffer::new::( @@ -127,7 +120,7 @@ impl GPUState { &device, Some(SPRITE_VERTICES), Some(SPRITE_INDICES), - Self::STARFIELD_INSTANCE_LIMIT, + STARFIELD_INSTANCE_LIMIT, )), }; @@ -217,8 +210,8 @@ impl GPUState { for s in game.get_sprites() { // Parallax is computed here, so we can check if this sprite is visible. - let pos = - (s.pos - game.camera.pos.to_vec()) / (s.parallax + game.camera.zoom / ZOOM_MIN); + let pos = (s.pos - game.camera.pos.to_vec()) + / (s.parallax + game.camera.zoom / consts::ZOOM_MIN); let texture = self.texture_array.get_sprite_texture(s.texture); // Game dimensions of this sprite post-scale. @@ -246,7 +239,7 @@ impl GPUState { } // Enforce sprite limit - if instances.len() as u64 > Self::SPRITE_INSTANCE_LIMIT { + if instances.len() as u64 > SPRITE_INSTANCE_LIMIT { // TODO: no panic, handle this better. unreachable!("Sprite limit exceeded!") } @@ -259,17 +252,17 @@ impl GPUState { /// /// Starfield data rarely changes, so this is called only when it's needed. pub fn update_starfield_buffer(&mut self, game: &Game) { - let sz = STARFIELD_SIZE as f32; + let sz = consts::STARFIELD_SIZE as f32; // Compute window size in starfield tiles let mut nw_tile: Point2 = { // Game coordinates (relative to camera) of nw corner of screen. - let clip_nw = Point2::from((self.window_aspect, 1.0)) * ZOOM_MAX; + let clip_nw = Point2::from((self.window_aspect, 1.0)) * consts::ZOOM_MAX; // Parallax correction. // Also, adjust v for mod to work properly // (v is centered at 0) - let v: Point2 = clip_nw * STARFIELD_PARALLAX_MIN; + let v: Point2 = clip_nw * consts::STARFIELD_PARALLAX_MIN; let v_adj: Point2 = (v.x + (sz / 2.0), v.y + (sz / 2.0)).into(); #[rustfmt::skip] @@ -293,8 +286,8 @@ impl GPUState { // Truncate tile grid to buffer size // (The window won't be full of stars if our instance limit is too small) - while ((nw_tile.x * 2 + 1) * (nw_tile.y * 2 + 1) * STARFIELD_COUNT as i32) - > Self::STARFIELD_INSTANCE_LIMIT as i32 + while ((nw_tile.x * 2 + 1) * (nw_tile.y * 2 + 1) * consts::STARFIELD_COUNT as i32) + > STARFIELD_INSTANCE_LIMIT as i32 { nw_tile -= Vector2::from((1, 1)); } @@ -319,7 +312,7 @@ impl GPUState { } // Enforce starfield limit - if instances.len() as u64 > Self::STARFIELD_INSTANCE_LIMIT { + if instances.len() as u64 > STARFIELD_INSTANCE_LIMIT { unreachable!("Starfield limit exceeded!") } @@ -377,7 +370,7 @@ impl GPUState { ], window_aspect: [self.window_aspect, 0.0], starfield_texture: [self.texture_array.get_starfield_texture().index, 0], - starfield_tile_size: [STARFIELD_SIZE as f32, 0.0], + starfield_tile_size: [consts::STARFIELD_SIZE as f32, 0.0], }]), ); diff --git a/src/render/mod.rs b/src/render/mod.rs index 4084fc6..30317c5 100644 --- a/src/render/mod.rs +++ b/src/render/mod.rs @@ -1,19 +1,22 @@ +mod consts; mod globaldata; mod gpustate; mod pipeline; +mod sprite; mod texturearray; mod vertexbuffer; pub use gpustate::GPUState; +pub use sprite::{Sprite, Spriteable}; /// A handle to a sprite texture #[derive(Debug, Clone)] pub struct SpriteTexture(pub String); +/* // API correction matrix. // cgmath uses OpenGL's matrix format, which // needs to be converted to wgpu's matrix format. -/* #[rustfmt::skip] const OPENGL_TO_WGPU_MATRIX: Matrix4 = Matrix4::new( 1.0, 0.0, 0.0, 0.0, diff --git a/src/render/pipeline.rs b/src/render/pipeline.rs index 5c35f8a..27d5567 100644 --- a/src/render/pipeline.rs +++ b/src/render/pipeline.rs @@ -1,6 +1,7 @@ use std::rc::Rc; use wgpu; +use super::consts::{SHADER_MAIN_FRAGMENT, SHADER_MAIN_VERTEX}; use super::vertexbuffer::VertexBuffer; pub struct PipelineBuilder<'a> { @@ -19,9 +20,6 @@ pub struct PipelineBuilder<'a> { } impl<'a> PipelineBuilder<'a> { - const VERTEX_MAIN: &'static str = "vertex_shader_main"; - const FRAGMENT_MAIN: &'static str = "fragment_shader_main"; - pub fn new(label: &'a str, device: &'a wgpu::Device) -> Self { Self { label, @@ -98,13 +96,13 @@ impl<'a> PipelineBuilder<'a> { vertex: wgpu::VertexState { module: &shader, - entry_point: Self::VERTEX_MAIN, + entry_point: SHADER_MAIN_VERTEX, buffers: &self.vertex_buffer.unwrap().layout, }, fragment: Some(wgpu::FragmentState { module: &shader, - entry_point: Self::FRAGMENT_MAIN, + entry_point: SHADER_MAIN_FRAGMENT, targets: &[Some(wgpu::ColorTargetState { format: self.format.unwrap(), blend: Some(wgpu::BlendState::ALPHA_BLENDING), diff --git a/src/render/shaders/sprite.wgsl b/src/render/shaders/sprite.wgsl index 201c131..de0c32c 100644 --- a/src/render/shaders/sprite.wgsl +++ b/src/render/shaders/sprite.wgsl @@ -1,4 +1,3 @@ -// Vertex shader struct InstanceInput { @location(2) rotation_matrix_0: vec2, @location(3) rotation_matrix_1: vec2, @@ -32,8 +31,16 @@ struct GlobalUniform { }; +@group(0) @binding(0) +var texture_array: binding_array>; +@group(0) @binding(1) +var sampler_array: binding_array; + + + + @vertex -fn vertex_shader_main( +fn vertex_main( vertex: VertexInput, instance: InstanceInput, ) -> VertexOutput { @@ -71,15 +78,8 @@ fn vertex_shader_main( } -// Fragment shader -@group(0) @binding(0) -var texture_array: binding_array>; -@group(0) @binding(1) -var sampler_array: binding_array; - - @fragment -fn fragment_shader_main(in: VertexOutput) -> @location(0) vec4 { +fn fragment_main(in: VertexOutput) -> @location(0) vec4 { return textureSampleLevel( texture_array[in.index], sampler_array[in.index], diff --git a/src/render/shaders/starfield.wgsl b/src/render/shaders/starfield.wgsl index 27b4355..707af0e 100644 --- a/src/render/shaders/starfield.wgsl +++ b/src/render/shaders/starfield.wgsl @@ -1,4 +1,3 @@ -// Vertex shader struct InstanceInput { @location(2) position: vec2, @location(3) parallax: f32, @@ -29,12 +28,20 @@ struct GlobalUniform { }; +@group(0) @binding(0) +var texture_array: binding_array>; +@group(0) @binding(1) +var sampler_array: binding_array; + + + + fn fmod(x: vec2, m: f32) -> vec2 { return x - floor(x / m) * m; } @vertex -fn vertex_shader_main( +fn vertex_main( vertex: VertexInput, instance: InstanceInput, ) -> VertexOutput { @@ -95,15 +102,8 @@ fn vertex_shader_main( return out; } - -@group(0) @binding(0) -var texture_array: binding_array>; -@group(0) @binding(1) -var sampler_array: binding_array; - -// Fragment shader @fragment -fn fragment_shader_main(in: VertexOutput) -> @location(0) vec4 { +fn fragment_main(in: VertexOutput) -> @location(0) vec4 { let b = 0.8 - (in.tint.x / 1.5); // brightness let c = in.tint.y; // color // TODO: saturation diff --git a/src/render/sprite.rs b/src/render/sprite.rs new file mode 100644 index 0000000..e3edb97 --- /dev/null +++ b/src/render/sprite.rs @@ -0,0 +1,33 @@ +use cgmath::{Deg, Point2}; + +use super::SpriteTexture; +use crate::physics::Pfloat; + +pub struct Sprite { + /// Name of the sprite to draw + pub texture: SpriteTexture, + + /// This object's position, in world coordinates. + pub pos: Point2, + + /// The size of this sprite, + /// given as height in world units. + pub size: Pfloat, + + /// Scale factor. + /// if this is 1, sprite height is exactly self.size. + pub scale: Pfloat, + + /// This sprite's rotation + /// (relative to north, measured ccw) + pub angle: Deg, + + /// Parallax factor. + /// Corresponds to z-distance, and affects + /// position and scale. + pub parallax: Pfloat, +} + +pub trait Spriteable { + fn get_sprite(&self) -> Sprite; +} diff --git a/src/render/texturearray/array.rs b/src/render/texturearray/array.rs index bfaf42e..6c95b77 100644 --- a/src/render/texturearray/array.rs +++ b/src/render/texturearray/array.rs @@ -2,10 +2,11 @@ use anyhow::Result; use std::{collections::HashMap, num::NonZeroU32, path::PathBuf}; use wgpu::BindGroupLayout; +use super::{ + super::consts::TEXTURE_INDEX_PATH, loader::TextureLoader, Texture, TextureArrayConfig, +}; use crate::render::SpriteTexture; -use super::{loader::TextureLoader, Texture, TextureArrayConfig}; - pub struct TextureArray { pub bind_group: wgpu::BindGroup, pub bind_group_layout: BindGroupLayout, @@ -14,8 +15,6 @@ pub struct TextureArray { } impl TextureArray { - const INDEX_PATH: &'static str = "assets"; - pub fn get_starfield_texture(&self) -> Texture { return self.get_texture(&self.config.starfield); } @@ -33,7 +32,7 @@ impl TextureArray { pub fn new(device: &wgpu::Device, queue: &wgpu::Queue) -> Result { // Load all textures - let loader = TextureLoader::load(device, queue, &PathBuf::from(Self::INDEX_PATH))?; + let loader = TextureLoader::load(device, queue, &PathBuf::from(TEXTURE_INDEX_PATH))?; let sampler = device.create_sampler(&wgpu::SamplerDescriptor { address_mode_u: wgpu::AddressMode::ClampToEdge, diff --git a/src/render/texturearray/loader.rs b/src/render/texturearray/loader.rs index d2dc4a3..6b4895d 100644 --- a/src/render/texturearray/loader.rs +++ b/src/render/texturearray/loader.rs @@ -1,7 +1,9 @@ use anyhow::Result; use std::{collections::HashMap, fs::File, io::Read, path::Path, path::PathBuf}; -use super::{rawtexture::RawTexture, Texture, TextureArrayConfig}; +use super::{ + super::consts::TEXTURE_INDEX_NAME, rawtexture::RawTexture, Texture, TextureArrayConfig, +}; mod fields { @@ -64,12 +66,10 @@ pub struct TextureLoader { } impl TextureLoader { - const INDEX_NAME: &'static str = "_index.toml"; - pub fn load(device: &wgpu::Device, queue: &wgpu::Queue, index_path: &Path) -> Result { let index: fields::Index = { let mut texture_index_string = String::new(); - let _ = File::open(index_path.join(Self::INDEX_NAME))? + let _ = File::open(index_path.join(TEXTURE_INDEX_NAME))? .read_to_string(&mut texture_index_string); toml::from_str(&texture_index_string)? }; @@ -79,6 +79,7 @@ impl TextureLoader { Self::inner_load_textures(device, queue, index, index_path, "".to_string())?; // TODO: validate configuration + // TODO: handle error states let mut textures = HashMap::new(); let mut texture_data = Vec::new(); @@ -157,7 +158,7 @@ impl TextureLoader { let sub_root = texture_root.join(&path); let index: fields::SubIndex = { let mut texture_index_string = String::new(); - let _ = File::open(sub_root.join(Self::INDEX_NAME))? + let _ = File::open(sub_root.join(TEXTURE_INDEX_NAME))? .read_to_string(&mut texture_index_string); toml::from_str(&texture_index_string)? }; diff --git a/src/render/vertexbuffer/data.rs b/src/render/vertexbuffer/consts.rs similarity index 100% rename from src/render/vertexbuffer/data.rs rename to src/render/vertexbuffer/consts.rs diff --git a/src/render/vertexbuffer/mod.rs b/src/render/vertexbuffer/mod.rs index 901610b..a297742 100644 --- a/src/render/vertexbuffer/mod.rs +++ b/src/render/vertexbuffer/mod.rs @@ -1,4 +1,4 @@ -pub mod data; +pub mod consts; pub mod types; mod vertexbuffer;