From aa2aadee626d8d90bc9fb67699cda2a588a6a992 Mon Sep 17 00:00:00 2001 From: core Date: Sun, 5 Jan 2025 21:52:19 -0500 Subject: [PATCH] switch to matricies for sprite positioning --- Cargo.lock | 42 +++++++++-- starkingdoms-client/Cargo.toml | 1 + starkingdoms-client/src/ecs.rs | 40 +++++++++- starkingdoms-client/src/lib.rs | 14 +++- starkingdoms-client/src/rendering/renderer.rs | 73 ++++++++++++------- starkingdoms-client/src/shaders/sprite.wgsl | 43 +++++++---- 6 files changed, 161 insertions(+), 52 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 170d7b28de57de29cf6611e506f167229de32986..31f20c78c82f870f09c3a22408a5297d30508835 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1068,7 +1068,7 @@ dependencies = [ "bevy", "bitflags 2.6.0", "log", - "nalgebra", + "nalgebra 0.32.6", "rapier2d", "serde", ] @@ -3687,7 +3687,23 @@ dependencies = [ "num-rational", "num-traits", "serde", - "simba", + "simba 0.8.1", + "typenum", +] + +[[package]] +name = "nalgebra" +version = "0.33.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26aecdf64b707efd1310e3544d709c5c0ac61c13756046aaaba41be5c4f66a3b" +dependencies = [ + "approx", + "matrixmultiply", + "nalgebra-macros", + "num-complex", + "num-rational", + "num-traits", + "simba 0.9.0", "typenum", ] @@ -4246,12 +4262,12 @@ dependencies = [ "bitflags 1.3.2", "downcast-rs", "either", - "nalgebra", + "nalgebra 0.32.6", "num-derive", "num-traits", "rustc-hash", "serde", - "simba", + "simba 0.8.1", "slab", "smallvec", "spade", @@ -4602,13 +4618,13 @@ dependencies = [ "bitflags 1.3.2", "crossbeam", "downcast-rs", - "nalgebra", + "nalgebra 0.32.6", "num-derive", "num-traits", "parry2d", "rustc-hash", "serde", - "simba", + "simba 0.8.1", ] [[package]] @@ -5052,6 +5068,19 @@ dependencies = [ "wide", ] +[[package]] +name = "simba" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3a386a501cd104797982c15ae17aafe8b9261315b5d07e3ec803f2ea26be0fa" +dependencies = [ + "approx", + "num-complex", + "num-traits", + "paste", + "wide", +] + [[package]] name = "simd-adler32" version = "0.3.7" @@ -5227,6 +5256,7 @@ dependencies = [ "egui-winit", "futures", "image 0.25.5", + "nalgebra 0.33.2", "pollster", "thiserror 2.0.9", "tracing", diff --git a/starkingdoms-client/Cargo.toml b/starkingdoms-client/Cargo.toml index 860e56fd23ec8f86e4315500593befcd405407f5..f4d5ab73b3e45df8ae3db893e27b7e2975057d79 100644 --- a/starkingdoms-client/Cargo.toml +++ b/starkingdoms-client/Cargo.toml @@ -20,6 +20,7 @@ egui-winit = { version = "0.30", default-features = false, features = ["links", egui-wgpu = "0.30" web-time = "1" futures = "0.3" +nalgebra = "0.33" # WASM dependencies [target.'cfg(target_arch = "wasm32")'.dependencies] diff --git a/starkingdoms-client/src/ecs.rs b/starkingdoms-client/src/ecs.rs index 17b7e377c6b69e3bb0e3d9456c1123fcb890a334..aa506efa0a0643b98981eda46271228b469386ea 100644 --- a/starkingdoms-client/src/ecs.rs +++ b/starkingdoms-client/src/ecs.rs @@ -1,17 +1,52 @@ use bevy_ecs::bundle::Bundle; use bevy_ecs::component::Component; use bevy_ecs::system::Resource; +use nalgebra::Matrix3; #[derive(Component, Debug, Clone, Copy)] -pub struct Position { +pub struct Translation { pub x: f32, pub y: f32, } +impl Translation { + pub fn as_matrix(&self) -> Matrix3 { + Matrix3::from_iterator([ + 1.0, 0.0, 0.0, + 0.0, 1.0, 0.0, + self.x, self.y, 1.0, + ]) + } +} + #[derive(Component, Debug, Clone, Copy)] pub struct Scale { pub width: f32, pub height: f32, } +impl Scale { + pub fn as_matrix(&self) -> Matrix3 { + Matrix3::from_iterator([ + self.width, 0.0, 0.0, + 0.0, self.height, 0.0, + 0.0, 0.0, 1.0 + ]) + } +} +#[derive(Component, Debug, Clone, Copy)] +pub struct Rotation { + pub radians: f32 +} +impl Rotation { + pub fn as_matrix(&self) -> Matrix3 { + let x = self.radians.cos(); + let y = self.radians.sin(); + Matrix3::from_iterator([ + x, y, 0.0, + -y, x, 0.0, + 0.0, 0.0, 1.0 + ]) + } +} #[derive(Component, Debug, Clone)] pub struct SpriteTexture { @@ -20,9 +55,10 @@ pub struct SpriteTexture { #[derive(Bundle)] pub struct SpriteBundle { - pub position: Position, + pub position: Translation, pub scale: Scale, pub texture: SpriteTexture, + pub rotation: Rotation } #[derive(Resource, Debug)] diff --git a/starkingdoms-client/src/lib.rs b/starkingdoms-client/src/lib.rs index f7e075a943ce7d00fecea9df0a243f8c6d0e366d..5c782e81dca8c10a365eb44fd12806987e1170a3 100644 --- a/starkingdoms-client/src/lib.rs +++ b/starkingdoms-client/src/lib.rs @@ -1,4 +1,4 @@ -use crate::ecs::{Camera, Position, Scale, SpriteBundle, SpriteTexture}; +use crate::ecs::{Camera, Translation, Rotation, Scale, SpriteBundle, SpriteTexture}; use crate::input::MouseWheelEvent; use crate::rendering::ui::UiRenderable; use crate::rendering::App; @@ -54,10 +54,16 @@ pub fn start() { update_schedule.add_systems(zoom_camera_on_mouse_events); world.spawn(SpriteBundle { - position: Position { x: 0.0, y: 0.0 }, + position: Translation { + x: 100.0, + y: 100.0 + }, scale: Scale { - width: 50.0, - height: 50.0, + width: 100.0, + height: 100.0, + }, + rotation: Rotation { + radians: 45.0_f32.to_radians() }, texture: SpriteTexture { texture: "happy-tree".to_string(), diff --git a/starkingdoms-client/src/rendering/renderer.rs b/starkingdoms-client/src/rendering/renderer.rs index 6fa50aafa11c6b92149a010a0afcef3b652f84ab..0c80b3c94b75c0935bebf0cc98bf7f0ca54d1020 100644 --- a/starkingdoms-client/src/rendering/renderer.rs +++ b/starkingdoms-client/src/rendering/renderer.rs @@ -1,4 +1,4 @@ -use crate::ecs::{Camera, Position, Scale, SpriteTexture}; +use crate::ecs::{Camera, Translation, Rotation, Scale, SpriteTexture}; use crate::rendering::mipmap::MipGenerator; use crate::rendering::renderer::RenderInitRes::{Initialized, NotReadyYet}; use crate::rendering::texture; @@ -47,7 +47,8 @@ pub struct Renderer { pub textures: HashMap, pub mip_generator: MipGenerator, - pub uniform_buffer: Buffer, + pub frame_uniform: Buffer, + pub local_uniform: Buffer, pub scale_factor: f64, pub window: Arc, @@ -159,9 +160,15 @@ impl Renderer { ); let gui_renderer = egui_wgpu::Renderer::new(&device, format.format, None, 1, false); - let uniform_buffer = device.create_buffer(&BufferDescriptor { - label: Some("quad uniforms"), - size: 16, + let frame_uniform = device.create_buffer(&BufferDescriptor { + label: Some("frame uniforms"), + size: 16, // vec2f, vec2f + usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST, + mapped_at_creation: false, + }); + let local_uniform = device.create_buffer(&BufferDescriptor { + label: Some("local uniforms"), + size: 48, // mat3x3f usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST, mapped_at_creation: false, }); @@ -185,7 +192,8 @@ impl Renderer { logical_size: LogicalSize::from_physical(size, window.scale_factor()), scale_factor: window.scale_factor(), window, - uniform_buffer, + frame_uniform, + local_uniform, size, surface_configuration: format, }) @@ -231,16 +239,25 @@ impl Renderer { label: Some("command encoder"), }); - let mut sprites_to_render: Vec<(Position, Scale, SpriteTexture)> = vec![]; + let mut sprites_to_render: Vec<(Translation, Scale, SpriteTexture, Rotation)> = vec![]; - let mut things_to_render = self.world.query::<(&Position, &Scale, &SpriteTexture)>(); + let mut things_to_render = self.world.query::<(&Translation, &Scale, &SpriteTexture, &Rotation)>(); for thing in things_to_render.iter_mut(&mut self.world) { - sprites_to_render.push((*thing.0, *thing.1, thing.2.clone())); + sprites_to_render.push((*thing.0, *thing.1, thing.2.clone(), *thing.3)); } let cam = self.world.resource::(); - for (pos, scale, tex) in sprites_to_render { + let mut frame_uniform = vec![]; + let frame_uniform_values = [cam.x, cam.y, self.logical_size.width as f32, self.logical_size.height as f32]; + for i in frame_uniform_values { + let mut bytes = i.to_ne_bytes().to_vec(); + frame_uniform.append(&mut bytes); + } + + self.queue.write_buffer(&self.frame_uniform, 0, &frame_uniform); + + for (pos, scale, tex, rot) in sprites_to_render { let tex = self.textures.entry(tex.texture.clone()).or_insert_with(|| { info!("loading texture {}", &tex.texture); let b: &[u8] = match tex.texture.as_str() { @@ -258,28 +275,26 @@ impl Renderer { ) }); - // need to calculate width, height, x, and y, using logical size & aspect + let xy_matrix = pos.as_matrix(); + let rot_matrix = rot.as_matrix(); + let scale_matrix = scale.as_matrix(); - // calculate "viewport position" w/ the camera - let viewport_x = pos.x - cam.x; - let viewport_y = pos.y - cam.y; - let scaled_w = scale.width * cam.zoom; - let scaled_h = scale.height * cam.zoom; + let transform_matrix = xy_matrix * rot_matrix * scale_matrix; - let x_screen = viewport_x / self.logical_size.width as f32; - let y_screen = viewport_y / self.logical_size.height as f32; - let w_screen = scaled_w / self.logical_size.width as f32; - let h_screen = scaled_h / self.logical_size.height as f32; - let mut uniform_buffer = vec![]; - let uniform_buffer_data = [w_screen, h_screen, x_screen, y_screen]; - for i in uniform_buffer_data { + let mut local_buffer = vec![]; + let local_buffer_data = [ + transform_matrix.m11, transform_matrix.m12, transform_matrix.m13, 0.0, + transform_matrix.m21, transform_matrix.m22, transform_matrix.m23, 0.0, + transform_matrix.m31, transform_matrix.m32, transform_matrix.m33, 0.0, + ]; + for i in local_buffer_data { let mut bytes = i.to_ne_bytes().to_vec(); - uniform_buffer.append(&mut bytes); + local_buffer.append(&mut bytes); } self.queue - .write_buffer(&self.uniform_buffer, 0, &uniform_buffer); + .write_buffer(&self.local_uniform, 0, &local_buffer); let bind_group = self.device.create_bind_group(&BindGroupDescriptor { label: Some("test_bind_group"), @@ -298,7 +313,13 @@ impl Renderer { BindGroupEntry { binding: 2, resource: BindingResource::Buffer( - self.uniform_buffer.as_entire_buffer_binding(), + self.frame_uniform.as_entire_buffer_binding(), + ), + }, + BindGroupEntry { + binding: 3, + resource: BindingResource::Buffer( + self.local_uniform.as_entire_buffer_binding(), ), }, ], diff --git a/starkingdoms-client/src/shaders/sprite.wgsl b/starkingdoms-client/src/shaders/sprite.wgsl index 7547bb72d9341fce69cb049ae3ddb5239e1c117a..198889206039d04f07c058f38c9f06d3cf8fefde 100644 --- a/starkingdoms-client/src/shaders/sprite.wgsl +++ b/starkingdoms-client/src/shaders/sprite.wgsl @@ -3,28 +3,43 @@ struct VertexShaderOut { @location(0) texcoord: vec2 } -struct Uniforms { - scale: vec2f, - offset: vec2f +struct FrameUniforms { + camera_position: vec2f, + viewport_size: vec2f } -@group(0) @binding(2) var uni: Uniforms; +struct LocalUniforms { + transform: mat3x3f, +} +@group(0) @binding(2) var frame_uni: FrameUniforms; +@group(0) @binding(3) var local_uni: LocalUniforms; @vertex fn vs( @builtin(vertex_index) vertexIndex : u32 ) -> VertexShaderOut { let pos = array( - vec2(0.0, 0.0), - vec2(1.0, 0.0), - vec2(0.0, 1.0), - vec2(0.0, 1.0), - vec2(1.0, 0.0), - vec2(1.0, 1.0) - ); + vec2(0.0, 0.0), + vec2(1.0, 0.0), + vec2(0.0, 1.0), + vec2(0.0, 1.0), + vec2(1.0, 0.0), + vec2(1.0, 1.0) + ); var vsOutput: VertexShaderOut; - let xy = pos[vertexIndex]; - vsOutput.position = vec4f(xy * uni.scale + uni.offset, 0.0, 1.0); - vsOutput.texcoord = vec2f(xy.x, 1.0 - xy.y); + + let position = (local_uni.transform * vec3f(pos[vertexIndex], 1)).xy; + // convert from pixels to 0.0 to 1.0 + let zeroToOne = position / frame_uni.viewport_size; + // convert from 0 - 1 to 0 - 2 + let zeroToTwo = zeroToOne * 2.0; + // convert from 0 - 2 to -1 - +1 (clip space) + let flippedClipSpace = zeroToTwo - 1.0; + // flip Y + let clipSpace = flippedClipSpace * vec2f(1, -1); + + vsOutput.position = vec4f(clipSpace, 0.0, 1.0); + vsOutput.texcoord = pos[vertexIndex]; + return vsOutput; }