From 4c54edf0e7c1c3e050ac5c5039673bad9782190b Mon Sep 17 00:00:00 2001 From: Kanabenki Date: Wed, 28 Feb 2024 16:41:18 +0100 Subject: [PATCH 1/8] Add light gizmos --- crates/bevy_gizmos/src/lib.rs | 6 +- crates/bevy_gizmos/src/light.rs | 181 ++++++++++++++++++++++ crates/bevy_gizmos/src/primitives/dim3.rs | 37 ++++- 3 files changed, 216 insertions(+), 8 deletions(-) create mode 100644 crates/bevy_gizmos/src/light.rs diff --git a/crates/bevy_gizmos/src/lib.rs b/crates/bevy_gizmos/src/lib.rs index dc0db0cbfa96a..100fc8d435a01 100644 --- a/crates/bevy_gizmos/src/lib.rs +++ b/crates/bevy_gizmos/src/lib.rs @@ -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")] @@ -87,6 +88,8 @@ use config::{ use gizmos::GizmoStorage; use std::{any::TypeId, mem}; +use crate::light::LightGizmoPlugin; + const LINE_SHADER_HANDLE: Handle = Handle::weak_from_u128(7414812689238026784); /// A [`Plugin`] that provides an immediate mode drawing api for visual debugging. @@ -108,7 +111,8 @@ impl Plugin for GizmoPlugin { .init_resource::() // We insert the Resource GizmoConfigStore into the world implicitly here if it does not exist. .init_gizmo_group::() - .add_plugins(AabbGizmoPlugin); + .add_plugins(AabbGizmoPlugin) + .add_plugins(LightGizmoPlugin); let Ok(render_app) = app.get_sub_app_mut(RenderApp) else { return; diff --git a/crates/bevy_gizmos/src/light.rs b/crates/bevy_gizmos/src/light.rs new file mode 100644 index 0000000000000..1ccc9feb8e8a7 --- /dev/null +++ b/crates/bevy_gizmos/src/light.rs @@ -0,0 +1,181 @@ +//! A module adding debug visualization of f [`PointLight`]s, [`SpotLight`]s and [`DirectionalLight`]s. + +use std::f32::consts::PI; + +use crate::{self as bevy_gizmos, primitives::dim3::GizmoPrimitive3d}; + +use bevy_app::{Plugin, PostUpdate}; +use bevy_color::LinearRgba; +use bevy_ecs::{ + component::Component, + query::{With, Without}, + reflect::ReflectComponent, + schedule::IntoSystemConfigs, + system::{Query, Res}, +}; +use bevy_math::{ + primitives::{Cone, Sphere}, + 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, +) where + P: 'a + IntoIterator, + S: 'a + IntoIterator, + D: 'a + IntoIterator, +{ + // Standard sphere for the radius, axis sphere for the range. + for (point_light, transform) in point_lights { + let position = transform.translation(); + gizmos + .primitive_3d( + Sphere { + radius: point_light.radius, + }, + position, + Quat::IDENTITY, + point_light.color, + ) + .segments(16); + gizmos + .sphere( + position, + Quat::IDENTITY, + point_light.range, + point_light.color, + ) + .circle_segments(32); + } + + // A sphere for the radius, two cones for the inner and outer angles, plus two 3d arcs crossing the + // farthest point of effect of the spot light along its direction. + for (spot_light, transform) in spot_lights { + let (_, rotation, translation) = transform.to_scale_rotation_translation(); + gizmos + .primitive_3d( + Sphere { + radius: spot_light.radius, + }, + translation, + Quat::IDENTITY, + spot_light.color, + ) + .segments(16); + + // 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); + } + } + + // An arrow alongside the directional light direction. + 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); + } +} + +/// A [`Plugin`] that provides visualization of [`PointLight`]s, [`SpotLight`]s +/// and [`DirectionalLight`]s for debugging. +pub struct LightGizmoPlugin; + +impl Plugin for LightGizmoPlugin { + fn build(&self, app: &mut bevy_app::App) { + app.register_type::() + .init_gizmo_group::() + .add_systems( + PostUpdate, + ( + draw_lights, + draw_all_lights.run_if(|config: Res| { + config.config::().1.draw_all + }), + ) + .after(TransformSystem::TransformPropagate), + ); + } +} + +/// The [`GizmoConfigGroup`] used to configure the visualization of lights. +#[derive(Clone, Default, Reflect, GizmoConfigGroup)] +pub struct LightGizmoConfigGroup { + /// Draw a gizmo for all lights if true. + pub draw_all: bool, +} + +/// Add this [`Component`] to an entity to draw any of its lights components +/// ([`PointLight`], [`SpotLight`] and [`DirectionalLight`]). +#[derive(Component, Reflect, Default, Debug)] +#[reflect(Component, Default)] +pub struct ShowLightGizmo; + +fn draw_lights( + point_query: Query<(&PointLight, &GlobalTransform), With>, + spot_query: Query<(&SpotLight, &GlobalTransform), With>, + directional_query: Query<(&DirectionalLight, &GlobalTransform), With>, + mut gizmos: Gizmos, +) { + draw_gizmos(&point_query, &spot_query, &directional_query, &mut gizmos); +} + +fn draw_all_lights( + point_query: Query<(&PointLight, &GlobalTransform), Without>, + spot_query: Query<(&SpotLight, &GlobalTransform), Without>, + directional_query: Query<(&DirectionalLight, &GlobalTransform), Without>, + mut gizmos: Gizmos, +) { + draw_gizmos(&point_query, &spot_query, &directional_query, &mut gizmos); +} diff --git a/crates/bevy_gizmos/src/primitives/dim3.rs b/crates/bevy_gizmos/src/primitives/dim3.rs index 6359517a68f66..1b1eb670b15d2 100644 --- a/crates/bevy_gizmos/src/primitives/dim3.rs +++ b/crates/bevy_gizmos/src/primitives/dim3.rs @@ -610,13 +610,34 @@ 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 Cone3dBuilder<'_, '_, '_, T> { - /// Set the number of segments used to approximate the cone geometry. + /// Set the number of segments used to approximate the cone geometry for its base and height. pub fn segments(mut self, segments: usize) -> Self { - self.segments = segments; + self.base_segments = segments; + self.height_segments = segments; + self + } + + /// Set the number of segments to approximate the height of the cone geometry. + /// + /// `segments` should be a multiple of the value passed to [`Self::height_segments`] + /// for the height to connect properly with the base. + pub fn base_segments(mut self, segments: usize) -> Self { + self.base_segments = segments; + self + } + + /// Set the number of segments to approximate the height of the cone geometry. + /// + /// `segments` should be a divider of the value passed to [`Self::base_segments`] + /// for the height to connect properly with the base. + pub fn height_segments(mut self, segments: usize) -> Self { + self.height_segments = segments; self } } @@ -638,7 +659,8 @@ impl<'w, 's, T: GizmoConfigGroup> GizmoPrimitive3d for Gizmos<'w, 's, T> { position, rotation, color, - segments: DEFAULT_NUMBER_SEGMENTS, + base_segments: DEFAULT_NUMBER_SEGMENTS, + height_segments: DEFAULT_NUMBER_SEGMENTS, } } } @@ -656,7 +678,8 @@ impl Drop for Cone3dBuilder<'_, '_, '_, T> { position, rotation, color, - segments, + base_segments, + height_segments, } = self; let half_height = *height * 0.5; @@ -665,7 +688,7 @@ impl Drop for Cone3dBuilder<'_, '_, '_, T> { draw_circle_3d( gizmos, *radius, - *segments, + *base_segments, *rotation, *position - *rotation * Vec3::Y * half_height, *color, @@ -673,7 +696,7 @@ impl Drop for Cone3dBuilder<'_, '_, '_, T> { // 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))) From be39109fc08ed98cd31c1ee2fbd1a13733f7498a Mon Sep 17 00:00:00 2001 From: Kanabenki Date: Sun, 3 Mar 2024 09:48:24 +0100 Subject: [PATCH 2/8] Fix typos Co-authored-by: Gino Valente <49806985+MrGVSV@users.noreply.github.com> --- crates/bevy_gizmos/src/light.rs | 2 +- crates/bevy_gizmos/src/primitives/dim3.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/bevy_gizmos/src/light.rs b/crates/bevy_gizmos/src/light.rs index 1ccc9feb8e8a7..2a1dd8f234748 100644 --- a/crates/bevy_gizmos/src/light.rs +++ b/crates/bevy_gizmos/src/light.rs @@ -1,4 +1,4 @@ -//! A module adding debug visualization of f [`PointLight`]s, [`SpotLight`]s and [`DirectionalLight`]s. +//! A module adding debug visualization of [`PointLight`]s, [`SpotLight`]s and [`DirectionalLight`]s. use std::f32::consts::PI; diff --git a/crates/bevy_gizmos/src/primitives/dim3.rs b/crates/bevy_gizmos/src/primitives/dim3.rs index 1b1eb670b15d2..bd169652bd287 100644 --- a/crates/bevy_gizmos/src/primitives/dim3.rs +++ b/crates/bevy_gizmos/src/primitives/dim3.rs @@ -634,7 +634,7 @@ impl Cone3dBuilder<'_, '_, '_, T> { /// Set the number of segments to approximate the height of the cone geometry. /// - /// `segments` should be a divider of the value passed to [`Self::base_segments`] + /// `segments` should be a divisor of the value passed to [`Self::base_segments`] /// for the height to connect properly with the base. pub fn height_segments(mut self, segments: usize) -> Self { self.height_segments = segments; From 86363b5533c4d1dbfb6c608421bfdb0ee3299f5a Mon Sep 17 00:00:00 2001 From: Kanabenki Date: Sun, 3 Mar 2024 11:12:20 +0100 Subject: [PATCH 3/8] Make light gizmo color configurable --- crates/bevy_gizmos/src/light.rs | 347 +++++++++++++++++++++++--------- 1 file changed, 252 insertions(+), 95 deletions(-) diff --git a/crates/bevy_gizmos/src/light.rs b/crates/bevy_gizmos/src/light.rs index 2a1dd8f234748..d915ce3e2ab82 100644 --- a/crates/bevy_gizmos/src/light.rs +++ b/crates/bevy_gizmos/src/light.rs @@ -5,10 +5,14 @@ use std::f32::consts::PI; use crate::{self as bevy_gizmos, primitives::dim3::GizmoPrimitive3d}; use bevy_app::{Plugin, PostUpdate}; -use bevy_color::LinearRgba; +use bevy_color::{ + palettes::basic::{BLUE, GREEN, RED}, + Color, LinearRgba, Oklcha, +}; use bevy_ecs::{ component::Component, - query::{With, Without}, + entity::Entity, + query::Without, reflect::ReflectComponent, schedule::IntoSystemConfigs, system::{Query, Res}, @@ -27,104 +31,119 @@ use crate::{ AppGizmoBuilder, }; -fn draw_gizmos<'a, P, S, D>( - point_lights: P, - spot_lights: S, - directional_lights: D, +/// Draws a standard sphere for the radius and an axis sphere for the range. +fn point_light_gizmo( + transform: &GlobalTransform, + point_light: &PointLight, + color: Color, gizmos: &mut Gizmos, -) where - P: 'a + IntoIterator, - S: 'a + IntoIterator, - D: 'a + IntoIterator, -{ - // Standard sphere for the radius, axis sphere for the range. - for (point_light, transform) in point_lights { - let position = transform.translation(); +) { + let position = transform.translation(); + gizmos + .primitive_3d( + Sphere { + radius: point_light.radius, + }, + position, + Quat::IDENTITY, + color, + ) + .segments(16); + gizmos + .sphere(position, Quat::IDENTITY, point_light.range, color) + .circle_segments(32); +} + +/// Draws a sphere for the radius, two cones for the inner and outer angles, plus two 3d arcs crossing the +/// farthest point of effect of the spot light along its direction. +fn spot_light_gizmo( + transform: &GlobalTransform, + spot_light: &SpotLight, + color: Color, + gizmos: &mut Gizmos, +) { + let (_, rotation, translation) = transform.to_scale_rotation_translation(); + gizmos + .primitive_3d( + Sphere { + radius: spot_light.radius, + }, + translation, + Quat::IDENTITY, + color, + ) + .segments(16); + + // 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( - Sphere { - radius: point_light.radius, + Cone { + radius: spot_light.range * angle.sin(), + height, }, position, - Quat::IDENTITY, - point_light.color, + rotation * Quat::from_rotation_x(PI / 2.0), + color, ) - .segments(16); - gizmos - .sphere( - position, - Quat::IDENTITY, - point_light.range, - point_light.color, - ) - .circle_segments(32); + .height_segments(4) + .base_segments(32); } - // A sphere for the radius, two cones for the inner and outer angles, plus two 3d arcs crossing the - // farthest point of effect of the spot light along its direction. - for (spot_light, transform) in spot_lights { - let (_, rotation, translation) = transform.to_scale_rotation_translation(); + 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 - .primitive_3d( - Sphere { - radius: spot_light.radius, - }, + .arc_3d( + 2.0 * spot_light.outer_angle, + spot_light.range, translation, - Quat::IDENTITY, - spot_light.color, + rotation * arc_rotation, + color, ) .segments(16); + } +} - // 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); - } +/// Draws an arrow alongside the directional light direction. +fn directional_light_gizmo( + transform: &GlobalTransform, + color: Color, + gizmos: &mut Gizmos, +) { + let (_, rotation, translation) = transform.to_scale_rotation_translation(); + gizmos + .arrow(translation, translation + rotation * Vec3::NEG_Z, color) + .with_tip_length(0.3); +} - 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); - } +fn draw_gizmos<'a, P, S, D>( + point_lights: P, + spot_lights: S, + directional_lights: D, + gizmos: &mut Gizmos, +) where + P: 'a + IntoIterator, + S: 'a + IntoIterator, + D: 'a + IntoIterator, +{ + for (point_light, transform, color) in point_lights { + point_light_gizmo(transform, point_light, color, gizmos); } - - // An arrow alongside the directional light direction. - 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); + for (spot_light, transform, color) in spot_lights { + spot_light_gizmo(transform, spot_light, color, gizmos); + } + for (transform, color) in directional_lights { + directional_light_gizmo(transform, color, gizmos); } } @@ -149,33 +168,171 @@ impl Plugin for LightGizmoPlugin { } } +/// Configures how a color is attributed to a light gizmo. +#[derive(Debug, Clone, Copy, Default, Reflect)] +pub enum LightGizmoColor { + /// User-specified color. + Manual(Color), + /// Random color derived from the light's [`Entity`]. + Varied, + /// Take the color of the represented light. + #[default] + MatchLightColor, + /// Take the color provided by [`LightGizmoConfigGroup`] depending on the light kind. + ByLightType, +} + /// The [`GizmoConfigGroup`] used to configure the visualization of lights. -#[derive(Clone, Default, Reflect, GizmoConfigGroup)] +#[derive(Clone, Reflect, GizmoConfigGroup)] pub struct LightGizmoConfigGroup { /// Draw a gizmo for all lights if true. + /// + /// Defaults to `false`. pub draw_all: bool, + /// Default color strategy for all light gizmos. + /// + /// Defaults to [`LightGizmoColor::MatchLightColor`]. + pub color: LightGizmoColor, + /// [`Color`] to use for drawing a [`PointLight`] gizmo when [`LightGizmoColor::ByLightType`] is used. + /// + /// Defaults to [`RED`]. + pub point_light_color: Color, + /// [`Color`] to use for drawing a [`SpotLight`] gizmo when [`LightGizmoColor::ByLightType`] is used. + /// + /// Defaults to [`GREEN`]. + pub spot_light_color: Color, + /// [`Color`] to use for drawing a [`DirectionalLight`] gizmo when [`LightGizmoColor::ByLightType`] is used. + /// + /// Defaults to [`BLUE`]. + pub directional_light_color: Color, +} + +impl Default for LightGizmoConfigGroup { + fn default() -> Self { + Self { + draw_all: false, + color: LightGizmoColor::MatchLightColor, + point_light_color: RED.into(), + spot_light_color: GREEN.into(), + directional_light_color: BLUE.into(), + } + } } /// Add this [`Component`] to an entity to draw any of its lights components /// ([`PointLight`], [`SpotLight`] and [`DirectionalLight`]). #[derive(Component, Reflect, Default, Debug)] #[reflect(Component, Default)] -pub struct ShowLightGizmo; +pub struct ShowLightGizmo { + /// Default color strategy for this light gizmo. if none, use the one provided by [`LightGizmoConfigGroup`]. + /// + /// Defaults to [`None`]. + color: Option, +} fn draw_lights( - point_query: Query<(&PointLight, &GlobalTransform), With>, - spot_query: Query<(&SpotLight, &GlobalTransform), With>, - directional_query: Query<(&DirectionalLight, &GlobalTransform), With>, + point_query: Query<(Entity, &PointLight, &GlobalTransform, &ShowLightGizmo)>, + spot_query: Query<(Entity, &SpotLight, &GlobalTransform, &ShowLightGizmo)>, + directional_query: Query<(Entity, &DirectionalLight, &GlobalTransform, &ShowLightGizmo)>, mut gizmos: Gizmos, ) { - draw_gizmos(&point_query, &spot_query, &directional_query, &mut gizmos); + let color = |entity: Entity, gizmo_color: Option, light_color, type_color| { + match gizmo_color.unwrap_or(gizmos.config_ext.color) { + LightGizmoColor::Manual(color) => color, + LightGizmoColor::Varied => Oklcha::sequential_dispersed(entity.index()).into(), + LightGizmoColor::MatchLightColor => light_color, + LightGizmoColor::ByLightType => type_color, + } + }; + + draw_gizmos( + point_query + .iter() + .map(|(entity, light, transform, light_gizmo)| { + ( + light, + transform, + color( + entity, + light_gizmo.color, + light.color, + gizmos.config_ext.point_light_color, + ), + ) + }), + spot_query + .iter() + .map(|(entity, light, transform, light_gizmo)| { + ( + light, + transform, + color( + entity, + light_gizmo.color, + light.color, + gizmos.config_ext.point_light_color, + ), + ) + }), + directional_query + .iter() + .map(|(entity, light, transform, light_gizmo)| { + ( + transform, + color( + entity, + light_gizmo.color, + light.color, + gizmos.config_ext.point_light_color, + ), + ) + }), + &mut gizmos, + ); } fn draw_all_lights( - point_query: Query<(&PointLight, &GlobalTransform), Without>, - spot_query: Query<(&SpotLight, &GlobalTransform), Without>, - directional_query: Query<(&DirectionalLight, &GlobalTransform), Without>, + point_query: Query<(Entity, &PointLight, &GlobalTransform), Without>, + spot_query: Query<(Entity, &SpotLight, &GlobalTransform), Without>, + directional_query: Query< + (Entity, &DirectionalLight, &GlobalTransform), + Without, + >, mut gizmos: Gizmos, ) { - draw_gizmos(&point_query, &spot_query, &directional_query, &mut gizmos); + match gizmos.config_ext.color { + LightGizmoColor::Manual(color) => draw_gizmos( + point_query.iter().map(|(_, l, t)| (l, t, color)), + spot_query.iter().map(|(_, l, t)| (l, t, color)), + directional_query.iter().map(|(_, _, t)| (t, color)), + &mut gizmos, + ), + LightGizmoColor::Varied => { + let color = |entity: Entity| Oklcha::sequential_dispersed(entity.index()).into(); + draw_gizmos( + point_query.iter().map(|(e, l, t)| (l, t, color(e))), + spot_query.iter().map(|(e, l, t)| (l, t, color(e))), + directional_query.iter().map(|(e, _, t)| (t, color(e))), + &mut gizmos, + ); + } + LightGizmoColor::MatchLightColor => draw_gizmos( + point_query.iter().map(|(_, l, t)| (l, t, l.color)), + spot_query.iter().map(|(_, l, t)| (l, t, l.color)), + directional_query.iter().map(|(_, l, t)| (t, l.color)), + &mut gizmos, + ), + LightGizmoColor::ByLightType => draw_gizmos( + point_query + .iter() + .map(|(_, l, t)| (l, t, gizmos.config_ext.point_light_color)), + spot_query + .iter() + .map(|(_, l, t)| (l, t, gizmos.config_ext.spot_light_color)), + directional_query + .iter() + .map(|(_, _, t)| (t, gizmos.config_ext.directional_light_color)), + &mut gizmos, + ), + } } From cdb8c9f2673e0b6a2c3f10db51304eeb1974a364 Mon Sep 17 00:00:00 2001 From: Kanabenki Date: Sun, 3 Mar 2024 11:22:40 +0100 Subject: [PATCH 4/8] Remove `draw_gizmos` to simplify light gizmo drawing --- crates/bevy_gizmos/src/light.rs | 179 +++++++++++++++----------------- 1 file changed, 83 insertions(+), 96 deletions(-) diff --git a/crates/bevy_gizmos/src/light.rs b/crates/bevy_gizmos/src/light.rs index d915ce3e2ab82..ff40c7bd4737d 100644 --- a/crates/bevy_gizmos/src/light.rs +++ b/crates/bevy_gizmos/src/light.rs @@ -126,27 +126,6 @@ fn directional_light_gizmo( .with_tip_length(0.3); } -fn draw_gizmos<'a, P, S, D>( - point_lights: P, - spot_lights: S, - directional_lights: D, - gizmos: &mut Gizmos, -) where - P: 'a + IntoIterator, - S: 'a + IntoIterator, - D: 'a + IntoIterator, -{ - for (point_light, transform, color) in point_lights { - point_light_gizmo(transform, point_light, color, gizmos); - } - for (spot_light, transform, color) in spot_lights { - spot_light_gizmo(transform, spot_light, color, gizmos); - } - for (transform, color) in directional_lights { - directional_light_gizmo(transform, color, gizmos); - } -} - /// A [`Plugin`] that provides visualization of [`PointLight`]s, [`SpotLight`]s /// and [`DirectionalLight`]s for debugging. pub struct LightGizmoPlugin; @@ -244,51 +223,33 @@ fn draw_lights( LightGizmoColor::ByLightType => type_color, } }; - - draw_gizmos( - point_query - .iter() - .map(|(entity, light, transform, light_gizmo)| { - ( - light, - transform, - color( - entity, - light_gizmo.color, - light.color, - gizmos.config_ext.point_light_color, - ), - ) - }), - spot_query - .iter() - .map(|(entity, light, transform, light_gizmo)| { - ( - light, - transform, - color( - entity, - light_gizmo.color, - light.color, - gizmos.config_ext.point_light_color, - ), - ) - }), - directional_query - .iter() - .map(|(entity, light, transform, light_gizmo)| { - ( - transform, - color( - entity, - light_gizmo.color, - light.color, - gizmos.config_ext.point_light_color, - ), - ) - }), - &mut gizmos, - ); + for (entity, light, transform, light_gizmo) in &point_query { + let color = color( + entity, + light_gizmo.color, + light.color, + gizmos.config_ext.point_light_color, + ); + point_light_gizmo(transform, light, color, &mut gizmos); + } + for (entity, light, transform, light_gizmo) in &spot_query { + let color = color( + entity, + light_gizmo.color, + light.color, + gizmos.config_ext.point_light_color, + ); + spot_light_gizmo(transform, light, color, &mut gizmos); + } + for (entity, light, transform, light_gizmo) in &directional_query { + let color = color( + entity, + light_gizmo.color, + light.color, + gizmos.config_ext.point_light_color, + ); + directional_light_gizmo(transform, color, &mut gizmos); + } } fn draw_all_lights( @@ -301,38 +262,64 @@ fn draw_all_lights( mut gizmos: Gizmos, ) { match gizmos.config_ext.color { - LightGizmoColor::Manual(color) => draw_gizmos( - point_query.iter().map(|(_, l, t)| (l, t, color)), - spot_query.iter().map(|(_, l, t)| (l, t, color)), - directional_query.iter().map(|(_, _, t)| (t, color)), - &mut gizmos, - ), + LightGizmoColor::Manual(color) => { + for (_, light, transform) in &point_query { + point_light_gizmo(transform, light, color, &mut gizmos); + } + for (_, light, transform) in &spot_query { + spot_light_gizmo(transform, light, color, &mut gizmos); + } + for (_, _, transform) in &directional_query { + directional_light_gizmo(transform, color, &mut gizmos); + } + } LightGizmoColor::Varied => { let color = |entity: Entity| Oklcha::sequential_dispersed(entity.index()).into(); - draw_gizmos( - point_query.iter().map(|(e, l, t)| (l, t, color(e))), - spot_query.iter().map(|(e, l, t)| (l, t, color(e))), - directional_query.iter().map(|(e, _, t)| (t, color(e))), - &mut gizmos, - ); + for (entity, light, transform) in &point_query { + point_light_gizmo(transform, light, color(entity), &mut gizmos); + } + for (entity, light, transform) in &spot_query { + spot_light_gizmo(transform, light, color(entity), &mut gizmos); + } + for (entity, _, transform) in &directional_query { + directional_light_gizmo(transform, color(entity), &mut gizmos); + } + } + LightGizmoColor::MatchLightColor => { + for (_, light, transform) in &point_query { + point_light_gizmo(transform, light, light.color, &mut gizmos); + } + for (_, light, transform) in &spot_query { + spot_light_gizmo(transform, light, light.color, &mut gizmos); + } + for (_, light, transform) in &directional_query { + directional_light_gizmo(transform, light.color, &mut gizmos); + } + } + LightGizmoColor::ByLightType => { + for (_, light, transform) in &point_query { + point_light_gizmo( + transform, + light, + gizmos.config_ext.point_light_color, + &mut gizmos, + ); + } + for (_, light, transform) in &spot_query { + spot_light_gizmo( + transform, + light, + gizmos.config_ext.spot_light_color, + &mut gizmos, + ); + } + for (_, _, transform) in &directional_query { + directional_light_gizmo( + transform, + gizmos.config_ext.directional_light_color, + &mut gizmos, + ); + } } - LightGizmoColor::MatchLightColor => draw_gizmos( - point_query.iter().map(|(_, l, t)| (l, t, l.color)), - spot_query.iter().map(|(_, l, t)| (l, t, l.color)), - directional_query.iter().map(|(_, l, t)| (t, l.color)), - &mut gizmos, - ), - LightGizmoColor::ByLightType => draw_gizmos( - point_query - .iter() - .map(|(_, l, t)| (l, t, gizmos.config_ext.point_light_color)), - spot_query - .iter() - .map(|(_, l, t)| (l, t, gizmos.config_ext.spot_light_color)), - directional_query - .iter() - .map(|(_, _, t)| (t, gizmos.config_ext.directional_light_color)), - &mut gizmos, - ), } } From a74923e10f26bb8740cc8c1514051c81d50037aa Mon Sep 17 00:00:00 2001 From: Kanabenki Date: Sun, 3 Mar 2024 14:21:57 +0100 Subject: [PATCH 5/8] Add light gizmos example and add update gizmos prelude --- Cargo.toml | 11 ++ crates/bevy_gizmos/src/lib.rs | 4 +- crates/bevy_gizmos/src/light.rs | 7 +- crates/bevy_gizmos/src/primitives/dim3.rs | 3 +- examples/gizmos/light_gizmos.rs | 190 ++++++++++++++++++++++ 5 files changed, 208 insertions(+), 7 deletions(-) create mode 100644 examples/gizmos/light_gizmos.rs diff --git a/Cargo.toml b/Cargo.toml index 16010567e3e87..caba362e0b6e0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2669,6 +2669,17 @@ description = "A scene showcasing 3D gizmos" category = "Gizmos" wasm = true +[[example]] +name = "light_gizmos" +path = "examples/gizmos/light_gizmos.rs" +doc-scrape-examples = true + +[package.metadata.example.light_gizmos] +name = "Light Gizmos" +description = "A scene showcasing light gizmos" +category = "Gizmos" +wasm = true + [profile.wasm-release] inherits = "release" opt-level = "z" diff --git a/crates/bevy_gizmos/src/lib.rs b/crates/bevy_gizmos/src/lib.rs index 100fc8d435a01..bab9744fc2327 100644 --- a/crates/bevy_gizmos/src/lib.rs +++ b/crates/bevy_gizmos/src/lib.rs @@ -47,6 +47,7 @@ pub mod prelude { aabb::{AabbGizmoConfigGroup, ShowAabbGizmo}, config::{DefaultGizmoConfigGroup, GizmoConfig, GizmoConfigGroup, GizmoConfigStore}, gizmos::Gizmos, + light::{LightGizmoColor, LightGizmoConfigGroup, ShowLightGizmo}, primitives::{dim2::GizmoPrimitive2d, dim3::GizmoPrimitive3d}, AppGizmoBuilder, }; @@ -86,10 +87,9 @@ use config::{ DefaultGizmoConfigGroup, GizmoConfig, GizmoConfigGroup, GizmoConfigStore, GizmoMeshConfig, }; use gizmos::GizmoStorage; +use light::LightGizmoPlugin; use std::{any::TypeId, mem}; -use crate::light::LightGizmoPlugin; - const LINE_SHADER_HANDLE: Handle = Handle::weak_from_u128(7414812689238026784); /// A [`Plugin`] that provides an immediate mode drawing api for visual debugging. diff --git a/crates/bevy_gizmos/src/light.rs b/crates/bevy_gizmos/src/light.rs index ff40c7bd4737d..400674b87645d 100644 --- a/crates/bevy_gizmos/src/light.rs +++ b/crates/bevy_gizmos/src/light.rs @@ -75,7 +75,6 @@ fn spot_light_gizmo( .segments(16); // 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; @@ -203,7 +202,7 @@ impl Default for LightGizmoConfigGroup { #[derive(Component, Reflect, Default, Debug)] #[reflect(Component, Default)] pub struct ShowLightGizmo { - /// Default color strategy for this light gizmo. if none, use the one provided by [`LightGizmoConfigGroup`]. + /// Default color strategy for this light gizmo. if [`None`], use the one provided by [`LightGizmoConfigGroup`]. /// /// Defaults to [`None`]. color: Option, @@ -237,7 +236,7 @@ fn draw_lights( entity, light_gizmo.color, light.color, - gizmos.config_ext.point_light_color, + gizmos.config_ext.spot_light_color, ); spot_light_gizmo(transform, light, color, &mut gizmos); } @@ -246,7 +245,7 @@ fn draw_lights( entity, light_gizmo.color, light.color, - gizmos.config_ext.point_light_color, + gizmos.config_ext.directional_light_color, ); directional_light_gizmo(transform, color, &mut gizmos); } diff --git a/crates/bevy_gizmos/src/primitives/dim3.rs b/crates/bevy_gizmos/src/primitives/dim3.rs index bd169652bd287..eae53b1fcf3b4 100644 --- a/crates/bevy_gizmos/src/primitives/dim3.rs +++ b/crates/bevy_gizmos/src/primitives/dim3.rs @@ -609,9 +609,10 @@ pub struct Cone3dBuilder<'a, 'w, 's, T: GizmoConfigGroup> { // Color of the cone color: Color, - // Number of segments used to approximate the cone geometry + // Number of segments used to approximate the cone base geometry base_segments: usize, + // Number of segments used to approximate the cone height geometry height_segments: usize, } diff --git a/examples/gizmos/light_gizmos.rs b/examples/gizmos/light_gizmos.rs new file mode 100644 index 0000000000000..e06b6c0e8dedd --- /dev/null +++ b/examples/gizmos/light_gizmos.rs @@ -0,0 +1,190 @@ +//! This example demonstrates how to visualize lights properties through the gizmo API. + +use std::f32::consts::{FRAC_PI_2, PI}; + +use bevy::{ + color::palettes::css::{DARK_CYAN, GOLD, GRAY, PURPLE}, + gizmos::light::{LightGizmoColor, LightGizmoConfigGroup}, + prelude::*, +}; + +fn main() { + App::new() + .add_plugins(DefaultPlugins) + .add_systems(Startup, setup) + .add_systems(Update, rotate_camera) + .add_systems(Update, update_config) + .run(); +} + +#[derive(Component)] +struct GizmoColorText; + +fn gizmo_color_text(config: &LightGizmoConfigGroup) -> String { + match config.color { + LightGizmoColor::Manual(color) => format!("Manual {}", Srgba::from(color).to_hex()), + LightGizmoColor::Varied => "Random from entity".to_owned(), + LightGizmoColor::MatchLightColor => "Match light color".to_owned(), + LightGizmoColor::ByLightType => { + format!( + "Point {}, Spot {}, Directional {}", + Srgba::from(config.point_light_color).to_hex(), + Srgba::from(config.spot_light_color).to_hex(), + Srgba::from(config.directional_light_color).to_hex() + ) + } + } +} + +fn setup( + mut commands: Commands, + mut meshes: ResMut>, + mut materials: ResMut>, + mut config_store: ResMut, +) { + // Circular base. + commands.spawn(PbrBundle { + mesh: meshes.add(Circle::new(4.0)), + material: materials.add(Color::WHITE), + transform: Transform::from_rotation(Quat::from_rotation_x(-FRAC_PI_2)), + ..default() + }); + + // Cubes. + { + let mesh = meshes.add(Cuboid::new(1.0, 1.0, 1.0)); + let material = materials.add(Color::srgb_u8(124, 144, 255)); + for x in [-2.0, 0.0, 2.0] { + commands.spawn(PbrBundle { + mesh: mesh.clone(), + material: material.clone(), + transform: Transform::from_xyz(x, 0.5, 0.0), + ..default() + }); + } + } + + // Lights. + { + commands.spawn(PointLightBundle { + point_light: PointLight { + shadows_enabled: true, + range: 2.0, + color: DARK_CYAN.into(), + ..default() + }, + transform: Transform::from_xyz(0.0, 1.5, 0.0), + ..default() + }); + commands.spawn(SpotLightBundle { + spot_light: SpotLight { + shadows_enabled: true, + range: 3.5, + color: PURPLE.into(), + outer_angle: PI / 4.0, + inner_angle: PI / 4.0 * 0.8, + ..default() + }, + transform: Transform::from_xyz(4.0, 2.0, 0.0).looking_at(Vec3::X * 1.5, Vec3::Y), + ..default() + }); + commands.spawn(DirectionalLightBundle { + directional_light: DirectionalLight { + color: GOLD.into(), + illuminance: DirectionalLight::default().illuminance * 0.05, + shadows_enabled: true, + ..default() + }, + transform: Transform::from_xyz(-4.0, 2.0, 0.0).looking_at(Vec3::NEG_X * 1.5, Vec3::Y), + ..default() + }); + } + + // Camera. + commands.spawn(Camera3dBundle { + transform: Transform::from_xyz(-2.5, 4.5, 9.0).looking_at(Vec3::ZERO, Vec3::Y), + ..default() + }); + + // Example instructions and gizmo config. + { + let text_style = TextStyle { + font_size: 20.0, + ..default() + }; + commands.spawn( + TextBundle::from_section( + "Press 'D' to toggle drawing gizmos on top of everything else in the scene\n\ + Hold 'Left' or 'Right' to change the line width of the gizmos\n\ + Press 'A' to toggle drawing of the light gizmos\n\ + Press 'C' to cycle between the light gizmos coloring modes", + text_style.clone(), + ) + .with_style(Style { + position_type: PositionType::Absolute, + top: Val::Px(12.0), + left: Val::Px(12.0), + ..default() + }), + ); + + let (_, light_config) = config_store.config_mut::(); + light_config.draw_all = true; + light_config.color = LightGizmoColor::MatchLightColor; + + commands.spawn(( + TextBundle::from_sections([ + TextSection::new("Gizmo color mode: ", text_style.clone()), + TextSection::new(gizmo_color_text(light_config), text_style), + ]) + .with_style(Style { + position_type: PositionType::Absolute, + bottom: Val::Px(12.0), + left: Val::Px(12.0), + ..default() + }), + GizmoColorText, + )); + } +} + +fn rotate_camera(mut query: Query<&mut Transform, With>, time: Res