From 112c7bc94d1cbad4e3d6a554278dbd674e9bd8e6 Mon Sep 17 00:00:00 2001 From: Derek Christ Date: Thu, 26 Jan 2023 21:24:50 +0100 Subject: [PATCH] Work in progress towards 2D lighting --- Cargo.lock | 58 +++++++++++++++++------- assets/canvas_shader.wgsl | 33 ++++++++++++-- src/canvas.rs | 93 +++++++++++++++++++++++++++++++++++++-- src/canvas/tile_light.rs | 88 ++++++++++++++++++++++++++++++++++++ src/fruit.rs | 1 + src/main.rs | 9 ++-- src/snake.rs | 1 + 7 files changed, 254 insertions(+), 29 deletions(-) create mode 100644 src/canvas/tile_light.rs diff --git a/Cargo.lock b/Cargo.lock index 014d96e..f048b8f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -43,9 +43,9 @@ dependencies = [ [[package]] name = "ahash" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf6ccdb167abbf410dcb915cabd428929d7f6a04980b54a11f26a39f1c7f7107" +checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" dependencies = [ "cfg-if", "getrandom", @@ -1494,7 +1494,7 @@ version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc9fcd393c3daaaf5909008a1d948319d538b79c51871e4df0993260260a94e4" dependencies = [ - "ahash 0.8.2", + "ahash 0.8.3", "epaint", "nohash-hasher", ] @@ -1563,7 +1563,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ba04741be7f6602b1a1b28f1082cce45948a7032961c52814f8946b28493300" dependencies = [ "ab_glyph", - "ahash 0.8.2", + "ahash 0.8.3", "atomic_refcell", "bytemuck", "emath", @@ -2524,6 +2524,15 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "nom8" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae01545c9c7fc4486ab7debaf2aad7003ac19431791868fb2e8066df97fad2f8" +dependencies = [ + "memchr", +] + [[package]] name = "notify" version = "5.0.0" @@ -2617,18 +2626,18 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.5.7" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf5395665662ef45796a4ff5486c5d41d29e0c09640af4c5f17fd94ee2c119c9" +checksum = "8d829733185c1ca374f17e52b762f24f535ec625d2cc1f070e34c8a9068f341b" dependencies = [ "num_enum_derive", ] [[package]] name = "num_enum_derive" -version = "0.5.7" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b0498641e53dd6ac1a4f22547548caa6864cc4933784319cd1775271c5a46ce" +checksum = "2be1598bf1c313dcdd12092e3f1920f463462525a21b7b4e11b4168353d0123e" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -2842,19 +2851,18 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "pretty-type-name" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8815d101cfb4cb491154896bdab292a395a7ac9ab185a9941a2f5be0135900d" +checksum = "f0f73cdaf19b52e6143685c3606206e114a4dfa969d6b14ec3894c88eb38bd4b" [[package]] name = "proc-macro-crate" -version = "1.2.1" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eda0fc3b0fb7c975631757e14d9049da17374063edb6ebbcbc54d880d4fe94e9" +checksum = "66618389e4ec1c7afe67d51a9bf34ff9236480f8d51e7489b7d5ab0303c13f34" dependencies = [ "once_cell", - "thiserror", - "toml", + "toml_edit", ] [[package]] @@ -3370,6 +3378,23 @@ dependencies = [ "serde", ] +[[package]] +name = "toml_datetime" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4553f467ac8e3d374bc9a177a26801e5d0f9b211aa1673fb137a403afd1c9cf5" + +[[package]] +name = "toml_edit" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "729bfd096e40da9c001f778f5cdecbd2957929a24e10e5883d9392220a751581" +dependencies = [ + "indexmap", + "nom8", + "toml_datetime", +] + [[package]] name = "tracing" version = "0.1.37" @@ -3700,9 +3725,9 @@ dependencies = [ [[package]] name = "webbrowser" -version = "0.8.4" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e74f5ff7786c4c21f61ba8e30ea29c9745f06fca0a4a02d083b3c662583399e8" +checksum = "769f1a8831de12cad7bd6f9693b15b1432d93a151557810f617f626af823acae" dependencies = [ "core-foundation", "dirs", @@ -3713,7 +3738,6 @@ dependencies = [ "raw-window-handle 0.5.0", "url", "web-sys", - "windows 0.43.0", ] [[package]] diff --git a/assets/canvas_shader.wgsl b/assets/canvas_shader.wgsl index 3c11390..bf32d14 100644 --- a/assets/canvas_shader.wgsl +++ b/assets/canvas_shader.wgsl @@ -1,12 +1,37 @@ -struct VertexOutput { - @builtin(position) clip_position: vec4, +struct FragmentInput { + @builtin(position) fragment_coordinate: vec4, @location(0) world_position: vec4, @location(1) world_normal: vec3, @location(2) uv: vec2, }; +struct TileLight { + color: vec4, + intensity: f32, + position: vec2, +}; + +struct CanvasMaterial { + ambient_color: vec4, + tile_lights: array, + number_of_lights: u32, +}; + + +@group(1) @binding(0) +var canvas_material: CanvasMaterial; + @fragment -fn fragment(input: VertexOutput) -> @location(0) vec4 { - var output_color = vec4(input.uv, 1.0, 1.0); +fn fragment(input: FragmentInput) -> @location(0) vec4 { + var output_color = canvas_material.ambient_color; + + var lighting = vec4(); + for (var i: u32 = 0u; i < canvas_material.number_of_lights; i++) { + var luminance = canvas_material.tile_lights[i].intensity * pow(length(canvas_material.tile_lights[i].position - input.world_position.xy), -1.7) * 2.0; + lighting += vec4(luminance * canvas_material.tile_lights[i].color.rgb, 0.0); + } + + output_color += lighting; + return output_color; } diff --git a/src/canvas.rs b/src/canvas.rs index f335c2f..74bbd52 100644 --- a/src/canvas.rs +++ b/src/canvas.rs @@ -1,10 +1,20 @@ +mod tile_light; + use crate::grid; use bevy::{ prelude::*, reflect::TypeUuid, - render::render_resource::{AsBindGroup, ShaderRef}, - sprite::{Material2d, Material2dPlugin, MaterialMesh2dBundle}, + render::{ + render_resource::{encase, AsBindGroup, OwnedBindingResource, ShaderRef, ShaderType}, + renderer::RenderQueue, + RenderApp, RenderStage, + }, + sprite::{Material2d, Material2dPlugin, MaterialMesh2dBundle, RenderMaterials2d}, }; +use tile_light::{ + extract_tile_lights, prepare_tile_lights, TileLight, TileLights, TileLightsUniform, +}; +pub(crate) use tile_light::EmissiveTile; pub struct CanvasPlugin; @@ -12,17 +22,92 @@ impl Plugin for CanvasPlugin { fn build(&self, app: &mut App) { app.add_plugin(Material2dPlugin::::default()) .add_startup_system(spawn_canvas); + + app.sub_app_mut(RenderApp) + .init_resource::() + .add_system_to_stage(RenderStage::Extract, extract_tile_lights) + .add_system_to_stage(RenderStage::Prepare, prepare_canvas_material); + // .add_system_to_stage(RenderStage::Prepare, prepare_tile_lights); } } #[derive(AsBindGroup, TypeUuid, Clone)] #[uuid = "24f83f6e-e52d-41a6-bf1d-0e46e57a4995"] -struct CanvasMaterial {} +struct CanvasMaterial { + #[uniform(0)] + ambient_color: Color, + #[uniform(0)] + tile_lights: tile_light::TileLightsUniform, + #[uniform(0)] + number_of_lights: u32, +} + +impl Default for CanvasMaterial { + fn default() -> Self { + Self { + ambient_color: Color::rgb(0.1, 0.1, 0.1), + number_of_lights: 0, + tile_lights: Default::default(), + } + } +} + +#[derive(ShaderType)] +struct CanvasMaterialUniform { + ambient_color: Color, + tile_lights: tile_light::TileLightsUniform, + number_of_lights: u32, +} impl Material2d for CanvasMaterial { fn fragment_shader() -> ShaderRef { "canvas_shader.wgsl".into() } + + // fn specialize( + // descriptor: &mut bevy::render::render_resource::RenderPipelineDescriptor, + // layout: &bevy::render::mesh::MeshVertexBufferLayout, + // key: bevy::sprite::Material2dKey, + // ) -> Result<(), bevy::render::render_resource::SpecializedMeshPipelineError> { + // if let Some(layouts) = descriptor.layout { + // layouts.push(BindGr); + // } + + // Ok(()) + // } +} + +fn prepare_canvas_material( + materials: Res>, + canvas_query: Query<&Handle>, + tile_light_query: Query<&TileLight>, + render_queue: Res, +) { + let tile_lights = tile_light_query.iter().cloned().collect::>(); + let mut tile_lights_uniform = tile_light::TileLightsUniform::default(); + // tile_lights_uniform.data.copy_from_slice(&tile_lights); + + let len = tile_lights.len().min(256); + let src = &tile_lights[..len]; + let dst = &mut tile_lights_uniform.data[..len]; + dst.copy_from_slice(src); + + for handle in canvas_query.iter() { + if let Some(material) = materials.get(handle) { + let binding = &material.bindings[0]; + if let OwnedBindingResource::Buffer(cur_buffer) = binding { + let mut buffer = encase::UniformBuffer::new(Vec::new()); + buffer + .write(&CanvasMaterialUniform { + ambient_color: Color::rgb_u8(12, 12, 12), + tile_lights: tile_lights_uniform.clone(), + number_of_lights: tile_lights.len() as _, + }) + .unwrap(); + render_queue.write_buffer(cur_buffer, 0, buffer.as_ref()); + } + } + } } fn spawn_canvas( @@ -33,7 +118,7 @@ fn spawn_canvas( commands .spawn(MaterialMesh2dBundle { mesh: mesh_assets.add(Mesh::from(shape::Quad::default())).into(), - material: canvas_material_assets.add(CanvasMaterial {}), + material: canvas_material_assets.add(CanvasMaterial::default()), transform: Transform::from_scale(Vec3::splat( grid::SEGMENT_SIZE * f32::from(grid::SIZE), )), diff --git a/src/canvas/tile_light.rs b/src/canvas/tile_light.rs new file mode 100644 index 0000000..12a492c --- /dev/null +++ b/src/canvas/tile_light.rs @@ -0,0 +1,88 @@ +use bevy::{ + math::Vec3Swizzles, + prelude::*, + render::{ + render_resource::{ShaderType, UniformBuffer}, + renderer::{RenderDevice, RenderQueue}, + Extract, + }, + sprite::Material2dPipeline, +}; + +use super::CanvasMaterial; + +const MAX_TILE_LIGHTS: usize = 256; + +#[derive(Component, Clone, Copy)] +pub(crate) struct EmissiveTile { + pub intensity: f32, +} + +impl Default for EmissiveTile { + fn default() -> Self { + Self { intensity: 1.0 } + } +} + +#[derive(Copy, Clone, Default, ShaderType, Component)] +pub(super) struct TileLight { + color: Color, + intensity: f32, + position: Vec2, +} + +#[derive(Default, Resource)] +pub(super) struct TileLights(UniformBuffer); + +#[derive(ShaderType, Clone)] +pub(super) struct TileLightsUniform { + pub data: Box<[TileLight; MAX_TILE_LIGHTS]>, +} + +impl Default for TileLightsUniform { + fn default() -> Self { + Self { + data: Box::new([TileLight::default(); MAX_TILE_LIGHTS]), + } + } +} + +impl TileLights { + fn set(&mut self, lights: &[TileLight]) { + let len = lights.len().min(MAX_TILE_LIGHTS); + let src = &lights[..len]; + let dst = &mut self.0.get_mut().data[..len]; + dst.copy_from_slice(src); + } + + fn write_buffer(&mut self, render_device: &RenderDevice, render_queue: &RenderQueue) { + self.0.write_buffer(render_device, render_queue); + } +} + +pub(super) fn extract_tile_lights( + mut commands: Commands, + emissive_sprite_query: Extract>, +) { + for (entity, emissive_sprite, sprite, global_transform) in emissive_sprite_query.iter() { + commands.get_or_spawn(entity).insert(TileLight { + color: sprite.color, + intensity: emissive_sprite.intensity, + position: global_transform.translation().xy(), + }); + } +} + +pub(super) fn prepare_tile_lights( + render_device: Res, + render_queue: Res, + canvas_material_pipeline: Res>, + light_query: Query<&TileLight>, + mut tile_lights: ResMut, +) { + let lights = light_query.iter().cloned().collect::>(); + tile_lights.set(&lights); + tile_lights.write_buffer(&render_device, &render_queue); + + // canvas_material_pipeline. +} diff --git a/src/fruit.rs b/src/fruit.rs index 31a72ca..0a6de24 100644 --- a/src/fruit.rs +++ b/src/fruit.rs @@ -63,6 +63,7 @@ fn spawn_fruit_system(mut commands: Commands, coordinate_query: Query<&grid::Coo }) .insert(fruit_coordinate) .insert(Fruit) + .insert(crate::canvas::EmissiveTile { intensity: 1.0 }) .insert(Name::new("Fruit")); } diff --git a/src/main.rs b/src/main.rs index 36d63cb..b8b68d2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -75,10 +75,11 @@ fn setup_system(mut commands: Commands) { ..Default::default() }) .insert(Name::new("Orthographic Camera")) - .insert(BloomSettings { - threshold: 0.7, - ..Default::default() - }); + // .insert(BloomSettings { + // threshold: 0.7, + // ..Default::default() + // }) + ; } fn camera_move_system( diff --git a/src/snake.rs b/src/snake.rs index ab218ad..d61fd09 100644 --- a/src/snake.rs +++ b/src/snake.rs @@ -129,6 +129,7 @@ fn create_snake_segment(commands: &mut Commands, grid_position: grid::Coordinate }) .insert(SnakeSegment) .insert(grid_position) + .insert(crate::canvas::EmissiveTile { intensity: 1.0 }) .id() }