Skip to content

Commit 19ef747

Browse files
committed
Add light gizmos
1 parent 499c978 commit 19ef747

File tree

3 files changed

+182
-7
lines changed

3 files changed

+182
-7
lines changed

crates/bevy_gizmos/src/lib.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ pub mod circles;
3232
pub mod config;
3333
pub mod gizmos;
3434
pub mod grid;
35+
pub mod light;
3536
pub mod primitives;
3637

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

91+
use crate::light::LightGizmoPlugin;
92+
9093
const LINE_SHADER_HANDLE: Handle<Shader> = Handle::weak_from_u128(7414812689238026784);
9194

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

113117
let Ok(render_app) = app.get_sub_app_mut(RenderApp) else {
114118
return;

crates/bevy_gizmos/src/light.rs

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
use std::f32::consts::PI;
2+
3+
use crate::{self as bevy_gizmos, primitives::dim3::GizmoPrimitive3d};
4+
5+
use bevy_app::{Plugin, PostUpdate};
6+
use bevy_color::{Color, LinearRgba};
7+
use bevy_ecs::{
8+
component::Component,
9+
query::{With, Without},
10+
reflect::ReflectComponent,
11+
schedule::IntoSystemConfigs,
12+
system::{Query, Res},
13+
};
14+
use bevy_math::{primitives::Cone, Quat, Vec3};
15+
use bevy_pbr::{DirectionalLight, PointLight, SpotLight};
16+
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
17+
use bevy_transform::{components::GlobalTransform, TransformSystem};
18+
19+
use crate::{
20+
config::{GizmoConfigGroup, GizmoConfigStore},
21+
gizmos::Gizmos,
22+
AppGizmoBuilder,
23+
};
24+
25+
fn draw_gizmos<'a, P, S, D>(
26+
point_lights: P,
27+
spot_lights: S,
28+
directional_lights: D,
29+
gizmos: &mut Gizmos<LightGizmoConfigGroup>,
30+
) where
31+
P: 'a + IntoIterator<Item = (&'a PointLight, &'a GlobalTransform)>,
32+
S: 'a + IntoIterator<Item = (&'a SpotLight, &'a GlobalTransform)>,
33+
D: 'a + IntoIterator<Item = (&'a DirectionalLight, &'a GlobalTransform)>,
34+
{
35+
for (point_light, transform) in point_lights {
36+
let position = transform.translation();
37+
gizmos.sphere(
38+
position,
39+
Quat::IDENTITY,
40+
point_light.radius,
41+
point_light.color,
42+
);
43+
gizmos
44+
.sphere(
45+
position,
46+
Quat::IDENTITY,
47+
point_light.range,
48+
point_light.color,
49+
)
50+
.circle_segments(32);
51+
}
52+
53+
for (spot_light, transform) in spot_lights {
54+
let (_, rotation, translation) = transform.to_scale_rotation_translation();
55+
gizmos.sphere(
56+
translation,
57+
Quat::IDENTITY,
58+
spot_light.radius,
59+
spot_light.color,
60+
);
61+
62+
// Offset the tip of the cone to the light position.
63+
gizmos.sphere(translation, Quat::IDENTITY, 0.01, LinearRgba::GREEN);
64+
for angle in [spot_light.inner_angle, spot_light.outer_angle] {
65+
let height = spot_light.range * angle.cos();
66+
let position = translation + rotation * Vec3::NEG_Z * height / 2.0;
67+
gizmos
68+
.primitive_3d(
69+
Cone {
70+
radius: spot_light.range * angle.sin(),
71+
height,
72+
},
73+
position,
74+
rotation * Quat::from_rotation_x(PI / 2.0),
75+
spot_light.color,
76+
)
77+
.height_segments(4)
78+
.base_segments(32);
79+
}
80+
81+
for arc_rotation in [
82+
Quat::from_rotation_y(PI / 2.0 - spot_light.outer_angle),
83+
Quat::from_euler(
84+
bevy_math::EulerRot::XZY,
85+
0.0,
86+
PI / 2.0,
87+
PI / 2.0 - spot_light.outer_angle,
88+
),
89+
] {
90+
gizmos
91+
.arc_3d(
92+
2.0 * spot_light.outer_angle,
93+
spot_light.range,
94+
translation,
95+
rotation * arc_rotation,
96+
spot_light.color,
97+
)
98+
.segments(16);
99+
}
100+
}
101+
102+
for (directional_light, transform) in directional_lights {
103+
let (_, rotation, translation) = transform.to_scale_rotation_translation();
104+
gizmos
105+
.arrow(
106+
translation,
107+
translation + rotation * Vec3::NEG_Z,
108+
directional_light.color,
109+
)
110+
.with_tip_length(0.3);
111+
}
112+
}
113+
114+
pub struct LightGizmoPlugin;
115+
116+
impl Plugin for LightGizmoPlugin {
117+
fn build(&self, app: &mut bevy_app::App) {
118+
app.register_type::<LightGizmoConfigGroup>()
119+
.init_gizmo_group::<LightGizmoConfigGroup>()
120+
.add_systems(
121+
PostUpdate,
122+
(
123+
draw_lights,
124+
draw_all_lights.run_if(|config: Res<GizmoConfigStore>| {
125+
config.config::<LightGizmoConfigGroup>().1.draw_all
126+
}),
127+
)
128+
.after(TransformSystem::TransformPropagate),
129+
);
130+
}
131+
}
132+
133+
#[derive(Clone, Default, Reflect, GizmoConfigGroup)]
134+
pub struct LightGizmoConfigGroup {
135+
pub draw_all: bool,
136+
}
137+
138+
#[derive(Component, Reflect, Default, Debug)]
139+
#[reflect(Component, Default)]
140+
pub struct ShowLightGizmo;
141+
142+
fn draw_lights(
143+
point_query: Query<(&PointLight, &GlobalTransform), With<ShowLightGizmo>>,
144+
spot_query: Query<(&SpotLight, &GlobalTransform), With<ShowLightGizmo>>,
145+
directional_query: Query<(&DirectionalLight, &GlobalTransform), With<ShowLightGizmo>>,
146+
mut gizmos: Gizmos<LightGizmoConfigGroup>,
147+
) {
148+
draw_gizmos(&point_query, &spot_query, &directional_query, &mut gizmos);
149+
}
150+
151+
fn draw_all_lights(
152+
point_query: Query<(&PointLight, &GlobalTransform), Without<ShowLightGizmo>>,
153+
spot_query: Query<(&SpotLight, &GlobalTransform), Without<ShowLightGizmo>>,
154+
directional_query: Query<(&DirectionalLight, &GlobalTransform), Without<ShowLightGizmo>>,
155+
mut gizmos: Gizmos<LightGizmoConfigGroup>,
156+
) {
157+
draw_gizmos(&point_query, &spot_query, &directional_query, &mut gizmos);
158+
}

