Skip to content

Instantly share code, notes, and snippets.

@rparrett
Last active February 23, 2023 22:24
Show Gist options
  • Save rparrett/7f028c5893df13920ab792b398e65787 to your computer and use it in GitHub Desktop.
Save rparrett/7f028c5893df13920ab792b398e65787 to your computer and use it in GitHub Desktop.
Bevy 0.10-pre Directional Light Playground
use bevy::{
ecs::world::Ref,
pbr::{CascadeShadowConfig, CascadeShadowConfigBuilder},
prelude::*,
};
use std::f32::consts::PI;
#[derive(Component)]
struct LightRotation {
x: f32,
y: f32,
}
#[derive(Resource, Clone)]
struct ConfigParams {
num_cascades: usize,
first_cascade_far_bound: f32,
minimum_distance: f32,
maximum_distance: f32,
overlap_proportion: f32,
near_plane: f32,
}
impl Default for ConfigParams {
fn default() -> Self {
Self {
num_cascades: 4,
first_cascade_far_bound: 140.0,
minimum_distance: 120.,
// first_cascade_far_bound: 5.0,
// minimum_distance: 0.,
maximum_distance: 1800.0,
overlap_proportion: 0.2,
near_plane: 0.1,
}
}
}
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();
}
#[derive(Resource, Debug)]
struct Test(u32);
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,
subdivisions: 10,
})),
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 * 1.55,
},
));
// camera
// commands.spawn(Camera3dBundle {
// transform: Transform::from_xyz(-200.0, 100.0, 200.0).looking_at(Vec3::ZERO, Vec3::Y),
// ..default()
// });
commands.spawn(Camera3dBundle {
projection: OrthographicProjection::default().into(),
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>,
mut setup: Local<bool>,
) {
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.first_cascade_far_bound -= 25. * time.delta_seconds();
params.first_cascade_far_bound = params.first_cascade_far_bound.max(0.001);
params.first_cascade_far_bound = params
.first_cascade_far_bound
.max(params.minimum_distance + 0.1);
changed = true;
}
if input.pressed(KeyCode::G) {
params.first_cascade_far_bound += 25. * time.delta_seconds();
params.first_cascade_far_bound = params
.first_cascade_far_bound
.min(params.maximum_distance - 0.1);
changed = true;
}
if input.pressed(KeyCode::C) {
params.maximum_distance -= 50. * time.delta_seconds();
changed = true;
}
if input.pressed(KeyCode::V) {
params.maximum_distance += 50. * time.delta_seconds();
changed = true;
}
if input.pressed(KeyCode::N) {
params.minimum_distance -= 50. * time.delta_seconds();
params.minimum_distance = params.minimum_distance.max(0.);
changed = true;
}
if input.pressed(KeyCode::M) {
params.minimum_distance += 50. * time.delta_seconds();
params.minimum_distance = params
.minimum_distance
.min(params.first_cascade_far_bound - 0.1);
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 || !*setup {
*config = CascadeShadowConfigBuilder {
num_cascades: params.num_cascades,
first_cascade_far_bound: params.first_cascade_far_bound,
minimum_distance: params.minimum_distance,
maximum_distance: params.maximum_distance,
overlap_proportion: params.overlap_proportion,
}
.into();
*setup = true;
}
if camera_changed || !*setup {
for mut proj in &mut proj_query {
if let Projection::Perspective(perspective) = &mut *proj {
perspective.near = params.near_plane;
}
if let Projection::Orthographic(perspective) = &mut *proj {
perspective.near = params.near_plane;
}
}
*setup = true;
}
}
}
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] first_cascade_far_bound: {}\n",
"[c/v] maximum_distance: {}\n",
"[y/u] overlap_proportion: {}\n",
"[n/m] minimum_distance: {}\n",
"[h/j] near_plane: {}\n",
"bounds: {:?}\n",
"[←/→ ↑/↓ o/l] camera: {:?}\n",
"[q/w w/s] shadow rotation: {}, {}"
),
params.num_cascades,
params.first_cascade_far_bound,
params.maximum_distance,
params.overlap_proportion,
params.minimum_distance,
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