Compare commits
No commits in common. "c1894235ba0c5632588b8483539ac255e4f97f63" and "3fbe81d3883bcc6cf5e121a2c54a4eac9cf59801" have entirely different histories.
c1894235ba
...
3fbe81d388
13
src/game.rs
13
src/game.rs
|
|
@ -2,6 +2,8 @@ use crate::{debug, game_setup};
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use bevy_ecs_ldtk::{LdtkWorldBundle, LevelSelection};
|
use bevy_ecs_ldtk::{LdtkWorldBundle, LevelSelection};
|
||||||
|
|
||||||
|
use self::darkness::SpotLight2D;
|
||||||
|
|
||||||
pub mod camera;
|
pub mod camera;
|
||||||
pub mod darkness;
|
pub mod darkness;
|
||||||
pub mod ldtk;
|
pub mod ldtk;
|
||||||
|
|
@ -24,4 +26,15 @@ fn setup(mut commands: Commands, assets: Res<AssetServer>) {
|
||||||
ldtk_handle: assets.load("levels/world.ldtk"),
|
ldtk_handle: assets.load("levels/world.ldtk"),
|
||||||
..default()
|
..default()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
commands.spawn((
|
||||||
|
Name::new("Spot light"),
|
||||||
|
SpatialBundle::from_transform(Transform::from_xyz(32.0, 31.0, 0.0).with_rotation(
|
||||||
|
Quat::from_euler(EulerRot::YXZ, 0.0, 0.0, f32::to_radians(-90.0)),
|
||||||
|
)),
|
||||||
|
SpotLight2D {
|
||||||
|
radius: 100.0,
|
||||||
|
angle: 1.0,
|
||||||
|
},
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,11 +8,8 @@ use bevy::{
|
||||||
sprite::{Material2dPlugin, Mesh2dHandle},
|
sprite::{Material2dPlugin, Mesh2dHandle},
|
||||||
};
|
};
|
||||||
use bevy_ecs_ldtk::{LdtkLevel, LevelEvent};
|
use bevy_ecs_ldtk::{LdtkLevel, LevelEvent};
|
||||||
use bevy_prototype_debug_lines::DebugLines;
|
|
||||||
use bevy_rapier2d::prelude::{Collider, QueryFilter, RapierContext};
|
use bevy_rapier2d::prelude::{Collider, QueryFilter, RapierContext};
|
||||||
|
|
||||||
use crate::debug::DebugMode;
|
|
||||||
|
|
||||||
mod material;
|
mod material;
|
||||||
|
|
||||||
pub use material::*;
|
pub use material::*;
|
||||||
|
|
@ -177,12 +174,8 @@ fn add_to_level(
|
||||||
fn prepare_lights(
|
fn prepare_lights(
|
||||||
mut materials: ResMut<Assets<DarknessMaterial>>,
|
mut materials: ResMut<Assets<DarknessMaterial>>,
|
||||||
mut images: ResMut<Assets<Image>>,
|
mut images: ResMut<Assets<Image>>,
|
||||||
mut debug_draw: ResMut<DebugLines>,
|
|
||||||
debug_mode: Res<DebugMode>,
|
|
||||||
rapier_context: Res<RapierContext>,
|
rapier_context: Res<RapierContext>,
|
||||||
visibility_blocker_query: Query<&VisibilityBlocker>,
|
visibility_blocker_query: Query<&VisibilityBlocker>,
|
||||||
transform_query: Query<&GlobalTransform>,
|
|
||||||
collider_query: Query<&Collider>,
|
|
||||||
material_query: Query<&Handle<DarknessMaterial>>,
|
material_query: Query<&Handle<DarknessMaterial>>,
|
||||||
point_light_query: Query<(&GlobalTransform, &PointLight2D)>,
|
point_light_query: Query<(&GlobalTransform, &PointLight2D)>,
|
||||||
spot_light_query: Query<(&GlobalTransform, &SpotLight2D)>,
|
spot_light_query: Query<(&GlobalTransform, &SpotLight2D)>,
|
||||||
|
|
@ -208,43 +201,14 @@ fn prepare_lights(
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.for_each(|(i, (transform, light))| {
|
.for_each(|(i, (transform, light))| {
|
||||||
let rect = light.aabb();
|
get_light_geometry(&rapier_context, &visibility_blocker_query, *light);
|
||||||
let polygon = get_light_geometry(
|
|
||||||
&rapier_context,
|
|
||||||
&visibility_blocker_query,
|
|
||||||
&transform_query,
|
|
||||||
&collider_query,
|
|
||||||
Rect::from_corners(
|
|
||||||
rect.min + transform.translation().truncate(),
|
|
||||||
rect.max + transform.translation().truncate(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
if debug_mode.enabled {
|
|
||||||
for (i, arr) in polygon.iter().as_slice().windows(2).enumerate() {
|
|
||||||
let p1 = arr[0];
|
|
||||||
let p2 = arr[1];
|
|
||||||
let t1 = i as f32 / polygon.len() as f32;
|
|
||||||
let t2 = (i + 1) as f32 / polygon.len() as f32;
|
|
||||||
let color1 = Color::rgba(1.0 - t1, t1, 0.0, 1.0);
|
|
||||||
let color2 = Color::rgba(1.0 - t2, t2, 0.0, 1.0);
|
|
||||||
debug_draw.line_gradient(
|
|
||||||
p1.extend(0.0),
|
|
||||||
p2.extend(0.0),
|
|
||||||
0.0,
|
|
||||||
color1,
|
|
||||||
color2,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
material.point_lights[i] = GpuPointLight2D {
|
material.point_lights[i] = GpuPointLight2D {
|
||||||
position: transform.translation().truncate(),
|
position: transform.translation().truncate(),
|
||||||
radius: light.radius,
|
radius: light.radius,
|
||||||
};
|
};
|
||||||
for x in 0..SHADOWMAP_RESOLUTION {
|
for x in 0..SHADOWMAP_RESOLUTION {
|
||||||
let offset = (i * SHADOWMAP_RESOLUTION + x) * 4;
|
let offset = (i * SHADOWMAP_RESOLUTION + x) * 4;
|
||||||
let distance = light.radius;
|
let distance = x as f32 / SHADOWMAP_RESOLUTION as f32 * light.radius;
|
||||||
let distance_bytes = bytes_of(&distance);
|
let distance_bytes = bytes_of(&distance);
|
||||||
shadowmap.data[offset + 0] = distance_bytes[0];
|
shadowmap.data[offset + 0] = distance_bytes[0];
|
||||||
shadowmap.data[offset + 1] = distance_bytes[1];
|
shadowmap.data[offset + 1] = distance_bytes[1];
|
||||||
|
|
@ -283,89 +247,17 @@ fn prepare_lights(
|
||||||
fn get_light_geometry(
|
fn get_light_geometry(
|
||||||
rapier_context: &Res<RapierContext>,
|
rapier_context: &Res<RapierContext>,
|
||||||
visibility_blocker_query: &Query<&VisibilityBlocker>,
|
visibility_blocker_query: &Query<&VisibilityBlocker>,
|
||||||
transform_query: &Query<&GlobalTransform>,
|
light: &impl LightAabb,
|
||||||
collider_query: &Query<&Collider>,
|
|
||||||
aabb: Rect,
|
|
||||||
) -> Vec<Vec2> {
|
) -> Vec<Vec2> {
|
||||||
let mut points = vec![
|
let rect = light.aabb();
|
||||||
Vec2::new(aabb.min.x, aabb.min.y),
|
let collider = Collider::cuboid(rect.half_size().x, rect.half_size().y);
|
||||||
Vec2::new(aabb.max.x, aabb.min.y),
|
|
||||||
Vec2::new(aabb.max.x, aabb.max.y),
|
|
||||||
Vec2::new(aabb.min.x, aabb.max.y),
|
|
||||||
];
|
|
||||||
|
|
||||||
let collider = Collider::cuboid(aabb.half_size().x, aabb.half_size().y);
|
|
||||||
let mut filter = QueryFilter::new().exclude_sensors();
|
let mut filter = QueryFilter::new().exclude_sensors();
|
||||||
let predicate = |coll_entity| visibility_blocker_query.get(coll_entity).is_ok();
|
let predicate = |coll_entity| visibility_blocker_query.get(coll_entity).is_ok();
|
||||||
filter.predicate = Some(&predicate);
|
filter.predicate = Some(&predicate);
|
||||||
|
let mut colls = vec![];
|
||||||
rapier_context.intersections_with_shape(aabb.center(), 0.0, &collider, filter, |coll| {
|
rapier_context.intersections_with_shape(rect.center(), 0.0, &collider, filter, |coll| {
|
||||||
let transform = transform_query
|
colls.push(coll);
|
||||||
.get(coll)
|
|
||||||
.expect("Collider should have GlobalTransform");
|
|
||||||
if let Ok(collider) = collider_query.get(coll) {
|
|
||||||
if let Some(cuboid) = collider.as_cuboid() {
|
|
||||||
let rect = Rect::from_center_half_size(
|
|
||||||
transform.translation().truncate(),
|
|
||||||
cuboid.half_extents(),
|
|
||||||
);
|
|
||||||
points.push(rect.min);
|
|
||||||
points.push(rect.max);
|
|
||||||
points.push(Vec2::new(rect.min.x, rect.max.y));
|
|
||||||
points.push(Vec2::new(rect.max.x, rect.min.y));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
true
|
true
|
||||||
});
|
});
|
||||||
|
vec![]
|
||||||
let center = aabb.center();
|
|
||||||
points.sort_unstable_by(|a, b| {
|
|
||||||
f32::atan2(a.y - center.y, a.x - center.x)
|
|
||||||
.partial_cmp(&f32::atan2(b.y - center.y, b.x - center.x))
|
|
||||||
.unwrap()
|
|
||||||
});
|
|
||||||
|
|
||||||
// Build visibility polygon
|
|
||||||
let mut polygon: Vec<_> = vec![];
|
|
||||||
for point in points.drain(..) {
|
|
||||||
// We shoot 2 rays offset by this angle from the point.
|
|
||||||
const ANGLE_OFFSET: f32 = 0.0001;
|
|
||||||
offset_cast(
|
|
||||||
aabb.center(),
|
|
||||||
(point - aabb.center()).normalize_or_zero(),
|
|
||||||
aabb.size().max_element(),
|
|
||||||
true,
|
|
||||||
filter,
|
|
||||||
ANGLE_OFFSET,
|
|
||||||
&rapier_context,
|
|
||||||
)
|
|
||||||
.iter()
|
|
||||||
.for_each(|ray| polygon.push(aabb.center() + *ray));
|
|
||||||
}
|
|
||||||
|
|
||||||
polygon.push(*polygon.first().unwrap());
|
|
||||||
polygon
|
|
||||||
}
|
|
||||||
|
|
||||||
fn offset_cast(
|
|
||||||
ray_origin: Vec2,
|
|
||||||
ray_dir: Vec2,
|
|
||||||
max_toi: f32,
|
|
||||||
solid: bool,
|
|
||||||
filter: QueryFilter,
|
|
||||||
angle_offset: f32,
|
|
||||||
rapier_context: &Res<RapierContext>,
|
|
||||||
) -> Vec<Vec2> {
|
|
||||||
let dir1 = Vec2::new((-angle_offset).cos(), (-angle_offset).sin()).rotate(ray_dir);
|
|
||||||
let dir2 = Vec2::new(angle_offset.cos(), angle_offset.sin()).rotate(ray_dir);
|
|
||||||
let ray1 = rapier_context.cast_ray(ray_origin, dir1, max_toi, solid, filter);
|
|
||||||
let ray2 = rapier_context.cast_ray(ray_origin, dir2, max_toi, solid, filter);
|
|
||||||
let toi1 = ray1.map_or(max_toi, |(_, toi)| toi);
|
|
||||||
let toi2 = ray2.map_or(max_toi, |(_, toi)| toi);
|
|
||||||
|
|
||||||
if (toi1 - toi2).abs() > toi1 * 0.1 {
|
|
||||||
vec![dir1 * toi1, dir2 * toi2]
|
|
||||||
} else {
|
|
||||||
vec![(dir1 * toi1 + dir2 * toi2) / 2.0]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue