Skip to content

Commit

Permalink
Add light gizmos
Browse files Browse the repository at this point in the history
  • Loading branch information
Kanabenki committed Mar 1, 2024
1 parent 499c978 commit 19ef747
Show file tree
Hide file tree
Showing 3 changed files with 182 additions and 7 deletions.
6 changes: 5 additions & 1 deletion crates/bevy_gizmos/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ pub mod circles;
pub mod config;
pub mod gizmos;
pub mod grid;
pub mod light;
pub mod primitives;

#[cfg(feature = "bevy_sprite")]
Expand Down Expand Up @@ -87,6 +88,8 @@ use config::{
use gizmos::GizmoStorage;
use std::{any::TypeId, mem};

use crate::light::LightGizmoPlugin;

const LINE_SHADER_HANDLE: Handle<Shader> = Handle::weak_from_u128(7414812689238026784);

/// A [`Plugin`] that provides an immediate mode drawing api for visual debugging.
Expand All @@ -108,7 +111,8 @@ impl Plugin for GizmoPlugin {
.init_resource::<LineGizmoHandles>()
// We insert the Resource GizmoConfigStore into the world implicitly here if it does not exist.
.init_gizmo_group::<DefaultGizmoConfigGroup>()
.add_plugins(AabbGizmoPlugin);
.add_plugins(AabbGizmoPlugin)
.add_plugins(LightGizmoPlugin);

let Ok(render_app) = app.get_sub_app_mut(RenderApp) else {
return;
Expand Down
158 changes: 158 additions & 0 deletions crates/bevy_gizmos/src/light.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
use std::f32::consts::PI;

use crate::{self as bevy_gizmos, primitives::dim3::GizmoPrimitive3d};

use bevy_app::{Plugin, PostUpdate};
use bevy_color::{Color, LinearRgba};
use bevy_ecs::{
component::Component,
query::{With, Without},
reflect::ReflectComponent,
schedule::IntoSystemConfigs,
system::{Query, Res},
};
use bevy_math::{primitives::Cone, Quat, Vec3};
use bevy_pbr::{DirectionalLight, PointLight, SpotLight};
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
use bevy_transform::{components::GlobalTransform, TransformSystem};

use crate::{
config::{GizmoConfigGroup, GizmoConfigStore},
gizmos::Gizmos,
AppGizmoBuilder,
};

fn draw_gizmos<'a, P, S, D>(
point_lights: P,
spot_lights: S,
directional_lights: D,
gizmos: &mut Gizmos<LightGizmoConfigGroup>,
) where
P: 'a + IntoIterator<Item = (&'a PointLight, &'a GlobalTransform)>,
S: 'a + IntoIterator<Item = (&'a SpotLight, &'a GlobalTransform)>,
D: 'a + IntoIterator<Item = (&'a DirectionalLight, &'a GlobalTransform)>,
{
for (point_light, transform) in point_lights {
let position = transform.translation();
gizmos.sphere(
position,
Quat::IDENTITY,
point_light.radius,
point_light.color,
);
gizmos
.sphere(
position,
Quat::IDENTITY,
point_light.range,
point_light.color,
)
.circle_segments(32);
}

for (spot_light, transform) in spot_lights {
let (_, rotation, translation) = transform.to_scale_rotation_translation();
gizmos.sphere(
translation,
Quat::IDENTITY,
spot_light.radius,
spot_light.color,
);

// Offset the tip of the cone to the light position.
gizmos.sphere(translation, Quat::IDENTITY, 0.01, LinearRgba::GREEN);
for angle in [spot_light.inner_angle, spot_light.outer_angle] {
let height = spot_light.range * angle.cos();
let position = translation + rotation * Vec3::NEG_Z * height / 2.0;
gizmos
.primitive_3d(
Cone {
radius: spot_light.range * angle.sin(),
height,
},
position,
rotation * Quat::from_rotation_x(PI / 2.0),
spot_light.color,
)
.height_segments(4)
.base_segments(32);
}

for arc_rotation in [
Quat::from_rotation_y(PI / 2.0 - spot_light.outer_angle),
Quat::from_euler(
bevy_math::EulerRot::XZY,
0.0,
PI / 2.0,
PI / 2.0 - spot_light.outer_angle,
),
] {
gizmos
.arc_3d(
2.0 * spot_light.outer_angle,
spot_light.range,
translation,
rotation * arc_rotation,
spot_light.color,
)
.segments(16);
}
}

for (directional_light, transform) in directional_lights {
let (_, rotation, translation) = transform.to_scale_rotation_translation();
gizmos
.arrow(
translation,
translation + rotation * Vec3::NEG_Z,
directional_light.color,
)
.with_tip_length(0.3);
}
}

pub struct LightGizmoPlugin;

impl Plugin for LightGizmoPlugin {
fn build(&self, app: &mut bevy_app::App) {
app.register_type::<LightGizmoConfigGroup>()
.init_gizmo_group::<LightGizmoConfigGroup>()
.add_systems(
PostUpdate,
(
draw_lights,
draw_all_lights.run_if(|config: Res<GizmoConfigStore>| {
config.config::<LightGizmoConfigGroup>().1.draw_all
}),
)
.after(TransformSystem::TransformPropagate),
);
}
}

#[derive(Clone, Default, Reflect, GizmoConfigGroup)]
pub struct LightGizmoConfigGroup {
pub draw_all: bool,
}

#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component, Default)]
pub struct ShowLightGizmo;

fn draw_lights(
point_query: Query<(&PointLight, &GlobalTransform), With<ShowLightGizmo>>,
spot_query: Query<(&SpotLight, &GlobalTransform), With<ShowLightGizmo>>,
directional_query: Query<(&DirectionalLight, &GlobalTransform), With<ShowLightGizmo>>,
mut gizmos: Gizmos<LightGizmoConfigGroup>,
) {
draw_gizmos(&point_query, &spot_query, &directional_query, &mut gizmos);
}

fn draw_all_lights(
point_query: Query<(&PointLight, &GlobalTransform), Without<ShowLightGizmo>>,
spot_query: Query<(&SpotLight, &GlobalTransform), Without<ShowLightGizmo>>,
directional_query: Query<(&DirectionalLight, &GlobalTransform), Without<ShowLightGizmo>>,
mut gizmos: Gizmos<LightGizmoConfigGroup>,
) {
draw_gizmos(&point_query, &spot_query, &directional_query, &mut gizmos);
}
25 changes: 19 additions & 6 deletions crates/bevy_gizmos/src/primitives/dim3.rs
Original file line number Diff line number Diff line change
Expand Up @@ -610,13 +610,24 @@ pub struct Cone3dBuilder<'a, 'w, 's, T: GizmoConfigGroup> {
color: Color,

// Number of segments used to approximate the cone geometry
segments: usize,
base_segments: usize,

height_segments: usize,
}

impl<T: GizmoConfigGroup> Cone3dBuilder<'_, '_, '_, T> {
/// Set the number of segments used to approximate the cone geometry.
pub fn segments(mut self, segments: usize) -> Self {
self.segments = segments;
self.base_segments = segments;
self.height_segments = segments;
self
}
pub fn base_segments(mut self, segments: usize) -> Self {
self.base_segments = segments;
self
}
pub fn height_segments(mut self, segments: usize) -> Self {
self.height_segments = segments;
self
}
}
Expand All @@ -638,7 +649,8 @@ impl<'w, 's, T: GizmoConfigGroup> GizmoPrimitive3d<Cone> for Gizmos<'w, 's, T> {
position,
rotation,
color,
segments: DEFAULT_NUMBER_SEGMENTS,
base_segments: DEFAULT_NUMBER_SEGMENTS,
height_segments: DEFAULT_NUMBER_SEGMENTS,
}
}
}
Expand All @@ -656,7 +668,8 @@ impl<T: GizmoConfigGroup> Drop for Cone3dBuilder<'_, '_, '_, T> {
position,
rotation,
color,
segments,
base_segments,
height_segments,
} = self;

let half_height = *height * 0.5;
Expand All @@ -665,15 +678,15 @@ impl<T: GizmoConfigGroup> Drop for Cone3dBuilder<'_, '_, '_, T> {
draw_circle_3d(
gizmos,
*radius,
*segments,
*base_segments,
*rotation,
*position - *rotation * Vec3::Y * half_height,
*color,
);

// connect the base circle with the tip of the cone
let end = Vec3::Y * half_height;
circle_coordinates(*radius, *segments)
circle_coordinates(*radius, *height_segments)
.map(|p| Vec3::new(p.x, -half_height, p.y))
.map(move |p| [p, end])
.map(|ps| ps.map(rotate_then_translate_3d(*rotation, *position)))
Expand Down

0 comments on commit 19ef747

Please sign in to comment.