Add Grid gizmos (#11988)

# Objective

- Implement grid gizmos, suggestion of #9400 

## Solution

- Added `gizmos.grid(...) ` and `gizmos.grid_2d(...)`
- The grids may be configured using `.outer_edges(...)` to specify
whether to draw the outer border/edges of the grid and `.skew(...)`to
specify the skew of the grid along the x or y directions.

---

## Changelog

- Added a `grid` module to `bevy_gizmos` containing `gizmos.grid(...) `
and `gizmos.grid_2d(...)` as well as assorted items.
- Updated the `2d_gizmos` and `3d_gizmos` examples to use grids.

## Additional

The 2D and 3D examples now look like this:
<img width="1440" alt="Screenshot 2024-02-20 at 15 09 40"
src="https://github.com/bevyengine/bevy/assets/62256001/ce04191e-d839-4faf-a6e3-49b6bb4b922b">
<img width="1440" alt="Screenshot 2024-02-20 at 15 10 07"
src="https://github.com/bevyengine/bevy/assets/62256001/317459ba-d452-42eb-ae95-7c84cdbd569b">
This commit is contained in:
Lynn 2024-02-28 01:18:26 +01:00 committed by GitHub
parent 9ae5ba2e71
commit 35cec8bd3a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 237 additions and 0 deletions

View file

@ -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<T: GizmoConfigGroup> 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<T: GizmoConfigGroup> 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,
}
}
}

View file

@ -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")]

View file

@ -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),

View file

@ -86,6 +86,15 @@ fn draw_example_collection(
mut my_gizmos: Gizmos<MyRoundGizmos>,
time: Res<Time>,
) {
gizmos.grid(
Vec3::ZERO,
Quat::from_rotation_x(PI / 2.),
UVec2::splat(20),
Vec2::new(2., 2.),
// Light gray
LegacyColor::rgb(0.65, 0.65, 0.65),
);
gizmos.cuboid(
Transform::from_translation(Vec3::Y * 0.5).with_scale(Vec3::splat(1.25)),
LegacyColor::BLACK,