Skip to content

feat: implement proof of concept animated gizmos #18895

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions crates/bevy_gizmos/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ bevy_time = { path = "../bevy_time", version = "0.16.0-dev" }
# other
bytemuck = "1.0"
tracing = { version = "0.1", default-features = false, features = ["std"] }
itertools = { version = "0.14.0", default-features = false }

[lints]
workspace = true
Expand Down
4 changes: 4 additions & 0 deletions crates/bevy_gizmos/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ pub enum GizmoLineStyle {
gap_scale: f32,
/// The length of the visible line in `line_width`s
line_scale: f32,
/// Whether or not the gizmo lines are animated which makes the line move from src to dst
animated: bool,
},
}

Expand All @@ -67,10 +69,12 @@ impl Hash for GizmoLineStyle {
Self::Dashed {
gap_scale,
line_scale,
animated,
} => {
2u64.hash(state);
gap_scale.to_bits().hash(state);
line_scale.to_bits().hash(state);
animated.hash(state);
}
}
}
Expand Down
18 changes: 16 additions & 2 deletions crates/bevy_gizmos/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ impl Plugin for GizmoPlugin {
#[cfg(feature = "bevy_render")]
app.add_plugins(aabb::AabbGizmoPlugin)
.add_plugins(UniformComponentPlugin::<LineGizmoUniform>::default())
.add_systems(bevy_app::Update, update_gizmo_time)
.add_plugins(RenderAssetPlugin::<GpuLineGizmo>::default());

#[cfg(all(feature = "bevy_pbr", feature = "bevy_render"))]
Expand Down Expand Up @@ -217,7 +218,7 @@ impl Plugin for GizmoPlugin {
let line_layout = render_device.create_bind_group_layout(
"LineGizmoUniform layout",
&BindGroupLayoutEntries::single(
ShaderStages::VERTEX,
ShaderStages::VERTEX | ShaderStages::FRAGMENT,
uniform_buffer::<LineGizmoUniform>(true),
),
);
Expand Down Expand Up @@ -418,11 +419,21 @@ fn update_gizmo_meshes<Config: GizmoConfigGroup>(
}
}

fn update_gizmo_time(
mut q: bevy_ecs::system::Query<&mut LineGizmoUniform>,
time: Res<bevy_time::Time>,
) {
q.iter_mut().for_each(|mut uniform| {
uniform.time = time.elapsed_secs();
});
}

#[cfg(feature = "bevy_render")]
fn extract_gizmo_data(
mut commands: Commands,
handles: Extract<Res<GizmoHandles>>,
config: Extract<Res<GizmoConfigStore>>,
time: Extract<Res<bevy_time::Time>>,
) {
use bevy_utils::once;
use config::GizmoLineStyle;
Expand Down Expand Up @@ -450,6 +461,7 @@ fn extract_gizmo_data(
let (gap_scale, line_scale) = if let GizmoLineStyle::Dashed {
gap_scale,
line_scale,
..
} = config.line.style
{
if gap_scale <= 0.0 {
Expand All @@ -471,6 +483,7 @@ fn extract_gizmo_data(
joints_resolution,
gap_scale,
line_scale,
time: time.elapsed_secs(),
#[cfg(feature = "webgl")]
_padding: Default::default(),
},
Expand Down Expand Up @@ -501,9 +514,10 @@ struct LineGizmoUniform {
// Only used if the current configs `line_style` is set to `GizmoLineStyle::Dashed{_}`
gap_scale: f32,
line_scale: f32,
time: f32,
/// WebGL2 structs must be 16 byte aligned.
#[cfg(feature = "webgl")]
_padding: bevy_math::Vec3,
_padding: bevy_math::Vec2,
}

/// A collection of gizmos.
Expand Down
38 changes: 37 additions & 1 deletion crates/bevy_gizmos/src/lines.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@ struct LineGizmoUniform {
_joints_resolution: u32,
gap_scale: f32,
line_scale: f32,
time: f32,
#ifdef SIXTEEN_BYTE_ALIGNMENT
// WebGL2 structs must be 16 byte aligned.
_padding: vec3<f32>,
_padding: vec2<f32>,
#endif
}

Expand Down Expand Up @@ -164,6 +165,7 @@ struct FragmentOutput {
fn fragment_solid(in: FragmentInput) -> FragmentOutput {
return FragmentOutput(in.color);
}

@fragment
fn fragment_dotted(in: FragmentInput) -> FragmentOutput {
var alpha: f32;
Expand All @@ -187,3 +189,37 @@ fn fragment_dashed(in: FragmentInput) -> FragmentOutput {

return FragmentOutput(vec4(in.color.xyz, in.color.w * alpha));
}

@fragment
fn fragment_dashed_animated(in: FragmentInput) -> FragmentOutput {
#ifdef PERSPECTIVE
let uv = in.uv;
#else
let uv = in.uv * in.position.w;
#endif
// apply animation speed, hardcoded but can be passed via configuration
let speed = 0.5;
let scaled_time = line_gizmo.time * speed;

// - the uv coordinates are going to be wrapped every 2.0 units
// - we're going to subtract the time from the uv values
//
// the modulo operation doesn't play nice with these two facts which is why
// we need to manually shift and wrap the time
let wrapped_time = scaled_time % 2.0;

// move the uv values, add two to ensure positive values
let shifted_uv = uv - wrapped_time + 2.0;

// wrap the uv values and scale appropriately to dash length
let wrapped_uv = (shifted_uv % 2.0) / in.line_fraction;

// calculate the alpha value to mask out the gaps of the dashed lines
// completely via the opacity of the color
//
// NOTE: the `1.0 -` at the start is a bit useless but I kept it for
// consistency since the other code does it as well
let alpha = 1.0 - floor(min(wrapped_uv, 1.0));

return FragmentOutput(vec4(in.color.xyz, in.color.w * alpha));
}
5 changes: 4 additions & 1 deletion crates/bevy_gizmos/src/pipeline_2d.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,10 @@ impl SpecializedRenderPipeline for LineGizmoPipeline {
let fragment_entry_point = match key.line_style {
GizmoLineStyle::Solid => "fragment_solid",
GizmoLineStyle::Dotted => "fragment_dotted",
GizmoLineStyle::Dashed { .. } => "fragment_dashed",
GizmoLineStyle::Dashed { animated: true, .. } => "fragment_dashed_animated",
GizmoLineStyle::Dashed {
animated: false, ..
} => "fragment_dashed",
};

RenderPipelineDescriptor {
Expand Down
3 changes: 3 additions & 0 deletions crates/bevy_gizmos/src/retained.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ pub(crate) fn extract_linegizmos(
mut commands: Commands,
mut previous_len: Local<usize>,
query: Extract<Query<(Entity, &Gizmo, &GlobalTransform, Option<&RenderLayers>)>>,
time: Extract<bevy_ecs::system::Res<bevy_time::Time>>,
) {
use bevy_math::Affine3;
use bevy_render::sync_world::{MainEntity, TemporaryRenderEntity};
Expand All @@ -119,6 +120,7 @@ pub(crate) fn extract_linegizmos(
let (gap_scale, line_scale) = if let GizmoLineStyle::Dashed {
gap_scale,
line_scale,
..
} = gizmo.line_config.style
{
if gap_scale <= 0.0 {
Expand All @@ -140,6 +142,7 @@ pub(crate) fn extract_linegizmos(
joints_resolution,
gap_scale,
line_scale,
time: time.elapsed_secs(),
#[cfg(feature = "webgl")]
_padding: Default::default(),
},
Expand Down
24 changes: 23 additions & 1 deletion examples/gizmos/2d_gizmos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ fn main() {
App::new()
.add_plugins(DefaultPlugins)
.init_gizmo_group::<MyRoundGizmos>()
.init_gizmo_group::<MyAnimatedGizmos>()
.add_systems(Startup, setup)
.add_systems(Update, (draw_example_collection, update_config))
.run();
Expand All @@ -17,7 +18,11 @@ fn main() {
#[derive(Default, Reflect, GizmoConfigGroup)]
struct MyRoundGizmos {}

fn setup(mut commands: Commands) {
// We can create our own gizmo config group!
#[derive(Default, Reflect, GizmoConfigGroup)]
struct MyAnimatedGizmos {}

fn setup(mut commands: Commands, mut config_store: ResMut<GizmoConfigStore>) {
commands.spawn(Camera2d);
// text
commands.spawn((
Expand All @@ -35,11 +40,17 @@ fn setup(mut commands: Commands) {
..default()
},
));
config_store.config_mut::<MyAnimatedGizmos>().0.line.style = GizmoLineStyle::Dashed {
gap_scale: 10.0,
line_scale: 10.0,
animated: true,
};
}

fn draw_example_collection(
mut gizmos: Gizmos,
mut my_gizmos: Gizmos<MyRoundGizmos>,
mut my_animated: Gizmos<MyAnimatedGizmos>,
time: Res<Time>,
) {
let sin_t_scaled = ops::sin(time.elapsed_secs()) * 50.;
Expand Down Expand Up @@ -120,6 +131,8 @@ fn draw_example_collection(
)
.with_double_end()
.with_tip_length(10.);

my_animated.line_2d(Vec2::ZERO, Vec2::ONE * 100.0, YELLOW_GREEN);
}

fn update_config(
Expand All @@ -145,6 +158,7 @@ fn update_config(
GizmoLineStyle::Dotted => GizmoLineStyle::Dashed {
gap_scale: 3.0,
line_scale: 5.0,
animated: false,
},
_ => GizmoLineStyle::Solid,
};
Expand Down Expand Up @@ -176,6 +190,14 @@ fn update_config(
GizmoLineStyle::Dotted => GizmoLineStyle::Dashed {
gap_scale: 3.0,
line_scale: 5.0,
animated: false,
},
GizmoLineStyle::Dashed {
animated: false, ..
} => GizmoLineStyle::Dashed {
gap_scale: 3.0,
line_scale: 5.0,
animated: true,
},
_ => GizmoLineStyle::Solid,
};
Expand Down
Loading