Skip to content

Instantly share code, notes, and snippets.

@rparrett
Last active January 25, 2023 23:16
Show Gist options
  • Save rparrett/0b8a2de7443d41941ad6463e2a42c331 to your computer and use it in GitHub Desktop.
Save rparrett/0b8a2de7443d41941ad6463e2a42c331 to your computer and use it in GitHub Desktop.
Bevy 0.10-dev Directional Light Playground
use bevy::{ecs::world::Ref, pbr::CascadeShadowConfig, prelude::*};
use std::f32::consts::PI;
#[derive(Component)]
struct LightRotation {
x: f32,
y: f32,
}
#[derive(Resource, Clone)]
struct ConfigParams {
num_cascades: usize,
nearest_bound: f32,
shadow_maximum_distance: f32,
overlap_proportion: f32,
near_plane: f32,
}
impl Default for ConfigParams {
fn default() -> Self {
Self {
num_cascades: 4,
nearest_bound: 5.0,
shadow_maximum_distance: 1000.0,
overlap_proportion: 0.2,
near_plane: 0.1,
}
}
}
impl std::fmt::Display for ConfigParams {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "num_cascades: {}\nnearest_bound: {}\nshadow_maximum_distance: {}\noverlap_proportion: {}\nnear_plane: {}\n", self.num_cascades, self.nearest_bound, self.shadow_maximum_distance, self.overlap_proportion, self.near_plane)?;
Ok(())
}
}
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.init_resource::<ConfigParams>()
.add_startup_system(setup)
.add_startup_system(setup_ui)
.add_system(movement)
.add_system(rotation)
.add_system(config)
.add_system(ui)
.run();
}
fn setup_ui(mut commands: Commands, asset_server: Res<AssetServer>) {
commands.spawn(TextBundle::from_section(
"",
TextStyle {
font: asset_server.load("fonts/FiraMono-Medium.ttf"),
font_size: 14.0,
color: Color::BLACK,
},
));
}
fn setup(
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
) {
let extents = 2000.0;
let num = 75;
let spacing = extents / (num - 1) as f32;
// plane
commands.spawn(PbrBundle {
mesh: meshes.add(Mesh::from(shape::Plane { size: 2100.0 })),
material: materials.add(Color::rgb(0.3, 0.5, 0.3).into()),
..default()
});
// gnomon
let unlit = materials.add(StandardMaterial {
base_color: Color::rgb(0.8, 0.7, 0.6),
unlit: true,
..default()
});
let gnomon = meshes.add(Mesh::from(shape::Cube { size: 1.0 }));
for x in 0..num {
for z in 0..num {
commands.spawn(PbrBundle {
mesh: gnomon.clone(),
material: unlit.clone(),
transform: Transform::from_xyz(
-extents / 2. + x as f32 * spacing,
0.5,
-extents / 2. + z as f32 * spacing,
)
.with_scale(Vec3::new(1., 20., 1.)),
..default()
});
}
}
commands.spawn((
DirectionalLightBundle {
directional_light: DirectionalLight {
shadows_enabled: true,
..default()
},
..default()
},
LightRotation {
x: -PI / 4.,
y: PI / 4.,
},
));
// camera
commands.spawn(Camera3dBundle {
transform: Transform::from_xyz(-200.0, 100.0, 200.0).looking_at(Vec3::ZERO, Vec3::Y),
..default()
});
}
fn movement(
input: Res<Input<KeyCode>>,
time: Res<Time>,
mut query: Query<&mut Transform, With<Camera3d>>,
) {
for mut transform in &mut query {
let mut direction = Vec3::ZERO;
let mut speed = 20.;
if input.pressed(KeyCode::Up) {
direction.z += 1.0;
}
if input.pressed(KeyCode::Down) {
direction.z -= 1.0;
}
if input.pressed(KeyCode::Left) {
direction.x -= 1.0;
}
if input.pressed(KeyCode::Right) {
direction.x += 1.0;
}
if input.pressed(KeyCode::O) {
direction.y -= 1.0;
}
if input.pressed(KeyCode::L) {
direction.y += 1.0;
}
if input.any_pressed([KeyCode::LShift, KeyCode::RShift]) {
speed *= 10.;
}
if direction != Vec3::ZERO {
let vel = direction.normalize() * speed;
let dt = time.delta_seconds();
let forward = transform.forward() * Vec3::new(1., 0., 1.);
let right = transform.right();
transform.translation +=
vel.x * dt * right + vel.y * dt * Vec3::Y + vel.z * dt * forward;
}
}
}
fn rotation(
input: Res<Input<KeyCode>>,
time: Res<Time>,
mut query: Query<(&mut Transform, &mut LightRotation)>,
) {
for (mut transform, mut rotation) in &mut query {
let mut direction = 0.;
if input.pressed(KeyCode::Q) {
direction -= 1.0;
}
if input.pressed(KeyCode::E) {
direction += 1.0;
}
rotation.y += time.delta_seconds() * 1.0 * direction;
let mut direction = 0.;
if input.pressed(KeyCode::W) {
direction -= 1.0;
}
if input.pressed(KeyCode::S) {
direction += 1.0;
}
rotation.x += time.delta_seconds() * 1.0 * direction;
transform.rotation = Quat::from_euler(EulerRot::YXZ, rotation.y, rotation.x, 0.);
}
}
fn config(
input: Res<Input<KeyCode>>,
time: Res<Time>,
mut params: ResMut<ConfigParams>,
mut query: Query<&mut CascadeShadowConfig>,
mut proj_query: Query<&mut Projection>,
) {
for mut config in &mut query {
let mut changed = false;
if input.just_pressed(KeyCode::R) {
params.num_cascades = (params.num_cascades - 1).max(1);
changed = true;
}
if input.just_pressed(KeyCode::T) {
params.num_cascades += 1;
changed = true;
}
if input.pressed(KeyCode::F) {
params.nearest_bound -= 25. * time.delta_seconds();
params.nearest_bound = params.nearest_bound.max(0.001);
changed = true;
}
if input.pressed(KeyCode::G) {
params.nearest_bound += 25. * time.delta_seconds();
changed = true;
}
if input.pressed(KeyCode::C) {
params.shadow_maximum_distance -= 50. * time.delta_seconds();
changed = true;
}
if input.pressed(KeyCode::V) {
params.shadow_maximum_distance += 50. * time.delta_seconds();
changed = true;
}
if input.pressed(KeyCode::Y) {
params.overlap_proportion -= 0.05 * time.delta_seconds();
changed = true;
}
if input.pressed(KeyCode::U) {
params.overlap_proportion += 0.05 * time.delta_seconds();
changed = true;
}
let mut camera_changed = false;
if input.pressed(KeyCode::H) {
params.near_plane -= 25. * time.delta_seconds();
params.near_plane = params.near_plane.max(0.);
camera_changed = true;
}
if input.pressed(KeyCode::J) {
params.near_plane += 25. * time.delta_seconds();
camera_changed = true;
}
if changed {
*config = CascadeShadowConfig::new(
params.num_cascades,
params.nearest_bound,
params.shadow_maximum_distance,
params.overlap_proportion,
);
}
if camera_changed {
for mut proj in &mut proj_query {
if let Projection::Perspective(perspective) = &mut *proj {
perspective.near = params.near_plane;
}
}
}
}
}
fn ui(
mut text_query: Query<&mut Text>,
params: Res<ConfigParams>,
rotation_query: Query<Ref<LightRotation>>,
config_query: Query<Ref<CascadeShadowConfig>>,
camera_query: Query<Ref<Transform>, With<Camera>>,
) {
let camera = camera_query.single();
let rotation = rotation_query.single();
let config = config_query.single();
if !camera.is_changed()
&& !params.is_changed()
&& !rotation.is_changed()
&& !config.is_changed()
{
return;
}
for mut text in text_query.iter_mut() {
text.sections[0].value = format!(
concat!(
"[r/t] num_cascades: {}\n",
"[f/g] nearest_bound: {}\n",
"[c/v] shadow_maximum_distance: {}\n",
"[y/u] overlap_proportion: {}\n",
"[h/j] near_plane: {}\n",
"bounds: {:?}\n",
"[←/→ ↑/↓ o/l] camera: {:?}\n",
"[q/w w/s] shadow rotation: {}, {}"
),
params.num_cascades,
params.nearest_bound,
params.shadow_maximum_distance,
params.overlap_proportion,
params.near_plane,
config.bounds,
camera.translation,
rotation.x,
rotation.y
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment