Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
95e82d7
basic visibility for retained gizmos
blamelessgames Dec 6, 2025
d251dfb
flesh out the visibility integration
blamelessgames Dec 7, 2025
92dfd0c
fleshed out the example some
blamelessgames Dec 7, 2025
ccda694
generated examples/README.md
blamelessgames Dec 7, 2025
d07ee96
fixing CI errors
blamelessgames Dec 7, 2025
a789873
Merge branch 'main' into visibility-for-retained-gizmos
blamelessgames Dec 9, 2025
5785504
Merge branch 'main' into visibility-for-retained-gizmos
blamelessgames Dec 11, 2025
97e4f18
Merge branch 'main' into visibility-for-retained-gizmos
blamelessgames Dec 18, 2025
068acc8
formatted
blamelessgames Dec 18, 2025
c1c6498
Merge branch 'main' into visibility-for-retained-gizmos
blamelessgames Dec 24, 2025
7b0a768
Merge branch 'main' into visibility-for-retained-gizmos
blamelessgames Dec 31, 2025
78954a4
Merge branch 'main' into visibility-for-retained-gizmos
blamelessgames Jan 3, 2026
a1cc4cc
Merge branch 'main' into visibility-for-retained-gizmos
blamelessgames Jan 5, 2026
ff84bfe
Merge branch 'main' into visibility-for-retained-gizmos
blamelessgames Jan 6, 2026
521ae38
Merge branch 'main' into visibility-for-retained-gizmos
blamelessgames Jan 7, 2026
86a491d
Merge branch 'main' into visibility-for-retained-gizmos
blamelessgames Jan 7, 2026
9eecc79
Merge branch 'main' into visibility-for-retained-gizmos
blamelessgames Jan 7, 2026
637522e
Merge branch 'main' into visibility-for-retained-gizmos
blamelessgames Jan 8, 2026
2e03531
Merge branch 'main' into visibility-for-retained-gizmos
blamelessgames Jan 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4427,6 +4427,18 @@ description = "A scene showcasing light gizmos"
category = "Gizmos"
wasm = true

[[example]]
name = "retained_gizmos"
path = "examples/gizmos/retained_gizmos.rs"
doc-scrape-examples = true
required-features = ["free_camera"]

[package.metadata.example.retained_gizmos]
name = "Retained Gizmos"
description = "A scene showcasing retained gizmos"
category = "Gizmos"
wasm = true

[[example]]
name = "custom_gltf_vertex_attribute"
path = "examples/gltf/custom_gltf_vertex_attribute.rs"
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_gizmos_render/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ bevy_render = { path = "../bevy_render", version = "0.18.0-dev" }
bevy_utils = { path = "../bevy_utils", version = "0.18.0-dev" }
bevy_core_pipeline = { path = "../bevy_core_pipeline", version = "0.18.0-dev" }
bevy_transform = { path = "../bevy_transform", version = "0.18.0-dev" }
bevy_platform = { path = "../bevy_platform", version = "0.18.0-dev" }

# other
bytemuck = "1.0"
Expand Down
57 changes: 33 additions & 24 deletions crates/bevy_gizmos_render/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,35 +24,39 @@ mod pipeline_2d;
#[cfg(feature = "bevy_pbr")]
mod pipeline_3d;

use bevy_app::{App, Plugin};
use bevy_ecs::{
resource::Resource,
schedule::{IntoScheduleConfigs, SystemSet},
system::Res,
};

use {bevy_gizmos::config::GizmoMeshConfig, bevy_mesh::VertexBufferLayout};

