From d2a07f9f72ea2b2d57383d91e63c37d0b7952324 Mon Sep 17 00:00:00 2001 From: Tim Date: Wed, 4 Dec 2024 21:21:06 +0000 Subject: [PATCH] Retained `Gizmo`s (#15473) # Objective Add a way to use the gizmo API in a retained manner, for increased performance. ## Solution - Move gizmo API from `Gizmos` to `GizmoBuffer`, ~ab~using `Deref` to keep usage the same as before. - Merge non-strip and strip variant of `LineGizmo` into one, storing the data in a `GizmoBuffer` to have the same API for retained `LineGizmo`s. ### Review guide - The meat of the changes are in `lib.rs`, `retained.rs`, `gizmos.rs`, `pipeline_3d.rs` and `pipeline_2d.rs` - The other files contain almost exclusively the churn from moving the gizmo API from `Gizmos` to `GizmoBuffer` ## Testing ### Performance Performance compared to the immediate mode API is from 65 to 80 times better for static lines. ``` 7900 XTX, 3700X 1707.9k lines/ms: gizmos_retained (21.3ms) 3488.5k lines/ms: gizmos_retained_continuous_polyline (31.3ms) 0.5k lines/ms: gizmos_retained_separate (97.7ms) 3054.9k lines/ms: bevy_polyline_retained_nan (16.8ms) 3596.3k lines/ms: bevy_polyline_retained_continuous_polyline (14.2ms) 0.6k lines/ms: bevy_polyline_retained_separate (78.9ms) 26.9k lines/ms: gizmos_immediate (14.9ms) 43.8k lines/ms: gizmos_immediate_continuous_polyline (18.3ms) ``` Looks like performance is good enough, being close to par with `bevy_polyline`. Benchmarks can be found here: This branch: https://github.com/tim-blackbird/line_racing/tree/retained-gizmos Bevy 0.14: https://github.com/DGriffin91/line_racing ## Showcase ```rust fn setup( mut commands: Commands, mut gizmo_assets: ResMut> ) { let mut gizmo = GizmoAsset::default(); // A sphere made out of one million lines! gizmo .sphere(default(), 1., CRIMSON) .resolution(1_000_000 / 3); commands.spawn(Gizmo { handle: gizmo_assets.add(gizmo), ..default() }); } ``` ## Follow-up work - Port over to the retained rendering world proper - Calculate visibility and cull `Gizmo`s --- .../src/ui_debug_overlay/inset.rs | 6 +- .../src/ui_debug_overlay/mod.rs | 2 +- crates/bevy_gizmos/src/arcs.rs | 49 ++-- crates/bevy_gizmos/src/arrows.rs | 26 +- crates/bevy_gizmos/src/circles.rs | 48 ++-- crates/bevy_gizmos/src/config.rs | 69 +++-- crates/bevy_gizmos/src/cross.rs | 9 +- crates/bevy_gizmos/src/curves.rs | 10 +- crates/bevy_gizmos/src/gizmos.rs | 127 +++++++-- crates/bevy_gizmos/src/grid.rs | 38 +-- crates/bevy_gizmos/src/lib.rs | 254 ++++++++++-------- crates/bevy_gizmos/src/line_joints.wgsl | 27 +- crates/bevy_gizmos/src/lines.wgsl | 9 +- crates/bevy_gizmos/src/pipeline_2d.rs | 72 +++-- crates/bevy_gizmos/src/pipeline_3d.rs | 79 ++++-- crates/bevy_gizmos/src/primitives/dim2.rs | 80 +++--- crates/bevy_gizmos/src/primitives/dim3.rs | 106 ++++---- crates/bevy_gizmos/src/retained.rs | 138 ++++++++++ crates/bevy_gizmos/src/rounded_box.rs | 63 +++-- examples/gizmos/2d_gizmos.rs | 24 +- examples/gizmos/3d_gizmos.rs | 58 ++-- examples/gizmos/light_gizmos.rs | 8 +- 22 files changed, 840 insertions(+), 462 deletions(-) create mode 100644 crates/bevy_gizmos/src/retained.rs diff --git a/crates/bevy_dev_tools/src/ui_debug_overlay/inset.rs b/crates/bevy_dev_tools/src/ui_debug_overlay/inset.rs index 92522f6fbe..1e52723d20 100644 --- a/crates/bevy_dev_tools/src/ui_debug_overlay/inset.rs +++ b/crates/bevy_dev_tools/src/ui_debug_overlay/inset.rs @@ -184,9 +184,9 @@ impl<'w, 's> InsetGizmo<'w, 's> { Vec2::new(right, bottom), Vec2::new(right, top), Vec2::new(left, top), - ]; - self.draw - .linestrip_2d(strip.map(|v| self.relative(v)), color); + ] + .map(|v| self.relative(v)); + self.draw.linestrip_2d(strip, color); } } } diff --git a/crates/bevy_dev_tools/src/ui_debug_overlay/mod.rs b/crates/bevy_dev_tools/src/ui_debug_overlay/mod.rs index 4e72778f15..97726ce289 100644 --- a/crates/bevy_dev_tools/src/ui_debug_overlay/mod.rs +++ b/crates/bevy_dev_tools/src/ui_debug_overlay/mod.rs @@ -204,7 +204,7 @@ fn outline_roots( let line_width = outline .gizmo_config .get_config_dyn(&UiGizmosDebug.type_id()) - .map_or(2., |(config, _)| config.line_width) + .map_or(2., |(config, _)| config.line.width) / window_scale; let mut draw = InsetGizmo::new(draw, cam.debug_camera, line_width); for (entity, trans, node, view_visibility, maybe_target_camera) in &roots { diff --git a/crates/bevy_gizmos/src/arcs.rs b/crates/bevy_gizmos/src/arcs.rs index ce57450f2d..65f5f67ee7 100644 --- a/crates/bevy_gizmos/src/arcs.rs +++ b/crates/bevy_gizmos/src/arcs.rs @@ -1,19 +1,16 @@ -//! Additional [`Gizmos`] Functions -- Arcs +//! Additional [`GizmoBuffer`] Functions -- Arcs //! -//! Includes the implementation of [`Gizmos::arc_2d`], +//! Includes the implementation of [`GizmoBuffer::arc_2d`], //! and assorted support items. -use crate::{ - circles::DEFAULT_CIRCLE_RESOLUTION, - prelude::{GizmoConfigGroup, Gizmos}, -}; +use crate::{circles::DEFAULT_CIRCLE_RESOLUTION, gizmos::GizmoBuffer, prelude::GizmoConfigGroup}; use bevy_color::Color; use bevy_math::{Isometry2d, Isometry3d, Quat, Rot2, Vec2, Vec3}; use core::f32::consts::{FRAC_PI_2, TAU}; // === 2D === -impl<'w, 's, Config, Clear> Gizmos<'w, 's, Config, Clear> +impl GizmoBuffer where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, @@ -54,7 +51,7 @@ where arc_angle: f32, radius: f32, color: impl Into, - ) -> Arc2dBuilder<'_, 'w, 's, Config, Clear> { + ) -> Arc2dBuilder<'_, Config, Clear> { Arc2dBuilder { gizmos: self, isometry: isometry.into(), @@ -66,13 +63,13 @@ where } } -/// A builder returned by [`Gizmos::arc_2d`]. -pub struct Arc2dBuilder<'a, 'w, 's, Config, Clear> +/// A builder returned by [`GizmoBuffer::arc_2d`]. +pub struct Arc2dBuilder<'a, Config, Clear> where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, { - gizmos: &'a mut Gizmos<'w, 's, Config, Clear>, + gizmos: &'a mut GizmoBuffer, isometry: Isometry2d, arc_angle: f32, radius: f32, @@ -80,7 +77,7 @@ where resolution: Option, } -impl Arc2dBuilder<'_, '_, '_, Config, Clear> +impl Arc2dBuilder<'_, Config, Clear> where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, @@ -92,7 +89,7 @@ where } } -impl Drop for Arc2dBuilder<'_, '_, '_, Config, Clear> +impl Drop for Arc2dBuilder<'_, Config, Clear> where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, @@ -122,7 +119,7 @@ fn arc_2d_inner(arc_angle: f32, radius: f32, resolution: u32) -> impl Iterator Gizmos<'w, 's, Config, Clear> +impl GizmoBuffer where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, @@ -178,7 +175,7 @@ where radius: f32, isometry: impl Into, color: impl Into, - ) -> Arc3dBuilder<'_, 'w, 's, Config, Clear> { + ) -> Arc3dBuilder<'_, Config, Clear> { Arc3dBuilder { gizmos: self, start_vertex: Vec3::X, @@ -233,7 +230,7 @@ where from: Vec3, to: Vec3, color: impl Into, - ) -> Arc3dBuilder<'_, 'w, 's, Config, Clear> { + ) -> Arc3dBuilder<'_, Config, Clear> { self.arc_from_to(center, from, to, color, |x| x) } @@ -279,7 +276,7 @@ where from: Vec3, to: Vec3, color: impl Into, - ) -> Arc3dBuilder<'_, 'w, 's, Config, Clear> { + ) -> Arc3dBuilder<'_, Config, Clear> { self.arc_from_to(center, from, to, color, |angle| { if angle > 0.0 { TAU - angle @@ -299,7 +296,7 @@ where to: Vec3, color: impl Into, angle_fn: impl Fn(f32) -> f32, - ) -> Arc3dBuilder<'_, 'w, 's, Config, Clear> { + ) -> Arc3dBuilder<'_, Config, Clear> { // `from` and `to` can be the same here since in either case nothing gets rendered and the // orientation ambiguity of `up` doesn't matter let from_axis = (from - center).normalize_or_zero(); @@ -366,7 +363,7 @@ where from: Vec2, to: Vec2, color: impl Into, - ) -> Arc2dBuilder<'_, 'w, 's, Config, Clear> { + ) -> Arc2dBuilder<'_, Config, Clear> { self.arc_2d_from_to(center, from, to, color, core::convert::identity) } @@ -412,7 +409,7 @@ where from: Vec2, to: Vec2, color: impl Into, - ) -> Arc2dBuilder<'_, 'w, 's, Config, Clear> { + ) -> Arc2dBuilder<'_, Config, Clear> { self.arc_2d_from_to(center, from, to, color, |angle| angle - TAU) } @@ -424,7 +421,7 @@ where to: Vec2, color: impl Into, angle_fn: impl Fn(f32) -> f32, - ) -> Arc2dBuilder<'_, 'w, 's, Config, Clear> { + ) -> Arc2dBuilder<'_, Config, Clear> { // `from` and `to` can be the same here since in either case nothing gets rendered and the // orientation ambiguity of `up` doesn't matter let from_axis = (from - center).normalize_or_zero(); @@ -446,13 +443,13 @@ where } } -/// A builder returned by [`Gizmos::arc_2d`]. -pub struct Arc3dBuilder<'a, 'w, 's, Config, Clear> +/// A builder returned by [`GizmoBuffer::arc_2d`]. +pub struct Arc3dBuilder<'a, Config, Clear> where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, { - gizmos: &'a mut Gizmos<'w, 's, Config, Clear>, + gizmos: &'a mut GizmoBuffer, // this is the vertex the arc starts on in the XZ plane. For the normal arc_3d method this is // always starting at Vec3::X. For the short/long arc methods we actually need a way to start // at the from position and this is where this internal field comes into play. Some implicit @@ -470,7 +467,7 @@ where resolution: Option, } -impl Arc3dBuilder<'_, '_, '_, Config, Clear> +impl Arc3dBuilder<'_, Config, Clear> where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, @@ -482,7 +479,7 @@ where } } -impl Drop for Arc3dBuilder<'_, '_, '_, Config, Clear> +impl Drop for Arc3dBuilder<'_, Config, Clear> where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, diff --git a/crates/bevy_gizmos/src/arrows.rs b/crates/bevy_gizmos/src/arrows.rs index 29d5119e79..74fbdc9726 100644 --- a/crates/bevy_gizmos/src/arrows.rs +++ b/crates/bevy_gizmos/src/arrows.rs @@ -1,9 +1,9 @@ -//! Additional [`Gizmos`] Functions -- Arrows +//! Additional [`GizmoBuffer`] Functions -- Arrows //! -//! Includes the implementation of [`Gizmos::arrow`] and [`Gizmos::arrow_2d`], +//! Includes the implementation of [`GizmoBuffer::arrow`] and [`GizmoBuffer::arrow_2d`], //! and assorted support items. -use crate::prelude::{GizmoConfigGroup, Gizmos}; +use crate::{gizmos::GizmoBuffer, prelude::GizmoConfigGroup}; use bevy_color::{ palettes::basic::{BLUE, GREEN, RED}, Color, @@ -11,13 +11,13 @@ use bevy_color::{ use bevy_math::{Quat, Vec2, Vec3, Vec3Swizzles}; use bevy_transform::TransformPoint; -/// A builder returned by [`Gizmos::arrow`] and [`Gizmos::arrow_2d`] -pub struct ArrowBuilder<'a, 'w, 's, Config, Clear> +/// A builder returned by [`GizmoBuffer::arrow`] and [`GizmoBuffer::arrow_2d`] +pub struct ArrowBuilder<'a, Config, Clear> where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, { - gizmos: &'a mut Gizmos<'w, 's, Config, Clear>, + gizmos: &'a mut GizmoBuffer, start: Vec3, end: Vec3, color: Color, @@ -25,7 +25,7 @@ where tip_length: f32, } -impl ArrowBuilder<'_, '_, '_, Config, Clear> +impl ArrowBuilder<'_, Config, Clear> where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, @@ -58,12 +58,12 @@ where } } -impl Drop for ArrowBuilder<'_, '_, '_, Config, Clear> +impl Drop for ArrowBuilder<'_, Config, Clear> where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, { - /// Draws the arrow, by drawing lines with the stored [`Gizmos`] + /// Draws the arrow, by drawing lines with the stored [`GizmoBuffer`] fn drop(&mut self) { if !self.gizmos.enabled { return; @@ -101,7 +101,7 @@ where } } -impl<'w, 's, Config, Clear> Gizmos<'w, 's, Config, Clear> +impl GizmoBuffer where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, @@ -125,7 +125,7 @@ where start: Vec3, end: Vec3, color: impl Into, - ) -> ArrowBuilder<'_, 'w, 's, Config, Clear> { + ) -> ArrowBuilder<'_, Config, Clear> { let length = (end - start).length(); ArrowBuilder { gizmos: self, @@ -156,12 +156,12 @@ where start: Vec2, end: Vec2, color: impl Into, - ) -> ArrowBuilder<'_, 'w, 's, Config, Clear> { + ) -> ArrowBuilder<'_, Config, Clear> { self.arrow(start.extend(0.), end.extend(0.), color) } } -impl<'w, 's, Config, Clear> Gizmos<'w, 's, Config, Clear> +impl GizmoBuffer where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, diff --git a/crates/bevy_gizmos/src/circles.rs b/crates/bevy_gizmos/src/circles.rs index 6cd11e5381..e5a4939808 100644 --- a/crates/bevy_gizmos/src/circles.rs +++ b/crates/bevy_gizmos/src/circles.rs @@ -1,9 +1,9 @@ -//! Additional [`Gizmos`] Functions -- Circles +//! Additional [`GizmoBuffer`] Functions -- Circles //! -//! Includes the implementation of [`Gizmos::circle`] and [`Gizmos::circle_2d`], +//! Includes the implementation of [`GizmoBuffer::circle`] and [`GizmoBuffer::circle_2d`], //! and assorted support items. -use crate::prelude::{GizmoConfigGroup, Gizmos}; +use crate::{gizmos::GizmoBuffer, prelude::GizmoConfigGroup}; use bevy_color::Color; use bevy_math::{ops, Isometry2d, Isometry3d, Quat, Vec2, Vec3}; use core::f32::consts::TAU; @@ -18,7 +18,7 @@ fn ellipse_inner(half_size: Vec2, resolution: u32) -> impl Iterator }) } -impl<'w, 's, Config, Clear> Gizmos<'w, 's, Config, Clear> +impl GizmoBuffer where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, @@ -54,7 +54,7 @@ where isometry: impl Into, half_size: Vec2, color: impl Into, - ) -> EllipseBuilder<'_, 'w, 's, Config, Clear> { + ) -> EllipseBuilder<'_, Config, Clear> { EllipseBuilder { gizmos: self, isometry: isometry.into(), @@ -95,7 +95,7 @@ where isometry: impl Into, half_size: Vec2, color: impl Into, - ) -> Ellipse2dBuilder<'_, 'w, 's, Config, Clear> { + ) -> Ellipse2dBuilder<'_, Config, Clear> { Ellipse2dBuilder { gizmos: self, isometry: isometry.into(), @@ -134,7 +134,7 @@ where isometry: impl Into, radius: f32, color: impl Into, - ) -> EllipseBuilder<'_, 'w, 's, Config, Clear> { + ) -> EllipseBuilder<'_, Config, Clear> { EllipseBuilder { gizmos: self, isometry: isometry.into(), @@ -175,7 +175,7 @@ where isometry: impl Into, radius: f32, color: impl Into, - ) -> Ellipse2dBuilder<'_, 'w, 's, Config, Clear> { + ) -> Ellipse2dBuilder<'_, Config, Clear> { Ellipse2dBuilder { gizmos: self, isometry: isometry.into(), @@ -217,7 +217,7 @@ where isometry: impl Into, radius: f32, color: impl Into, - ) -> SphereBuilder<'_, 'w, 's, Config, Clear> { + ) -> SphereBuilder<'_, Config, Clear> { SphereBuilder { gizmos: self, radius, @@ -228,20 +228,20 @@ where } } -/// A builder returned by [`Gizmos::ellipse`]. -pub struct EllipseBuilder<'a, 'w, 's, Config, Clear> +/// A builder returned by [`GizmoBuffer::ellipse`]. +pub struct EllipseBuilder<'a, Config, Clear> where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, { - gizmos: &'a mut Gizmos<'w, 's, Config, Clear>, + gizmos: &'a mut GizmoBuffer, isometry: Isometry3d, half_size: Vec2, color: Color, resolution: u32, } -impl EllipseBuilder<'_, '_, '_, Config, Clear> +impl EllipseBuilder<'_, Config, Clear> where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, @@ -253,7 +253,7 @@ where } } -impl Drop for EllipseBuilder<'_, '_, '_, Config, Clear> +impl Drop for EllipseBuilder<'_, Config, Clear> where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, @@ -269,20 +269,20 @@ where } } -/// A builder returned by [`Gizmos::ellipse_2d`]. -pub struct Ellipse2dBuilder<'a, 'w, 's, Config, Clear> +/// A builder returned by [`GizmoBuffer::ellipse_2d`]. +pub struct Ellipse2dBuilder<'a, Config, Clear> where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, { - gizmos: &'a mut Gizmos<'w, 's, Config, Clear>, + gizmos: &'a mut GizmoBuffer, isometry: Isometry2d, half_size: Vec2, color: Color, resolution: u32, } -impl Ellipse2dBuilder<'_, '_, '_, Config, Clear> +impl Ellipse2dBuilder<'_, Config, Clear> where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, @@ -294,7 +294,7 @@ where } } -impl Drop for Ellipse2dBuilder<'_, '_, '_, Config, Clear> +impl Drop for Ellipse2dBuilder<'_, Config, Clear> where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, @@ -311,13 +311,13 @@ where } } -/// A builder returned by [`Gizmos::sphere`]. -pub struct SphereBuilder<'a, 'w, 's, Config, Clear> +/// A builder returned by [`GizmoBuffer::sphere`]. +pub struct SphereBuilder<'a, Config, Clear> where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, { - gizmos: &'a mut Gizmos<'w, 's, Config, Clear>, + gizmos: &'a mut GizmoBuffer, // Radius of the sphere radius: f32, @@ -330,7 +330,7 @@ where resolution: u32, } -impl SphereBuilder<'_, '_, '_, Config, Clear> +impl SphereBuilder<'_, Config, Clear> where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, @@ -342,7 +342,7 @@ where } } -impl Drop for SphereBuilder<'_, '_, '_, Config, Clear> +impl Drop for SphereBuilder<'_, Config, Clear> where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, diff --git a/crates/bevy_gizmos/src/config.rs b/crates/bevy_gizmos/src/config.rs index a9c466b342..946ca4f338 100644 --- a/crates/bevy_gizmos/src/config.rs +++ b/crates/bevy_gizmos/src/config.rs @@ -7,7 +7,7 @@ pub use bevy_gizmos_macros::GizmoConfigGroup; feature = "bevy_render", any(feature = "bevy_pbr", feature = "bevy_sprite") ))] -use {crate::LineGizmo, bevy_asset::Handle, bevy_ecs::component::Component}; +use {crate::GizmoAsset, bevy_asset::Handle, bevy_ecs::component::Component}; use bevy_ecs::{reflect::ReflectResource, system::Resource}; use bevy_reflect::{std_traits::ReflectDefault, Reflect, TypePath}; @@ -57,6 +57,11 @@ pub trait GizmoConfigGroup: Reflect + TypePath + Default {} #[derive(Default, Reflect, GizmoConfigGroup)] pub struct DefaultGizmoConfigGroup; +/// Used when the gizmo config group needs to be type-erased. +/// Also used for retained gizmos, which can't have a gizmo config group. +#[derive(Default, Reflect, GizmoConfigGroup, Debug, Clone)] +pub struct ErasedGizmoConfigGroup; + /// A [`Resource`] storing [`GizmoConfig`] and [`GizmoConfigGroup`] structs /// /// Use `app.init_gizmo_group::()` to register a custom config group. @@ -133,27 +138,15 @@ impl GizmoConfigStore { } /// A struct that stores configuration for gizmos. -#[derive(Clone, Reflect)] +#[derive(Clone, Reflect, Debug)] pub struct GizmoConfig { /// Set to `false` to stop drawing gizmos. /// /// Defaults to `true`. pub enabled: bool, - /// Line width specified in pixels. - /// - /// If `line_perspective` is `true` then this is the size in pixels at the camera's near plane. - /// - /// Defaults to `2.0`. - pub line_width: f32, - /// Apply perspective to gizmo lines. - /// - /// This setting only affects 3D, non-orthographic cameras. - /// - /// Defaults to `false`. - pub line_perspective: bool, - /// Determine the style of gizmo lines. - pub line_style: GizmoLineStyle, - /// How closer to the camera than real geometry the line should be. + /// Line settings. + pub line: GizmoLineConfig, + /// How closer to the camera than real geometry the gizmos should be. /// /// In 2D this setting has no effect and is effectively always -1. /// @@ -171,23 +164,48 @@ pub struct GizmoConfig { /// Gizmos will only be rendered to cameras with intersecting layers. #[cfg(feature = "bevy_render")] pub render_layers: bevy_render::view::RenderLayers, - - /// Describe how lines should join - pub line_joints: GizmoLineJoint, } impl Default for GizmoConfig { fn default() -> Self { Self { enabled: true, - line_width: 2., - line_perspective: false, - line_style: GizmoLineStyle::Solid, + line: Default::default(), depth_bias: 0., #[cfg(feature = "bevy_render")] render_layers: Default::default(), + } + } +} - line_joints: GizmoLineJoint::None, +/// A struct that stores configuration for gizmos. +#[derive(Clone, Reflect, Debug)] +pub struct GizmoLineConfig { + /// Line width specified in pixels. + /// + /// If `perspective` is `true` then this is the size in pixels at the camera's near plane. + /// + /// Defaults to `2.0`. + pub width: f32, + /// Apply perspective to gizmo lines. + /// + /// This setting only affects 3D, non-orthographic cameras. + /// + /// Defaults to `false`. + pub perspective: bool, + /// Determine the style of gizmo lines. + pub style: GizmoLineStyle, + /// Describe how lines should join. + pub joints: GizmoLineJoint, +} + +impl Default for GizmoLineConfig { + fn default() -> Self { + Self { + width: 2., + perspective: false, + style: GizmoLineStyle::Solid, + joints: GizmoLineJoint::None, } } } @@ -200,6 +218,7 @@ impl Default for GizmoConfig { pub(crate) struct GizmoMeshConfig { pub line_perspective: bool, pub line_style: GizmoLineStyle, + pub line_joints: GizmoLineJoint, pub render_layers: bevy_render::view::RenderLayers, - pub handle: Handle, + pub handle: Handle, } diff --git a/crates/bevy_gizmos/src/cross.rs b/crates/bevy_gizmos/src/cross.rs index d08e2ddb8d..7c3eff77f3 100644 --- a/crates/bevy_gizmos/src/cross.rs +++ b/crates/bevy_gizmos/src/cross.rs @@ -1,15 +1,16 @@ -//! Additional [`Gizmos`] Functions -- Crosses +//! Additional [`GizmoBuffer`] Functions -- Crosses //! -//! Includes the implementation of [`Gizmos::cross`] and [`Gizmos::cross_2d`], +//! Includes the implementation of [`GizmoBuffer::cross`] and [`GizmoBuffer::cross_2d`], //! and assorted support items. -use crate::prelude::{GizmoConfigGroup, Gizmos}; +use crate::{gizmos::GizmoBuffer, prelude::GizmoConfigGroup}; use bevy_color::Color; use bevy_math::{Isometry2d, Isometry3d, Vec2, Vec3}; -impl Gizmos<'_, '_, Config> +impl GizmoBuffer where Config: GizmoConfigGroup, + Clear: 'static + Send + Sync, { /// Draw a cross in 3D with the given `isometry` applied. /// diff --git a/crates/bevy_gizmos/src/curves.rs b/crates/bevy_gizmos/src/curves.rs index be747626de..522bf8ebd5 100644 --- a/crates/bevy_gizmos/src/curves.rs +++ b/crates/bevy_gizmos/src/curves.rs @@ -1,14 +1,14 @@ -//! Additional [`Gizmos`] Functions -- Curves +//! Additional [`GizmoBuffer`] Functions -- Curves //! -//! Includes the implementation of [`Gizmos::curve_2d`], -//! [`Gizmos::curve_3d`] and assorted support items. +//! Includes the implementation of [`GizmoBuffer::curve_2d`], +//! [`GizmoBuffer::curve_3d`] and assorted support items. use bevy_color::Color; use bevy_math::{curve::Curve, Vec2, Vec3}; -use crate::prelude::{GizmoConfigGroup, Gizmos}; +use crate::{gizmos::GizmoBuffer, prelude::GizmoConfigGroup}; -impl<'w, 's, Config, Clear> Gizmos<'w, 's, Config, Clear> +impl GizmoBuffer where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, diff --git a/crates/bevy_gizmos/src/gizmos.rs b/crates/bevy_gizmos/src/gizmos.rs index 508cd5519c..3580b41b61 100644 --- a/crates/bevy_gizmos/src/gizmos.rs +++ b/crates/bevy_gizmos/src/gizmos.rs @@ -1,6 +1,11 @@ //! A module for the [`Gizmos`] [`SystemParam`]. -use core::{iter, marker::PhantomData, mem}; +use core::{ + iter, + marker::PhantomData, + mem, + ops::{Deref, DerefMut}, +}; use bevy_color::{Color, LinearRgba}; use bevy_ecs::{ @@ -9,6 +14,7 @@ use bevy_ecs::{ world::{unsafe_world_cell::UnsafeWorldCell, World}, }; use bevy_math::{Isometry2d, Isometry3d, Vec2, Vec3}; +use bevy_reflect::Reflect; use bevy_transform::TransformPoint; use bevy_utils::default; @@ -135,13 +141,34 @@ where Clear: 'static + Send + Sync, { buffer: Deferred<'s, GizmoBuffer>, - pub(crate) enabled: bool, /// The currently used [`GizmoConfig`] pub config: &'w GizmoConfig, /// The currently used [`GizmoConfigGroup`] pub config_ext: &'w Config, } +impl<'w, 's, Config, Clear> Deref for Gizmos<'w, 's, Config, Clear> +where + Config: GizmoConfigGroup, + Clear: 'static + Send + Sync, +{ + type Target = GizmoBuffer; + + fn deref(&self) -> &Self::Target { + &self.buffer + } +} + +impl<'w, 's, Config, Clear> DerefMut for Gizmos<'w, 's, Config, Clear> +where + Config: GizmoConfigGroup, + Clear: 'static + Send + Sync, +{ + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.buffer + } +} + type GizmosState = ( Deferred<'static, GizmoBuffer>, Res<'static, GizmoConfigStore>, @@ -204,7 +231,7 @@ where change_tick: Tick, ) -> Self::Item<'w, 's> { // SAFETY: Delegated to existing `SystemParam` implementations. - let (f0, f1) = unsafe { + let (mut f0, f1) = unsafe { GizmosState::::get_param( &mut state.state, system_meta, @@ -212,13 +239,15 @@ where change_tick, ) }; - // Accessing the GizmoConfigStore in the immediate mode API reduces performance significantly. - // Implementing SystemParam manually allows us to do it to here - // Having config available allows for early returns when gizmos are disabled + + // Accessing the GizmoConfigStore in every API call reduces performance significantly. + // Implementing SystemParam manually allows us to cache whether the config is currently enabled. + // Having this available allows for cheap early returns when gizmos are disabled. let (config, config_ext) = f1.into_inner().config::(); + f0.enabled = config.enabled; + Gizmos { buffer: f0, - enabled: config.enabled, config, config_ext, } @@ -236,16 +265,20 @@ where { } -struct GizmoBuffer +/// Buffer for gizmo vertex data. +#[derive(Debug, Clone, Reflect)] +pub struct GizmoBuffer where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, { - list_positions: Vec, - list_colors: Vec, - strip_positions: Vec, - strip_colors: Vec, - marker: PhantomData<(Config, Clear)>, + pub(crate) enabled: bool, + pub(crate) list_positions: Vec, + pub(crate) list_colors: Vec, + pub(crate) strip_positions: Vec, + pub(crate) strip_colors: Vec, + #[reflect(ignore)] + pub(crate) marker: PhantomData<(Config, Clear)>, } impl Default for GizmoBuffer @@ -254,16 +287,29 @@ where Clear: 'static + Send + Sync, { fn default() -> Self { - Self { - list_positions: default(), - list_colors: default(), - strip_positions: default(), - strip_colors: default(), + GizmoBuffer { + enabled: true, + list_positions: Vec::new(), + list_colors: Vec::new(), + strip_positions: Vec::new(), + strip_colors: Vec::new(), marker: PhantomData, } } } +/// Read-only view into [`GizmoBuffer`] data. +pub struct GizmoBufferView<'a> { + /// Vertex positions for line-list topology. + pub list_positions: &'a Vec, + /// Vertex colors for line-list topology. + pub list_colors: &'a Vec, + /// Vertex positions for line-strip topology. + pub strip_positions: &'a Vec, + /// Vertex colors for line-strip topology. + pub strip_colors: &'a Vec, +} + impl SystemBuffer for GizmoBuffer where Config: GizmoConfigGroup, @@ -278,11 +324,35 @@ where } } -impl<'w, 's, Config, Clear> Gizmos<'w, 's, Config, Clear> +impl GizmoBuffer where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, { + /// Clear all data. + pub fn clear(&mut self) { + self.list_positions.clear(); + self.list_colors.clear(); + self.strip_positions.clear(); + self.strip_colors.clear(); + } + + /// Read-only view into the buffers data. + pub fn buffer(&self) -> GizmoBufferView { + let GizmoBuffer { + list_positions, + list_colors, + strip_positions, + strip_colors, + .. + } = self; + GizmoBufferView { + list_positions, + list_colors, + strip_positions, + strip_colors, + } + } /// Draw a line in 3D from `start` to `end`. /// /// This should be called for each frame the line needs to be rendered. @@ -409,10 +479,10 @@ where return; } self.extend_strip_positions(positions); - let len = self.buffer.strip_positions.len(); + let len = self.strip_positions.len(); let linear_color = LinearRgba::from(color.into()); - self.buffer.strip_colors.resize(len - 1, linear_color); - self.buffer.strip_colors.push(LinearRgba::NAN); + self.strip_colors.resize(len - 1, linear_color); + self.strip_colors.push(LinearRgba::NAN); } /// Draw a line in 3D made of straight segments between the points, with a color gradient. @@ -447,7 +517,7 @@ where strip_positions, strip_colors, .. - } = &mut *self.buffer; + } = self; let (min, _) = points.size_hint(); strip_positions.reserve(min); @@ -726,12 +796,12 @@ where #[inline] fn extend_list_positions(&mut self, positions: impl IntoIterator) { - self.buffer.list_positions.extend(positions); + self.list_positions.extend(positions); } #[inline] fn extend_list_colors(&mut self, colors: impl IntoIterator>) { - self.buffer.list_colors.extend( + self.list_colors.extend( colors .into_iter() .map(|color| LinearRgba::from(color.into())), @@ -743,15 +813,14 @@ where let polymorphic_color: Color = color.into(); let linear_color = LinearRgba::from(polymorphic_color); - self.buffer - .list_colors + self.list_colors .extend(iter::repeat(linear_color).take(count)); } #[inline] fn extend_strip_positions(&mut self, positions: impl IntoIterator) { - self.buffer.strip_positions.extend(positions); - self.buffer.strip_positions.push(Vec3::NAN); + self.strip_positions.extend(positions); + self.strip_positions.push(Vec3::NAN); } } diff --git a/crates/bevy_gizmos/src/grid.rs b/crates/bevy_gizmos/src/grid.rs index 20cb4e115b..03ee5c6654 100644 --- a/crates/bevy_gizmos/src/grid.rs +++ b/crates/bevy_gizmos/src/grid.rs @@ -1,19 +1,19 @@ -//! Additional [`Gizmos`] Functions -- Grids +//! Additional [`GizmoBuffer`] Functions -- Grids //! -//! Includes the implementation of [`Gizmos::grid`] and [`Gizmos::grid_2d`]. +//! Includes the implementation of [`GizmoBuffer::grid`] and [`GizmoBuffer::grid_2d`]. //! and assorted support items. -use crate::prelude::{GizmoConfigGroup, Gizmos}; +use crate::{gizmos::GizmoBuffer, prelude::GizmoConfigGroup}; use bevy_color::Color; use bevy_math::{ops, Isometry2d, Isometry3d, Quat, UVec2, UVec3, Vec2, Vec3, Vec3Swizzles}; -/// A builder returned by [`Gizmos::grid_3d`] -pub struct GridBuilder3d<'a, 'w, 's, Config, Clear> +/// A builder returned by [`GizmoBuffer::grid_3d`] +pub struct GridBuilder3d<'a, Config, Clear> where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, { - gizmos: &'a mut Gizmos<'w, 's, Config, Clear>, + gizmos: &'a mut GizmoBuffer, isometry: Isometry3d, spacing: Vec3, cell_count: UVec3, @@ -21,13 +21,13 @@ where outer_edges: [bool; 3], color: Color, } -/// A builder returned by [`Gizmos::grid`] and [`Gizmos::grid_2d`] -pub struct GridBuilder2d<'a, 'w, 's, Config, Clear> +/// A builder returned by [`GizmoBuffer::grid`] and [`GizmoBuffer::grid_2d`] +pub struct GridBuilder2d<'a, Config, Clear> where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, { - gizmos: &'a mut Gizmos<'w, 's, Config, Clear>, + gizmos: &'a mut GizmoBuffer, isometry: Isometry3d, spacing: Vec2, cell_count: UVec2, @@ -36,7 +36,7 @@ where color: Color, } -impl GridBuilder3d<'_, '_, '_, Config, Clear> +impl GridBuilder3d<'_, Config, Clear> where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, @@ -92,7 +92,7 @@ where } } -impl GridBuilder2d<'_, '_, '_, Config, Clear> +impl GridBuilder2d<'_, Config, Clear> where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, @@ -136,12 +136,12 @@ where } } -impl Drop for GridBuilder3d<'_, '_, '_, Config, Clear> +impl Drop for GridBuilder3d<'_, Config, Clear> where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, { - /// Draws a grid, by drawing lines with the stored [`Gizmos`] + /// Draws a grid, by drawing lines with the stored [`GizmoBuffer`] fn drop(&mut self) { draw_grid( self.gizmos, @@ -155,7 +155,7 @@ where } } -impl Drop for GridBuilder2d<'_, '_, '_, Config, Clear> +impl Drop for GridBuilder2d<'_, Config, Clear> where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, @@ -172,7 +172,7 @@ where ); } } -impl<'w, 's, Config, Clear> Gizmos<'w, 's, Config, Clear> +impl GizmoBuffer where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, @@ -222,7 +222,7 @@ where cell_count: UVec2, spacing: Vec2, color: impl Into, - ) -> GridBuilder2d<'_, 'w, 's, Config, Clear> { + ) -> GridBuilder2d<'_, Config, Clear> { GridBuilder2d { gizmos: self, isometry: isometry.into(), @@ -276,7 +276,7 @@ where cell_count: UVec3, spacing: Vec3, color: impl Into, - ) -> GridBuilder3d<'_, 'w, 's, Config, Clear> { + ) -> GridBuilder3d<'_, Config, Clear> { GridBuilder3d { gizmos: self, isometry: isometry.into(), @@ -330,7 +330,7 @@ where cell_count: UVec2, spacing: Vec2, color: impl Into, - ) -> GridBuilder2d<'_, 'w, 's, Config, Clear> { + ) -> GridBuilder2d<'_, Config, Clear> { let isometry = isometry.into(); GridBuilder2d { gizmos: self, @@ -349,7 +349,7 @@ where #[allow(clippy::too_many_arguments)] fn draw_grid( - gizmos: &mut Gizmos<'_, '_, Config, Clear>, + gizmos: &mut GizmoBuffer, isometry: Isometry3d, spacing: Vec3, cell_count: UVec3, diff --git a/crates/bevy_gizmos/src/lib.rs b/crates/bevy_gizmos/src/lib.rs index e83a6a080c..a3e34e9ca7 100644 --- a/crates/bevy_gizmos/src/lib.rs +++ b/crates/bevy_gizmos/src/lib.rs @@ -41,6 +41,7 @@ pub mod curves; pub mod gizmos; pub mod grid; pub mod primitives; +pub mod retained; pub mod rounded_box; #[cfg(all(feature = "bevy_pbr", feature = "bevy_render"))] @@ -62,11 +63,12 @@ pub mod prelude { pub use crate::{ config::{ DefaultGizmoConfigGroup, GizmoConfig, GizmoConfigGroup, GizmoConfigStore, - GizmoLineJoint, GizmoLineStyle, + GizmoLineConfig, GizmoLineJoint, GizmoLineStyle, }, gizmos::Gizmos, primitives::{dim2::GizmoPrimitive2d, dim3::GizmoPrimitive3d}, - AppGizmoBuilder, + retained::Gizmo, + AppGizmoBuilder, GizmoAsset, }; #[cfg(all(feature = "bevy_pbr", feature = "bevy_render"))] @@ -75,12 +77,11 @@ pub mod prelude { use bevy_app::{App, FixedFirst, FixedLast, Last, Plugin, RunFixedMainLoop}; use bevy_asset::{Asset, AssetApp, AssetId, Assets, Handle}; -use bevy_color::LinearRgba; use bevy_ecs::{ schedule::{IntoSystemConfigs, SystemSet}, system::{Res, ResMut, Resource}, }; -use bevy_math::Vec3; +use bevy_math::Vec4; use bevy_reflect::TypePath; #[cfg(all( @@ -89,8 +90,11 @@ use bevy_reflect::TypePath; ))] use crate::config::GizmoMeshConfig; +use crate::{config::ErasedGizmoConfigGroup, gizmos::GizmoBuffer}; + #[cfg(feature = "bevy_render")] use { + crate::retained::extract_linegizmos, bevy_ecs::{ component::Component, entity::Entity, @@ -100,6 +104,7 @@ use { Commands, SystemParamItem, }, }, + bevy_math::{Affine3, Affine3A}, bevy_render::{ extract_component::{ComponentUniforms, DynamicUniformIndex, UniformComponentPlugin}, render_asset::{PrepareAssetError, RenderAsset, RenderAssetPlugin, RenderAssets}, @@ -126,7 +131,7 @@ use bevy_utils::TypeIdMap; use config::{ DefaultGizmoConfigGroup, GizmoConfig, GizmoConfigGroup, GizmoConfigStore, GizmoLineJoint, }; -use core::{any::TypeId, mem}; +use core::{any::TypeId, marker::PhantomData, mem}; use gizmos::{GizmoStorage, Swap}; #[cfg(all(feature = "bevy_pbr", feature = "bevy_render"))] use light::LightGizmoPlugin; @@ -158,8 +163,8 @@ impl Plugin for GizmoPlugin { app.register_type::() .register_type::() - .init_asset::() - .init_resource::() + .init_asset::() + .init_resource::() // We insert the Resource GizmoConfigStore into the world implicitly here if it does not exist. .init_gizmo_group::(); @@ -178,7 +183,7 @@ impl Plugin for GizmoPlugin { prepare_line_gizmo_bind_group.in_set(RenderSet::PrepareBindGroups), ); - render_app.add_systems(ExtractSchedule, extract_gizmo_data); + render_app.add_systems(ExtractSchedule, (extract_gizmo_data, extract_linegizmos)); #[cfg(feature = "bevy_sprite")] if app.is_plugin_added::() { @@ -245,13 +250,12 @@ impl AppGizmoBuilder for App { .get_resource_or_init::() .register::(); - let mut handles = self.world_mut().get_resource_or_init::(); + let mut handles = self.world_mut().get_resource_or_init::(); - handles.list.insert(TypeId::of::(), None); - handles.strip.insert(TypeId::of::(), None); + handles.handles.insert(TypeId::of::(), None); // These handles are safe to mutate in any order - self.allow_ambiguous_resource::(); + self.allow_ambiguous_resource::(); self.init_resource::>() .init_resource::>() @@ -300,9 +304,8 @@ impl AppGizmoBuilder for App { // That way iteration order is stable across executions and depends on the order of configuration // group creation. #[derive(Resource, Default)] -struct LineGizmoHandles { - list: TypeIdMap>>, - strip: TypeIdMap>>, +struct GizmoHandles { + handles: TypeIdMap>>, } /// Start a new gizmo clearing context. @@ -378,52 +381,34 @@ pub struct UpdateGizmoMeshes; /// /// This also clears the default `GizmoStorage`. fn update_gizmo_meshes( - mut line_gizmos: ResMut>, - mut handles: ResMut, + mut gizmo_assets: ResMut>, + mut handles: ResMut, mut storage: ResMut>, - config_store: Res, ) { - if storage.list_positions.is_empty() { - handles.list.insert(TypeId::of::(), None); - } else if let Some(handle) = handles.list.get_mut(&TypeId::of::()) { + if storage.list_positions.is_empty() && storage.strip_positions.is_empty() { + handles.handles.insert(TypeId::of::(), None); + } else if let Some(handle) = handles.handles.get_mut(&TypeId::of::()) { if let Some(handle) = handle { - let list = line_gizmos.get_mut(handle.id()).unwrap(); + let gizmo = gizmo_assets.get_mut(handle.id()).unwrap(); - list.positions = mem::take(&mut storage.list_positions); - list.colors = mem::take(&mut storage.list_colors); + gizmo.buffer.list_positions = mem::take(&mut storage.list_positions); + gizmo.buffer.list_colors = mem::take(&mut storage.list_colors); + gizmo.buffer.strip_positions = mem::take(&mut storage.strip_positions); + gizmo.buffer.strip_colors = mem::take(&mut storage.strip_colors); } else { - let list = LineGizmo { - strip: false, + let gizmo = GizmoAsset { config_ty: TypeId::of::(), - positions: mem::take(&mut storage.list_positions), - colors: mem::take(&mut storage.list_colors), - joints: GizmoLineJoint::None, + buffer: GizmoBuffer { + enabled: true, + list_positions: mem::take(&mut storage.list_positions), + list_colors: mem::take(&mut storage.list_colors), + strip_positions: mem::take(&mut storage.strip_positions), + strip_colors: mem::take(&mut storage.strip_colors), + marker: PhantomData, + }, }; - *handle = Some(line_gizmos.add(list)); - } - } - - let (config, _) = config_store.config::(); - if storage.strip_positions.is_empty() { - handles.strip.insert(TypeId::of::(), None); - } else if let Some(handle) = handles.strip.get_mut(&TypeId::of::()) { - if let Some(handle) = handle { - let strip = line_gizmos.get_mut(handle.id()).unwrap(); - - strip.positions = mem::take(&mut storage.strip_positions); - strip.colors = mem::take(&mut storage.strip_colors); - strip.joints = config.line_joints; - } else { - let strip = LineGizmo { - strip: true, - joints: config.line_joints, - config_ty: TypeId::of::(), - positions: mem::take(&mut storage.strip_positions), - colors: mem::take(&mut storage.strip_colors), - }; - - *handle = Some(line_gizmos.add(strip)); + *handle = Some(gizmo_assets.add(gizmo)); } } } @@ -431,10 +416,10 @@ fn update_gizmo_meshes( #[cfg(feature = "bevy_render")] fn extract_gizmo_data( mut commands: Commands, - handles: Extract>, + handles: Extract>, config: Extract>, ) { - for (group_type_id, handle) in handles.list.iter().chain(handles.strip.iter()) { + for (group_type_id, handle) in &handles.handles { let Some((config, _)) = config.get_config_dyn(group_type_id) else { continue; }; @@ -447,7 +432,7 @@ fn extract_gizmo_data( continue; }; - let joints_resolution = if let GizmoLineJoint::Round(resolution) = config.line_joints { + let joints_resolution = if let GizmoLineJoint::Round(resolution) = config.line.joints { resolution } else { 0 @@ -455,7 +440,8 @@ fn extract_gizmo_data( commands.spawn(( LineGizmoUniform { - line_width: config.line_width, + world_from_local: Affine3::from(&Affine3A::IDENTITY).to_transpose(), + line_width: config.line.width, depth_bias: config.depth_bias, joints_resolution, #[cfg(feature = "webgl")] @@ -463,8 +449,9 @@ fn extract_gizmo_data( }, #[cfg(any(feature = "bevy_pbr", feature = "bevy_sprite"))] GizmoMeshConfig { - line_perspective: config.line_perspective, - line_style: config.line_style, + line_perspective: config.line.perspective, + line_style: config.line.style, + line_joints: config.line.joints, render_layers: config.render_layers.clone(), handle: handle.clone(), }, @@ -479,6 +466,7 @@ fn extract_gizmo_data( #[cfg(feature = "bevy_render")] #[derive(Component, ShaderType, Clone, Copy)] struct LineGizmoUniform { + world_from_local: [Vec4; 3], line_width: f32, depth_bias: f32, // Only used by gizmo line t if the current configs `line_joints` is set to `GizmoLineJoint::Round(_)` @@ -488,34 +476,51 @@ struct LineGizmoUniform { _padding: f32, } -/// A gizmo asset that represents a line. +/// A collection of gizmos. +/// +/// Has the same gizmo drawing API as [`Gizmos`](crate::gizmos::Gizmos). #[derive(Asset, Debug, Clone, TypePath)] -pub struct LineGizmo { - /// Positions of the gizmo's vertices - pub positions: Vec, - /// Colors of the gizmo's vertices - pub colors: Vec, - /// Whether this gizmo's topology is a line-strip or line-list - pub strip: bool, - /// Whether this gizmo should draw line joints. This is only applicable if the gizmo's topology is line-strip. - pub joints: GizmoLineJoint, - /// The type of the gizmo's configuration group - pub config_ty: TypeId, +pub struct GizmoAsset { + /// vertex buffers. + buffer: GizmoBuffer, + config_ty: TypeId, +} + +impl GizmoAsset { + /// Create a new [`GizmoAsset`]. + pub fn new() -> Self { + GizmoAsset { + buffer: GizmoBuffer::default(), + config_ty: TypeId::of::(), + } + } + + /// The type of the gizmo's configuration group. + pub fn config_typeid(&self) -> TypeId { + self.config_ty + } +} + +impl Default for GizmoAsset { + fn default() -> Self { + GizmoAsset::new() + } } #[cfg(feature = "bevy_render")] #[derive(Debug, Clone)] struct GpuLineGizmo { - position_buffer: Buffer, - color_buffer: Buffer, - vertex_count: u32, - strip: bool, - joints: GizmoLineJoint, + list_position_buffer: Buffer, + list_color_buffer: Buffer, + list_vertex_count: u32, + strip_position_buffer: Buffer, + strip_color_buffer: Buffer, + strip_vertex_count: u32, } #[cfg(feature = "bevy_render")] impl RenderAsset for GpuLineGizmo { - type SourceAsset = LineGizmo; + type SourceAsset = GizmoAsset; type Param = SRes; fn prepare_asset( @@ -523,26 +528,37 @@ impl RenderAsset for GpuLineGizmo { _: AssetId, render_device: &mut SystemParamItem, ) -> Result> { - let position_buffer_data = cast_slice(&gizmo.positions); - let position_buffer = render_device.create_buffer_with_data(&BufferInitDescriptor { + let list_position_buffer = render_device.create_buffer_with_data(&BufferInitDescriptor { usage: BufferUsages::VERTEX, label: Some("LineGizmo Position Buffer"), - contents: position_buffer_data, + contents: cast_slice(&gizmo.buffer.list_positions), }); - let color_buffer_data = cast_slice(&gizmo.colors); - let color_buffer = render_device.create_buffer_with_data(&BufferInitDescriptor { + let list_color_buffer = render_device.create_buffer_with_data(&BufferInitDescriptor { usage: BufferUsages::VERTEX, label: Some("LineGizmo Color Buffer"), - contents: color_buffer_data, + contents: cast_slice(&gizmo.buffer.list_colors), + }); + + let strip_position_buffer = render_device.create_buffer_with_data(&BufferInitDescriptor { + usage: BufferUsages::VERTEX, + label: Some("LineGizmo Strip Position Buffer"), + contents: cast_slice(&gizmo.buffer.strip_positions), + }); + + let strip_color_buffer = render_device.create_buffer_with_data(&BufferInitDescriptor { + usage: BufferUsages::VERTEX, + label: Some("LineGizmo Strip Color Buffer"), + contents: cast_slice(&gizmo.buffer.strip_colors), }); Ok(GpuLineGizmo { - position_buffer, - color_buffer, - vertex_count: gizmo.positions.len() as u32, - strip: gizmo.strip, - joints: gizmo.joints, + list_position_buffer, + list_color_buffer, + list_vertex_count: gizmo.buffer.list_positions.len() as u32, + strip_position_buffer, + strip_color_buffer, + strip_vertex_count: gizmo.buffer.strip_positions.len() as u32, }) } } @@ -606,12 +622,12 @@ impl RenderCommand

for SetLineGizmoBindGroup } #[cfg(feature = "bevy_render")] -struct DrawLineGizmo; +struct DrawLineGizmo; #[cfg(all( feature = "bevy_render", any(feature = "bevy_pbr", feature = "bevy_sprite") ))] -impl RenderCommand

