diff --git a/crates/bevy_gizmos/src/grid.rs b/crates/bevy_gizmos/src/grid.rs new file mode 100644 index 0000000000000..838ce334e8a2c --- /dev/null +++ b/crates/bevy_gizmos/src/grid.rs @@ -0,0 +1,216 @@ +//! Additional [`Gizmos`] Functions -- Grids +//! +//! Includes the implementation of[`Gizmos::grid`] and [`Gizmos::grid_2d`]. +//! and assorted support items. + +use crate::prelude::{GizmoConfigGroup, Gizmos}; +use bevy_math::{Quat, UVec2, Vec2, Vec3}; +use bevy_render::color::LegacyColor; + +/// A builder returned by [`Gizmos::grid`] and [`Gizmos::grid_2d`] +pub struct GridBuilder<'a, 'w, 's, T: GizmoConfigGroup> { + gizmos: &'a mut Gizmos<'w, 's, T>, + position: Vec3, + rotation: Quat, + spacing: Vec2, + cell_count: UVec2, + skew: Vec2, + outer_edges: bool, + color: LegacyColor, +} + +impl GridBuilder<'_, '_, '_, T> { + /// Skews the grid by `tan(skew)` in the x direction. + /// `skew` is in radians + pub fn skew_x(mut self, skew: f32) -> Self { + self.skew.x = skew; + self + } + /// Skews the grid by `tan(skew)` in the x direction. + /// `skew` is in radians + pub fn skew_y(mut self, skew: f32) -> Self { + self.skew.y = skew; + self + } + /// Skews the grid by `tan(skew)` in the x and y directions. + /// `skew` is in radians + pub fn skew(mut self, skew: Vec2) -> Self { + self.skew = skew; + self + } + + /// Toggle whether the outer edges of the grid should be drawn. + /// By default, the outer edges will not be drawn. + pub fn outer_edges(mut self, outer_edges: bool) -> Self { + self.outer_edges = outer_edges; + self + } +} + +impl Drop for GridBuilder<'_, '_, '_, T> { + /// Draws a grid, by drawing lines with the stored [`Gizmos`] + fn drop(&mut self) { + if !self.gizmos.enabled { + return; + } + + // Offset between two adjacent grid cells along the x/y-axis and accounting for skew. + let dx = Vec3::new(self.spacing.x, self.spacing.x * self.skew.y.tan(), 0.); + let dy = Vec3::new(self.spacing.y * self.skew.x.tan(), self.spacing.y, 0.); + + // Bottom-left corner of the grid + let grid_start = self.position + - self.cell_count.x as f32 / 2.0 * dx + - self.cell_count.y as f32 / 2.0 * dy; + + let (line_count, vertical_start, horizontal_start) = if self.outer_edges { + (self.cell_count + UVec2::ONE, grid_start, grid_start) + } else { + ( + self.cell_count.saturating_sub(UVec2::ONE), + grid_start + dx, + grid_start + dy, + ) + }; + + // Vertical lines + let dline = dy * self.cell_count.y as f32; + for i in 0..line_count.x { + let i = i as f32; + let line_start = vertical_start + i * dx; + let line_end = line_start + dline; + + self.gizmos.line( + self.rotation * line_start, + self.rotation * line_end, + self.color, + ); + } + + // Horizontal lines + let dline = dx * self.cell_count.x as f32; + for i in 0..line_count.y { + let i = i as f32; + let line_start = horizontal_start + i * dy; + let line_end = line_start + dline; + + self.gizmos.line( + self.rotation * line_start, + self.rotation * line_end, + self.color, + ); + } + } +} + +impl<'w, 's, T: GizmoConfigGroup> Gizmos<'w, 's, T> { + /// Draw a 2D grid in 3D. + /// + /// This should be called for each frame the grid needs to be rendered. + /// + /// # Arguments + /// + /// - `position`: The center point of the grid. + /// - `rotation`: defines the orientation of the grid, by default we assume the grid is contained in a plane parallel to the XY plane. + /// - `cell_count`: defines the amount of cells in the x and y axes + /// - `spacing`: defines the distance between cells along the x and y axes + /// - `color`: color of the grid + /// + /// # Builder methods + /// + /// - The skew of the grid can be adjusted using the `.skew(...)`, `.skew_x(...)` or `.skew_y(...)` methods. They behave very similar to their CSS equivalents. + /// - The outer edges can be toggled on or off using `.outer_edges(...)`. + /// + /// # Example + /// ``` + /// # use bevy_gizmos::prelude::*; + /// # use bevy_render::prelude::*; + /// # use bevy_math::prelude::*; + /// fn system(mut gizmos: Gizmos) { + /// gizmos.grid( + /// Vec3::ZERO, + /// Quat::IDENTITY, + /// UVec2::new(10, 10), + /// Vec2::splat(2.), + /// LegacyColor::GREEN + /// ) + /// .skew_x(0.25) + /// .outer_edges(true); + /// } + /// # bevy_ecs::system::assert_is_system(system); + /// ``` + pub fn grid( + &mut self, + position: Vec3, + rotation: Quat, + cell_count: UVec2, + spacing: Vec2, + color: LegacyColor, + ) -> GridBuilder<'_, 'w, 's, T> { + GridBuilder { + gizmos: self, + position, + rotation, + spacing, + cell_count, + skew: Vec2::ZERO, + outer_edges: false, + color, + } + } + + /// Draw a grid in 2D. + /// + /// This should be called for each frame the grid needs to be rendered. + /// + /// # Arguments + /// + /// - `position`: The center point of the grid. + /// - `rotation`: defines the orientation of the grid. + /// - `cell_count`: defines the amount of cells in the x and y axes + /// - `spacing`: defines the distance between cells along the x and y axes + /// - `color`: color of the grid + /// + /// # Builder methods + /// + /// - The skew of the grid can be adjusted using the `.skew(...)`, `.skew_x(...)` or `.skew_y(...)` methods. They behave very similar to their CSS equivalents. + /// - The outer edges can be toggled on or off using `.outer_edges(...)`. + /// + /// # Example + /// ``` + /// # use bevy_gizmos::prelude::*; + /// # use bevy_render::prelude::*; + /// # use bevy_math::prelude::*; + /// fn system(mut gizmos: Gizmos) { + /// gizmos.grid_2d( + /// Vec2::ZERO, + /// 0.0, + /// UVec2::new(10, 10), + /// Vec2::splat(1.), + /// LegacyColor::GREEN + /// ) + /// .skew_x(0.25) + /// .outer_edges(true); + /// } + /// # bevy_ecs::system::assert_is_system(system); + /// ``` + pub fn grid_2d( + &mut self, + position: Vec2, + rotation: f32, + cell_count: UVec2, + spacing: Vec2, + color: LegacyColor, + ) -> GridBuilder<'_, 'w, 's, T> { + GridBuilder { + gizmos: self, + position: position.extend(0.), + rotation: Quat::from_rotation_z(rotation), + spacing, + cell_count, + skew: Vec2::ZERO, + outer_edges: false, + color, + } + } +} diff --git a/crates/bevy_gizmos/src/lib.rs b/crates/bevy_gizmos/src/lib.rs index 1dd86c5ac577f..188c726efcf14 100644 --- a/crates/bevy_gizmos/src/lib.rs +++ b/crates/bevy_gizmos/src/lib.rs @@ -30,6 +30,7 @@ pub mod arrows; pub mod circles; pub mod config; pub mod gizmos; +pub mod grid; pub mod primitives; #[cfg(feature = "bevy_sprite")] diff --git a/examples/gizmos/2d_gizmos.rs b/examples/gizmos/2d_gizmos.rs index 2072ebe01168c..2532068ae6f48 100644 --- a/examples/gizmos/2d_gizmos.rs +++ b/examples/gizmos/2d_gizmos.rs @@ -41,6 +41,17 @@ fn draw_example_collection( gizmos.line_2d(Vec2::Y * -sin, Vec2::splat(-80.), LegacyColor::RED); gizmos.ray_2d(Vec2::Y * sin, Vec2::splat(80.), LegacyColor::GREEN); + gizmos + .grid_2d( + Vec2::ZERO, + 0.0, + UVec2::new(16, 12), + Vec2::new(60., 60.), + // Light gray + LegacyColor::rgb(0.65, 0.65, 0.65), + ) + .outer_edges(true); + // Triangle gizmos.linestrip_gradient_2d([ (Vec2::Y * 300., LegacyColor::BLUE), diff --git a/examples/gizmos/3d_gizmos.rs b/examples/gizmos/3d_gizmos.rs index 4e97e0e8e8358..482294d4de96a 100644 --- a/examples/gizmos/3d_gizmos.rs +++ b/examples/gizmos/3d_gizmos.rs @@ -86,6 +86,15 @@ fn draw_example_collection( mut my_gizmos: Gizmos, time: Res