diff --git a/crates/bevy_gizmos/Cargo.toml b/crates/bevy_gizmos/Cargo.toml index cee285784c..f44a2d1ec9 100644 --- a/crates/bevy_gizmos/Cargo.toml +++ b/crates/bevy_gizmos/Cargo.toml @@ -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" } diff --git a/crates/bevy_gizmos/src/gizmos.rs b/crates/bevy_gizmos/src/gizmos.rs index 9d734dc96b..974532d11e 100644 --- a/crates/bevy_gizmos/src/gizmos.rs +++ b/crates/bevy_gizmos/src/gizmos.rs @@ -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]; @@ -280,27 +281,31 @@ impl<'s> Gizmos<'s> { /// ``` /// # use bevy_gizmos::prelude::*; /// # use bevy_render::prelude::*; - /// # use bevy_math::prelude::*; + /// # use bevy_transform::prelude::*; /// fn system(mut gizmos: Gizmos) { - /// gizmos.cuboid(Vec3::ZERO, Quat::IDENTITY, Vec3::ONE, Color::GREEN); + /// gizmos.cuboid(Transform::IDENTITY, Color::GREEN); /// } /// # 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`. diff --git a/crates/bevy_gizmos/src/lib.rs b/crates/bevy_gizmos/src/lib.rs index 01a2067a7a..fe11eb2d88 100644 --- a/crates/bevy_gizmos/src/lib.rs +++ b/crates/bevy_gizmos/src/lib.rs @@ -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; @@ -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 = @@ -68,7 +77,14 @@ impl Plugin for GizmoPlugin { app.init_resource::() .init_resource::() .init_resource::() - .add_systems(Last, update_gizmo_meshes); + .add_systems(Last, update_gizmo_meshes) + .add_systems( + Update, + ( + draw_aabbs, + draw_all_aabbs.run_if(|config: Res| config.aabb.draw_all), + ), + ); let Ok(render_app) = app.get_sub_app_mut(RenderApp) else { return; }; @@ -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. /// @@ -113,6 +129,8 @@ pub struct GizmoConfig { /// /// Defaults to `false`. pub on_top: bool, + /// Configuration for the [`AabbGizmo`]. + pub aabb: AabbGizmoConfig, } impl Default for GizmoConfig { @@ -120,10 +138,79 @@ impl Default for GizmoConfig { Self { enabled: true, on_top: false, + aabb: Default::default(), } } } +/// Configuration for drawing the [`Aabb`] component on entities. +#[derive(Clone, Default)] +pub struct AabbGizmoConfig { + /// 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, +} + +/// 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, +} + +fn draw_aabbs( + query: Query<(Entity, &Aabb, &GlobalTransform, &AabbGizmo)>, + config: Res, + 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>, + config: Res, + 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>, @@ -198,7 +285,7 @@ fn extract_gizmo_data( config: Extract>, ) { if config.is_changed() { - commands.insert_resource(**config); + commands.insert_resource(config.clone()); } if !config.enabled { diff --git a/crates/bevy_transform/src/lib.rs b/crates/bevy_transform/src/lib.rs index 45ed286bd8..c8b01c1098 100644 --- a/crates/bevy_transform/src/lib.rs +++ b/crates/bevy_transform/src/lib.rs @@ -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}; @@ -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; +} + +impl TransformPoint for Transform { + #[inline] + fn transform_point(&self, point: impl Into) -> Vec3 { + self.transform_point(point.into()) + } +} + +impl TransformPoint for GlobalTransform { + #[inline] + fn transform_point(&self, point: impl Into) -> Vec3 { + self.transform_point(point.into()) + } +} + +impl TransformPoint for Mat4 { + #[inline] + fn transform_point(&self, point: impl Into) -> Vec3 { + self.transform_point3(point.into()) + } +} + +impl TransformPoint for Affine3A { + #[inline] + fn transform_point(&self, point: impl Into) -> Vec3 { + self.transform_point3(point.into()) + } +} diff --git a/examples/3d/3d_gizmos.rs b/examples/3d/3d_gizmos.rs index 0499e6c66e..7ae97cd49d 100644 --- a/examples/3d/3d_gizmos.rs +++ b/examples/3d/3d_gizmos.rs @@ -56,9 +56,7 @@ fn setup( fn system(mut gizmos: Gizmos, time: Res