Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_gizmos/src/grid.rs
6595 views
1
//! Additional [`GizmoBuffer`] Functions -- Grids
2
//!
3
//! Includes the implementation of [`GizmoBuffer::grid`] and [`GizmoBuffer::grid_2d`].
4
//! and assorted support items.
5
6
use crate::{gizmos::GizmoBuffer, prelude::GizmoConfigGroup};
7
use bevy_color::Color;
8
use bevy_math::{ops, Isometry2d, Isometry3d, Quat, UVec2, UVec3, Vec2, Vec3};
9
10
/// A builder returned by [`GizmoBuffer::grid_3d`]
11
pub struct GridBuilder3d<'a, Config, Clear>
12
where
13
Config: GizmoConfigGroup,
14
Clear: 'static + Send + Sync,
15
{
16
gizmos: &'a mut GizmoBuffer<Config, Clear>,
17
isometry: Isometry3d,
18
spacing: Vec3,
19
cell_count: UVec3,
20
skew: Vec3,
21
outer_edges: [bool; 3],
22
color: Color,
23
}
24
/// A builder returned by [`GizmoBuffer::grid`] and [`GizmoBuffer::grid_2d`]
25
pub struct GridBuilder2d<'a, Config, Clear>
26
where
27
Config: GizmoConfigGroup,
28
Clear: 'static + Send + Sync,
29
{
30
gizmos: &'a mut GizmoBuffer<Config, Clear>,
31
isometry: Isometry3d,
32
spacing: Vec2,
33
cell_count: UVec2,
34
skew: Vec2,
35
outer_edges: [bool; 2],
36
color: Color,
37
}
38
39
impl<Config, Clear> GridBuilder3d<'_, Config, Clear>
40
where
41
Config: GizmoConfigGroup,
42
Clear: 'static + Send + Sync,
43
{
44
/// Skews the grid by `tan(skew)` in the x direction.
45
/// `skew` is in radians
46
pub fn skew_x(mut self, skew: f32) -> Self {
47
self.skew.x = skew;
48
self
49
}
50
/// Skews the grid by `tan(skew)` in the y direction.
51
/// `skew` is in radians
52
pub fn skew_y(mut self, skew: f32) -> Self {
53
self.skew.y = skew;
54
self
55
}
56
/// Skews the grid by `tan(skew)` in the z direction.
57
/// `skew` is in radians
58
pub fn skew_z(mut self, skew: f32) -> Self {
59
self.skew.z = skew;
60
self
61
}
62
/// Skews the grid by `tan(skew)` in the x, y and z directions.
63
/// `skew` is in radians
64
pub fn skew(mut self, skew: Vec3) -> Self {
65
self.skew = skew;
66
self
67
}
68
69
/// Declare that the outer edges of the grid parallel to the x axis should be drawn.
70
/// By default, the outer edges will not be drawn.
71
pub fn outer_edges_x(mut self) -> Self {
72
self.outer_edges[0] = true;
73
self
74
}
75
/// Declare that the outer edges of the grid parallel to the y axis should be drawn.
76
/// By default, the outer edges will not be drawn.
77
pub fn outer_edges_y(mut self) -> Self {
78
self.outer_edges[1] = true;
79
self
80
}
81
/// Declare that the outer edges of the grid parallel to the z axis should be drawn.
82
/// By default, the outer edges will not be drawn.
83
pub fn outer_edges_z(mut self) -> Self {
84
self.outer_edges[2] = true;
85
self
86
}
87
/// Declare that all outer edges of the grid should be drawn.
88
/// By default, the outer edges will not be drawn.
89
pub fn outer_edges(mut self) -> Self {
90
self.outer_edges.fill(true);
91
self
92
}
93
}
94
95
impl<Config, Clear> GridBuilder2d<'_, Config, Clear>
96
where
97
Config: GizmoConfigGroup,
98
Clear: 'static + Send + Sync,
99
{
100
/// Skews the grid by `tan(skew)` in the x direction.
101
/// `skew` is in radians
102
pub fn skew_x(mut self, skew: f32) -> Self {
103
self.skew.x = skew;
104
self
105
}
106
/// Skews the grid by `tan(skew)` in the y direction.
107
/// `skew` is in radians
108
pub fn skew_y(mut self, skew: f32) -> Self {
109
self.skew.y = skew;
110
self
111
}
112
/// Skews the grid by `tan(skew)` in the x and y directions.
113
/// `skew` is in radians
114
pub fn skew(mut self, skew: Vec2) -> Self {
115
self.skew = skew;
116
self
117
}
118
119
/// Declare that the outer edges of the grid parallel to the x axis should be drawn.
120
/// By default, the outer edges will not be drawn.
121
pub fn outer_edges_x(mut self) -> Self {
122
self.outer_edges[0] = true;
123
self
124
}
125
/// Declare that the outer edges of the grid parallel to the y axis should be drawn.
126
/// By default, the outer edges will not be drawn.
127
pub fn outer_edges_y(mut self) -> Self {
128
self.outer_edges[1] = true;
129
self
130
}
131
/// Declare that all outer edges of the grid should be drawn.
132
/// By default, the outer edges will not be drawn.
133
pub fn outer_edges(mut self) -> Self {
134
self.outer_edges.fill(true);
135
self
136
}
137
}
138
139
impl<Config, Clear> Drop for GridBuilder3d<'_, Config, Clear>
140
where
141
Config: GizmoConfigGroup,
142
Clear: 'static + Send + Sync,
143
{
144
/// Draws a grid, by drawing lines with the stored [`GizmoBuffer`]
145
fn drop(&mut self) {
146
draw_grid(
147
self.gizmos,
148
self.isometry,
149
self.spacing,
150
self.cell_count,
151
self.skew,
152
self.outer_edges,
153
self.color,
154
);
155
}
156
}
157
158
impl<Config, Clear> Drop for GridBuilder2d<'_, Config, Clear>
159
where
160
Config: GizmoConfigGroup,
161
Clear: 'static + Send + Sync,
162
{
163
fn drop(&mut self) {
164
draw_grid(
165
self.gizmos,
166
self.isometry,
167
self.spacing.extend(0.),
168
self.cell_count.extend(0),
169
self.skew.extend(0.),
170
[self.outer_edges[0], self.outer_edges[1], true],
171
self.color,
172
);
173
}
174
}
175
176
impl<Config, Clear> GizmoBuffer<Config, Clear>
177
where
178
Config: GizmoConfigGroup,
179
Clear: 'static + Send + Sync,
180
{
181
/// Draw a 2D grid in 3D.
182
///
183
/// This should be called for each frame the grid needs to be rendered.
184
///
185
/// The grid's default orientation aligns with the XY-plane.
186
///
187
/// # Arguments
188
///
189
/// - `isometry` defines the translation and rotation of the grid.
190
/// - the translation specifies the center of the grid
191
/// - defines the orientation of the grid, by default we assume the grid is contained in a
192
/// plane parallel to the XY plane
193
/// - `cell_count`: defines the amount of cells in the x and y axes
194
/// - `spacing`: defines the distance between cells along the x and y axes
195
/// - `color`: color of the grid
196
///
197
/// # Builder methods
198
///
199
/// - 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.
200
/// - All outer edges can be toggled on or off using `.outer_edges(...)`. Alternatively you can use `.outer_edges_x(...)` or `.outer_edges_y(...)` to toggle the outer edges along an axis.
201
///
202
/// # Example
203
/// ```
204
/// # use bevy_gizmos::prelude::*;
205
/// # use bevy_math::prelude::*;
206
/// # use bevy_color::palettes::basic::GREEN;
207
/// fn system(mut gizmos: Gizmos) {
208
/// gizmos.grid(
209
/// Isometry3d::IDENTITY,
210
/// UVec2::new(10, 10),
211
/// Vec2::splat(2.),
212
/// GREEN
213
/// )
214
/// .skew_x(0.25)
215
/// .outer_edges();
216
/// }
217
/// # bevy_ecs::system::assert_is_system(system);
218
/// ```
219
pub fn grid(
220
&mut self,
221
isometry: impl Into<Isometry3d>,
222
cell_count: UVec2,
223
spacing: Vec2,
224
color: impl Into<Color>,
225
) -> GridBuilder2d<'_, Config, Clear> {
226
GridBuilder2d {
227
gizmos: self,
228
isometry: isometry.into(),
229
spacing,
230
cell_count,
231
skew: Vec2::ZERO,
232
outer_edges: [false, false],
233
color: color.into(),
234
}
235
}
236
237
/// Draw a 3D grid of voxel-like cells.
238
///
239
/// This should be called for each frame the grid needs to be rendered.
240
///
241
/// # Arguments
242
///
243
/// - `isometry` defines the translation and rotation of the grid.
244
/// - the translation specifies the center of the grid
245
/// - defines the orientation of the grid, by default we assume the grid is aligned with all axes
246
/// - `cell_count`: defines the amount of cells in the x, y and z axes
247
/// - `spacing`: defines the distance between cells along the x, y and z axes
248
/// - `color`: color of the grid
249
///
250
/// # Builder methods
251
///
252
/// - The skew of the grid can be adjusted using the `.skew(...)`, `.skew_x(...)`, `.skew_y(...)` or `.skew_z(...)` methods. They behave very similar to their CSS equivalents.
253
/// - All outer edges can be toggled on or off using `.outer_edges(...)`. Alternatively you can use `.outer_edges_x(...)`, `.outer_edges_y(...)` or `.outer_edges_z(...)` to toggle the outer edges along an axis.
254
///
255
/// # Example
256
/// ```
257
/// # use bevy_gizmos::prelude::*;
258
/// # use bevy_math::prelude::*;
259
/// # use bevy_color::palettes::basic::GREEN;
260
/// fn system(mut gizmos: Gizmos) {
261
/// gizmos.grid_3d(
262
/// Isometry3d::IDENTITY,
263
/// UVec3::new(10, 2, 10),
264
/// Vec3::splat(2.),
265
/// GREEN
266
/// )
267
/// .skew_x(0.25)
268
/// .outer_edges();
269
/// }
270
/// # bevy_ecs::system::assert_is_system(system);
271
/// ```
272
pub fn grid_3d(
273
&mut self,
274
isometry: impl Into<Isometry3d>,
275
cell_count: UVec3,
276
spacing: Vec3,
277
color: impl Into<Color>,
278
) -> GridBuilder3d<'_, Config, Clear> {
279
GridBuilder3d {
280
gizmos: self,
281
isometry: isometry.into(),
282
spacing,
283
cell_count,
284
skew: Vec3::ZERO,
285
outer_edges: [false, false, false],
286
color: color.into(),
287
}
288
}
289
290
/// Draw a grid in 2D.
291
///
292
/// This should be called for each frame the grid needs to be rendered.
293
///
294
/// # Arguments
295
///
296
/// - `isometry` defines the translation and rotation of the grid.
297
/// - the translation specifies the center of the grid
298
/// - defines the orientation of the grid, by default we assume the grid is aligned with all axes
299
/// - `cell_count`: defines the amount of cells in the x and y axes
300
/// - `spacing`: defines the distance between cells along the x and y axes
301
/// - `color`: color of the grid
302
///
303
/// # Builder methods
304
///
305
/// - 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.
306
/// - All outer edges can be toggled on or off using `.outer_edges(...)`. Alternatively you can use `.outer_edges_x(...)` or `.outer_edges_y(...)` to toggle the outer edges along an axis.
307
///
308
/// # Example
309
/// ```
310
/// # use bevy_gizmos::prelude::*;
311
/// # use bevy_math::prelude::*;
312
/// # use bevy_color::palettes::basic::GREEN;
313
/// fn system(mut gizmos: Gizmos) {
314
/// gizmos.grid_2d(
315
/// Isometry2d::IDENTITY,
316
/// UVec2::new(10, 10),
317
/// Vec2::splat(1.),
318
/// GREEN
319
/// )
320
/// .skew_x(0.25)
321
/// .outer_edges();
322
/// }
323
/// # bevy_ecs::system::assert_is_system(system);
324
/// ```
325
pub fn grid_2d(
326
&mut self,
327
isometry: impl Into<Isometry2d>,
328
cell_count: UVec2,
329
spacing: Vec2,
330
color: impl Into<Color>,
331
) -> GridBuilder2d<'_, Config, Clear> {
332
let isometry = isometry.into();
333
GridBuilder2d {
334
gizmos: self,
335
isometry: Isometry3d::new(
336
isometry.translation.extend(0.0),
337
Quat::from_rotation_z(isometry.rotation.as_radians()),
338
),
339
spacing,
340
cell_count,
341
skew: Vec2::ZERO,
342
outer_edges: [false, false],
343
color: color.into(),
344
}
345
}
346
}
347
348
fn draw_grid<Config, Clear>(
349
gizmos: &mut GizmoBuffer<Config, Clear>,
350
isometry: Isometry3d,
351
spacing: Vec3,
352
cell_count: UVec3,
353
skew: Vec3,
354
outer_edges: [bool; 3],
355
color: Color,
356
) where
357
Config: GizmoConfigGroup,
358
Clear: 'static + Send + Sync,
359
{
360
if !gizmos.enabled {
361
return;
362
}
363
364
#[inline]
365
fn or_zero(cond: bool, val: Vec3) -> Vec3 {
366
if cond {
367
val
368
} else {
369
Vec3::ZERO
370
}
371
}
372
373
// Offset between two adjacent grid cells along the x/y-axis and accounting for skew.
374
let skew_tan = Vec3::from(skew.to_array().map(ops::tan));
375
let dx = or_zero(
376
cell_count.x != 0,
377
spacing.x * Vec3::new(1., skew_tan.y, skew_tan.z),
378
);
379
let dy = or_zero(
380
cell_count.y != 0,
381
spacing.y * Vec3::new(skew_tan.x, 1., skew_tan.z),
382
);
383
let dz = or_zero(
384
cell_count.z != 0,
385
spacing.z * Vec3::new(skew_tan.x, skew_tan.y, 1.),
386
);
387
388
// Bottom-left-front corner of the grid
389
let cell_count_half = cell_count.as_vec3() * 0.5;
390
let grid_start = -cell_count_half.x * dx - cell_count_half.y * dy - cell_count_half.z * dz;
391
392
#[inline]
393
fn cell_count_to_line_count(include_outer: bool, cell_count: u32) -> u32 {
394
if include_outer {
395
cell_count.saturating_add(1)
396
} else {
397
cell_count.saturating_sub(1).max(1)
398
}
399
}
400
401
let x_line_count = UVec2::new(
402
cell_count_to_line_count(outer_edges[0], cell_count.y),
403
cell_count_to_line_count(outer_edges[0], cell_count.z),
404
);
405
let y_line_count = UVec2::new(
406
cell_count_to_line_count(outer_edges[1], cell_count.z),
407
cell_count_to_line_count(outer_edges[1], cell_count.x),
408
);
409
let z_line_count = UVec2::new(
410
cell_count_to_line_count(outer_edges[2], cell_count.x),
411
cell_count_to_line_count(outer_edges[2], cell_count.y),
412
);
413
414
let x_start = grid_start + or_zero(!outer_edges[0], dy + dz);
415
let y_start = grid_start + or_zero(!outer_edges[1], dx + dz);
416
let z_start = grid_start + or_zero(!outer_edges[2], dx + dy);
417
418
fn iter_lines(
419
delta_a: Vec3,
420
delta_b: Vec3,
421
delta_c: Vec3,
422
line_count: UVec2,
423
cell_count: u32,
424
start: Vec3,
425
) -> impl Iterator<Item = [Vec3; 2]> {
426
let dline = delta_a * cell_count as f32;
427
(0..line_count.x).map(|v| v as f32).flat_map(move |b| {
428
(0..line_count.y).map(|v| v as f32).map(move |c| {
429
let line_start = start + b * delta_b + c * delta_c;
430
let line_end = line_start + dline;
431
[line_start, line_end]
432
})
433
})
434
}
435
436
// Lines along the x direction
437
let x_lines = iter_lines(dx, dy, dz, x_line_count, cell_count.x, x_start);
438
// Lines along the y direction
439
let y_lines = iter_lines(dy, dz, dx, y_line_count, cell_count.y, y_start);
440
// Lines along the z direction
441
let z_lines = iter_lines(dz, dx, dy, z_line_count, cell_count.z, z_start);
442
443
x_lines
444
.chain(y_lines)
445
.chain(z_lines)
446
.map(|vec3s| vec3s.map(|vec3| isometry * vec3))
447
.for_each(|[start, end]| {
448
gizmos.line(start, end, color);
449
});
450
}
451
452