-
-
Notifications
You must be signed in to change notification settings - Fork 3.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Multiple Configurations for Gizmos (#10342)
# Objective This PR aims to implement multiple configs for gizmos as discussed in #9187. ## Solution Configs for the new `GizmoConfigGroup`s are stored in a `GizmoConfigStore` resource and can be accesses using a type based key or iterated over. This type based key doubles as a standardized location where plugin authors can put their own configuration not covered by the standard `GizmoConfig` struct. For example the `AabbGizmoGroup` has a default color and toggle to show all AABBs. New configs can be registered using `app.init_gizmo_group::<T>()` during startup. When requesting the `Gizmos<T>` system parameter the generic type determines which config is used. The config structs are available through the `Gizmos` system parameter allowing for easy access while drawing your gizmos. Internally, resources and systems used for rendering (up to an including the extract system) are generic over the type based key and inserted on registering a new config. ## Alternatives The configs could be stored as components on entities with markers which would make better use of the ECS. I also implemented this approach ([here](https://github.com/jeliag/bevy/tree/gizmo-multiconf-comp)) and believe that the ergonomic benefits of a central config store outweigh the decreased use of the ECS. ## Unsafe Code Implementing system parameter by hand is unsafe but seems to be required to access the config store once and not on every gizmo draw function call. This is critical for performance. ~Is there a better way to do this?~ ## Future Work New gizmos (such as #10038, and ideas from #9400) will require custom configuration structs. Should there be a new custom config for every gizmo type, or should we group them together in a common configuration? (for example `EditorGizmoConfig`, or something more fine-grained) ## Changelog - Added `GizmoConfigStore` resource and `GizmoConfigGroup` trait - Added `init_gizmo_group` to `App` - Added early returns to gizmo drawing increasing performance when gizmos are disabled - Changed `GizmoConfig` and aabb gizmos to use new `GizmoConfigStore` - Changed `Gizmos` system parameter to use type based key to retrieve config - Changed resources and systems used for gizmo rendering to be generic over type based key - Changed examples (3d_gizmos, 2d_gizmos) to showcase new API ## Migration Guide - `GizmoConfig` is no longer a resource and has to be accessed through `GizmoConfigStore` resource. The default config group is `DefaultGizmoGroup`, but consider using your own custom config group if applicable. --------- Co-authored-by: Nicola Papale <[email protected]>
- Loading branch information
Showing
15 changed files
with
674 additions
and
254 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
[package] | ||
name = "bevy_gizmos_macros" | ||
version = "0.12.0" | ||
edition = "2021" | ||
description = "Derive implementations for bevy_gizmos" | ||
homepage = "https://bevyengine.org" | ||
repository = "https://github.com/bevyengine/bevy" | ||
license = "MIT OR Apache-2.0" | ||
keywords = ["bevy"] | ||
|
||
[lib] | ||
proc-macro = true | ||
|
||
[dependencies] | ||
bevy_macro_utils = { path = "../../bevy_macro_utils", version = "0.12.0" } | ||
|
||
syn = "2.0" | ||
proc-macro2 = "1.0" | ||
quote = "1.0" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
use bevy_macro_utils::BevyManifest; | ||
use proc_macro::TokenStream; | ||
use quote::quote; | ||
use syn::{parse_macro_input, parse_quote, DeriveInput, Path}; | ||
|
||
#[proc_macro_derive(GizmoConfigGroup)] | ||
pub fn derive_gizmo_config_group(input: TokenStream) -> TokenStream { | ||
let mut ast = parse_macro_input!(input as DeriveInput); | ||
let bevy_gizmos_path: Path = BevyManifest::default().get_path("bevy_gizmos"); | ||
let bevy_reflect_path: Path = BevyManifest::default().get_path("bevy_reflect"); | ||
|
||
ast.generics.make_where_clause().predicates.push( | ||
parse_quote! { Self: #bevy_reflect_path::Reflect + #bevy_reflect_path::TypePath + Default}, | ||
); | ||
|
||
let struct_name = &ast.ident; | ||
let (impl_generics, type_generics, where_clause) = &ast.generics.split_for_impl(); | ||
|
||
TokenStream::from(quote! { | ||
impl #impl_generics #bevy_gizmos_path::config::GizmoConfigGroup for #struct_name #type_generics #where_clause { | ||
} | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
//! A module adding debug visualization of [`Aabb`]s. | ||
use crate as bevy_gizmos; | ||
|
||
use bevy_app::{Plugin, PostUpdate}; | ||
use bevy_ecs::{ | ||
component::Component, | ||
entity::Entity, | ||
query::Without, | ||
reflect::ReflectComponent, | ||
schedule::IntoSystemConfigs, | ||
system::{Query, Res}, | ||
}; | ||
use bevy_reflect::{std_traits::ReflectDefault, Reflect}; | ||
use bevy_render::{color::Color, primitives::Aabb}; | ||
use bevy_transform::{ | ||
components::{GlobalTransform, Transform}, | ||
TransformSystem, | ||
}; | ||
|
||
use crate::{ | ||
config::{GizmoConfigGroup, GizmoConfigStore}, | ||
gizmos::Gizmos, | ||
AppGizmoBuilder, | ||
}; | ||
|
||
/// A [`Plugin`] that provides visualization of [`Aabb`]s for debugging. | ||
pub struct AabbGizmoPlugin; | ||
|
||
impl Plugin for AabbGizmoPlugin { | ||
fn build(&self, app: &mut bevy_app::App) { | ||
app.register_type::<AabbGizmoConfigGroup>() | ||
.init_gizmo_group::<AabbGizmoConfigGroup>() | ||
.add_systems( | ||
PostUpdate, | ||
( | ||
draw_aabbs, | ||
draw_all_aabbs.run_if(|config: Res<GizmoConfigStore>| { | ||
config.config::<AabbGizmoConfigGroup>().1.draw_all | ||
}), | ||
) | ||
.after(TransformSystem::TransformPropagate), | ||
); | ||
} | ||
} | ||
/// The [`GizmoConfigGroup`] used for debug visualizations of [`Aabb`] components on entities | ||
#[derive(Clone, Default, Reflect, GizmoConfigGroup)] | ||
pub struct AabbGizmoConfigGroup { | ||
/// Draws all bounding boxes in the scene when set to `true`. | ||
/// | ||
/// To draw a specific entity's bounding box, you can add the [`ShowAabbGizmo`] 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, Default, Debug)] | ||
#[reflect(Component, Default)] | ||
pub struct ShowAabbGizmo { | ||
/// The color of the box. | ||
/// | ||
/// The default color from the [`AabbGizmoConfigGroup`] config is used if `None`, | ||
pub color: Option<Color>, | ||
} | ||
|
||
fn draw_aabbs( | ||
query: Query<(Entity, &Aabb, &GlobalTransform, &ShowAabbGizmo)>, | ||
mut gizmos: Gizmos<AabbGizmoConfigGroup>, | ||
) { | ||
for (entity, &aabb, &transform, gizmo) in &query { | ||
let color = gizmo | ||
.color | ||
.or(gizmos.config_ext.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<ShowAabbGizmo>>, | ||
mut gizmos: Gizmos<AabbGizmoConfigGroup>, | ||
) { | ||
for (entity, &aabb, &transform) in &query { | ||
let color = gizmos | ||
.config_ext | ||
.default_color | ||
.unwrap_or_else(|| color_from_entity(entity)); | ||
gizmos.cuboid(aabb_transform(aabb, transform), color); | ||
} | ||
} | ||
|
||
fn color_from_entity(entity: Entity) -> Color { | ||
let index = entity.index(); | ||
|
||
// from https://extremelearning.com.au/unreasonable-effectiveness-of-quasirandom-sequences/ | ||
// | ||
// See https://en.wikipedia.org/wiki/Low-discrepancy_sequence | ||
// Map a sequence of integers (eg: 154, 155, 156, 157, 158) into the [0.0..1.0] range, | ||
// so that the closer the numbers are, the larger the difference of their image. | ||
const FRAC_U32MAX_GOLDEN_RATIO: u32 = 2654435769; // (u32::MAX / Φ) rounded up | ||
const RATIO_360: f32 = 360.0 / u32::MAX as f32; | ||
let hue = index.wrapping_mul(FRAC_U32MAX_GOLDEN_RATIO) as f32 * RATIO_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()), | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.