Retained Gizmos (#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<Assets<GizmoAsset>>
) {
    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
This commit is contained in:
Tim 2024-12-04 21:21:06 +00:00 committed by GitHub
parent f59ae0f5e8
commit d2a07f9f72
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
22 changed files with 840 additions and 462 deletions

View file

@ -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);
}
}
}

View file

@ -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 {

View file

@ -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<Config, Clear> GizmoBuffer<Config, Clear>
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
@ -54,7 +51,7 @@ where
arc_angle: f32,
radius: f32,
color: impl Into<Color>,
) -> 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<Config, Clear>,
isometry: Isometry2d,
arc_angle: f32,
radius: f32,
@ -80,7 +77,7 @@ where
resolution: Option<u32>,
}
impl<Config, Clear> Arc2dBuilder<'_, '_, '_, Config, Clear>
impl<Config, Clear> Arc2dBuilder<'_, Config, Clear>
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
@ -92,7 +89,7 @@ where
}
}
impl<Config, Clear> Drop for Arc2dBuilder<'_, '_, '_, Config, Clear>
impl<Config, Clear> 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<I
// === 3D ===
impl<'w, 's, Config, Clear> Gizmos<'w, 's, Config, Clear>
impl<Config, Clear> GizmoBuffer<Config, Clear>
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
@ -178,7 +175,7 @@ where
radius: f32,
isometry: impl Into<Isometry3d>,
color: impl Into<Color>,
) -> 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<Color>,
) -> 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<Color>,
) -> 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<Color>,
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<Color>,
) -> 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<Color>,
) -> 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<Color>,
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<Config, Clear>,
// 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<u32>,
}
impl<Config, Clear> Arc3dBuilder<'_, '_, '_, Config, Clear>
impl<Config, Clear> Arc3dBuilder<'_, Config, Clear>
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
@ -482,7 +479,7 @@ where
}
}
impl<Config, Clear> Drop for Arc3dBuilder<'_, '_, '_, Config, Clear>
impl<Config, Clear> Drop for Arc3dBuilder<'_, Config, Clear>
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,

View file

@ -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<Config, Clear>,
start: Vec3,
end: Vec3,
color: Color,
@ -25,7 +25,7 @@ where
tip_length: f32,
}
impl<Config, Clear> ArrowBuilder<'_, '_, '_, Config, Clear>
impl<Config, Clear> ArrowBuilder<'_, Config, Clear>
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
@ -58,12 +58,12 @@ where
}
}
impl<Config, Clear> Drop for ArrowBuilder<'_, '_, '_, Config, Clear>
impl<Config, Clear> 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<Config, Clear> GizmoBuffer<Config, Clear>
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
@ -125,7 +125,7 @@ where
start: Vec3,
end: Vec3,
color: impl Into<Color>,
) -> 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<Color>,
) -> 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<Config, Clear> GizmoBuffer<Config, Clear>
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,

View file

@ -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<Item = Vec2>
})
}
impl<'w, 's, Config, Clear> Gizmos<'w, 's, Config, Clear>
impl<Config, Clear> GizmoBuffer<Config, Clear>
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
@ -54,7 +54,7 @@ where
isometry: impl Into<Isometry3d>,
half_size: Vec2,
color: impl Into<Color>,
) -> EllipseBuilder<'_, 'w, 's, Config, Clear> {
) -> EllipseBuilder<'_, Config, Clear> {
EllipseBuilder {
gizmos: self,
isometry: isometry.into(),
@ -95,7 +95,7 @@ where
isometry: impl Into<Isometry2d>,
half_size: Vec2,
color: impl Into<Color>,
) -> Ellipse2dBuilder<'_, 'w, 's, Config, Clear> {
) -> Ellipse2dBuilder<'_, Config, Clear> {
Ellipse2dBuilder {
gizmos: self,
isometry: isometry.into(),
@ -134,7 +134,7 @@ where
isometry: impl Into<Isometry3d>,
radius: f32,
color: impl Into<Color>,
) -> EllipseBuilder<'_, 'w, 's, Config, Clear> {
) -> EllipseBuilder<'_, Config, Clear> {
EllipseBuilder {
gizmos: self,
isometry: isometry.into(),
@ -175,7 +175,7 @@ where
isometry: impl Into<Isometry2d>,
radius: f32,
color: impl Into<Color>,
) -> Ellipse2dBuilder<'_, 'w, 's, Config, Clear> {
) -> Ellipse2dBuilder<'_, Config, Clear> {
Ellipse2dBuilder {
gizmos: self,
isometry: isometry.into(),
@ -217,7 +217,7 @@ where
isometry: impl Into<Isometry3d>,
radius: f32,
color: impl Into<Color>,
) -> 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<Config, Clear>,
isometry: Isometry3d,
half_size: Vec2,
color: Color,
resolution: u32,
}
impl<Config, Clear> EllipseBuilder<'_, '_, '_, Config, Clear>
impl<Config, Clear> EllipseBuilder<'_, Config, Clear>
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
@ -253,7 +253,7 @@ where
}
}
impl<Config, Clear> Drop for EllipseBuilder<'_, '_, '_, Config, Clear>
impl<Config, Clear> 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<Config, Clear>,
isometry: Isometry2d,
half_size: Vec2,
color: Color,
resolution: u32,
}
impl<Config, Clear> Ellipse2dBuilder<'_, '_, '_, Config, Clear>
impl<Config, Clear> Ellipse2dBuilder<'_, Config, Clear>
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
@ -294,7 +294,7 @@ where
}
}
impl<Config, Clear> Drop for Ellipse2dBuilder<'_, '_, '_, Config, Clear>
impl<Config, Clear> 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<Config, Clear>,
// Radius of the sphere
radius: f32,
@ -330,7 +330,7 @@ where
resolution: u32,
}
impl<Config, Clear> SphereBuilder<'_, '_, '_, Config, Clear>
impl<Config, Clear> SphereBuilder<'_, Config, Clear>
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
@ -342,7 +342,7 @@ where
}
}
impl<Config, Clear> Drop for SphereBuilder<'_, '_, '_, Config, Clear>
impl<Config, Clear> Drop for SphereBuilder<'_, Config, Clear>
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,

