Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a bounding box gizmo #8468

Merged
merged 6 commits into from
Apr 24, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions crates/bevy_gizmos/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@ bevy_utils = { path = "../bevy_utils", version = "0.11.0-dev" }
bevy_core = { path = "../bevy_core", version = "0.11.0-dev" }
bevy_reflect = { path = "../bevy_reflect", version = "0.11.0-dev" }
bevy_core_pipeline = { path = "../bevy_core_pipeline", version = "0.11.0-dev" }
bevy_transform = { path = "../bevy_transform", version = "0.11.0-dev" }
27 changes: 16 additions & 11 deletions crates/bevy_gizmos/src/gizmos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ use bevy_ecs::{
world::World,
};
use bevy_math::{Mat2, Quat, Vec2, Vec3};
use bevy_render::prelude::Color;
use bevy_render::color::Color;
use bevy_transform::TransformPoint;

type PositionItem = [f32; 3];
type ColorItem = [f32; 4];
Expand Down Expand Up @@ -287,20 +288,24 @@ impl<'s> Gizmos<'s> {
/// # bevy_ecs::system::assert_is_system(system);
/// ```
#[inline]
pub fn cuboid(&mut self, position: Vec3, rotation: Quat, size: Vec3, color: Color) {
let rect = rect_inner(size.truncate());
pub fn cuboid(&mut self, transform: impl TransformPoint, color: Color) {
let rect = rect_inner(Vec2::ONE);
// Front
let [tlf, trf, brf, blf] = rect.map(|vec2| position + rotation * vec2.extend(size.z / 2.));
let [tlf, trf, brf, blf] = rect.map(|vec2| transform.transform_point(vec2.extend(0.5)));
// Back
let [tlb, trb, brb, blb] = rect.map(|vec2| position + rotation * vec2.extend(-size.z / 2.));
let [tlb, trb, brb, blb] = rect.map(|vec2| transform.transform_point(vec2.extend(-0.5)));

let positions = [
tlf, trf, trf, brf, brf, blf, blf, tlf, // Front
tlb, trb, trb, brb, brb, blb, blb, tlb, // Back
tlf, tlb, trf, trb, brf, brb, blf, blb, // Front to back
let strip_positions = [
tlf, trf, brf, blf, tlf, // Front
tlb, trb, brb, blb, tlb, // Back
];
self.extend_list_positions(positions);
self.add_list_color(color, 24);
self.linestrip(strip_positions, color);

let list_positions = [
trf, trb, brf, brb, blf, blb, // Front to back
];
self.extend_list_positions(list_positions);
self.add_list_color(color, 6);
}

/// Draw a line from `start` to `end`.
Expand Down
105 changes: 96 additions & 9 deletions crates/bevy_gizmos/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,22 +18,31 @@

use std::mem;

use bevy_app::{Last, Plugin};
use bevy_app::{Last, Plugin, Update};
use bevy_asset::{load_internal_asset, Assets, Handle, HandleUntyped};
use bevy_ecs::{
prelude::{Component, DetectChanges},
change_detection::DetectChanges,
component::Component,
entity::Entity,
query::Without,
reflect::ReflectComponent,
schedule::IntoSystemConfigs,
system::{Commands, Res, ResMut, Resource},
system::{Commands, Query, Res, ResMut, Resource},
world::{FromWorld, World},
};
use bevy_math::Mat4;
use bevy_reflect::TypeUuid;
use bevy_reflect::{
std_traits::ReflectDefault, FromReflect, Reflect, ReflectFromReflect, TypeUuid,
};
use bevy_render::{
color::Color,
mesh::Mesh,
primitives::Aabb,
render_phase::AddRenderCommand,
render_resource::{PrimitiveTopology, Shader, SpecializedMeshPipelines},
Extract, ExtractSchedule, Render, RenderApp, RenderSet,
};
use bevy_transform::components::{GlobalTransform, Transform};

#[cfg(feature = "bevy_pbr")]
use bevy_pbr::MeshUniform;
Expand All @@ -47,12 +56,12 @@ mod pipeline_2d;
#[cfg(feature = "bevy_pbr")]
mod pipeline_3d;

use crate::gizmos::GizmoStorage;
use gizmos::{GizmoStorage, Gizmos};

/// The `bevy_gizmos` prelude.
pub mod prelude {
#[doc(hidden)]
pub use crate::{gizmos::Gizmos, GizmoConfig};
pub use crate::{gizmos::Gizmos, AabbGizmo, AabbGizmoConfig, GizmoConfig};
}

const LINE_SHADER_HANDLE: HandleUntyped =
Expand All @@ -68,7 +77,14 @@ impl Plugin for GizmoPlugin {
app.init_resource::<MeshHandles>()
.init_resource::<GizmoConfig>()
.init_resource::<GizmoStorage>()
.add_systems(Last, update_gizmo_meshes);
.add_systems(Last, update_gizmo_meshes)
.add_systems(
Update,
(
draw_aabbs,
draw_all_aabbs.run_if(|config: Res<GizmoConfig>| config.aabb.draw_all),
),
);

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

Expand Down Expand Up @@ -101,7 +117,7 @@ impl Plugin for GizmoPlugin {
}

/// A [`Resource`] that stores configuration for gizmos.
#[derive(Resource, Clone, Copy)]
#[derive(Resource, Clone)]
pub struct GizmoConfig {
/// Set to `false` to stop drawing gizmos.
///
Expand All @@ -113,17 +129,88 @@ pub struct GizmoConfig {
///
/// Defaults to `false`.
pub on_top: bool,
/// Configuration for the [`AabbGizmo`].
pub aabb: AabbGizmoConfig,
}

impl Default for GizmoConfig {
fn default() -> Self {
Self {
enabled: true,
on_top: false,
aabb: Default::default(),
}
}
}

/// Configuration for bounding box gizmos.
#[derive(Clone, Default)]
pub struct AabbGizmoConfig {
tim-blackbird marked this conversation as resolved.
Show resolved Hide resolved
/// Draws all bounding boxes in the scene when set to `true`.
///
/// To draw a specific entity's bounding box, you can add the [`AabbGizmo`] component.
///
/// Defaults to `false`.
pub draw_all: bool,
/// The default color for bounding box gizmos.
///
/// A random color is chosen per box if `None`.
///
/// Defaults to `None`.
pub default_color: Option<Color>,
}

/// Add this [`Component`] to an entity to draw its [`Aabb`] component.
#[derive(Component, Reflect, FromReflect, Default, Debug)]
#[reflect(Component, FromReflect, Default)]
pub struct AabbGizmo {
/// The color of the box.
///
/// The default color from the [`GizmoConfig`] resource is used if `None`,
pub color: Option<Color>,
}

fn draw_aabbs(
query: Query<(Entity, &Aabb, &GlobalTransform, &AabbGizmo)>,
config: Res<GizmoConfig>,
mut gizmos: Gizmos,
) {
for (entity, &aabb, &transform, gizmo) in &query {
let color = gizmo
.color
.or(config.aabb.default_color)
.unwrap_or_else(|| color_from_entity(entity));
gizmos.cuboid(aabb_transform(aabb, transform), color);
}
}

fn draw_all_aabbs(
query: Query<(Entity, &Aabb, &GlobalTransform), Without<AabbGizmo>>,
config: Res<GizmoConfig>,
mut gizmos: Gizmos,
) {
for (entity, &aabb, &transform) in &query {
let color = config
.aabb
.default_color
.unwrap_or_else(|| color_from_entity(entity));
gizmos.cuboid(aabb_transform(aabb, transform), color);
}
}

fn color_from_entity(entity: Entity) -> Color {
let hue = entity.to_bits() as f32 * 100_000. % 360.;
Color::hsl(hue, 1., 0.5)
}

fn aabb_transform(aabb: Aabb, transform: GlobalTransform) -> GlobalTransform {
transform
* GlobalTransform::from(
Transform::from_translation(aabb.center.into())
.with_scale((aabb.half_extents * 2.).into()),
)
}

#[derive(Resource)]
struct MeshHandles {
list: Option<Handle<Mesh>>,
Expand Down Expand Up @@ -198,7 +285,7 @@ fn extract_gizmo_data(
config: Extract<Res<GizmoConfig>>,
) {
if config.is_changed() {
commands.insert_resource(**config);
commands.insert_resource(config.clone());
}

if !config.enabled {
Expand Down
37 changes: 37 additions & 0 deletions crates/bevy_transform/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,15 @@ pub mod prelude {
#[doc(hidden)]
pub use crate::{
commands::BuildChildrenTransformExt, components::*, TransformBundle, TransformPlugin,
TransformPoint,
};
}

use bevy_app::prelude::*;
use bevy_ecs::prelude::*;
use bevy_hierarchy::ValidParentCheckPlugin;
use bevy_math::{Affine3A, Mat4, Vec3};

use prelude::{GlobalTransform, Transform};
use systems::{propagate_transforms, sync_simple_transforms};

Expand Down Expand Up @@ -131,3 +134,37 @@ impl Plugin for TransformPlugin {
);
}
}

/// A trait for point transformation methods.
pub trait TransformPoint {
/// Transform a point.
fn transform_point(&self, point: impl Into<Vec3>) -> Vec3;
}

impl TransformPoint for Transform {
#[inline]
fn transform_point(&self, point: impl Into<Vec3>) -> Vec3 {
self.transform_point(point.into())
}
}

impl TransformPoint for GlobalTransform {
#[inline]
fn transform_point(&self, point: impl Into<Vec3>) -> Vec3 {
self.transform_point(point.into())
}
}

impl TransformPoint for Mat4 {
#[inline]
fn transform_point(&self, point: impl Into<Vec3>) -> Vec3 {
self.transform_point3(point.into())
}
}

impl TransformPoint for Affine3A {
#[inline]
fn transform_point(&self, point: impl Into<Vec3>) -> Vec3 {
self.transform_point3(point.into())
}
}
4 changes: 1 addition & 3 deletions examples/3d/3d_gizmos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,7 @@ fn setup(

fn system(mut gizmos: Gizmos, time: Res<Time>) {
gizmos.cuboid(
Vec3::Y * -0.5,
Quat::IDENTITY,
Vec3::new(5., 1., 2.),
Transform::from_translation(Vec3::Y * -0.5).with_scale(Vec3::new(5., 1., 2.)),
Color::BLACK,
);
gizmos.rect(
Expand Down
11 changes: 10 additions & 1 deletion examples/tools/scene_viewer/scene_viewer_plugin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
//! - Copy the code for the `SceneViewerPlugin` and add the plugin to your App.
//! - Insert an initialized `SceneHandle` resource into your App's `AssetServer`.

use bevy::{asset::LoadState, gltf::Gltf, prelude::*, scene::InstanceId};
use bevy::{
asset::LoadState, gltf::Gltf, input::common_conditions::input_just_pressed, prelude::*,
scene::InstanceId,
};

use std::f32::consts::*;
use std::fmt;
Expand Down Expand Up @@ -43,6 +46,7 @@ impl fmt::Display for SceneHandle {
Scene Controls:
L - animate light direction
U - toggle shadows
B - toggle bounding boxes
C - cycle through the camera controller and any cameras loaded from the scene

Space - Play/Pause animation
Expand All @@ -63,13 +67,18 @@ impl Plugin for SceneViewerPlugin {
(
update_lights,
camera_tracker,
toggle_bounding_boxes.run_if(input_just_pressed(KeyCode::B)),
#[cfg(feature = "animation")]
(start_animation, keyboard_animation_control),
),
);
}
}

fn toggle_bounding_boxes(mut config: ResMut<GizmoConfig>) {
config.aabb.draw_all ^= true;
}

fn scene_load_check(
asset_server: Res<AssetServer>,
mut scenes: ResMut<Assets<Scene>>,
Expand Down