bevy/examples/gizmos/light_gizmos.rs
UkoeHB c2c19e5ae4
Text rework (#15591)
**Ready for review. Examples migration progress: 100%.**

# Objective

- Implement https://github.com/bevyengine/bevy/discussions/15014

## Solution

This implements [cart's
proposal](https://github.com/bevyengine/bevy/discussions/15014#discussioncomment-10574459)
faithfully except for one change. I separated `TextSpan` from
`TextSpan2d` because `TextSpan` needs to require the `GhostNode`
component, which is a `bevy_ui` component only usable by UI.

Extra changes:
- Added `EntityCommands::commands_mut` that returns a mutable reference.
This is a blocker for extension methods that return something other than
`self`. Note that `sickle_ui`'s `UiBuilder::commands` returns a mutable
reference for this reason.

## Testing

- [x] Text examples all work.

---

## Showcase

TODO: showcase-worthy

## Migration Guide

TODO: very breaking

### Accessing text spans by index

Text sections are now text sections on different entities in a
hierarchy, Use the new `TextReader` and `TextWriter` system parameters
to access spans by index.

Before:
```rust
fn refresh_text(mut query: Query<&mut Text, With<TimeText>>, time: Res<Time>) {
    let text = query.single_mut();
    text.sections[1].value = format_time(time.elapsed());
}
```

After:
```rust
fn refresh_text(
    query: Query<Entity, With<TimeText>>,
    mut writer: UiTextWriter,
    time: Res<Time>
) {
    let entity = query.single();
    *writer.text(entity, 1) = format_time(time.elapsed());
}
```

### Iterating text spans

Text spans are now entities in a hierarchy, so the new `UiTextReader`
and `UiTextWriter` system parameters provide ways to iterate that
hierarchy. The `UiTextReader::iter` method will give you a normal
iterator over spans, and `UiTextWriter::for_each` lets you visit each of
the spans.

---------

Co-authored-by: ickshonpe <david.curthoys@googlemail.com>
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2024-10-09 18:35:36 +00:00

179 lines
5.9 KiB
Rust

//! 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},
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<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
mut config_store: ResMut<GizmoConfigStore>,
) {
// Circular base.
commands.spawn((
Mesh3d(meshes.add(Circle::new(4.0))),
MeshMaterial3d(materials.add(Color::WHITE)),
Transform::from_rotation(Quat::from_rotation_x(-FRAC_PI_2)),
));
// 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((
Mesh3d(mesh.clone()),
MeshMaterial3d(material.clone()),
Transform::from_xyz(x, 0.5, 0.0),
));
}
}
// Lights.
{
commands.spawn((
PointLight {
shadows_enabled: true,
range: 2.0,
color: DARK_CYAN.into(),
..default()
},
Transform::from_xyz(0.0, 1.5, 0.0),
));
commands.spawn((
SpotLight {
shadows_enabled: true,
range: 3.5,
color: PURPLE.into(),
outer_angle: PI / 4.0,
inner_angle: PI / 4.0 * 0.8,
..default()
},
Transform::from_xyz(4.0, 2.0, 0.0).looking_at(Vec3::X * 1.5, Vec3::Y),
));
commands.spawn((
DirectionalLight {
color: GOLD.into(),
illuminance: DirectionalLight::default().illuminance * 0.05,
shadows_enabled: true,
..default()
},
Transform::from_xyz(-4.0, 2.0, 0.0).looking_at(Vec3::NEG_X * 1.5, Vec3::Y),
));
}
// Camera.
commands.spawn((
Camera3d::default(),
Transform::from_xyz(-2.5, 4.5, 9.0).looking_at(Vec3::ZERO, Vec3::Y),
));
// Example instructions and gizmo config.
{
commands.spawn((
Text::new(
"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",
),
Style {
position_type: PositionType::Absolute,
top: Val::Px(12.0),
left: Val::Px(12.0),
..default()
},
));
let (_, light_config) = config_store.config_mut::<LightGizmoConfigGroup>();
light_config.draw_all = true;
light_config.color = LightGizmoColor::MatchLightColor;
commands
.spawn((
Text::new("Gizmo color mode: "),
GizmoColorText,
Style {
position_type: PositionType::Absolute,
bottom: Val::Px(12.0),
left: Val::Px(12.0),
..default()
},
))
.with_child(TextSpan(gizmo_color_text(light_config)));
}
}
fn rotate_camera(mut query: Query<&mut Transform, With<Camera>>, time: Res<Time>) {
let mut transform = query.single_mut();
transform.rotate_around(Vec3::ZERO, Quat::from_rotation_y(time.delta_seconds() / 2.));
}
fn update_config(
mut config_store: ResMut<GizmoConfigStore>,
keyboard: Res<ButtonInput<KeyCode>>,
time: Res<Time>,
color_text_query: Query<Entity, With<GizmoColorText>>,
mut writer: UiTextWriter,
) {
if keyboard.just_pressed(KeyCode::KeyD) {
for (_, config, _) in config_store.iter_mut() {
config.depth_bias = if config.depth_bias == 0. { -1. } else { 0. };
}
}
let (config, light_config) = config_store.config_mut::<LightGizmoConfigGroup>();
if keyboard.pressed(KeyCode::ArrowRight) {
config.line_width += 5. * time.delta_seconds();
config.line_width = config.line_width.clamp(0., 50.);
}
if keyboard.pressed(KeyCode::ArrowLeft) {
config.line_width -= 5. * time.delta_seconds();
config.line_width = config.line_width.clamp(0., 50.);
}
if keyboard.just_pressed(KeyCode::KeyA) {
config.enabled ^= true;
}
if keyboard.just_pressed(KeyCode::KeyC) {
light_config.color = match light_config.color {
LightGizmoColor::Manual(_) => LightGizmoColor::Varied,
LightGizmoColor::Varied => LightGizmoColor::MatchLightColor,
LightGizmoColor::MatchLightColor => LightGizmoColor::ByLightType,
LightGizmoColor::ByLightType => LightGizmoColor::Manual(GRAY.into()),
};
*writer.text(color_text_query.single(), 1) = gizmo_color_text(light_config);
}
}