View file

@ -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::<T>()` 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<LineGizmo>,
pub handle: Handle<GizmoAsset>,
}

View file

@ -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<Config> Gizmos<'_, '_, Config>
impl<Config, Clear> GizmoBuffer<Config, Clear>
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
{
/// Draw a cross in 3D with the given `isometry` applied.
///

View file

@ -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<Config, Clear> GizmoBuffer<Config, Clear>
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,

View file

@ -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<Config, Clear>>,
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<Config, Clear>;
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<Config, Clear> = (
Deferred<'static, GizmoBuffer<Config, Clear>>,
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::<Config, Clear>::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::<Config>();
f0.enabled = config.enabled;
Gizmos {
buffer: f0,
enabled: config.enabled,
config,
config_ext,
}
@ -236,16 +265,20 @@ where
{
}
struct GizmoBuffer<Config, Clear>
/// Buffer for gizmo vertex data.
#[derive(Debug, Clone, Reflect)]
pub struct GizmoBuffer<Config, Clear>
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
{
list_positions: Vec<Vec3>,
list_colors: Vec<LinearRgba>,
strip_positions: Vec<Vec3>,
strip_colors: Vec<LinearRgba>,
marker: PhantomData<(Config, Clear)>,
pub(crate) enabled: bool,
pub(crate) list_positions: Vec<Vec3>,
pub(crate) list_colors: Vec<LinearRgba>,
pub(crate) strip_positions: Vec<Vec3>,
pub(crate) strip_colors: Vec<LinearRgba>,
#[reflect(ignore)]
pub(crate) marker: PhantomData<(Config, Clear)>,
}
impl<Config, Clear> Default for GizmoBuffer<Config, Clear>
@ -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<Vec3>,
/// Vertex colors for line-list topology.
pub list_colors: &'a Vec<LinearRgba>,
/// Vertex positions for line-strip topology.
pub strip_positions: &'a Vec<Vec3>,
/// Vertex colors for line-strip topology.
pub strip_colors: &'a Vec<LinearRgba>,
}
impl<Config, Clear> SystemBuffer for GizmoBuffer<Config, Clear>
where
Config: GizmoConfigGroup,
@ -278,11 +324,35 @@ where
}
}
impl<'w, 's, Config, Clear> Gizmos<'w, 's, Config, Clear>
impl<Config, Clear> GizmoBuffer<Config, Clear>
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<Item = Vec3>) {
self.buffer.list_positions.extend(positions);
self.list_positions.extend(positions);
}
#[inline]
fn extend_list_colors(&mut self, colors: impl IntoIterator<Item = impl Into<Color>>) {
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<Item = Vec3>) {
self.buffer.strip_positions.extend(positions);
self.buffer.strip_positions.push(Vec3::NAN);
self.strip_positions.extend(positions);
self.strip_positions.push(Vec3::NAN);
}
}

View file

@ -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<Config, Clear>,
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<Config, Clear>,
isometry: Isometry3d,
spacing: Vec2,
cell_count: UVec2,
@ -36,7 +36,7 @@ where
color: Color,
}
impl<Config, Clear> GridBuilder3d<'_, '_, '_, Config, Clear>
impl<Config, Clear> GridBuilder3d<'_, Config, Clear>
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
@ -92,7 +92,7 @@ where
}
}
impl<Config, Clear> GridBuilder2d<'_, '_, '_, Config, Clear>
impl<Config, Clear> GridBuilder2d<'_, Config, Clear>
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
@ -136,12 +136,12 @@ where
}
}
impl<Config, Clear> Drop for GridBuilder3d<'_, '_, '_, Config, Clear>
impl<Config, Clear> 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<Config, Clear> Drop for GridBuilder2d<'_, '_, '_, Config, Clear>
impl<Config, Clear> 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<Config, Clear> GizmoBuffer<Config, Clear>
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
@ -222,7 +222,7 @@ where
cell_count: UVec2,
spacing: Vec2,
color: impl Into<Color>,
) -> 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<Color>,
) -> 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<Color>,
) -> 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<Config, Clear>(
gizmos: &mut Gizmos<'_, '_, Config, Clear>,
gizmos: &mut GizmoBuffer<Config, Clear>,
isometry: Isometry3d,
spacing: Vec3,
cell_count: UVec3,

View file

@ -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::<GizmoConfig>()
.register_type::<GizmoConfigStore>()
.init_asset::<LineGizmo>()
.init_resource::<LineGizmoHandles>()
.init_asset::<GizmoAsset>()
.init_resource::<GizmoHandles>()
// We insert the Resource GizmoConfigStore into the world implicitly here if it does not exist.
.init_gizmo_group::<DefaultGizmoConfigGroup>();
@ -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::<bevy_sprite::SpritePlugin>() {
@ -245,13 +250,12 @@ impl AppGizmoBuilder for App {
.get_resource_or_init::<GizmoConfigStore>()
.register::<Config>();
let mut handles = self.world_mut().get_resource_or_init::<LineGizmoHandles>();
let mut handles = self.world_mut().get_resource_or_init::<GizmoHandles>();
handles.list.insert(TypeId::of::<Config>(), None);
handles.strip.insert(TypeId::of::<Config>(), None);
handles.handles.insert(TypeId::of::<Config>(), None);
// These handles are safe to mutate in any order
self.allow_ambiguous_resource::<LineGizmoHandles>();
self.allow_ambiguous_resource::<GizmoHandles>();
self.init_resource::<GizmoStorage<Config, ()>>()
.init_resource::<GizmoStorage<Config, Fixed>>()
@ -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<Option<Handle<LineGizmo>>>,
strip: TypeIdMap<Option<Handle<LineGizmo>>>,
struct GizmoHandles {
handles: TypeIdMap<Option<Handle<GizmoAsset>>>,
}
/// Start a new gizmo clearing context.
@ -378,52 +381,34 @@ pub struct UpdateGizmoMeshes;
///
/// This also clears the default `GizmoStorage`.
fn update_gizmo_meshes<Config: GizmoConfigGroup>(
mut line_gizmos: ResMut<Assets<LineGizmo>>,
mut handles: ResMut<LineGizmoHandles>,
mut gizmo_assets: ResMut<Assets<GizmoAsset>>,
mut handles: ResMut<GizmoHandles>,
mut storage: ResMut<GizmoStorage<Config, ()>>,
config_store: Res<GizmoConfigStore>,
) {
if storage.list_positions.is_empty() {
handles.list.insert(TypeId::of::<Config>(), None);
} else if let Some(handle) = handles.list.get_mut(&TypeId::of::<Config>()) {
if storage.list_positions.is_empty() && storage.strip_positions.is_empty() {
handles.handles.insert(TypeId::of::<Config>(), None);
} else if let Some(handle) = handles.handles.get_mut(&TypeId::of::<Config>()) {
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::<Config>(),
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::<Config>();
if storage.strip_positions.is_empty() {
handles.strip.insert(TypeId::of::<Config>(), None);
} else if let Some(handle) = handles.strip.get_mut(&TypeId::of::<Config>()) {
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::<Config>(),
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<Config: GizmoConfigGroup>(
#[cfg(feature = "bevy_render")]
fn extract_gizmo_data(
mut commands: Commands,
handles: Extract<Res<LineGizmoHandles>>,
handles: Extract<Res<GizmoHandles>>,
config: Extract<Res<GizmoConfigStore>>,
) {
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<Vec3>,
/// Colors of the gizmo's vertices
pub colors: Vec<LinearRgba>,
/// 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<ErasedGizmoConfigGroup, ()>,
config_ty: TypeId,
}
impl GizmoAsset {
/// Create a new [`GizmoAsset`].
pub fn new() -> Self {
GizmoAsset {
buffer: GizmoBuffer::default(),
config_ty: TypeId::of::<ErasedGizmoConfigGroup>(),
}
}
/// 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<RenderDevice>;
fn prepare_asset(
@ -523,26 +528,37 @@ impl RenderAsset for GpuLineGizmo {
_: AssetId<Self::SourceAsset>,
render_device: &mut SystemParamItem<Self::Param>,
) -> Result<Self, PrepareAssetError<Self::SourceAsset>> {
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<const I: usize, P: PhaseItem> RenderCommand<P> for SetLineGizmoBindGroup<I>
}
#[cfg(feature = "bevy_render")]
struct DrawLineGizmo;
struct DrawLineGizmo<const STRIP: bool>;
#[cfg(all(
feature = "bevy_render",
any(feature = "bevy_pbr", feature = "bevy_sprite")
))]
impl<P: PhaseItem> RenderCommand<P> for DrawLineGizmo {
impl<P: PhaseItem, const STRIP: bool> RenderCommand<P> for DrawLineGizmo<STRIP> {
type Param = SRes<RenderAssets<GpuLineGizmo>>;
type ViewQuery = ();
type ItemQuery = Read<GizmoMeshConfig>;
@ -631,28 +647,35 @@ impl<P: PhaseItem> RenderCommand<P> 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<P: PhaseItem> RenderCommand<P> 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,

View file

@ -1,9 +1,10 @@
#import bevy_render::view::View
#import bevy_render::{view::View, maths::affine3_to_square}
@group(0) @binding(0) var<uniform> view: View;
struct LineGizmoUniform {
world_from_local: mat3x4<f32>,
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);

View file

@ -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<uniform> view: View;
struct LineGizmoUniform {
world_from_local: mat3x4<f32>,
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);

View file

@ -39,6 +39,7 @@ impl Plugin for LineGizmo2dPlugin {
render_app
.add_render_command::<Transparent2d, DrawLineGizmo2d>()
.add_render_command::<Transparent2d, DrawLineGizmo2dStrip>()
.add_render_command::<Transparent2d, DrawLineJointGizmo2d>()
.init_resource::<SpecializedRenderPipelines<LineGizmoPipeline>>()
.init_resource::<SpecializedRenderPipelines<LineJointGizmoPipeline>>()
@ -271,7 +272,13 @@ type DrawLineGizmo2d = (
SetItemPipeline,
SetMesh2dViewBindGroup<0>,
SetLineGizmoBindGroup<1>,
DrawLineGizmo,
DrawLineGizmo<false>,
);
type DrawLineGizmo2dStrip = (
SetItemPipeline,
SetMesh2dViewBindGroup<0>,
SetLineGizmoBindGroup<1>,
DrawLineGizmo<true>,
);
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::<DrawLineGizmo2d>().unwrap();
let draw_function_strip = draw_functions
.read()
.get_id::<DrawLineGizmo2dStrip>()
.unwrap();
for (view_entity, view, msaa, render_layers) in &mut views {
let Some(transparent_phase) = transparent_render_phases.get_mut(&view_entity) else {
@ -311,16 +322,16 @@ fn queue_line_gizmos_2d(
continue;
};
if line_gizmo.list_vertex_count > 0 {
let pipeline = pipelines.specialize(
&pipeline_cache,
&pipeline,
LineGizmoPipelineKey {
mesh_key,
strip: line_gizmo.strip,
strip: false,
line_style: config.line_style,
},
);
transparent_phase.add(Transparent2d {
entity: (entity, *main_entity),
draw_function,
@ -330,6 +341,27 @@ fn queue_line_gizmos_2d(
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 {

View file

@ -41,6 +41,7 @@ impl Plugin for LineGizmo3dPlugin {
render_app
.add_render_command::<Transparent3d, DrawLineGizmo3d>()
.add_render_command::<Transparent3d, DrawLineGizmo3dStrip>()
.add_render_command::<Transparent3d, DrawLineJointGizmo3d>()
.init_resource::<SpecializedRenderPipelines<LineGizmoPipeline>>()
.init_resource::<SpecializedRenderPipelines<LineJointGizmoPipeline>>()
@ -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<false>,
);
type DrawLineGizmo3dStrip = (
SetItemPipeline,
SetMeshViewBindGroup<0>,
SetLineGizmoBindGroup<1>,
DrawLineGizmo<true>,
);
type DrawLineJointGizmo3d = (
SetItemPipeline,
@ -298,6 +305,10 @@ fn queue_line_gizmos_3d(
)>,
) {
let draw_function = draw_functions.read().get_id::<DrawLineGizmo3d>().unwrap();
let draw_function_strip = draw_functions
.read()
.get_id::<DrawLineGizmo3dStrip>()
.unwrap();
for (
view_entity,
@ -341,17 +352,17 @@ fn queue_line_gizmos_3d(
continue;
};
if line_gizmo.list_vertex_count > 0 {
let pipeline = pipelines.specialize(
&pipeline_cache,
&pipeline,
LineGizmoPipelineKey {
view_key,
strip: line_gizmo.strip,
strip: false,
perspective: config.line_perspective,
line_style: config.line_style,
},
);
transparent_phase.add(Transparent3d {
entity: (entity, *main_entity),
draw_function,
@ -361,6 +372,28 @@ fn queue_line_gizmos_3d(
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,
},
);

View file

@ -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<P: Primitive2d> {
/// The output of `primitive_2d`. This is a builder to set non-default values.
type Output<'a>
@ -40,7 +40,7 @@ pub trait GizmoPrimitive2d<P: Primitive2d> {
// direction 2d
impl<'w, 's, Config, Clear> GizmoPrimitive2d<Dir2> for Gizmos<'w, 's, Config, Clear>
impl<Config, Clear> GizmoPrimitive2d<Dir2> for GizmoBuffer<Config, Clear>
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
@ -68,7 +68,7 @@ where
// arc 2d
impl<'w, 's, Config, Clear> GizmoPrimitive2d<Arc2d> for Gizmos<'w, 's, Config, Clear>
impl<Config, Clear> GizmoPrimitive2d<Arc2d> for GizmoBuffer<Config, Clear>
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
@ -102,13 +102,13 @@ where
// circle 2d
impl<'w, 's, Config, Clear> GizmoPrimitive2d<Circle> for Gizmos<'w, 's, Config, Clear>
impl<Config, Clear> GizmoPrimitive2d<Circle> for GizmoBuffer<Config, Clear>
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<CircularSector> for Gizmos<'w, 's, Config, Clear>
impl<Config, Clear> GizmoPrimitive2d<CircularSector> for GizmoBuffer<Config, Clear>
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
@ -167,7 +167,7 @@ where
// circular segment 2d
impl<'w, 's, Config, Clear> GizmoPrimitive2d<CircularSegment> for Gizmos<'w, 's, Config, Clear>
impl<Config, Clear> GizmoPrimitive2d<CircularSegment> for GizmoBuffer<Config, Clear>
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
@ -209,13 +209,13 @@ where
// ellipse 2d
impl<'w, 's, Config, Clear> GizmoPrimitive2d<Ellipse> for Gizmos<'w, 's, Config, Clear>
impl<Config, Clear> GizmoPrimitive2d<Ellipse> for GizmoBuffer<Config, Clear>
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<Config, Clear>,
isometry: Isometry2d,
inner_radius: f32,
outer_radius: f32,
@ -246,7 +246,7 @@ where
outer_resolution: u32,
}
impl<Config, Clear> Annulus2dBuilder<'_, '_, '_, Config, Clear>
impl<Config, Clear> Annulus2dBuilder<'_, Config, Clear>
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
@ -271,13 +271,13 @@ where
}
}
impl<'w, 's, Config, Clear> GizmoPrimitive2d<Annulus> for Gizmos<'w, 's, Config, Clear>
impl<Config, Clear> GizmoPrimitive2d<Annulus> for GizmoBuffer<Config, Clear>
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<Config, Clear> Drop for Annulus2dBuilder<'_, '_, '_, Config, Clear>
impl<Config, Clear> 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<Rhombus> for Gizmos<'w, 's, Config, Clear>
impl<Config, Clear> GizmoPrimitive2d<Rhombus> for GizmoBuffer<Config, Clear>
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
@ -365,7 +365,7 @@ where
// capsule 2d
impl<'w, 's, Config, Clear> GizmoPrimitive2d<Capsule2d> for Gizmos<'w, 's, Config, Clear>
impl<Config, Clear> GizmoPrimitive2d<Capsule2d> for GizmoBuffer<Config, Clear>
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<Config, Clear>,
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<Config, Clear> Line2dBuilder<'_, '_, '_, Config, Clear>
impl<Config, Clear> Line2dBuilder<'_, Config, Clear>
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
@ -458,13 +458,13 @@ where
}
}
impl<'w, 's, Config, Clear> GizmoPrimitive2d<Line2d> for Gizmos<'w, 's, Config, Clear>
impl<Config, Clear> GizmoPrimitive2d<Line2d> for GizmoBuffer<Config, Clear>
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<Config, Clear> Drop for Line2dBuilder<'_, '_, '_, Config, Clear>
impl<Config, Clear> 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<Plane2d> for Gizmos<'w, 's, Config, Clear>
impl<Config, Clear> GizmoPrimitive2d<Plane2d> for GizmoBuffer<Config, Clear>
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<Config, Clear>,
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<Config, Clear> Segment2dBuilder<'_, '_, '_, Config, Clear>
impl<Config, Clear> Segment2dBuilder<'_, Config, Clear>
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
@ -598,13 +598,13 @@ where
}
}
impl<'w, 's, Config, Clear> GizmoPrimitive2d<Segment2d> for Gizmos<'w, 's, Config, Clear>
impl<Config, Clear> GizmoPrimitive2d<Segment2d> for GizmoBuffer<Config, Clear>
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<Config, Clear> Drop for Segment2dBuilder<'_, '_, '_, Config, Clear>
impl<Config, Clear> 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<Polyline2d<N>>
for Gizmos<'w, 's, Config, Clear>
impl<const N: usize, Config, Clear> GizmoPrimitive2d<Polyline2d<N>> for GizmoBuffer<Config, Clear>
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
@ -687,7 +686,7 @@ where
// boxed polyline 2d
impl<'w, 's, Config, Clear> GizmoPrimitive2d<BoxedPolyline2d> for Gizmos<'w, 's, Config, Clear>
impl<Config, Clear> GizmoPrimitive2d<BoxedPolyline2d> for GizmoBuffer<Config, Clear>
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
@ -722,7 +721,7 @@ where
// triangle 2d
impl<'w, 's, Config, Clear> GizmoPrimitive2d<Triangle2d> for Gizmos<'w, 's, Config, Clear>
impl<Config, Clear> GizmoPrimitive2d<Triangle2d> for GizmoBuffer<Config, Clear>
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
@ -752,7 +751,7 @@ where
// rectangle 2d
impl<'w, 's, Config, Clear> GizmoPrimitive2d<Rectangle> for Gizmos<'w, 's, Config, Clear>
impl<Config, Clear> GizmoPrimitive2d<Rectangle> for GizmoBuffer<Config, Clear>
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
@ -788,8 +787,7 @@ where
// polygon 2d
impl<'w, 's, const N: usize, Config, Clear> GizmoPrimitive2d<Polygon<N>>
for Gizmos<'w, 's, Config, Clear>
impl<const N: usize, Config, Clear> GizmoPrimitive2d<Polygon<N>> for GizmoBuffer<Config, Clear>
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
@ -834,7 +832,7 @@ where
// boxed polygon 2d
impl<'w, 's, Config, Clear> GizmoPrimitive2d<BoxedPolygon> for Gizmos<'w, 's, Config, Clear>
impl<Config, Clear> GizmoPrimitive2d<BoxedPolygon> for GizmoBuffer<Config, Clear>
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
@ -877,7 +875,7 @@ where
// regular polygon 2d
impl<'w, 's, Config, Clear> GizmoPrimitive2d<RegularPolygon> for Gizmos<'w, 's, Config, Clear>
impl<Config, Clear> GizmoPrimitive2d<RegularPolygon> for GizmoBuffer<Config, Clear>
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,

View file

@ -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<P: Primitive3d> {
/// The output of `primitive_3d`. This is a builder to set non-default values.
type Output<'a>
@ -38,7 +35,7 @@ pub trait GizmoPrimitive3d<P: Primitive3d> {
// direction 3d
impl<'w, 's, Config, Clear> GizmoPrimitive3d<Dir3> for Gizmos<'w, 's, Config, Clear>
impl<Config, Clear> GizmoPrimitive3d<Dir3> for GizmoBuffer<Config, Clear>
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
@ -63,13 +60,13 @@ where
// sphere
impl<'w, 's, Config, Clear> GizmoPrimitive3d<Sphere> for Gizmos<'w, 's, Config, Clear>
impl<Config, Clear> GizmoPrimitive3d<Sphere> for GizmoBuffer<Config, Clear>
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<Config, Clear>,
// Direction of the normal orthogonal to the plane
normal: Dir3,
@ -106,7 +103,7 @@ where
spacing: Vec2,
}
impl<Config, Clear> Plane3dBuilder<'_, '_, '_, Config, Clear>
impl<Config, Clear> Plane3dBuilder<'_, Config, Clear>
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
@ -124,13 +121,13 @@ where
}
}
impl<'w, 's, Config, Clear> GizmoPrimitive3d<Plane3d> for Gizmos<'w, 's, Config, Clear>
impl<Config, Clear> GizmoPrimitive3d<Plane3d> for GizmoBuffer<Config, Clear>
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<Config, Clear> Drop for Plane3dBuilder<'_, '_, '_, Config, Clear>
impl<Config, Clear> 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<Line3d> for Gizmos<'w, 's, Config, Clear>
impl<Config, Clear> GizmoPrimitive3d<Line3d> for GizmoBuffer<Config, Clear>
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
@ -211,7 +208,7 @@ where
// segment 3d
impl<'w, 's, Config, Clear> GizmoPrimitive3d<Segment3d> for Gizmos<'w, 's, Config, Clear>
impl<Config, Clear> GizmoPrimitive3d<Segment3d> for GizmoBuffer<Config, Clear>
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
@ -239,8 +236,7 @@ where
// polyline 3d
impl<'w, 's, const N: usize, Config, Clear> GizmoPrimitive3d<Polyline3d<N>>
for Gizmos<'w, 's, Config, Clear>
impl<const N: usize, Config, Clear> GizmoPrimitive3d<Polyline3d<N>> for GizmoBuffer<Config, Clear>
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
@ -267,7 +263,7 @@ where
// boxed polyline 3d
impl<'w, 's, Config, Clear> GizmoPrimitive3d<BoxedPolyline3d> for Gizmos<'w, 's, Config, Clear>
impl<Config, Clear> GizmoPrimitive3d<BoxedPolyline3d> for GizmoBuffer<Config, Clear>
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
@ -301,7 +297,7 @@ where
// triangle 3d
impl<'w, 's, Config, Clear> GizmoPrimitive3d<Triangle3d> for Gizmos<'w, 's, Config, Clear>
impl<Config, Clear> GizmoPrimitive3d<Triangle3d> for GizmoBuffer<Config, Clear>
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
@ -329,7 +325,7 @@ where
// cuboid
impl<'w, 's, Config, Clear> GizmoPrimitive3d<Cuboid> for Gizmos<'w, 's, Config, Clear>
impl<Config, Clear> GizmoPrimitive3d<Cuboid> for GizmoBuffer<Config, Clear>
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<Config, Clear>,
// Radius of the cylinder
radius: f32,
@ -412,7 +408,7 @@ where
resolution: u32,
}
impl<Config, Clear> Cylinder3dBuilder<'_, '_, '_, Config, Clear>
impl<Config, Clear> Cylinder3dBuilder<'_, Config, Clear>
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
@ -424,13 +420,13 @@ where
}
}
impl<'w, 's, Config, Clear> GizmoPrimitive3d<Cylinder> for Gizmos<'w, 's, Config, Clear>
impl<Config, Clear> GizmoPrimitive3d<Cylinder> for GizmoBuffer<Config, Clear>
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<Config, Clear> Drop for Cylinder3dBuilder<'_, '_, '_, Config, Clear>
impl<Config, Clear> 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<Config, Clear>,
// Radius of the capsule
radius: f32,
@ -498,7 +494,7 @@ where
resolution: u32,
}
impl<Config, Clear> Capsule3dBuilder<'_, '_, '_, Config, Clear>
impl<Config, Clear> Capsule3dBuilder<'_, Config, Clear>
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
@ -510,13 +506,13 @@ where
}
}
impl<'w, 's, Config, Clear> GizmoPrimitive3d<Capsule3d> for Gizmos<'w, 's, Config, Clear>
impl<Config, Clear> GizmoPrimitive3d<Capsule3d> for GizmoBuffer<Config, Clear>
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<Config, Clear> Drop for Capsule3dBuilder<'_, '_, '_, Config, Clear>
impl<Config, Clear> 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<Config, Clear>,
// Radius of the cone
radius: f32,
@ -619,7 +615,7 @@ where
height_resolution: u32,
}
impl<Config, Clear> Cone3dBuilder<'_, '_, '_, Config, Clear>
impl<Config, Clear> Cone3dBuilder<'_, Config, Clear>
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
@ -650,13 +646,13 @@ where
}
}
impl<'w, 's, Config, Clear> GizmoPrimitive3d<Cone> for Gizmos<'w, 's, Config, Clear>
impl<Config, Clear> GizmoPrimitive3d<Cone> for GizmoBuffer<Config, Clear>
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<Config, Clear> Drop for Cone3dBuilder<'_, '_, '_, Config, Clear>
impl<Config, Clear> 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<Config, Clear>,
// Radius of the top circle
radius_top: f32,
@ -740,7 +736,7 @@ where
resolution: u32,
}
impl<Config, Clear> ConicalFrustum3dBuilder<'_, '_, '_, Config, Clear>
impl<Config, Clear> ConicalFrustum3dBuilder<'_, Config, Clear>
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
@ -752,13 +748,13 @@ where
}
}
impl<'w, 's, Config, Clear> GizmoPrimitive3d<ConicalFrustum> for Gizmos<'w, 's, Config, Clear>
impl<Config, Clear> GizmoPrimitive3d<ConicalFrustum> for GizmoBuffer<Config, Clear>
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<Config, Clear> Drop for ConicalFrustum3dBuilder<'_, '_, '_, Config, Clear>
impl<Config, Clear> 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<Config, Clear>,
// Radius of the minor circle (tube)
minor_radius: f32,
@ -838,7 +834,7 @@ where
major_resolution: u32,
}
impl<Config, Clear> Torus3dBuilder<'_, '_, '_, Config, Clear>
impl<Config, Clear> Torus3dBuilder<'_, Config, Clear>
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
@ -856,13 +852,13 @@ where
}
}
impl<'w, 's, Config, Clear> GizmoPrimitive3d<Torus> for Gizmos<'w, 's, Config, Clear>
impl<Config, Clear> GizmoPrimitive3d<Torus> for GizmoBuffer<Config, Clear>
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<Config, Clear> Drop for Torus3dBuilder<'_, '_, '_, Config, Clear>
impl<Config, Clear> Drop for Torus3dBuilder<'_, Config, Clear>
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
@ -936,7 +932,11 @@ where
// tetrahedron
impl<'w, 's, T: GizmoConfigGroup> GizmoPrimitive3d<Tetrahedron> for Gizmos<'w, 's, T> {
impl<Config, Clear> GizmoPrimitive3d<Tetrahedron> for GizmoBuffer<Config, Clear>
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
{
type Output<'a>
= ()
where

View file

@ -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<ErasedGizmoConfigGroup, ()>;
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<Assets<GizmoAsset>>,
/// ) {
/// 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<GizmoAsset>,
/// 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<usize>,
query: Extract<Query<(Entity, &Gizmo, &GlobalTransform, Option<&RenderLayers>)>>,
) {
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);
}

View file

@ -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, Clear>,
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, Clear>,
config: RoundedBoxConfig,
}
struct RoundedBoxConfig {
@ -29,7 +37,11 @@ struct RoundedBoxConfig {
arc_resolution: u32,
}
impl<T: GizmoConfigGroup> RoundedRectBuilder<'_, '_, '_, T> {
impl<Config, Clear> 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<T: GizmoConfigGroup> RoundedRectBuilder<'_, '_, '_, T> {
self
}
}
impl<T: GizmoConfigGroup> RoundedCuboidBuilder<'_, '_, '_, T> {
impl<Config, Clear> 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<T: GizmoConfigGroup> RoundedCuboidBuilder<'_, '_, '_, T> {
}
}
impl<T: GizmoConfigGroup> Drop for RoundedRectBuilder<'_, '_, '_, T> {
impl<Config, Clear> 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<T: GizmoConfigGroup> Drop for RoundedRectBuilder<'_, '_, '_, T> {
}
}
impl<T: GizmoConfigGroup> Drop for RoundedCuboidBuilder<'_, '_, '_, T> {
impl<Config, Clear> 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<T: GizmoConfigGroup> Drop for RoundedCuboidBuilder<'_, '_, '_, T> {
}
}
impl<'w, 's, T: GizmoConfigGroup> Gizmos<'w, 's, T> {
impl<Config, Clear> GizmoBuffer<Config, Clear>
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<Isometry3d>,
size: Vec2,
color: impl Into<Color>,
) -> 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<Isometry2d>,
size: Vec2,
color: impl Into<Color>,
) -> 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<Isometry3d>,
size: Vec3,
color: impl Into<Color>,
) -> RoundedCuboidBuilder<'_, 'w, 's, T> {
) -> RoundedCuboidBuilder<'_, Config, Clear> {
let corner_radius = size.min_element() * DEFAULT_CORNER_RADIUS;
RoundedCuboidBuilder {
gizmos: self,

View file

@ -129,24 +129,24 @@ fn update_config(
) {
let (config, _) = config_store.config_mut::<DefaultGizmoConfigGroup>();
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::<MyRoundGizmos>();
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,

View file

@ -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<Assets<GizmoAsset>>,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
) {
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::<DefaultGizmoConfigGroup>();
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::<MyRoundGizmos>();
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,

View file

@ -155,12 +155,12 @@ fn update_config(
let (config, light_config) = config_store.config_mut::<LightGizmoConfigGroup>();
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;