use {
crate::retained::extract_linegizmos,
bevy_asset::AssetId,
crate::retained::{
calculate_bounds, extract_linegizmos, mark_gizmos_as_changed_if_their_assets_changed,
},
bevy_app::{App, Plugin, PostUpdate},
bevy_asset::{AssetEventSystems, AssetId},
bevy_camera::visibility::{self, Visibility, VisibilityClass, VisibilitySystems},
bevy_ecs::{
component::Component,
entity::Entity,
query::ROQueryItem,
resource::Resource,
schedule::{IntoScheduleConfigs, SystemSet},
system::{
lifetimeless::{Read, SRes},
Commands, SystemParamItem,
Commands, Res, SystemParamItem,
},
},
bevy_gizmos::{
config::{GizmoConfigStore, GizmoLineJoint, GizmoMeshConfig},
prelude::Gizmo,
GizmoAsset, GizmoHandles,
},
bevy_math::{Affine3, Affine3A, Vec4},
bevy_mesh::VertexBufferLayout,
bevy_render::{
extract_component::{ComponentUniforms, DynamicUniformIndex, UniformComponentPlugin},
render_asset::{PrepareAssetError, RenderAsset, RenderAssetPlugin, RenderAssets},
render_phase::{PhaseItem, RenderCommand, RenderCommandResult, TrackedRenderPass},
render_resource::{
binding_types::uniform_buffer, BindGroup, BindGroupEntries, BindGroupLayoutEntries,
Buffer, BufferInitDescriptor, BufferUsages, ShaderStages, ShaderType, VertexFormat,
binding_types::uniform_buffer, BindGroup, BindGroupEntries, BindGroupLayoutDescriptor,
BindGroupLayoutEntries, Buffer, BufferInitDescriptor, BufferUsages, PipelineCache,
ShaderStages, ShaderType, VertexAttribute, VertexFormat, VertexStepMode,
},
renderer::RenderDevice,
sync_world::{MainEntity, TemporaryRenderEntity},
Expand All @@ -61,15 +65,6 @@ use {
bytemuck::cast_slice,
};

use bevy_render::render_resource::{
BindGroupLayoutDescriptor, PipelineCache, VertexAttribute, VertexStepMode,
};

use bevy_gizmos::{
config::{GizmoConfigStore, GizmoLineJoint},
GizmoAsset, GizmoHandles,
};

/// A [`Plugin`] that provides an immediate mode drawing api for visual debugging.
///
/// Requires to be loaded after [`PbrPlugin`](bevy_pbr::PbrPlugin) or [`SpriteRenderPlugin`](bevy_sprite_render::SpriteRenderPlugin).
Expand All @@ -85,7 +80,21 @@ impl Plugin for GizmoRenderPlugin {
}

app.add_plugins(UniformComponentPlugin::<LineGizmoUniform>::default())
.add_plugins(RenderAssetPlugin::<GpuLineGizmo>::default());
.add_plugins(RenderAssetPlugin::<GpuLineGizmo>::default())
.register_required_components::<Gizmo, Visibility>()
.register_required_components::<Gizmo, VisibilityClass>()
.add_systems(
PostUpdate,
(
mark_gizmos_as_changed_if_their_assets_changed.after(AssetEventSystems),
calculate_bounds.in_set(VisibilitySystems::CalculateBounds),
)
.chain(),
);

app.world_mut()
.register_component_hooks::<Gizmo>()
.on_add(visibility::add_visibility_class::<Gizmo>);

if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
render_app.add_systems(RenderStartup, init_line_gizmo_uniform_bind_group_layout);
Expand Down
101 changes: 87 additions & 14 deletions crates/bevy_gizmos_render/src/retained.rs
Original file line number Diff line number Diff line change
@@ -1,38 +1,61 @@
//! This module is for 'retained' alternatives to the 'immediate mode' [`Gizmos`](bevy_gizmos::gizmos::Gizmos) system parameter.

use crate::LineGizmoUniform;
use bevy_camera::visibility::RenderLayers;
use bevy_gizmos::retained::Gizmo;
use bevy_math::Affine3;
use bevy_render::sync_world::{MainEntity, TemporaryRenderEntity};
use bevy_utils::once;
use tracing::warn;
use {
crate::LineGizmoUniform,
bevy_asset::{AssetEvent, AssetId, Assets},
bevy_camera::{
primitives::Aabb,
visibility::{NoFrustumCulling, RenderLayers, ViewVisibility},
},
bevy_ecs::{
change_detection::DetectChangesMut,
entity::Entity,
system::{Commands, Local, Query},
message::MessageReader,
query::{Changed, Or, Without},
system::{Commands, Local, Query, Res},
},
bevy_gizmos::{
config::{GizmoLineJoint, GizmoLineStyle},
retained::Gizmo,
GizmoAsset,
},
bevy_math::{bounding::Aabb3d, Affine3, Isometry3d, Vec3A},
bevy_platform::{collections::HashSet, hash::FixedHasher},
bevy_render::{
sync_world::{MainEntity, TemporaryRenderEntity},
Extract,
},
bevy_gizmos::config::GizmoLineJoint,
bevy_render::Extract,
bevy_transform::components::GlobalTransform,
bevy_utils::once,
tracing::warn,
};

use bevy_gizmos::config::GizmoLineStyle;

pub(crate) fn extract_linegizmos(
mut commands: Commands,
mut previous_len: Local<usize>,
query: Extract<Query<(Entity, &Gizmo, &GlobalTransform, Option<&RenderLayers>)>>,
query: Extract<
Query<(
Entity,
&Gizmo,
&GlobalTransform,
&ViewVisibility,
Option<&RenderLayers>,
)>,
>,
) {
let mut values = Vec::with_capacity(*previous_len);

#[cfg_attr(
not(any(feature = "bevy_pbr", feature = "bevy_sprite_render")),
expect(
unused_variables,
reason = "`render_layers` is unused when bevy_pbr and bevy_sprite_render are both disabled."
)
)]
for (entity, gizmo, transform, render_layers) in &query {
for (entity, gizmo, transform, view_visibility, render_layers) in &query {
if !view_visibility.get() {
continue;
}
let joints_resolution = if let GizmoLineJoint::Round(resolution) = gizmo.line_config.joints
{
resolution
Expand Down Expand Up @@ -81,3 +104,53 @@ pub(crate) fn extract_linegizmos(
*previous_len = values.len();
commands.spawn_batch(values);
}

pub(crate) fn calculate_bounds(
mut commands: Commands,
gizmo_assets: Res<Assets<GizmoAsset>>,
needs_aabb: Query<
(Entity, &Gizmo),
(
Or<(Changed<Gizmo>, Without<Aabb>)>,
Without<NoFrustumCulling>,
),
>,
) {
for (entity, gizmo) in &needs_aabb {
if let Some(gizmo_asset) = gizmo_assets.get(&gizmo.handle) {
let aabb_3d = Aabb3d::from_point_cloud(
Isometry3d::IDENTITY,
gizmo_asset
.list_positions
.iter()
.chain(gizmo_asset.strip_positions.iter())
.filter(|p| p.is_finite())
.map(|&p| Vec3A::from(p)),
);
let aabb: Aabb = aabb_3d.into();
commands.entity(entity).insert(aabb);
}
}
}

pub(crate) fn mark_gizmos_as_changed_if_their_assets_changed(
mut gizmos: Query<&mut Gizmo>,
mut gizmo_asset_events: MessageReader<AssetEvent<GizmoAsset>>,
) {
let mut changed_gizmos: HashSet<AssetId<GizmoAsset>, FixedHasher> = HashSet::default();
for mesh_asset_event in gizmo_asset_events.read() {
if let AssetEvent::Modified { id } = mesh_asset_event {
changed_gizmos.insert(*id);
}
}

if changed_gizmos.is_empty() {
return;
}

for mut gizmo in &mut gizmos {
if changed_gizmos.contains(&gizmo.handle.id()) {
gizmo.set_changed();
}
}
}
1 change: 1 addition & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,7 @@ Example | Description
[3D Gizmos](../examples/gizmos/3d_gizmos.rs) | A scene showcasing 3D gizmos
[Axes](../examples/gizmos/axes.rs) | Demonstrates the function of axes gizmos
[Light Gizmos](../examples/gizmos/light_gizmos.rs) | A scene showcasing light gizmos
[Retained Gizmos](../examples/gizmos/retained_gizmos.rs) | A scene showcasing retained gizmos

### Helpers

Expand Down
24 changes: 0 additions & 24 deletions examples/gizmos/3d_gizmos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,33 +22,9 @@ 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),
Expand Down
Loading
Loading