for DrawLineGizmo { +impl RenderCommand

for DrawLineGizmo { type Param = SRes>; type ViewQuery = (); type ItemQuery = Read; @@ -631,28 +647,35 @@ impl RenderCommand

for DrawLineGizmo { return RenderCommandResult::Skip; }; - if line_gizmo.vertex_count < 2 { + let vertex_count = if STRIP { + line_gizmo.strip_vertex_count + } else { + line_gizmo.list_vertex_count + }; + + if vertex_count < 2 { return RenderCommandResult::Success; } - let instances = if line_gizmo.strip { + let instances = if STRIP { let item_size = VertexFormat::Float32x3.size(); - let buffer_size = line_gizmo.position_buffer.size() - item_size; + let buffer_size = line_gizmo.strip_position_buffer.size() - item_size; - pass.set_vertex_buffer(0, line_gizmo.position_buffer.slice(..buffer_size)); - pass.set_vertex_buffer(1, line_gizmo.position_buffer.slice(item_size..)); + pass.set_vertex_buffer(0, line_gizmo.strip_position_buffer.slice(..buffer_size)); + pass.set_vertex_buffer(1, line_gizmo.strip_position_buffer.slice(item_size..)); let item_size = VertexFormat::Float32x4.size(); - let buffer_size = line_gizmo.color_buffer.size() - item_size; - pass.set_vertex_buffer(2, line_gizmo.color_buffer.slice(..buffer_size)); - pass.set_vertex_buffer(3, line_gizmo.color_buffer.slice(item_size..)); + let buffer_size = line_gizmo.strip_color_buffer.size() - item_size; - u32::max(line_gizmo.vertex_count, 1) - 1 + pass.set_vertex_buffer(2, line_gizmo.strip_color_buffer.slice(..buffer_size)); + pass.set_vertex_buffer(3, line_gizmo.strip_color_buffer.slice(item_size..)); + + vertex_count - 1 } else { - pass.set_vertex_buffer(0, line_gizmo.position_buffer.slice(..)); - pass.set_vertex_buffer(1, line_gizmo.color_buffer.slice(..)); + pass.set_vertex_buffer(0, line_gizmo.list_position_buffer.slice(..)); + pass.set_vertex_buffer(1, line_gizmo.list_color_buffer.slice(..)); - line_gizmo.vertex_count / 2 + vertex_count / 2 }; pass.draw(0..6, 0..instances); @@ -687,38 +710,43 @@ impl RenderCommand

for DrawLineJointGizmo { return RenderCommandResult::Skip; }; - if line_gizmo.vertex_count <= 2 || !line_gizmo.strip { + if line_gizmo.strip_vertex_count <= 2 { return RenderCommandResult::Success; }; - if line_gizmo.joints == GizmoLineJoint::None { + if config.line_joints == GizmoLineJoint::None { return RenderCommandResult::Success; }; let instances = { let item_size = VertexFormat::Float32x3.size(); // position_a - let buffer_size_a = line_gizmo.position_buffer.size() - item_size * 2; - pass.set_vertex_buffer(0, line_gizmo.position_buffer.slice(..buffer_size_a)); + let buffer_size_a = line_gizmo.strip_position_buffer.size() - item_size * 2; + pass.set_vertex_buffer(0, line_gizmo.strip_position_buffer.slice(..buffer_size_a)); // position_b - let buffer_size_b = line_gizmo.position_buffer.size() - item_size; + let buffer_size_b = line_gizmo.strip_position_buffer.size() - item_size; pass.set_vertex_buffer( 1, - line_gizmo.position_buffer.slice(item_size..buffer_size_b), + line_gizmo + .strip_position_buffer + .slice(item_size..buffer_size_b), ); // position_c - pass.set_vertex_buffer(2, line_gizmo.position_buffer.slice(item_size * 2..)); + pass.set_vertex_buffer(2, line_gizmo.strip_position_buffer.slice(item_size * 2..)); // color let item_size = VertexFormat::Float32x4.size(); - let buffer_size = line_gizmo.color_buffer.size() - item_size; + let buffer_size = line_gizmo.strip_color_buffer.size() - item_size; // This corresponds to the color of position_b, hence starts from `item_size` - pass.set_vertex_buffer(3, line_gizmo.color_buffer.slice(item_size..buffer_size)); + pass.set_vertex_buffer( + 3, + line_gizmo.strip_color_buffer.slice(item_size..buffer_size), + ); - u32::max(line_gizmo.vertex_count, 2) - 2 + line_gizmo.strip_vertex_count - 2 }; - let vertices = match line_gizmo.joints { + let vertices = match config.line_joints { GizmoLineJoint::None => unreachable!(), GizmoLineJoint::Miter => 6, GizmoLineJoint::Round(resolution) => resolution * 3, diff --git a/crates/bevy_gizmos/src/line_joints.wgsl b/crates/bevy_gizmos/src/line_joints.wgsl index 8224429225..cbbf152c02 100644 --- a/crates/bevy_gizmos/src/line_joints.wgsl +++ b/crates/bevy_gizmos/src/line_joints.wgsl @@ -1,9 +1,10 @@ -#import bevy_render::view::View +#import bevy_render::{view::View, maths::affine3_to_square} @group(0) @binding(0) var view: View; struct LineGizmoUniform { + world_from_local: mat3x4, line_width: f32, depth_bias: f32, resolution: u32, @@ -39,9 +40,11 @@ fn vertex_bevel(vertex: VertexInput) -> VertexOutput { ); var position = positions[vertex.index]; - var clip_a = view.clip_from_world * vec4(vertex.position_a, 1.); - var clip_b = view.clip_from_world * vec4(vertex.position_b, 1.); - var clip_c = view.clip_from_world * vec4(vertex.position_c, 1.); + let world_from_local = affine3_to_square(joints_gizmo.world_from_local); + + var clip_a = view.clip_from_world * world_from_local * vec4(vertex.position_a, 1.); + var clip_b = view.clip_from_world * world_from_local * vec4(vertex.position_b, 1.); + var clip_c = view.clip_from_world * world_from_local * vec4(vertex.position_c, 1.); // Manual near plane clipping to avoid errors when doing the perspective divide inside this shader. clip_a = clip_near_plane(clip_a, clip_c); @@ -98,9 +101,11 @@ fn vertex_miter(vertex: VertexInput) -> VertexOutput { ); var position = positions[vertex.index]; - var clip_a = view.clip_from_world * vec4(vertex.position_a, 1.); - var clip_b = view.clip_from_world * vec4(vertex.position_b, 1.); - var clip_c = view.clip_from_world * vec4(vertex.position_c, 1.); + let world_from_local = affine3_to_square(joints_gizmo.world_from_local); + + var clip_a = view.clip_from_world * world_from_local * vec4(vertex.position_a, 1.); + var clip_b = view.clip_from_world * world_from_local * vec4(vertex.position_b, 1.); + var clip_c = view.clip_from_world * world_from_local * vec4(vertex.position_c, 1.); // Manual near plane clipping to avoid errors when doing the perspective divide inside this shader. clip_a = clip_near_plane(clip_a, clip_c); @@ -148,9 +153,11 @@ fn vertex_miter(vertex: VertexInput) -> VertexOutput { @vertex fn vertex_round(vertex: VertexInput) -> VertexOutput { - var clip_a = view.clip_from_world * vec4(vertex.position_a, 1.); - var clip_b = view.clip_from_world * vec4(vertex.position_b, 1.); - var clip_c = view.clip_from_world * vec4(vertex.position_c, 1.); + let world_from_local = affine3_to_square(joints_gizmo.world_from_local); + + var clip_a = view.clip_from_world * world_from_local * vec4(vertex.position_a, 1.); + var clip_b = view.clip_from_world * world_from_local * vec4(vertex.position_b, 1.); + var clip_c = view.clip_from_world * world_from_local * vec4(vertex.position_c, 1.); // Manual near plane clipping to avoid errors when doing the perspective divide inside this shader. clip_a = clip_near_plane(clip_a, clip_c); diff --git a/crates/bevy_gizmos/src/lines.wgsl b/crates/bevy_gizmos/src/lines.wgsl index e608b4beab..67d34e8a46 100644 --- a/crates/bevy_gizmos/src/lines.wgsl +++ b/crates/bevy_gizmos/src/lines.wgsl @@ -1,10 +1,11 @@ // TODO use common view binding -#import bevy_render::view::View +#import bevy_render::{view::View, maths::affine3_to_square} @group(0) @binding(0) var view: View; struct LineGizmoUniform { + world_from_local: mat3x4, line_width: f32, depth_bias: f32, #ifdef SIXTEEN_BYTE_ALIGNMENT @@ -43,9 +44,11 @@ fn vertex(vertex: VertexInput) -> VertexOutput { ); let position = positions[vertex.index]; + let world_from_local = affine3_to_square(line_gizmo.world_from_local); + // algorithm based on https://wwwtyro.net/2019/11/18/instanced-lines.html - var clip_a = view.clip_from_world * vec4(vertex.position_a, 1.); - var clip_b = view.clip_from_world * vec4(vertex.position_b, 1.); + var clip_a = view.clip_from_world * world_from_local * vec4(vertex.position_a, 1.); + var clip_b = view.clip_from_world * world_from_local * vec4(vertex.position_b, 1.); // Manual near plane clipping to avoid errors when doing the perspective divide inside this shader. clip_a = clip_near_plane(clip_a, clip_b); diff --git a/crates/bevy_gizmos/src/pipeline_2d.rs b/crates/bevy_gizmos/src/pipeline_2d.rs index 9e122782f7..754653ffd5 100644 --- a/crates/bevy_gizmos/src/pipeline_2d.rs +++ b/crates/bevy_gizmos/src/pipeline_2d.rs @@ -39,6 +39,7 @@ impl Plugin for LineGizmo2dPlugin { render_app .add_render_command::() + .add_render_command::() .add_render_command::() .init_resource::>() .init_resource::>() @@ -271,7 +272,13 @@ type DrawLineGizmo2d = ( SetItemPipeline, SetMesh2dViewBindGroup<0>, SetLineGizmoBindGroup<1>, - DrawLineGizmo, + DrawLineGizmo, +); +type DrawLineGizmo2dStrip = ( + SetItemPipeline, + SetMesh2dViewBindGroup<0>, + SetLineGizmoBindGroup<1>, + DrawLineGizmo, ); type DrawLineJointGizmo2d = ( SetItemPipeline, @@ -292,6 +299,10 @@ fn queue_line_gizmos_2d( mut views: Query<(Entity, &ExtractedView, &Msaa, Option<&RenderLayers>)>, ) { let draw_function = draw_functions.read().get_id::().unwrap(); + let draw_function_strip = draw_functions + .read() + .get_id::() + .unwrap(); for (view_entity, view, msaa, render_layers) in &mut views { let Some(transparent_phase) = transparent_render_phases.get_mut(&view_entity) else { @@ -311,24 +322,45 @@ fn queue_line_gizmos_2d( continue; }; - let pipeline = pipelines.specialize( - &pipeline_cache, - &pipeline, - LineGizmoPipelineKey { - mesh_key, - strip: line_gizmo.strip, - line_style: config.line_style, - }, - ); + if line_gizmo.list_vertex_count > 0 { + let pipeline = pipelines.specialize( + &pipeline_cache, + &pipeline, + LineGizmoPipelineKey { + mesh_key, + strip: false, + line_style: config.line_style, + }, + ); + transparent_phase.add(Transparent2d { + entity: (entity, *main_entity), + draw_function, + pipeline, + sort_key: FloatOrd(f32::INFINITY), + batch_range: 0..1, + extra_index: PhaseItemExtraIndex::NONE, + }); + } - transparent_phase.add(Transparent2d { - entity: (entity, *main_entity), - draw_function, - pipeline, - sort_key: FloatOrd(f32::INFINITY), - batch_range: 0..1, - extra_index: PhaseItemExtraIndex::NONE, - }); + if line_gizmo.strip_vertex_count >= 2 { + let pipeline = pipelines.specialize( + &pipeline_cache, + &pipeline, + LineGizmoPipelineKey { + mesh_key, + strip: true, + line_style: config.line_style, + }, + ); + transparent_phase.add(Transparent2d { + entity: (entity, *main_entity), + draw_function: draw_function_strip, + pipeline, + sort_key: FloatOrd(f32::INFINITY), + batch_range: 0..1, + extra_index: PhaseItemExtraIndex::NONE, + }); + } } } } @@ -367,7 +399,7 @@ fn queue_line_joint_gizmos_2d( continue; }; - if !line_gizmo.strip || line_gizmo.joints == GizmoLineJoint::None { + if line_gizmo.strip_vertex_count < 3 || config.line_joints == GizmoLineJoint::None { continue; } @@ -376,7 +408,7 @@ fn queue_line_joint_gizmos_2d( &pipeline, LineJointGizmoPipelineKey { mesh_key, - joints: line_gizmo.joints, + joints: config.line_joints, }, ); transparent_phase.add(Transparent2d { diff --git a/crates/bevy_gizmos/src/pipeline_3d.rs b/crates/bevy_gizmos/src/pipeline_3d.rs index 2d3f2bbd8d..7d7f3d9c3c 100644 --- a/crates/bevy_gizmos/src/pipeline_3d.rs +++ b/crates/bevy_gizmos/src/pipeline_3d.rs @@ -41,6 +41,7 @@ impl Plugin for LineGizmo3dPlugin { render_app .add_render_command::() + .add_render_command::() .add_render_command::() .init_resource::>() .init_resource::>() @@ -156,7 +157,7 @@ impl SpecializedRenderPipeline for LineGizmoPipeline { mask: !0, alpha_to_coverage_enabled: false, }, - label: Some("LineGizmo Pipeline".into()), + label: Some("LineGizmo 3d Pipeline".into()), push_constant_ranges: vec![], zero_initialize_workgroup_memory: false, } @@ -255,7 +256,7 @@ impl SpecializedRenderPipeline for LineJointGizmoPipeline { mask: !0, alpha_to_coverage_enabled: false, }, - label: Some("LineJointGizmo Pipeline".into()), + label: Some("LineJointGizmo 3d Pipeline".into()), push_constant_ranges: vec![], zero_initialize_workgroup_memory: false, } @@ -266,7 +267,13 @@ type DrawLineGizmo3d = ( SetItemPipeline, SetMeshViewBindGroup<0>, SetLineGizmoBindGroup<1>, - DrawLineGizmo, + DrawLineGizmo, +); +type DrawLineGizmo3dStrip = ( + SetItemPipeline, + SetMeshViewBindGroup<0>, + SetLineGizmoBindGroup<1>, + DrawLineGizmo, ); type DrawLineJointGizmo3d = ( SetItemPipeline, @@ -298,6 +305,10 @@ fn queue_line_gizmos_3d( )>, ) { let draw_function = draw_functions.read().get_id::().unwrap(); + let draw_function_strip = draw_functions + .read() + .get_id::() + .unwrap(); for ( view_entity, @@ -341,25 +352,47 @@ fn queue_line_gizmos_3d( continue; }; - let pipeline = pipelines.specialize( - &pipeline_cache, - &pipeline, - LineGizmoPipelineKey { - view_key, - strip: line_gizmo.strip, - perspective: config.line_perspective, - line_style: config.line_style, - }, - ); + if line_gizmo.list_vertex_count > 0 { + let pipeline = pipelines.specialize( + &pipeline_cache, + &pipeline, + LineGizmoPipelineKey { + view_key, + strip: false, + perspective: config.line_perspective, + line_style: config.line_style, + }, + ); + transparent_phase.add(Transparent3d { + entity: (entity, *main_entity), + draw_function, + pipeline, + distance: 0., + batch_range: 0..1, + extra_index: PhaseItemExtraIndex::NONE, + }); + } - transparent_phase.add(Transparent3d { - entity: (entity, *main_entity), - draw_function, - pipeline, - distance: 0., - batch_range: 0..1, - extra_index: PhaseItemExtraIndex::NONE, - }); + if line_gizmo.strip_vertex_count >= 2 { + let pipeline = pipelines.specialize( + &pipeline_cache, + &pipeline, + LineGizmoPipelineKey { + view_key, + strip: true, + perspective: config.line_perspective, + line_style: config.line_style, + }, + ); + transparent_phase.add(Transparent3d { + entity: (entity, *main_entity), + draw_function: draw_function_strip, + pipeline, + distance: 0., + batch_range: 0..1, + extra_index: PhaseItemExtraIndex::NONE, + }); + } } } } @@ -433,7 +466,7 @@ fn queue_line_joint_gizmos_3d( continue; }; - if !line_gizmo.strip || line_gizmo.joints == GizmoLineJoint::None { + if line_gizmo.strip_vertex_count < 3 || config.line_joints == GizmoLineJoint::None { continue; } @@ -443,7 +476,7 @@ fn queue_line_joint_gizmos_3d( LineJointGizmoPipelineKey { view_key, perspective: config.line_perspective, - joints: line_gizmo.joints, + joints: config.line_joints, }, ); diff --git a/crates/bevy_gizmos/src/primitives/dim2.rs b/crates/bevy_gizmos/src/primitives/dim2.rs index 0c3b44cd67..beb9c6f315 100644 --- a/crates/bevy_gizmos/src/primitives/dim2.rs +++ b/crates/bevy_gizmos/src/primitives/dim2.rs @@ -1,4 +1,4 @@ -//! A module for rendering each of the 2D [`bevy_math::primitives`] with [`Gizmos`]. +//! A module for rendering each of the 2D [`bevy_math::primitives`] with [`GizmoBuffer`]. use core::f32::consts::{FRAC_PI_2, PI}; @@ -14,7 +14,7 @@ use bevy_math::{ Dir2, Isometry2d, Rot2, Vec2, }; -use crate::prelude::{GizmoConfigGroup, Gizmos}; +use crate::{gizmos::GizmoBuffer, prelude::GizmoConfigGroup}; // some magic number since using directions as offsets will result in lines of length 1 pixel const MIN_LINE_LEN: f32 = 50.0; @@ -22,7 +22,7 @@ const HALF_MIN_LINE_LEN: f32 = 25.0; // length used to simulate infinite lines const INFINITE_LEN: f32 = 100_000.0; -/// A trait for rendering 2D geometric primitives (`P`) with [`Gizmos`]. +/// A trait for rendering 2D geometric primitives (`P`) with [`GizmoBuffer`]. pub trait GizmoPrimitive2d { /// The output of `primitive_2d`. This is a builder to set non-default values. type Output<'a> @@ -40,7 +40,7 @@ pub trait GizmoPrimitive2d { // direction 2d -impl<'w, 's, Config, Clear> GizmoPrimitive2d for Gizmos<'w, 's, Config, Clear> +impl GizmoPrimitive2d for GizmoBuffer where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, @@ -68,7 +68,7 @@ where // arc 2d -impl<'w, 's, Config, Clear> GizmoPrimitive2d for Gizmos<'w, 's, Config, Clear> +impl GizmoPrimitive2d for GizmoBuffer where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, @@ -102,13 +102,13 @@ where // circle 2d -impl<'w, 's, Config, Clear> GizmoPrimitive2d for Gizmos<'w, 's, Config, Clear> +impl GizmoPrimitive2d for GizmoBuffer where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, { type Output<'a> - = crate::circles::Ellipse2dBuilder<'a, 'w, 's, Config, Clear> + = crate::circles::Ellipse2dBuilder<'a, Config, Clear> where Self: 'a; @@ -124,7 +124,7 @@ where // circular sector 2d -impl<'w, 's, Config, Clear> GizmoPrimitive2d for Gizmos<'w, 's, Config, Clear> +impl GizmoPrimitive2d for GizmoBuffer where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, @@ -167,7 +167,7 @@ where // circular segment 2d -impl<'w, 's, Config, Clear> GizmoPrimitive2d for Gizmos<'w, 's, Config, Clear> +impl GizmoPrimitive2d for GizmoBuffer where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, @@ -209,13 +209,13 @@ where // ellipse 2d -impl<'w, 's, Config, Clear> GizmoPrimitive2d for Gizmos<'w, 's, Config, Clear> +impl GizmoPrimitive2d for GizmoBuffer where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, { type Output<'a> - = crate::circles::Ellipse2dBuilder<'a, 'w, 's, Config, Clear> + = crate::circles::Ellipse2dBuilder<'a, Config, Clear> where Self: 'a; @@ -232,12 +232,12 @@ where // annulus 2d /// Builder for configuring the drawing options of [`Annulus`]. -pub struct Annulus2dBuilder<'a, 'w, 's, Config, Clear> +pub struct Annulus2dBuilder<'a, Config, Clear> where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, { - gizmos: &'a mut Gizmos<'w, 's, Config, Clear>, + gizmos: &'a mut GizmoBuffer, isometry: Isometry2d, inner_radius: f32, outer_radius: f32, @@ -246,7 +246,7 @@ where outer_resolution: u32, } -impl Annulus2dBuilder<'_, '_, '_, Config, Clear> +impl Annulus2dBuilder<'_, Config, Clear> where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, @@ -271,13 +271,13 @@ where } } -impl<'w, 's, Config, Clear> GizmoPrimitive2d for Gizmos<'w, 's, Config, Clear> +impl GizmoPrimitive2d for GizmoBuffer where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, { type Output<'a> - = Annulus2dBuilder<'a, 'w, 's, Config, Clear> + = Annulus2dBuilder<'a, Config, Clear> where Self: 'a; @@ -299,7 +299,7 @@ where } } -impl Drop for Annulus2dBuilder<'_, '_, '_, Config, Clear> +impl Drop for Annulus2dBuilder<'_, Config, Clear> where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, @@ -331,7 +331,7 @@ where // rhombus 2d -impl<'w, 's, Config, Clear> GizmoPrimitive2d for Gizmos<'w, 's, Config, Clear> +impl GizmoPrimitive2d for GizmoBuffer where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, @@ -365,7 +365,7 @@ where // capsule 2d -impl<'w, 's, Config, Clear> GizmoPrimitive2d for Gizmos<'w, 's, Config, Clear> +impl GizmoPrimitive2d for GizmoBuffer where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, @@ -431,12 +431,12 @@ where // line 2d // /// Builder for configuring the drawing options of [`Line2d`]. -pub struct Line2dBuilder<'a, 'w, 's, Config, Clear> +pub struct Line2dBuilder<'a, Config, Clear> where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, { - gizmos: &'a mut Gizmos<'w, 's, Config, Clear>, + gizmos: &'a mut GizmoBuffer, direction: Dir2, // Direction of the line @@ -446,7 +446,7 @@ where draw_arrow: bool, // decides whether to indicate the direction of the line with an arrow } -impl Line2dBuilder<'_, '_, '_, Config, Clear> +impl Line2dBuilder<'_, Config, Clear> where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, @@ -458,13 +458,13 @@ where } } -impl<'w, 's, Config, Clear> GizmoPrimitive2d for Gizmos<'w, 's, Config, Clear> +impl GizmoPrimitive2d for GizmoBuffer where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, { type Output<'a> - = Line2dBuilder<'a, 'w, 's, Config, Clear> + = Line2dBuilder<'a, Config, Clear> where Self: 'a; @@ -484,7 +484,7 @@ where } } -impl Drop for Line2dBuilder<'_, '_, '_, Config, Clear> +impl Drop for Line2dBuilder<'_, Config, Clear> where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, @@ -516,7 +516,7 @@ where // plane 2d -impl<'w, 's, Config, Clear> GizmoPrimitive2d for Gizmos<'w, 's, Config, Clear> +impl GizmoPrimitive2d for GizmoBuffer where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, @@ -570,12 +570,12 @@ where // segment 2d /// Builder for configuring the drawing options of [`Segment2d`]. -pub struct Segment2dBuilder<'a, 'w, 's, Config, Clear> +pub struct Segment2dBuilder<'a, Config, Clear> where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, { - gizmos: &'a mut Gizmos<'w, 's, Config, Clear>, + gizmos: &'a mut GizmoBuffer, direction: Dir2, // Direction of the line segment half_length: f32, // Half-length of the line segment @@ -586,7 +586,7 @@ where draw_arrow: bool, // decides whether to draw just a line or an arrow } -impl Segment2dBuilder<'_, '_, '_, Config, Clear> +impl Segment2dBuilder<'_, Config, Clear> where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, @@ -598,13 +598,13 @@ where } } -impl<'w, 's, Config, Clear> GizmoPrimitive2d for Gizmos<'w, 's, Config, Clear> +impl GizmoPrimitive2d for GizmoBuffer where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, { type Output<'a> - = Segment2dBuilder<'a, 'w, 's, Config, Clear> + = Segment2dBuilder<'a, Config, Clear> where Self: 'a; @@ -627,7 +627,7 @@ where } } -impl Drop for Segment2dBuilder<'_, '_, '_, Config, Clear> +impl Drop for Segment2dBuilder<'_, Config, Clear> where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, @@ -651,8 +651,7 @@ where // polyline 2d -impl<'w, 's, const N: usize, Config, Clear> GizmoPrimitive2d> - for Gizmos<'w, 's, Config, Clear> +impl GizmoPrimitive2d> for GizmoBuffer where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, @@ -687,7 +686,7 @@ where // boxed polyline 2d -impl<'w, 's, Config, Clear> GizmoPrimitive2d for Gizmos<'w, 's, Config, Clear> +impl GizmoPrimitive2d for GizmoBuffer where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, @@ -722,7 +721,7 @@ where // triangle 2d -impl<'w, 's, Config, Clear> GizmoPrimitive2d for Gizmos<'w, 's, Config, Clear> +impl GizmoPrimitive2d for GizmoBuffer where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, @@ -752,7 +751,7 @@ where // rectangle 2d -impl<'w, 's, Config, Clear> GizmoPrimitive2d for Gizmos<'w, 's, Config, Clear> +impl GizmoPrimitive2d for GizmoBuffer where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, @@ -788,8 +787,7 @@ where // polygon 2d -impl<'w, 's, const N: usize, Config, Clear> GizmoPrimitive2d> - for Gizmos<'w, 's, Config, Clear> +impl GizmoPrimitive2d> for GizmoBuffer where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, @@ -834,7 +832,7 @@ where // boxed polygon 2d -impl<'w, 's, Config, Clear> GizmoPrimitive2d for Gizmos<'w, 's, Config, Clear> +impl GizmoPrimitive2d for GizmoBuffer where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, @@ -877,7 +875,7 @@ where // regular polygon 2d -impl<'w, 's, Config, Clear> GizmoPrimitive2d for Gizmos<'w, 's, Config, Clear> +impl GizmoPrimitive2d for GizmoBuffer where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, diff --git a/crates/bevy_gizmos/src/primitives/dim3.rs b/crates/bevy_gizmos/src/primitives/dim3.rs index 4c11628246..1af21869a9 100644 --- a/crates/bevy_gizmos/src/primitives/dim3.rs +++ b/crates/bevy_gizmos/src/primitives/dim3.rs @@ -1,4 +1,4 @@ -//! A module for rendering each of the 3D [`bevy_math::primitives`] with [`Gizmos`]. +//! A module for rendering each of the 3D [`bevy_math::primitives`] with [`GizmoBuffer`]. use super::helpers::*; @@ -11,16 +11,13 @@ use bevy_math::{ Dir3, Isometry3d, Quat, UVec2, Vec2, Vec3, }; -use crate::{ - circles::SphereBuilder, - prelude::{GizmoConfigGroup, Gizmos}, -}; +use crate::{circles::SphereBuilder, gizmos::GizmoBuffer, prelude::GizmoConfigGroup}; const DEFAULT_RESOLUTION: u32 = 5; // length used to simulate infinite lines const INFINITE_LEN: f32 = 10_000.0; -/// A trait for rendering 3D geometric primitives (`P`) with [`Gizmos`]. +/// A trait for rendering 3D geometric primitives (`P`) with [`GizmoBuffer`]. pub trait GizmoPrimitive3d { /// The output of `primitive_3d`. This is a builder to set non-default values. type Output<'a> @@ -38,7 +35,7 @@ pub trait GizmoPrimitive3d { // direction 3d -impl<'w, 's, Config, Clear> GizmoPrimitive3d for Gizmos<'w, 's, Config, Clear> +impl GizmoPrimitive3d for GizmoBuffer where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, @@ -63,13 +60,13 @@ where // sphere -impl<'w, 's, Config, Clear> GizmoPrimitive3d for Gizmos<'w, 's, Config, Clear> +impl GizmoPrimitive3d for GizmoBuffer where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, { type Output<'a> - = SphereBuilder<'a, 'w, 's, Config, Clear> + = SphereBuilder<'a, Config, Clear> where Self: 'a; @@ -86,12 +83,12 @@ where // plane 3d /// Builder for configuring the drawing options of [`Plane3d`]. -pub struct Plane3dBuilder<'a, 'w, 's, Config, Clear> +pub struct Plane3dBuilder<'a, Config, Clear> where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, { - gizmos: &'a mut Gizmos<'w, 's, Config, Clear>, + gizmos: &'a mut GizmoBuffer, // Direction of the normal orthogonal to the plane normal: Dir3, @@ -106,7 +103,7 @@ where spacing: Vec2, } -impl Plane3dBuilder<'_, '_, '_, Config, Clear> +impl Plane3dBuilder<'_, Config, Clear> where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, @@ -124,13 +121,13 @@ where } } -impl<'w, 's, Config, Clear> GizmoPrimitive3d for Gizmos<'w, 's, Config, Clear> +impl GizmoPrimitive3d for GizmoBuffer where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, { type Output<'a> - = Plane3dBuilder<'a, 'w, 's, Config, Clear> + = Plane3dBuilder<'a, Config, Clear> where Self: 'a; @@ -151,7 +148,7 @@ where } } -impl Drop for Plane3dBuilder<'_, '_, '_, Config, Clear> +impl Drop for Plane3dBuilder<'_, Config, Clear> where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, @@ -176,7 +173,7 @@ where // line 3d -impl<'w, 's, Config, Clear> GizmoPrimitive3d for Gizmos<'w, 's, Config, Clear> +impl GizmoPrimitive3d for GizmoBuffer where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, @@ -211,7 +208,7 @@ where // segment 3d -impl<'w, 's, Config, Clear> GizmoPrimitive3d for Gizmos<'w, 's, Config, Clear> +impl GizmoPrimitive3d for GizmoBuffer where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, @@ -239,8 +236,7 @@ where // polyline 3d -impl<'w, 's, const N: usize, Config, Clear> GizmoPrimitive3d> - for Gizmos<'w, 's, Config, Clear> +impl GizmoPrimitive3d> for GizmoBuffer where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, @@ -267,7 +263,7 @@ where // boxed polyline 3d -impl<'w, 's, Config, Clear> GizmoPrimitive3d for Gizmos<'w, 's, Config, Clear> +impl GizmoPrimitive3d for GizmoBuffer where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, @@ -301,7 +297,7 @@ where // triangle 3d -impl<'w, 's, Config, Clear> GizmoPrimitive3d for Gizmos<'w, 's, Config, Clear> +impl GizmoPrimitive3d for GizmoBuffer where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, @@ -329,7 +325,7 @@ where // cuboid -impl<'w, 's, Config, Clear> GizmoPrimitive3d for Gizmos<'w, 's, Config, Clear> +impl GizmoPrimitive3d for GizmoBuffer where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, @@ -392,12 +388,12 @@ where // cylinder 3d /// Builder for configuring the drawing options of [`Cylinder`]. -pub struct Cylinder3dBuilder<'a, 'w, 's, Config, Clear> +pub struct Cylinder3dBuilder<'a, Config, Clear> where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, { - gizmos: &'a mut Gizmos<'w, 's, Config, Clear>, + gizmos: &'a mut GizmoBuffer, // Radius of the cylinder radius: f32, @@ -412,7 +408,7 @@ where resolution: u32, } -impl Cylinder3dBuilder<'_, '_, '_, Config, Clear> +impl Cylinder3dBuilder<'_, Config, Clear> where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, @@ -424,13 +420,13 @@ where } } -impl<'w, 's, Config, Clear> GizmoPrimitive3d for Gizmos<'w, 's, Config, Clear> +impl GizmoPrimitive3d for GizmoBuffer where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, { type Output<'a> - = Cylinder3dBuilder<'a, 'w, 's, Config, Clear> + = Cylinder3dBuilder<'a, Config, Clear> where Self: 'a; @@ -451,7 +447,7 @@ where } } -impl Drop for Cylinder3dBuilder<'_, '_, '_, Config, Clear> +impl Drop for Cylinder3dBuilder<'_, Config, Clear> where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, @@ -478,12 +474,12 @@ where // capsule 3d /// Builder for configuring the drawing options of [`Capsule3d`]. -pub struct Capsule3dBuilder<'a, 'w, 's, Config, Clear> +pub struct Capsule3dBuilder<'a, Config, Clear> where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, { - gizmos: &'a mut Gizmos<'w, 's, Config, Clear>, + gizmos: &'a mut GizmoBuffer, // Radius of the capsule radius: f32, @@ -498,7 +494,7 @@ where resolution: u32, } -impl Capsule3dBuilder<'_, '_, '_, Config, Clear> +impl Capsule3dBuilder<'_, Config, Clear> where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, @@ -510,13 +506,13 @@ where } } -impl<'w, 's, Config, Clear> GizmoPrimitive3d for Gizmos<'w, 's, Config, Clear> +impl GizmoPrimitive3d for GizmoBuffer where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, { type Output<'a> - = Capsule3dBuilder<'a, 'w, 's, Config, Clear> + = Capsule3dBuilder<'a, Config, Clear> where Self: 'a; @@ -537,7 +533,7 @@ where } } -impl Drop for Capsule3dBuilder<'_, '_, '_, Config, Clear> +impl Drop for Capsule3dBuilder<'_, Config, Clear> where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, @@ -596,12 +592,12 @@ where // cone 3d /// Builder for configuring the drawing options of [`Cone`]. -pub struct Cone3dBuilder<'a, 'w, 's, Config, Clear> +pub struct Cone3dBuilder<'a, Config, Clear> where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, { - gizmos: &'a mut Gizmos<'w, 's, Config, Clear>, + gizmos: &'a mut GizmoBuffer, // Radius of the cone radius: f32, @@ -619,7 +615,7 @@ where height_resolution: u32, } -impl Cone3dBuilder<'_, '_, '_, Config, Clear> +impl Cone3dBuilder<'_, Config, Clear> where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, @@ -650,13 +646,13 @@ where } } -impl<'w, 's, Config, Clear> GizmoPrimitive3d for Gizmos<'w, 's, Config, Clear> +impl GizmoPrimitive3d for GizmoBuffer where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, { type Output<'a> - = Cone3dBuilder<'a, 'w, 's, Config, Clear> + = Cone3dBuilder<'a, Config, Clear> where Self: 'a; @@ -678,7 +674,7 @@ where } } -impl Drop for Cone3dBuilder<'_, '_, '_, Config, Clear> +impl Drop for Cone3dBuilder<'_, Config, Clear> where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, @@ -718,12 +714,12 @@ where // conical frustum 3d /// Builder for configuring the drawing options of [`ConicalFrustum`]. -pub struct ConicalFrustum3dBuilder<'a, 'w, 's, Config, Clear> +pub struct ConicalFrustum3dBuilder<'a, Config, Clear> where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, { - gizmos: &'a mut Gizmos<'w, 's, Config, Clear>, + gizmos: &'a mut GizmoBuffer, // Radius of the top circle radius_top: f32, @@ -740,7 +736,7 @@ where resolution: u32, } -impl ConicalFrustum3dBuilder<'_, '_, '_, Config, Clear> +impl ConicalFrustum3dBuilder<'_, Config, Clear> where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, @@ -752,13 +748,13 @@ where } } -impl<'w, 's, Config, Clear> GizmoPrimitive3d for Gizmos<'w, 's, Config, Clear> +impl GizmoPrimitive3d for GizmoBuffer where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, { type Output<'a> - = ConicalFrustum3dBuilder<'a, 'w, 's, Config, Clear> + = ConicalFrustum3dBuilder<'a, Config, Clear> where Self: 'a; @@ -780,7 +776,7 @@ where } } -impl Drop for ConicalFrustum3dBuilder<'_, '_, '_, Config, Clear> +impl Drop for ConicalFrustum3dBuilder<'_, Config, Clear> where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, @@ -816,12 +812,12 @@ where // torus 3d /// Builder for configuring the drawing options of [`Torus`]. -pub struct Torus3dBuilder<'a, 'w, 's, Config, Clear> +pub struct Torus3dBuilder<'a, Config, Clear> where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, { - gizmos: &'a mut Gizmos<'w, 's, Config, Clear>, + gizmos: &'a mut GizmoBuffer, // Radius of the minor circle (tube) minor_radius: f32, @@ -838,7 +834,7 @@ where major_resolution: u32, } -impl Torus3dBuilder<'_, '_, '_, Config, Clear> +impl Torus3dBuilder<'_, Config, Clear> where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, @@ -856,13 +852,13 @@ where } } -impl<'w, 's, Config, Clear> GizmoPrimitive3d for Gizmos<'w, 's, Config, Clear> +impl GizmoPrimitive3d for GizmoBuffer where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, { type Output<'a> - = Torus3dBuilder<'a, 'w, 's, Config, Clear> + = Torus3dBuilder<'a, Config, Clear> where Self: 'a; @@ -884,7 +880,7 @@ where } } -impl Drop for Torus3dBuilder<'_, '_, '_, Config, Clear> +impl Drop for Torus3dBuilder<'_, Config, Clear> where Config: GizmoConfigGroup, Clear: 'static + Send + Sync, @@ -936,7 +932,11 @@ where // tetrahedron -impl<'w, 's, T: GizmoConfigGroup> GizmoPrimitive3d for Gizmos<'w, 's, T> { +impl GizmoPrimitive3d for GizmoBuffer +where + Config: GizmoConfigGroup, + Clear: 'static + Send + Sync, +{ type Output<'a> = () where diff --git a/crates/bevy_gizmos/src/retained.rs b/crates/bevy_gizmos/src/retained.rs new file mode 100644 index 0000000000..1a7e14ca2d --- /dev/null +++ b/crates/bevy_gizmos/src/retained.rs @@ -0,0 +1,138 @@ +//! This module is for 'retained' alternatives to the 'immediate mode' [`Gizmos`](crate::gizmos::Gizmos) system parameter. + +use core::ops::{Deref, DerefMut}; + +use bevy_asset::Handle; +use bevy_ecs::component::{require, Component}; +use bevy_reflect::Reflect; +use bevy_transform::components::Transform; + +#[cfg(feature = "bevy_render")] +use { + crate::{config::GizmoLineJoint, LineGizmoUniform}, + bevy_ecs::{ + entity::Entity, + system::{Commands, Local, Query}, + }, + bevy_render::{view::RenderLayers, Extract}, + bevy_transform::components::GlobalTransform, +}; + +use crate::{ + config::{ErasedGizmoConfigGroup, GizmoLineConfig}, + gizmos::GizmoBuffer, + GizmoAsset, +}; + +impl Deref for GizmoAsset { + type Target = GizmoBuffer; + + fn deref(&self) -> &Self::Target { + &self.buffer + } +} + +impl DerefMut for GizmoAsset { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.buffer + } +} + +/// A component that draws the gizmos of a [`GizmoAsset`]. +/// +/// When drawing a greater number of static lines a [`Gizmo`] component can +/// have far better performance than the [`Gizmos`] system parameter, +/// but the system parameter will perform better for smaller lines that update often. +/// +/// ## Example +/// ``` +/// # use bevy_ecs::prelude::*; +/// # use bevy_gizmos::prelude::*; +/// # use bevy_asset::prelude::*; +/// # use bevy_color::palettes::css::*; +/// # use bevy_utils::default; +/// # use bevy_math::prelude::*; +/// fn system( +/// mut commands: Commands, +/// mut gizmo_assets: ResMut>, +/// ) { +/// let mut gizmo = GizmoAsset::default(); +/// +/// gizmo.sphere(Vec3::ZERO, 1., RED); +/// +/// commands.spawn(Gizmo { +/// handle: gizmo_assets.add(gizmo), +/// line_config: GizmoLineConfig { +/// width: 4., +/// ..default() +/// }, +/// ..default() +/// }); +/// } +/// ``` +/// +/// [`Gizmos`]: crate::gizmos::Gizmos +#[derive(Component, Clone, Debug, Default, Reflect)] +#[require(Transform)] +pub struct Gizmo { + /// The handle to the gizmo to draw. + pub handle: Handle, + /// The line specific configuration for this gizmo. + pub line_config: GizmoLineConfig, + /// How closer to the camera than real geometry the gizmo should be. + /// + /// In 2D this setting has no effect and is effectively always -1. + /// + /// Value between -1 and 1 (inclusive). + /// * 0 means that there is no change to the gizmo position when rendering + /// * 1 means it is furthest away from camera as possible + /// * -1 means that it will always render in front of other things. + /// + /// This is typically useful if you are drawing wireframes on top of polygons + /// and your wireframe is z-fighting (flickering on/off) with your main model. + /// You would set this value to a negative number close to 0. + pub depth_bias: f32, +} + +#[cfg(feature = "bevy_render")] +pub(crate) fn extract_linegizmos( + mut commands: Commands, + mut previous_len: Local, + query: Extract)>>, +) { + use bevy_math::Affine3; + use bevy_render::sync_world::{MainEntity, TemporaryRenderEntity}; + + let mut values = Vec::with_capacity(*previous_len); + for (entity, gizmo, transform, render_layers) in &query { + let joints_resolution = if let GizmoLineJoint::Round(resolution) = gizmo.line_config.joints + { + resolution + } else { + 0 + }; + + values.push(( + LineGizmoUniform { + world_from_local: Affine3::from(&transform.affine()).to_transpose(), + line_width: gizmo.line_config.width, + depth_bias: gizmo.depth_bias, + joints_resolution, + #[cfg(feature = "webgl")] + _padding: Default::default(), + }, + #[cfg(any(feature = "bevy_pbr", feature = "bevy_sprite"))] + crate::config::GizmoMeshConfig { + line_perspective: gizmo.line_config.perspective, + line_style: gizmo.line_config.style, + line_joints: gizmo.line_config.joints, + render_layers: render_layers.cloned().unwrap_or_default(), + handle: gizmo.handle.clone_weak(), + }, + MainEntity::from(entity), + TemporaryRenderEntity, + )); + } + *previous_len = values.len(); + commands.spawn_batch(values); +} diff --git a/crates/bevy_gizmos/src/rounded_box.rs b/crates/bevy_gizmos/src/rounded_box.rs index 2b107dbcf5..6f0df7ac0e 100644 --- a/crates/bevy_gizmos/src/rounded_box.rs +++ b/crates/bevy_gizmos/src/rounded_box.rs @@ -1,25 +1,33 @@ -//! Additional [`Gizmos`] Functions -- Rounded cuboids and rectangles +//! Additional [`GizmoBuffer`] Functions -- Rounded cuboids and rectangles //! -//! Includes the implementation of [`Gizmos::rounded_rect`], [`Gizmos::rounded_rect_2d`] and [`Gizmos::rounded_cuboid`]. +//! Includes the implementation of [`GizmoBuffer::rounded_rect`], [`GizmoBuffer::rounded_rect_2d`] and [`GizmoBuffer::rounded_cuboid`]. //! and assorted support items. use core::f32::consts::FRAC_PI_2; -use crate::prelude::{GizmoConfigGroup, Gizmos}; +use crate::{gizmos::GizmoBuffer, prelude::GizmoConfigGroup}; use bevy_color::Color; use bevy_math::{Isometry2d, Isometry3d, Quat, Vec2, Vec3}; use bevy_transform::components::Transform; -/// A builder returned by [`Gizmos::rounded_rect`] and [`Gizmos::rounded_rect_2d`] -pub struct RoundedRectBuilder<'a, 'w, 's, T: GizmoConfigGroup> { +/// A builder returned by [`GizmoBuffer::rounded_rect`] and [`GizmoBuffer::rounded_rect_2d`] +pub struct RoundedRectBuilder<'a, Config, Clear> +where + Config: GizmoConfigGroup, + Clear: 'static + Send + Sync, +{ size: Vec2, - gizmos: &'a mut Gizmos<'w, 's, T>, + gizmos: &'a mut GizmoBuffer, config: RoundedBoxConfig, } -/// A builder returned by [`Gizmos::rounded_cuboid`] -pub struct RoundedCuboidBuilder<'a, 'w, 's, T: GizmoConfigGroup> { +/// A builder returned by [`GizmoBuffer::rounded_cuboid`] +pub struct RoundedCuboidBuilder<'a, Config, Clear> +where + Config: GizmoConfigGroup, + Clear: 'static + Send + Sync, +{ size: Vec3, - gizmos: &'a mut Gizmos<'w, 's, T>, + gizmos: &'a mut GizmoBuffer, config: RoundedBoxConfig, } struct RoundedBoxConfig { @@ -29,7 +37,11 @@ struct RoundedBoxConfig { arc_resolution: u32, } -impl RoundedRectBuilder<'_, '_, '_, T> { +impl RoundedRectBuilder<'_, Config, Clear> +where + Config: GizmoConfigGroup, + Clear: 'static + Send + Sync, +{ /// Change the radius of the corners to be `corner_radius`. /// The default corner radius is [min axis of size] / 10.0 pub fn corner_radius(mut self, corner_radius: f32) -> Self { @@ -44,7 +56,12 @@ impl RoundedRectBuilder<'_, '_, '_, T> { self } } -impl RoundedCuboidBuilder<'_, '_, '_, T> { + +impl RoundedCuboidBuilder<'_, Config, Clear> +where + Config: GizmoConfigGroup, + Clear: 'static + Send + Sync, +{ /// Change the radius of the edges to be `edge_radius`. /// The default edge radius is [min axis of size] / 10.0 pub fn edge_radius(mut self, edge_radius: f32) -> Self { @@ -60,7 +77,11 @@ impl RoundedCuboidBuilder<'_, '_, '_, T> { } } -impl Drop for RoundedRectBuilder<'_, '_, '_, T> { +impl Drop for RoundedRectBuilder<'_, Config, Clear> +where + Config: GizmoConfigGroup, + Clear: 'static + Send + Sync, +{ fn drop(&mut self) { if !self.gizmos.enabled { return; @@ -140,7 +161,11 @@ impl Drop for RoundedRectBuilder<'_, '_, '_, T> { } } -impl Drop for RoundedCuboidBuilder<'_, '_, '_, T> { +impl Drop for RoundedCuboidBuilder<'_, Config, Clear> +where + Config: GizmoConfigGroup, + Clear: 'static + Send + Sync, +{ fn drop(&mut self) { if !self.gizmos.enabled { return; @@ -201,7 +226,11 @@ impl Drop for RoundedCuboidBuilder<'_, '_, '_, T> { } } -impl<'w, 's, T: GizmoConfigGroup> Gizmos<'w, 's, T> { +impl GizmoBuffer +where + Config: GizmoConfigGroup, + Clear: 'static + Send + Sync, +{ /// Draw a wireframe rectangle with rounded corners in 3D. /// /// This should be called for each frame the rectangle needs to be rendered. @@ -243,7 +272,7 @@ impl<'w, 's, T: GizmoConfigGroup> Gizmos<'w, 's, T> { isometry: impl Into, size: Vec2, color: impl Into, - ) -> RoundedRectBuilder<'_, 'w, 's, T> { + ) -> RoundedRectBuilder<'_, Config, Clear> { let corner_radius = size.min_element() * DEFAULT_CORNER_RADIUS; RoundedRectBuilder { gizmos: self, @@ -297,7 +326,7 @@ impl<'w, 's, T: GizmoConfigGroup> Gizmos<'w, 's, T> { isometry: impl Into, size: Vec2, color: impl Into, - ) -> RoundedRectBuilder<'_, 'w, 's, T> { + ) -> RoundedRectBuilder<'_, Config, Clear> { let isometry = isometry.into(); let corner_radius = size.min_element() * DEFAULT_CORNER_RADIUS; RoundedRectBuilder { @@ -355,7 +384,7 @@ impl<'w, 's, T: GizmoConfigGroup> Gizmos<'w, 's, T> { isometry: impl Into, size: Vec3, color: impl Into, - ) -> RoundedCuboidBuilder<'_, 'w, 's, T> { + ) -> RoundedCuboidBuilder<'_, Config, Clear> { let corner_radius = size.min_element() * DEFAULT_CORNER_RADIUS; RoundedCuboidBuilder { gizmos: self, diff --git a/examples/gizmos/2d_gizmos.rs b/examples/gizmos/2d_gizmos.rs index 9877b06907..bdf444dd5d 100644 --- a/examples/gizmos/2d_gizmos.rs +++ b/examples/gizmos/2d_gizmos.rs @@ -129,24 +129,24 @@ fn update_config( ) { let (config, _) = config_store.config_mut::(); if keyboard.pressed(KeyCode::ArrowRight) { - config.line_width += 5. * time.delta_secs(); - config.line_width = config.line_width.clamp(0., 50.); + config.line.width += 5. * time.delta_secs(); + config.line.width = config.line.width.clamp(0., 50.); } if keyboard.pressed(KeyCode::ArrowLeft) { - config.line_width -= 5. * time.delta_secs(); - config.line_width = config.line_width.clamp(0., 50.); + config.line.width -= 5. * time.delta_secs(); + config.line.width = config.line.width.clamp(0., 50.); } if keyboard.just_pressed(KeyCode::Digit1) { config.enabled ^= true; } if keyboard.just_pressed(KeyCode::KeyU) { - config.line_style = match config.line_style { + config.line.style = match config.line.style { GizmoLineStyle::Solid => GizmoLineStyle::Dotted, _ => GizmoLineStyle::Solid, }; } if keyboard.just_pressed(KeyCode::KeyJ) { - config.line_joints = match config.line_joints { + config.line.joints = match config.line.joints { GizmoLineJoint::Bevel => GizmoLineJoint::Miter, GizmoLineJoint::Miter => GizmoLineJoint::Round(4), GizmoLineJoint::Round(_) => GizmoLineJoint::None, @@ -156,24 +156,24 @@ fn update_config( let (my_config, _) = config_store.config_mut::(); if keyboard.pressed(KeyCode::ArrowUp) { - my_config.line_width += 5. * time.delta_secs(); - my_config.line_width = my_config.line_width.clamp(0., 50.); + my_config.line.width += 5. * time.delta_secs(); + my_config.line.width = my_config.line.width.clamp(0., 50.); } if keyboard.pressed(KeyCode::ArrowDown) { - my_config.line_width -= 5. * time.delta_secs(); - my_config.line_width = my_config.line_width.clamp(0., 50.); + my_config.line.width -= 5. * time.delta_secs(); + my_config.line.width = my_config.line.width.clamp(0., 50.); } if keyboard.just_pressed(KeyCode::Digit2) { my_config.enabled ^= true; } if keyboard.just_pressed(KeyCode::KeyI) { - my_config.line_style = match my_config.line_style { + my_config.line.style = match my_config.line.style { GizmoLineStyle::Solid => GizmoLineStyle::Dotted, _ => GizmoLineStyle::Solid, }; } if keyboard.just_pressed(KeyCode::KeyK) { - my_config.line_joints = match my_config.line_joints { + my_config.line.joints = match my_config.line.joints { GizmoLineJoint::Bevel => GizmoLineJoint::Miter, GizmoLineJoint::Miter => GizmoLineJoint::Round(4), GizmoLineJoint::Round(_) => GizmoLineJoint::None, diff --git a/examples/gizmos/3d_gizmos.rs b/examples/gizmos/3d_gizmos.rs index 81fe527c9a..1a34717bf3 100644 --- a/examples/gizmos/3d_gizmos.rs +++ b/examples/gizmos/3d_gizmos.rs @@ -18,13 +18,37 @@ fn main() { // We can create our own gizmo config group! #[derive(Default, Reflect, GizmoConfigGroup)] -struct MyRoundGizmos {} +struct MyRoundGizmos; fn setup( mut commands: Commands, + mut gizmo_assets: ResMut>, mut meshes: ResMut>, mut materials: ResMut>, ) { + let mut gizmo = GizmoAsset::new(); + + // When drawing a lot of static lines a Gizmo component can have + // far better performance than the Gizmos system parameter, + // but the system parameter will perform better for smaller lines that update often. + + // A sphere made out of 30_000 lines! + gizmo + .sphere(Isometry3d::IDENTITY, 0.5, CRIMSON) + .resolution(30_000 / 3); + + commands.spawn(( + Gizmo { + handle: gizmo_assets.add(gizmo), + line_config: GizmoLineConfig { + width: 5., + ..default() + }, + ..default() + }, + Transform::from_xyz(4., 1., 0.), + )); + commands.spawn(( Camera3d::default(), Transform::from_xyz(0., 1.5, 6.).looking_at(Vec3::ZERO, Vec3::Y), @@ -191,33 +215,33 @@ fn update_config( } if keyboard.just_pressed(KeyCode::KeyP) { for (_, config, _) in config_store.iter_mut() { - // Toggle line_perspective - config.line_perspective ^= true; - // Increase the line width when line_perspective is on - config.line_width *= if config.line_perspective { 5. } else { 1. / 5. }; + // Toggle line perspective + config.line.perspective ^= true; + // Increase the line width when line perspective is on + config.line.width *= if config.line.perspective { 5. } else { 1. / 5. }; } } let (config, _) = config_store.config_mut::(); if keyboard.pressed(KeyCode::ArrowRight) { - config.line_width += 5. * time.delta_secs(); - config.line_width = config.line_width.clamp(0., 50.); + config.line.width += 5. * time.delta_secs(); + config.line.width = config.line.width.clamp(0., 50.); } if keyboard.pressed(KeyCode::ArrowLeft) { - config.line_width -= 5. * time.delta_secs(); - config.line_width = config.line_width.clamp(0., 50.); + config.line.width -= 5. * time.delta_secs(); + config.line.width = config.line.width.clamp(0., 50.); } if keyboard.just_pressed(KeyCode::Digit1) { config.enabled ^= true; } if keyboard.just_pressed(KeyCode::KeyU) { - config.line_style = match config.line_style { + config.line.style = match config.line.style { GizmoLineStyle::Solid => GizmoLineStyle::Dotted, _ => GizmoLineStyle::Solid, }; } if keyboard.just_pressed(KeyCode::KeyJ) { - config.line_joints = match config.line_joints { + config.line.joints = match config.line.joints { GizmoLineJoint::Bevel => GizmoLineJoint::Miter, GizmoLineJoint::Miter => GizmoLineJoint::Round(4), GizmoLineJoint::Round(_) => GizmoLineJoint::None, @@ -227,24 +251,24 @@ fn update_config( let (my_config, _) = config_store.config_mut::(); if keyboard.pressed(KeyCode::ArrowUp) { - my_config.line_width += 5. * time.delta_secs(); - my_config.line_width = my_config.line_width.clamp(0., 50.); + my_config.line.width += 5. * time.delta_secs(); + my_config.line.width = my_config.line.width.clamp(0., 50.); } if keyboard.pressed(KeyCode::ArrowDown) { - my_config.line_width -= 5. * time.delta_secs(); - my_config.line_width = my_config.line_width.clamp(0., 50.); + my_config.line.width -= 5. * time.delta_secs(); + my_config.line.width = my_config.line.width.clamp(0., 50.); } if keyboard.just_pressed(KeyCode::Digit2) { my_config.enabled ^= true; } if keyboard.just_pressed(KeyCode::KeyI) { - my_config.line_style = match my_config.line_style { + my_config.line.style = match my_config.line.style { GizmoLineStyle::Solid => GizmoLineStyle::Dotted, _ => GizmoLineStyle::Solid, }; } if keyboard.just_pressed(KeyCode::KeyK) { - my_config.line_joints = match my_config.line_joints { + my_config.line.joints = match my_config.line.joints { GizmoLineJoint::Bevel => GizmoLineJoint::Miter, GizmoLineJoint::Miter => GizmoLineJoint::Round(4), GizmoLineJoint::Round(_) => GizmoLineJoint::None, diff --git a/examples/gizmos/light_gizmos.rs b/examples/gizmos/light_gizmos.rs index 2fb739b56a..c21b4b94e6 100644 --- a/examples/gizmos/light_gizmos.rs +++ b/examples/gizmos/light_gizmos.rs @@ -155,12 +155,12 @@ fn update_config( let (config, light_config) = config_store.config_mut::(); if keyboard.pressed(KeyCode::ArrowRight) { - config.line_width += 5. * time.delta_secs(); - config.line_width = config.line_width.clamp(0., 50.); + config.line.width += 5. * time.delta_secs(); + config.line.width = config.line.width.clamp(0., 50.); } if keyboard.pressed(KeyCode::ArrowLeft) { - config.line_width -= 5. * time.delta_secs(); - config.line_width = config.line_width.clamp(0., 50.); + config.line.width -= 5. * time.delta_secs(); + config.line.width = config.line.width.clamp(0., 50.); } if keyboard.just_pressed(KeyCode::KeyA) { config.enabled ^= true;