wip: Software shadows to corners only

feat/shadows
hheik 2023-12-07 17:03:59 +02:00
parent b13ee649be
commit 499e6ded41
1 changed files with 76 additions and 12 deletions

View File

@ -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::<DarknessMaterial>()
.add_plugins(Material2dPlugin::<DarknessMaterial>::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<ShadowVertex>,
}
@ -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<Assets<DarknessMaterial>>,
mut images: ResMut<Assets<Image>>,
shadow_mesh_query: Query<(&ShadowMesh, &GlobalTransform), Changed<ShadowMesh>>,
material_query: Query<&Handle<DarknessMaterial>>,
) {
// 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<ShadowVertex>,
) -> [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<RapierContext>,
visibility_blocker_query: &Query<&VisibilityBlocker>,