diff --git a/crates/bevy_gizmos/src/arrows.rs b/crates/bevy_gizmos/src/arrows.rs index 9a1325c821302..28548448fafeb 100644 --- a/crates/bevy_gizmos/src/arrows.rs +++ b/crates/bevy_gizmos/src/arrows.rs @@ -12,8 +12,29 @@ pub struct ArrowBuilder<'a, 's> { gizmos: &'a mut Gizmos<'s>, start: Vec3, end: Vec3, + front: ArrowHead, + back: ArrowHead, color: Color, tip_length: f32, + double_ended: bool, +} + +/// Represents how the end of an arrow should be drawn. +/// See also [`Gizmos::arrow`] and [`Gizmos::arrow_2d`]. +#[derive(Default, Debug, Copy, Clone)] +pub enum ArrowHead { + /// No head. Putting this on both ends causes the arrow to just be a line. + None, + /// General-purpose arrow head with four tips for viewing from any angle. Default + #[default] + Normal, + /// Two-tip arrow head, facing as close to `towards` as possible while still being inline with the arrow body + Billboarded( + /// Will attempt to be most visible from this direction. + /// - in 3d applications, this would typically be the camera position. + /// - in 2d applications, this would typically be [`Vec3::Y`] or [`Vec3::Z`] + Vec3, + ), } impl ArrowBuilder<'_, '_> { @@ -32,8 +53,47 @@ impl ArrowBuilder<'_, '_> { /// # bevy_ecs::system::assert_is_system(system); /// ``` #[doc(alias = "arrow_head_length")] - pub fn with_tip_length(&mut self, length: f32) { + pub fn with_tip_length(&mut self, length: f32) -> &mut Self { self.tip_length = length; + self + } + + /// Make the arrow double-ended. + /// sets both ends of the arrow to [`ArrowHead::Normal`] + /// if you want to use a different head, use [`ArrowBuilder::???`] + /// + /// # Example + /// ``` + /// # use bevy_gizmos::prelude::*; + /// # use bevy_render::prelude::*; + /// # use bevy_math::prelude::*; + /// fn system(mut gizmos: Gizmos) { + /// gizmos.arrow(Vec3::ZERO, Vec3::ONE, Color::GREEN) + /// .double_ended(); + /// } + /// # bevy_ecs::system::assert_is_system(system); + /// ``` + pub fn double_ended(&mut self) -> &mut Self { + self.double_ended = true; + return self; + } + + /// Make the arrow single-ended (the default). + /// + /// # Example + /// ``` + /// # use bevy_gizmos::prelude::*; + /// # use bevy_render::prelude::*; + /// # use bevy_math::prelude::*; + /// fn system(mut gizmos: Gizmos) { + /// gizmos.arrow(Vec3::ZERO, Vec3::ONE, Color::GREEN) + /// .with_double_ended(); + /// } + /// # bevy_ecs::system::assert_is_system(system); + /// ``` + pub fn with_single_ended(&mut self) -> &mut Self { + self.double_ended = false; + return self; } } @@ -53,12 +113,21 @@ impl Drop for ArrowBuilder<'_, '_> { Vec3::new(-1., 0., -1.), ]; // - extend the vectors so their length is `tip_length` - // - rotate the world so +x is facing in the same direction as the arrow - // - translate over to the tip of the arrow - let tips = tips.map(|v| rotation * (v.normalize() * self.tip_length) + self.end); + let tips = tips.map(|v| (v.normalize() * self.tip_length)); for v in tips { + // - rotate the world so +x is facing in the same direction as the arrow + // - translate over to the tip of the arrow // then actually draw the tips - self.gizmos.line(self.end, v, self.color); + self.gizmos + .line(self.end, self.end + (rotation * v), self.color); + } + if self.double_ended { + // same thing but draw starting from the start and use the inverse rotation + let rotation = Quat::from_rotation_arc(Vec3::NEG_X, pointing); + for v in tips { + self.gizmos + .line(self.start, self.start + (rotation * v), self.color); + } } } } @@ -86,6 +155,7 @@ impl<'s> Gizmos<'s> { end, color, tip_length: length / 10., + double_ended: false, } }