From 499e6ded4187ffd5b89f80a440c935b3f5a44c9e Mon Sep 17 00:00:00 2001 From: hheik <4469778+hheik@users.noreply.github.com> Date: Thu, 7 Dec 2023 17:03:59 +0200 Subject: [PATCH] wip: Software shadows to corners only --- src/game/darkness.rs | 88 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 76 insertions(+), 12 deletions(-) diff --git a/src/game/darkness.rs b/src/game/darkness.rs index 689d6d6..4147a04 100644 --- a/src/game/darkness.rs +++ b/src/game/darkness.rs @@ -20,7 +20,7 @@ pub use material::*; // Needs to be the same as in darkness.wgsl pub const MAX_POINT_LIGHTS: usize = 64; pub const MAX_SPOT_LIGHTS: usize = 64; -pub const SHADOWMAP_RESOLUTION: usize = 256; +pub const SHADOWMAP_RESOLUTION: usize = 1024; pub struct DarknessPlugin; @@ -33,7 +33,7 @@ impl Plugin for DarknessPlugin { .register_asset_reflect::() .add_plugins(Material2dPlugin::::default()) .add_systems(Update, add_to_level) - .add_systems(Last, prepare_lights); + .add_systems(Last, (prepare_lights, prepare_shadows).chain()); } } @@ -96,6 +96,7 @@ pub struct ShadowVertex { #[derive(Component, Reflect, Default, Debug, Clone)] #[reflect(Component)] pub struct ShadowMesh { + pub light_index: usize, pub vertices: Vec, } @@ -236,6 +237,7 @@ fn prepare_lights( ); if let Ok(mut shadow_mesh) = shadow_mesh_query.get_mut(*entity) { + shadow_mesh.light_index = i; shadow_mesh.vertices = polygon .iter() .map(|point| ShadowVertex { @@ -270,16 +272,6 @@ fn prepare_lights( position: transform.translation().truncate(), radius: light.radius, }; - // TODO: Remove when shadowmapping is done - for x in 0..SHADOWMAP_RESOLUTION { - let offset = (i * SHADOWMAP_RESOLUTION + x) * 4; - let distance = light.radius; - let distance_bytes = bytes_of(&distance); - shadowmap.data[offset + 0] = distance_bytes[0]; - shadowmap.data[offset + 1] = distance_bytes[1]; - shadowmap.data[offset + 2] = distance_bytes[2]; - shadowmap.data[offset + 3] = distance_bytes[3]; - } }); material.spot_light_count = spot_lights.len() as i32; @@ -310,6 +302,78 @@ fn prepare_lights( } } +fn prepare_shadows( + mut materials: ResMut>, + mut images: ResMut>, + shadow_mesh_query: Query<(&ShadowMesh, &GlobalTransform), Changed>, + material_query: Query<&Handle>, +) { + // TODO: Check that the shadow mesh overlaps the darkness material + for handle in &material_query { + let material = match materials.get_mut(handle) { + Some(material) => material, + None => continue, + }; + + let shadowmap = match &material.shadowmap_texture { + Some(handle) => match images.get_mut(&handle) { + Some(image) => image, + None => continue, + }, + None => continue, + }; + + for (shadow_mesh, global_transform) in &shadow_mesh_query { + let shadow_map = calculate_shadow_map( + global_transform.translation().truncate(), + &shadow_mesh.vertices, + ); + for x in 0..SHADOWMAP_RESOLUTION { + let offset = (shadow_mesh.light_index * SHADOWMAP_RESOLUTION + x) * 4; + let distance_bytes = bytes_of(&shadow_map[x]); + shadowmap.data[offset + 0] = distance_bytes[0]; + shadowmap.data[offset + 1] = distance_bytes[1]; + shadowmap.data[offset + 2] = distance_bytes[2]; + shadowmap.data[offset + 3] = distance_bytes[3]; + } + } + } +} + +fn calculate_shadow_map( + global_position: Vec2, + vertices: &Vec, +) -> [f32; SHADOWMAP_RESOLUTION] { + let mut shadow_map = [0.0; SHADOWMAP_RESOLUTION]; + let mut next_index = 0; + for x in 0..SHADOWMAP_RESOLUTION { + let angle = (x as f32 / SHADOWMAP_RESOLUTION as f32) * 2.0 * std::f32::consts::PI + - std::f32::consts::PI; + + let prev_vertex = if next_index == 0 { + vertices[vertices.len() - 1] + } else { + vertices[(next_index - 1) % vertices.len()] + }; + let next_vertex = vertices[next_index % vertices.len()]; + if angle >= next_vertex.angle && next_index < vertices.len() { + next_index += 1; + } + + let mut relative_angle = angle - prev_vertex.angle; + // Technically not correct, but works + if relative_angle < 0.0 { + relative_angle = + (std::f32::consts::PI - prev_vertex.angle) + (angle + std::f32::consts::PI); + } + + // TODO: Calculate between-points + let distance = (next_vertex.point - global_position).length(); + shadow_map[x] = distance; + } + shadow_map +} + fn get_light_geometry( rapier_context: &Res, visibility_blocker_query: &Query<&VisibilityBlocker>,