Add dashed lines (#16884)
# Objective - Fixes #16873 ## Solution - Added `GizmoLineStyle::Dashed {gap_scale, line_scale}` - The `gap_scale` and `line_scale` describe the lengths of the gaps and visible line-segments in terms of line-widths. For example, if `gap_scale == 1.0` and `line_scale == 3.0` the gaps are square and the the visible segments are three line-widths long. - The new `GizmoLineStyle` can be used both in 3D and 2D and with both perspective and orthographic cameras. - Updated the `2d_gizmos` and `3d_gizmos` examples to include the new line-style. - Display a warning, when using negative `gap_scale` or `line_scale`. - Notably, `Hash` and `Eq` are manually implemented for `GizmoLineStyle` since both are not implemented for `f32` which prevents deriving these traits for `GizmoLineStyle`. ## Testing - The results can be verified visually --- ## Showcase The following images depict dashed lines with `gap_scale == 3.0` and `line_scale == 5.0` in perspective 3D and orthographic 2D.   --------- Co-authored-by: Hennadii Chernyshchyk <genaloner@gmail.com>
This commit is contained in:
parent
d8796ae8b6
commit
c425fc7f32
@ -14,6 +14,7 @@ use bevy_reflect::{std_traits::ReflectDefault, Reflect, TypePath};
|
||||
use bevy_utils::TypeIdMap;
|
||||
use core::{
|
||||
any::TypeId,
|
||||
hash::Hash,
|
||||
ops::{Deref, DerefMut},
|
||||
panic,
|
||||
};
|
||||
@ -36,7 +37,7 @@ pub enum GizmoLineJoint {
|
||||
}
|
||||
|
||||
/// An enum used to configure the style of gizmo lines, similar to CSS line-style
|
||||
#[derive(Copy, Clone, Debug, Default, Hash, PartialEq, Eq, Reflect)]
|
||||
#[derive(Copy, Clone, Debug, Default, PartialEq, Reflect)]
|
||||
#[non_exhaustive]
|
||||
pub enum GizmoLineStyle {
|
||||
/// A solid line without any decorators
|
||||
@ -44,6 +45,34 @@ pub enum GizmoLineStyle {
|
||||
Solid,
|
||||
/// A dotted line
|
||||
Dotted,
|
||||
/// A dashed line with configurable gap and line sizes
|
||||
Dashed {
|
||||
/// The length of the gap in `line_width`s
|
||||
gap_scale: f32,
|
||||
/// The length of the visible line in `line_width`s
|
||||
line_scale: f32,
|
||||
},
|
||||
}
|
||||
|
||||
impl Eq for GizmoLineStyle {}
|
||||
|
||||
impl Hash for GizmoLineStyle {
|
||||
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
|
||||
match self {
|
||||
Self::Solid => {
|
||||
0u64.hash(state);
|
||||
}
|
||||
Self::Dotted => 1u64.hash(state),
|
||||
Self::Dashed {
|
||||
gap_scale,
|
||||
line_scale,
|
||||
} => {
|
||||
2u64.hash(state);
|
||||
gap_scale.to_bits().hash(state);
|
||||
line_scale.to_bits().hash(state);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A trait used to create gizmo configs groups.
|
||||
|
@ -81,7 +81,7 @@ use bevy_ecs::{
|
||||
schedule::{IntoSystemConfigs, SystemSet},
|
||||
system::{Res, ResMut, Resource},
|
||||
};
|
||||
use bevy_math::Vec4;
|
||||
use bevy_math::{Vec3, Vec4};
|
||||
use bevy_reflect::TypePath;
|
||||
|
||||
#[cfg(all(
|
||||
@ -419,6 +419,9 @@ fn extract_gizmo_data(
|
||||
handles: Extract<Res<GizmoHandles>>,
|
||||
config: Extract<Res<GizmoConfigStore>>,
|
||||
) {
|
||||
use bevy_utils::warn_once;
|
||||
use config::GizmoLineStyle;
|
||||
|
||||
for (group_type_id, handle) in &handles.handles {
|
||||
let Some((config, _)) = config.get_config_dyn(group_type_id) else {
|
||||
continue;
|
||||
@ -438,12 +441,30 @@ fn extract_gizmo_data(
|
||||
0
|
||||
};
|
||||
|
||||
let (gap_scale, line_scale) = if let GizmoLineStyle::Dashed {
|
||||
gap_scale,
|
||||
line_scale,
|
||||
} = config.line.style
|
||||
{
|
||||
if gap_scale <= 0.0 {
|
||||
warn_once!("When using gizmos with the line style `GizmoLineStyle::Dashed{{..}}` the gap scale should be greater than zero.");
|
||||
}
|
||||
if line_scale <= 0.0 {
|
||||
warn_once!("When using gizmos with the line style `GizmoLineStyle::Dashed{{..}}` the line scale should be greater than zero.");
|
||||
}
|
||||
(gap_scale, line_scale)
|
||||
} else {
|
||||
(1.0, 1.0)
|
||||
};
|
||||
|
||||
commands.spawn((
|
||||
LineGizmoUniform {
|
||||
world_from_local: Affine3::from(&Affine3A::IDENTITY).to_transpose(),
|
||||
line_width: config.line.width,
|
||||
depth_bias: config.depth_bias,
|
||||
joints_resolution,
|
||||
gap_scale,
|
||||
line_scale,
|
||||
#[cfg(feature = "webgl")]
|
||||
_padding: Default::default(),
|
||||
},
|
||||
@ -471,9 +492,12 @@ struct LineGizmoUniform {
|
||||
depth_bias: f32,
|
||||
// Only used by gizmo line t if the current configs `line_joints` is set to `GizmoLineJoint::Round(_)`
|
||||
joints_resolution: u32,
|
||||
// Only used if the current configs `line_style` is set to `GizmoLineStyle::Dashed{_}`
|
||||
gap_scale: f32,
|
||||
line_scale: f32,
|
||||
/// WebGL2 structs must be 16 byte aligned.
|
||||
#[cfg(feature = "webgl")]
|
||||
_padding: f32,
|
||||
_padding: Vec3,
|
||||
}
|
||||
|
||||
/// A collection of gizmos.
|
||||
|
@ -8,9 +8,12 @@ struct LineGizmoUniform {
|
||||
world_from_local: mat3x4<f32>,
|
||||
line_width: f32,
|
||||
depth_bias: f32,
|
||||
_joints_resolution: u32,
|
||||
gap_scale: f32,
|
||||
line_scale: f32,
|
||||
#ifdef SIXTEEN_BYTE_ALIGNMENT
|
||||
// WebGL2 structs must be 16 byte aligned.
|
||||
_padding: vec2<f32>,
|
||||
_padding: vec3<f32>,
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -28,6 +31,7 @@ struct VertexOutput {
|
||||
@builtin(position) clip_position: vec4<f32>,
|
||||
@location(0) color: vec4<f32>,
|
||||
@location(1) uv: f32,
|
||||
@location(2) line_fraction: f32,
|
||||
};
|
||||
|
||||
const EPSILON: f32 = 4.88e-04;
|
||||
@ -126,7 +130,9 @@ fn vertex(vertex: VertexInput) -> VertexOutput {
|
||||
|
||||
var clip_position = vec4(clip.w * ((2. * screen) / resolution - 1.), depth, clip.w);
|
||||
|
||||
return VertexOutput(clip_position, color, uv);
|
||||
let line_fraction = 2.0 * line_gizmo.line_scale / (line_gizmo.gap_scale + line_gizmo.line_scale);
|
||||
uv /= (line_gizmo.gap_scale + line_gizmo.line_scale) / 2.0;
|
||||
return VertexOutput(clip_position, color, uv, line_fraction);
|
||||
}
|
||||
|
||||
fn clip_near_plane(a: vec4<f32>, b: vec4<f32>) -> vec4<f32> {
|
||||
@ -147,6 +153,7 @@ struct FragmentInput {
|
||||
@builtin(position) position: vec4<f32>,
|
||||
@location(0) color: vec4<f32>,
|
||||
@location(1) uv: f32,
|
||||
@location(2) line_fraction: f32,
|
||||
};
|
||||
|
||||
struct FragmentOutput {
|
||||
@ -168,3 +175,15 @@ fn fragment_dotted(in: FragmentInput) -> FragmentOutput {
|
||||
|
||||
return FragmentOutput(vec4(in.color.xyz, in.color.w * alpha));
|
||||
}
|
||||
|
||||
@fragment
|
||||
fn fragment_dashed(in: FragmentInput) -> FragmentOutput {
|
||||
#ifdef PERSPECTIVE
|
||||
let uv = in.uv;
|
||||
#else
|
||||
let uv = in.uv * in.position.w;
|
||||
#endif
|
||||
let alpha = 1.0 - floor(min((uv % 2.0) / in.line_fraction, 1.0));
|
||||
|
||||
return FragmentOutput(vec4(in.color.xyz, in.color.w * alpha));
|
||||
}
|
||||
|
@ -118,6 +118,7 @@ impl SpecializedRenderPipeline for LineGizmoPipeline {
|
||||
let fragment_entry_point = match key.line_style {
|
||||
GizmoLineStyle::Solid => "fragment_solid",
|
||||
GizmoLineStyle::Dotted => "fragment_dotted",
|
||||
GizmoLineStyle::Dashed { .. } => "fragment_dashed",
|
||||
};
|
||||
|
||||
RenderPipelineDescriptor {
|
||||
|
@ -124,6 +124,7 @@ impl SpecializedRenderPipeline for LineGizmoPipeline {
|
||||
let fragment_entry_point = match key.line_style {
|
||||
GizmoLineStyle::Solid => "fragment_solid",
|
||||
GizmoLineStyle::Dotted => "fragment_dotted",
|
||||
GizmoLineStyle::Dashed { .. } => "fragment_dashed",
|
||||
};
|
||||
|
||||
RenderPipelineDescriptor {
|
||||
|
@ -106,6 +106,9 @@ pub(crate) fn extract_linegizmos(
|
||||
) {
|
||||
use bevy_math::Affine3;
|
||||
use bevy_render::sync_world::{MainEntity, TemporaryRenderEntity};
|
||||
use bevy_utils::warn_once;
|
||||
|
||||
use crate::config::GizmoLineStyle;
|
||||
|
||||
let mut values = Vec::with_capacity(*previous_len);
|
||||
for (entity, gizmo, transform, render_layers) in &query {
|
||||
@ -115,6 +118,21 @@ pub(crate) fn extract_linegizmos(
|
||||
} else {
|
||||
0
|
||||
};
|
||||
let (gap_scale, line_scale) = if let GizmoLineStyle::Dashed {
|
||||
gap_scale,
|
||||
line_scale,
|
||||
} = gizmo.line_config.style
|
||||
{
|
||||
if gap_scale <= 0.0 {
|
||||
warn_once!("when using gizmos with the line style `GizmoLineStyle::Dashed{{..}}` the gap scale should be greater than zero");
|
||||
}
|
||||
if line_scale <= 0.0 {
|
||||
warn_once!("when using gizmos with the line style `GizmoLineStyle::Dashed{{..}}` the line scale should be greater than zero");
|
||||
}
|
||||
(gap_scale, line_scale)
|
||||
} else {
|
||||
(1.0, 1.0)
|
||||
};
|
||||
|
||||
values.push((
|
||||
LineGizmoUniform {
|
||||
@ -122,6 +140,8 @@ pub(crate) fn extract_linegizmos(
|
||||
line_width: gizmo.line_config.width,
|
||||
depth_bias: gizmo.depth_bias,
|
||||
joints_resolution,
|
||||
gap_scale,
|
||||
line_scale,
|
||||
#[cfg(feature = "webgl")]
|
||||
_padding: Default::default(),
|
||||
},
|
||||
|
@ -142,6 +142,10 @@ fn update_config(
|
||||
if keyboard.just_pressed(KeyCode::KeyU) {
|
||||
config.line.style = match config.line.style {
|
||||
GizmoLineStyle::Solid => GizmoLineStyle::Dotted,
|
||||
GizmoLineStyle::Dotted => GizmoLineStyle::Dashed {
|
||||
gap_scale: 3.0,
|
||||
line_scale: 5.0,
|
||||
},
|
||||
_ => GizmoLineStyle::Solid,
|
||||
};
|
||||
}
|
||||
@ -169,6 +173,10 @@ fn update_config(
|
||||
if keyboard.just_pressed(KeyCode::KeyI) {
|
||||
my_config.line.style = match my_config.line.style {
|
||||
GizmoLineStyle::Solid => GizmoLineStyle::Dotted,
|
||||
GizmoLineStyle::Dotted => GizmoLineStyle::Dashed {
|
||||
gap_scale: 3.0,
|
||||
line_scale: 5.0,
|
||||
},
|
||||
_ => GizmoLineStyle::Solid,
|
||||
};
|
||||
}
|
||||
|
@ -237,6 +237,10 @@ fn update_config(
|
||||
if keyboard.just_pressed(KeyCode::KeyU) {
|
||||
config.line.style = match config.line.style {
|
||||
GizmoLineStyle::Solid => GizmoLineStyle::Dotted,
|
||||
GizmoLineStyle::Dotted => GizmoLineStyle::Dashed {
|
||||
gap_scale: 3.0,
|
||||
line_scale: 5.0,
|
||||
},
|
||||
_ => GizmoLineStyle::Solid,
|
||||
};
|
||||
}
|
||||
@ -264,6 +268,10 @@ fn update_config(
|
||||
if keyboard.just_pressed(KeyCode::KeyI) {
|
||||
my_config.line.style = match my_config.line.style {
|
||||
GizmoLineStyle::Solid => GizmoLineStyle::Dotted,
|
||||
GizmoLineStyle::Dotted => GizmoLineStyle::Dashed {
|
||||
gap_scale: 3.0,
|
||||
line_scale: 5.0,
|
||||
},
|
||||
_ => GizmoLineStyle::Solid,
|
||||
};
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user