Work in progress towards 2D lighting

This commit is contained in:
2023-01-26 21:24:50 +01:00
parent 0124123189
commit 112c7bc94d
7 changed files with 254 additions and 29 deletions

View File

@@ -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::<CanvasMaterial>::default())
.add_startup_system(spawn_canvas);
app.sub_app_mut(RenderApp)
.init_resource::<TileLights>()
.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<Self>,
// ) -> Result<(), bevy::render::render_resource::SpecializedMeshPipelineError> {
// if let Some(layouts) = descriptor.layout {
// layouts.push(BindGr);
// }
// Ok(())
// }
}
fn prepare_canvas_material(
materials: Res<RenderMaterials2d<CanvasMaterial>>,
canvas_query: Query<&Handle<CanvasMaterial>>,
tile_light_query: Query<&TileLight>,
render_queue: Res<RenderQueue>,
) {
let tile_lights = tile_light_query.iter().cloned().collect::<Vec<_>>();
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),
)),

88
src/canvas/tile_light.rs Normal file
View File

@@ -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<TileLightsUniform>);
#[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<Query<(Entity, &EmissiveTile, &Sprite, &GlobalTransform)>>,
) {
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<RenderDevice>,
render_queue: Res<RenderQueue>,
canvas_material_pipeline: Res<Material2dPipeline<CanvasMaterial>>,
light_query: Query<&TileLight>,
mut tile_lights: ResMut<TileLights>,
) {
let lights = light_query.iter().cloned().collect::<Vec<_>>();
tile_lights.set(&lights);
tile_lights.write_buffer(&render_device, &render_queue);
// canvas_material_pipeline.
}

View File

@@ -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"));
}

View File

@@ -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(

View File

@@ -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()
}