From 381dd4a38359bb178a0b918aa6bf9bf0a6cdedeb Mon Sep 17 00:00:00 2001 From: Mark Date: Tue, 2 Jan 2024 10:12:10 -0800 Subject: [PATCH] Added dynamic radar colors --- TODO.md | 2 +- assets | 2 +- content/factions.toml | 2 + crates/content/src/part/faction.rs | 6 +++ crates/render/shaders/ui.wgsl | 7 ++- crates/render/src/gpustate.rs | 34 ++++++------ crates/render/src/sprite.rs | 4 ++ crates/render/src/vertexbuffer/types.rs | 70 +++++++++++++++++++++++-- crates/ui/src/radar.rs | 31 +++++------ src/game.rs | 3 ++ 10 files changed, 119 insertions(+), 42 deletions(-) diff --git a/TODO.md b/TODO.md index 93f5208..866b5c8 100644 --- a/TODO.md +++ b/TODO.md @@ -4,7 +4,7 @@ - Sound system - Particles, impact effects - Debris on ship death - - Radar: dynamic colors, size, planets and suns + - Radar: ship size, planets and suns ---------------------------------- diff --git a/assets b/assets index 9137741..62fd7fc 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 91377416617d049acec68bb5a17647dcac7acd0e +Subproject commit 62fd7fc297a2631691b588b898694e220d58cf78 diff --git a/content/factions.toml b/content/factions.toml index 7877375..bd56d15 100644 --- a/content/factions.toml +++ b/content/factions.toml @@ -1,7 +1,9 @@ [faction."player"] display_name = "Player" relationship.enemy = "hostile" +color = [0.0, 1.0, 0.0] [faction."enemy"] display_name = "Enemy" relationship.player = "hostile" +color = [1.0, 0.0, 0.0] diff --git a/crates/content/src/part/faction.rs b/crates/content/src/part/faction.rs index f4b728a..d596b17 100644 --- a/crates/content/src/part/faction.rs +++ b/crates/content/src/part/faction.rs @@ -14,6 +14,7 @@ pub(crate) mod syntax { #[derive(Debug, Deserialize)] pub struct Faction { pub display_name: String, + pub color: [f32; 3], pub relationship: HashMap, } } @@ -45,6 +46,10 @@ pub struct Faction { /// The name of this faction pub name: String, + /// This faction's color. + /// Format is RGB, with each color between 0 and 1. + pub color: [f32; 3], + /// This faction's handle pub handle: FactionHandle, @@ -98,6 +103,7 @@ impl crate::Build for Faction { name: faction_name.to_owned(), handle: h, relationships, + color: faction.color, }); } diff --git a/crates/render/shaders/ui.wgsl b/crates/render/shaders/ui.wgsl index ea6de70..dc7774a 100644 --- a/crates/render/shaders/ui.wgsl +++ b/crates/render/shaders/ui.wgsl @@ -3,7 +3,8 @@ struct InstanceInput { @location(3) transform_matrix_1: vec4, @location(4) transform_matrix_2: vec4, @location(5) transform_matrix_3: vec4, - @location(6) texture_idx: u32, + @location(6) color_transform: vec4, + @location(7) texture_idx: u32, }; struct VertexInput { @@ -15,6 +16,7 @@ struct VertexOutput { @builtin(position) position: vec4, @location(0) texture_coords: vec2, @location(1) index: u32, + @location(2) color_transform: vec4, } @@ -57,6 +59,7 @@ fn vertex_main( out.position = transform * vec4(vertex.position, 1.0); out.texture_coords = vertex.texture_coords; out.index = instance.texture_idx; + out.color_transform = instance.color_transform; return out; } @@ -68,5 +71,5 @@ fn fragment_main(in: VertexOutput) -> @location(0) vec4 { sampler_array[0], in.texture_coords, 0.0 - ).rgba; + ).rgba * in.color_transform; } \ No newline at end of file diff --git a/crates/render/src/gpustate.rs b/crates/render/src/gpustate.rs index f65421d..9a71a28 100644 --- a/crates/render/src/gpustate.rs +++ b/crates/render/src/gpustate.rs @@ -15,7 +15,7 @@ use crate::{ texturearray::TextureArray, vertexbuffer::{ consts::{SPRITE_INDICES, SPRITE_VERTICES}, - types::{SpriteInstance, StarfieldInstance, TexturedVertex}, + types::{ObjectInstance, StarfieldInstance, TexturedVertex, UiInstance}, VertexBuffer, }, ObjectSprite, UiSprite, OPENGL_TO_WGPU_MATRIX, @@ -119,7 +119,7 @@ impl GPUState { } let vertex_buffers = VertexBuffers { - object: Rc::new(VertexBuffer::new::( + object: Rc::new(VertexBuffer::new::( "objecte", &device, Some(SPRITE_VERTICES), @@ -135,7 +135,7 @@ impl GPUState { galactica_constants::STARFIELD_SPRITE_INSTANCE_LIMIT, )), - ui: Rc::new(VertexBuffer::new::( + ui: Rc::new(VertexBuffer::new::( "ui", &device, Some(SPRITE_VERTICES), @@ -234,13 +234,13 @@ impl GPUState { self.update_starfield_buffer() } - /// Create a SpriteInstance for an object and add it to `instances`. + /// Create a ObjectInstance for an object and add it to `instances`. /// Also handles child sprites. fn push_object_sprite( &self, camera_zoom: f32, camera_pos: Point2, - instances: &mut Vec, + instances: &mut Vec, clip_ne: Point2, clip_sw: Point2, s: &ObjectSprite, @@ -314,7 +314,7 @@ impl GPUState { let t = OPENGL_TO_WGPU_MATRIX * translate * screen_aspect * rotate * sprite_aspect_and_scale; - instances.push(SpriteInstance { + instances.push(ObjectInstance { transform: t.into(), texture_index: texture.index, }); @@ -332,7 +332,7 @@ impl GPUState { fn push_object_subsprite( &self, camera_zoom: f32, - instances: &mut Vec, + instances: &mut Vec, s: &ObjectSubSprite, parent_pos: Point2, parent_angle: Deg, @@ -364,14 +364,14 @@ impl GPUState { * protate * translate * rotate * sprite_aspect_and_scale; - instances.push(SpriteInstance { + instances.push(ObjectInstance { transform: t.into(), texture_index: texture.index, }); } - /// Create a SpriteInstance for a ui sprite and add it to `instances` - fn push_ui_sprite(&self, instances: &mut Vec, s: &UiSprite) { + /// Create a ObjectInstance for a ui sprite and add it to `instances` + fn push_ui_sprite(&self, instances: &mut Vec, s: &UiSprite) { let logical_size: LogicalSize = self.window_size.to_logical(self.window.scale_factor()); @@ -417,16 +417,16 @@ impl GPUState { }); let screen_aspect = Matrix4::from_nonuniform_scale(1.0 / self.window_aspect, 1.0, 1.0); - instances.push(SpriteInstance { + instances.push(UiInstance { transform: (OPENGL_TO_WGPU_MATRIX * translate * screen_aspect * rotate * scale).into(), texture_index: texture.index, + color: s.color.unwrap_or([1.0, 1.0, 1.0, 1.0]), }); } - /// Make a SpriteInstance for each of the game's visible sprites. - /// Will panic if SPRITE_INSTANCE_LIMIT is exceeded. - /// - /// This is only called inside self.render() + /// Make an instance for all the game's sprites + /// (Objects and UI) + /// This will Will panic if any X_SPRITE_INSTANCE_LIMIT is exceeded. fn update_sprite_instances( &self, camera_zoom: f32, @@ -434,7 +434,7 @@ impl GPUState { objects: &Vec, ui: &Vec, ) -> (usize, usize) { - let mut object_instances: Vec = Vec::new(); + let mut object_instances: Vec = Vec::new(); // Game coordinates (relative to camera) of ne and sw corners of screen. // Used to skip off-screen sprites. @@ -464,7 +464,7 @@ impl GPUState { bytemuck::cast_slice(&object_instances), ); - let mut ui_instances: Vec = Vec::new(); + let mut ui_instances: Vec = Vec::new(); for s in ui { self.push_ui_sprite(&mut ui_instances, s); diff --git a/crates/render/src/sprite.rs b/crates/render/src/sprite.rs index 5b44fbd..11b2933 100644 --- a/crates/render/src/sprite.rs +++ b/crates/render/src/sprite.rs @@ -38,6 +38,10 @@ pub struct UiSprite { /// This object's position, in logical (dpi-adjusted) pixels pub pos: AnchoredUiPosition, + /// This sprite's color will be multiplied by this value. + /// If this is None, color will not be changed. + pub color: Option<[f32; 4]>, + /// The size of this sprite, in logical (dpi-adjusted) pixels pub dimensions: Point2, diff --git a/crates/render/src/vertexbuffer/types.rs b/crates/render/src/vertexbuffer/types.rs index b4060e1..52bc830 100644 --- a/crates/render/src/vertexbuffer/types.rs +++ b/crates/render/src/vertexbuffer/types.rs @@ -81,19 +81,18 @@ impl BufferObject for StarfieldInstance { } } -// Represents a sprite instance in WGSL #[repr(C)] #[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] -pub struct SpriteInstance { +pub struct ObjectInstance { /// Extra transformations this sprite /// (rotation, etc) pub transform: [[f32; 4]; 4], - // What texture to use for this sprite + /// What texture to use for this sprite pub texture_index: u32, } -impl BufferObject for SpriteInstance { +impl BufferObject for ObjectInstance { fn layout() -> wgpu::VertexBufferLayout<'static> { wgpu::VertexBufferLayout { array_stride: Self::SIZE, @@ -133,3 +132,66 @@ impl BufferObject for SpriteInstance { } } } + +#[repr(C)] +#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] +pub struct UiInstance { + /// Extra transformations this sprite + /// (rotation, etc) + pub transform: [[f32; 4]; 4], + + /// This lets us color ui sprites dynamically. + /// Each fragment's color is multiplied by this value. + /// Fill this array with ones if no recoloring should be done. + pub color: [f32; 4], + + /// What texture to use for this sprite + pub texture_index: u32, +} + +impl BufferObject for UiInstance { + fn layout() -> wgpu::VertexBufferLayout<'static> { + wgpu::VertexBufferLayout { + array_stride: Self::SIZE, + // Switch to a step mode of Vertex to Instance. + // This means that our shaders will only change to use the next + // instance when the shader starts processing a new instance + step_mode: wgpu::VertexStepMode::Instance, + attributes: &[ + // 4 arrays = 1 4x4 matrix + wgpu::VertexAttribute { + offset: 0, + shader_location: 2, + format: wgpu::VertexFormat::Float32x4, + }, + wgpu::VertexAttribute { + offset: mem::size_of::<[f32; 4]>() as wgpu::BufferAddress, + shader_location: 3, + format: wgpu::VertexFormat::Float32x4, + }, + wgpu::VertexAttribute { + offset: mem::size_of::<[f32; 8]>() as wgpu::BufferAddress, + shader_location: 4, + format: wgpu::VertexFormat::Float32x4, + }, + wgpu::VertexAttribute { + offset: mem::size_of::<[f32; 12]>() as wgpu::BufferAddress, + shader_location: 5, + format: wgpu::VertexFormat::Float32x4, + }, + // Color + wgpu::VertexAttribute { + offset: mem::size_of::<[f32; 16]>() as wgpu::BufferAddress, + shader_location: 6, + format: wgpu::VertexFormat::Float32x4, + }, + // Texture + wgpu::VertexAttribute { + offset: mem::size_of::<[f32; 20]>() as wgpu::BufferAddress, + shader_location: 7, + format: wgpu::VertexFormat::Uint32, + }, + ], + } + } +} diff --git a/crates/ui/src/radar.rs b/crates/ui/src/radar.rs index 7e98660..fca4ca3 100644 --- a/crates/ui/src/radar.rs +++ b/crates/ui/src/radar.rs @@ -24,6 +24,7 @@ pub fn build_radar( y: radar_size, }, angle: Deg(0.0), + color: None, }); // Draw viewport frame @@ -33,6 +34,7 @@ pub fn build_radar( }; let m = d.magnitude() / radar_range; let d = d / radar_range * (radar_size / 2.0); + let color = Some([0.5, 0.5, 0.5, 1.0]); if m < 0.8 { let texture = ct.get_texture_handle("ui::radarframe"); let dimensions = Point2 { @@ -47,6 +49,7 @@ pub fn build_radar( }), dimensions, angle: Deg(0.0), + color, }); out.push(UiSprite { @@ -57,6 +60,7 @@ pub fn build_radar( }), dimensions, angle: Deg(90.0), + color, }); out.push(UiSprite { @@ -67,6 +71,7 @@ pub fn build_radar( }), dimensions, angle: Deg(180.0), + color, }); out.push(UiSprite { @@ -77,6 +82,7 @@ pub fn build_radar( }), dimensions, angle: Deg(270.0), + color, }); } @@ -87,23 +93,13 @@ pub fn build_radar( let p = util::rigidbody_position(r); let d = p - pr; let m = d.magnitude() / radar_range; - let angle: Deg = util::rigidbody_rotation(r) - .angle(Vector2 { x: 0.0, y: 1.0 }) - .into(); - if s.physics_handle == *player { - out.push(UiSprite { - texture, - pos: AnchoredUiPosition::NwC(Point2 { - x: radar_size / 2.0 + 10.0, - y: radar_size / -2.0 - 10.0, - }), - dimensions: Point2 { - x: texture.aspect, - y: 1.0, - } * 5.0f32.min((0.8 - m) * 50.0), - angle: -angle, - }); - } else if m < 0.8 { + if m < 0.8 { + let angle: Deg = util::rigidbody_rotation(r) + .angle(Vector2 { x: 0.0, y: 1.0 }) + .into(); + let f = ct.get_faction(s.ship.faction).color; + let f = [f[0], f[1], f[2], 1.0]; + out.push(UiSprite { texture, pos: AnchoredUiPosition::NwC( @@ -117,6 +113,7 @@ pub fn build_radar( y: 1.0, } * 5.0f32.min((0.8 - m) * 50.0), angle: -angle, + color: Some(f), }); } } diff --git a/src/game.rs b/src/game.rs index 466f4e6..2817095 100644 --- a/src/game.rs +++ b/src/game.rs @@ -49,6 +49,9 @@ impl Game { let s = object::Ship::new( &ct, content::ShipHandle { index: 0 }, + // This method of specifying factions is non-deterministic, + // but that's ok since this is for debug. + // TODO: fix content::FactionHandle { index: 0 }, object::OutfitSet::new(ss), );