Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_gizmos/src/light.rs
6595 views
1
//! A module adding debug visualization of [`PointLight`]s, [`SpotLight`]s and [`DirectionalLight`]s.
2
3
use core::f32::consts::PI;
4
5
use crate::primitives::dim3::GizmoPrimitive3d;
6
7
use bevy_app::{Plugin, PostUpdate};
8
use bevy_color::{
9
palettes::basic::{BLUE, GREEN, RED},
10
Color, Oklcha,
11
};
12
use bevy_ecs::{
13
component::Component,
14
entity::Entity,
15
query::Without,
16
reflect::ReflectComponent,
17
schedule::IntoScheduleConfigs,
18
system::{Query, Res},
19
};
20
use bevy_light::{DirectionalLight, PointLight, SpotLight};
21
use bevy_math::{
22
ops,
23
primitives::{Cone, Sphere},
24
Isometry3d, Quat, Vec3,
25
};
26
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
27
use bevy_transform::{components::GlobalTransform, TransformSystems};
28
29
use crate::{
30
config::{GizmoConfigGroup, GizmoConfigStore},
31
gizmos::Gizmos,
32
AppGizmoBuilder,
33
};
34
35
/// Draws a standard sphere for the radius and an axis sphere for the range.
36
fn point_light_gizmo(
37
transform: &GlobalTransform,
38
point_light: &PointLight,
39
color: Color,
40
gizmos: &mut Gizmos<LightGizmoConfigGroup>,
41
) {
42
let position = transform.translation();
43
gizmos
44
.primitive_3d(&Sphere::new(point_light.radius), position, color)
45
.resolution(16);
46
gizmos
47
.sphere(position, point_light.range, color)
48
.resolution(32);
49
}
50
51
/// Draws a sphere for the radius, two cones for the inner and outer angles, plus two 3d arcs crossing the
52
/// farthest point of effect of the spot light along its direction.
53
fn spot_light_gizmo(
54
transform: &GlobalTransform,
55
spot_light: &SpotLight,
56
color: Color,
57
gizmos: &mut Gizmos<LightGizmoConfigGroup>,
58
) {
59
let (_, rotation, translation) = transform.to_scale_rotation_translation();
60
gizmos
61
.primitive_3d(&Sphere::new(spot_light.radius), translation, color)
62
.resolution(16);
63
64
// Offset the tip of the cone to the light position.
65
for angle in [spot_light.inner_angle, spot_light.outer_angle] {
66
let height = spot_light.range * ops::cos(angle);
67
let position = translation + rotation * Vec3::NEG_Z * height / 2.0;
68
gizmos
69
.primitive_3d(
70
&Cone {
71
radius: spot_light.range * ops::sin(angle),
72
height,
73
},
74
Isometry3d::new(position, rotation * Quat::from_rotation_x(PI / 2.0)),
75
color,
76
)
77
.height_resolution(4)
78
.base_resolution(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
Isometry3d::new(translation, rotation * arc_rotation),
95
color,
96
)
97
.resolution(16);
98
}
99
}
100
101
/// Draws an arrow alongside the directional light direction.
102
fn directional_light_gizmo(
103
transform: &GlobalTransform,
104
color: Color,
105
gizmos: &mut Gizmos<LightGizmoConfigGroup>,
106
) {
107
let (_, rotation, translation) = transform.to_scale_rotation_translation();
108
gizmos
109
.arrow(translation, translation + rotation * Vec3::NEG_Z, color)
110
.with_tip_length(0.3);
111
}
112
113
/// A [`Plugin`] that provides visualization of [`PointLight`]s, [`SpotLight`]s
114
/// and [`DirectionalLight`]s for debugging.
115
pub struct LightGizmoPlugin;
116
117
impl Plugin for LightGizmoPlugin {
118
fn build(&self, app: &mut bevy_app::App) {
119
app.init_gizmo_group::<LightGizmoConfigGroup>().add_systems(
120
PostUpdate,
121
(
122
draw_lights,
123
draw_all_lights.run_if(|config: Res<GizmoConfigStore>| {
124
config.config::<LightGizmoConfigGroup>().1.draw_all
125
}),
126
)
127
.after(TransformSystems::Propagate),
128
);
129
}
130
}
131
132
/// Configures how a color is attributed to a light gizmo.
133
#[derive(Debug, Clone, Copy, Default, Reflect)]
134
#[reflect(Clone, Default)]
135
pub enum LightGizmoColor {
136
/// User-specified color.
137
Manual(Color),
138
/// Random color derived from the light's [`Entity`].
139
Varied,
140
/// Take the color of the represented light.
141
#[default]
142
MatchLightColor,
143
/// Take the color provided by [`LightGizmoConfigGroup`] depending on the light kind.
144
ByLightType,
145
}
146
147
/// The [`GizmoConfigGroup`] used to configure the visualization of lights.
148
#[derive(Clone, Reflect, GizmoConfigGroup)]
149
#[reflect(Clone, Default)]
150
pub struct LightGizmoConfigGroup {
151
/// Draw a gizmo for all lights if true.
152
///
153
/// Defaults to `false`.
154
pub draw_all: bool,
155
/// Default color strategy for all light gizmos.
156
///
157
/// Defaults to [`LightGizmoColor::MatchLightColor`].
158
pub color: LightGizmoColor,
159
/// [`Color`] to use for drawing a [`PointLight`] gizmo when [`LightGizmoColor::ByLightType`] is used.
160
///
161
/// Defaults to [`RED`].
162
pub point_light_color: Color,
163
/// [`Color`] to use for drawing a [`SpotLight`] gizmo when [`LightGizmoColor::ByLightType`] is used.
164
///
165
/// Defaults to [`GREEN`].
166
pub spot_light_color: Color,
167
/// [`Color`] to use for drawing a [`DirectionalLight`] gizmo when [`LightGizmoColor::ByLightType`] is used.
168
///
169
/// Defaults to [`BLUE`].
170
pub directional_light_color: Color,
171
}
172
173
impl Default for LightGizmoConfigGroup {
174
fn default() -> Self {
175
Self {
176
draw_all: false,
177
color: LightGizmoColor::MatchLightColor,
178
point_light_color: RED.into(),
179
spot_light_color: GREEN.into(),
180
directional_light_color: BLUE.into(),
181
}
182
}
183
}
184
185
/// Add this [`Component`] to an entity to draw any of its lights components
186
/// ([`PointLight`], [`SpotLight`] and [`DirectionalLight`]).
187
#[derive(Component, Reflect, Default, Debug)]
188
#[reflect(Component, Default, Debug)]
189
pub struct ShowLightGizmo {
190
/// Default color strategy for this light gizmo. if [`None`], use the one provided by [`LightGizmoConfigGroup`].
191
///
192
/// Defaults to [`None`].
193
pub color: Option<LightGizmoColor>,
194
}
195
196
fn draw_lights(
197
point_query: Query<(Entity, &PointLight, &GlobalTransform, &ShowLightGizmo)>,
198
spot_query: Query<(Entity, &SpotLight, &GlobalTransform, &ShowLightGizmo)>,
199
directional_query: Query<(Entity, &DirectionalLight, &GlobalTransform, &ShowLightGizmo)>,
200
mut gizmos: Gizmos<LightGizmoConfigGroup>,
201
) {
202
let color = |entity: Entity, gizmo_color: Option<LightGizmoColor>, light_color, type_color| {
203
match gizmo_color.unwrap_or(gizmos.config_ext.color) {
204
LightGizmoColor::Manual(color) => color,
205
LightGizmoColor::Varied => Oklcha::sequential_dispersed(entity.index()).into(),
206
LightGizmoColor::MatchLightColor => light_color,
207
LightGizmoColor::ByLightType => type_color,
208
}
209
};
210
for (entity, light, transform, light_gizmo) in &point_query {
211
let color = color(
212
entity,
213
light_gizmo.color,
214
light.color,
215
gizmos.config_ext.point_light_color,
216
);
217
point_light_gizmo(transform, light, color, &mut gizmos);
218
}
219
for (entity, light, transform, light_gizmo) in &spot_query {
220
let color = color(
221
entity,
222
light_gizmo.color,
223
light.color,
224
gizmos.config_ext.spot_light_color,
225
);
226
spot_light_gizmo(transform, light, color, &mut gizmos);
227
}
228
for (entity, light, transform, light_gizmo) in &directional_query {
229
let color = color(
230
entity,
231
light_gizmo.color,
232
light.color,
233
gizmos.config_ext.directional_light_color,
234
);
235
directional_light_gizmo(transform, color, &mut gizmos);
236
}
237
}
238
239
fn draw_all_lights(
240
point_query: Query<(Entity, &PointLight, &GlobalTransform), Without<ShowLightGizmo>>,
241
spot_query: Query<(Entity, &SpotLight, &GlobalTransform), Without<ShowLightGizmo>>,
242
directional_query: Query<
243
(Entity, &DirectionalLight, &GlobalTransform),
244
Without<ShowLightGizmo>,
245
>,
246
mut gizmos: Gizmos<LightGizmoConfigGroup>,
247
) {
248
match gizmos.config_ext.color {
249
LightGizmoColor::Manual(color) => {
250
for (_, light, transform) in &point_query {
251
point_light_gizmo(transform, light, color, &mut gizmos);
252
}
253
for (_, light, transform) in &spot_query {
254
spot_light_gizmo(transform, light, color, &mut gizmos);
255
}
256
for (_, _, transform) in &directional_query {
257
directional_light_gizmo(transform, color, &mut gizmos);
258
}
259
}
260
LightGizmoColor::Varied => {
261
let color = |entity: Entity| Oklcha::sequential_dispersed(entity.index()).into();
262
for (entity, light, transform) in &point_query {
263
point_light_gizmo(transform, light, color(entity), &mut gizmos);
264
}
265
for (entity, light, transform) in &spot_query {
266
spot_light_gizmo(transform, light, color(entity), &mut gizmos);
267
}
268
for (entity, _, transform) in &directional_query {
269
directional_light_gizmo(transform, color(entity), &mut gizmos);
270
}
271
}
272
LightGizmoColor::MatchLightColor => {
273
for (_, light, transform) in &point_query {
274
point_light_gizmo(transform, light, light.color, &mut gizmos);
275
}
276
for (_, light, transform) in &spot_query {
277
spot_light_gizmo(transform, light, light.color, &mut gizmos);
278
}
279
for (_, light, transform) in &directional_query {
280
directional_light_gizmo(transform, light.color, &mut gizmos);
281
}
282
}
283
LightGizmoColor::ByLightType => {
284
for (_, light, transform) in &point_query {
285
point_light_gizmo(
286
transform,
287
light,
288
gizmos.config_ext.point_light_color,
289
&mut gizmos,
290
);
291
}
292
for (_, light, transform) in &spot_query {
293
spot_light_gizmo(
294
transform,
295
light,
296
gizmos.config_ext.spot_light_color,
297
&mut gizmos,
298
);
299
}
300
for (_, _, transform) in &directional_query {
301
directional_light_gizmo(
302
transform,
303
gizmos.config_ext.directional_light_color,
304
&mut gizmos,
305
);
306
}
307
}
308
}
309
}
310
311