crates/bevy_gizmos/src/primitives/dim3.rs

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -610,13 +610,24 @@ pub struct Cone3dBuilder<'a, 'w, 's, T: GizmoConfigGroup> {
610610
color: Color,
611611

612612
// Number of segments used to approximate the cone geometry
613-
segments: usize,
613+
base_segments: usize,
614+
615+
height_segments: usize,
614616
}
615617

616618
impl<T: GizmoConfigGroup> Cone3dBuilder<'_, '_, '_, T> {
617619
/// Set the number of segments used to approximate the cone geometry.
618620
pub fn segments(mut self, segments: usize) -> Self {
619-
self.segments = segments;
621+
self.base_segments = segments;
622+
self.height_segments = segments;
623+
self
624+
}
625+
pub fn base_segments(mut self, segments: usize) -> Self {
626+
self.base_segments = segments;
627+
self
628+
}
629+
pub fn height_segments(mut self, segments: usize) -> Self {
630+
self.height_segments = segments;
620631
self
621632
}
622633
}
@@ -638,7 +649,8 @@ impl<'w, 's, T: GizmoConfigGroup> GizmoPrimitive3d<Cone> for Gizmos<'w, 's, T> {
638649
position,
639650
rotation,
640651
color,
641-
segments: DEFAULT_NUMBER_SEGMENTS,
652+
base_segments: DEFAULT_NUMBER_SEGMENTS,
653+
height_segments: DEFAULT_NUMBER_SEGMENTS,
642654
}
643655
}
644656
}
@@ -656,7 +668,8 @@ impl<T: GizmoConfigGroup> Drop for Cone3dBuilder<'_, '_, '_, T> {
656668
position,
657669
rotation,
658670
color,
659-
segments,
671+
base_segments,
672+
height_segments,
660673
} = self;
661674

662675
let half_height = *height * 0.5;
@@ -665,15 +678,15 @@ impl<T: GizmoConfigGroup> Drop for Cone3dBuilder<'_, '_, '_, T> {
665678
draw_circle_3d(
666679
gizmos,
667680
*radius,
668-
*segments,
681+
*base_segments,
669682
*rotation,
670683
*position - *rotation * Vec3::Y * half_height,
671684
*color,
672685
);
673686

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

0 commit comments

Comments
 (0)