Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_gizmos/src/lib.rs
9374 views
1
#![cfg_attr(docsrs, feature(doc_cfg))]
2
#![doc(
3
html_logo_url = "https://bevy.org/assets/icon.png",
4
html_favicon_url = "https://bevy.org/assets/icon.png"
5
)]
6
7
//! This crate adds an immediate mode drawing api to Bevy for visual debugging.
8
//!
9
//! # Example
10
//! ```
11
//! # use bevy_gizmos::prelude::*;
12
//! # use bevy_math::prelude::*;
13
//! # use bevy_color::palettes::basic::GREEN;
14
//! fn system(mut gizmos: Gizmos) {
15
//! gizmos.line(Vec3::ZERO, Vec3::X, GREEN);
16
//! }
17
//! # bevy_ecs::system::assert_is_system(system);
18
//! ```
19
//!
20
//! See the documentation on [Gizmos](crate::gizmos::Gizmos) for more examples.
21
22
// Required to make proc macros work in bevy itself.
23
extern crate self as bevy_gizmos;
24
25
pub mod aabb;
26
pub mod arcs;
27
pub mod arrows;
28
pub mod circles;
29
pub mod config;
30
pub mod cross;
31
pub mod curves;
32
pub mod frustum;
33
pub mod gizmos;
34
mod global;
35
pub mod grid;
36
pub mod primitives;
37
pub mod retained;
38
pub mod rounded_box;
39
mod simplex_stroke_font;
40
pub mod stroke_text;
41
42
#[cfg(feature = "bevy_mesh")]
43
pub mod skinned_mesh_bounds;
44
45
/// The gizmos prelude.
46
///
47
/// This includes the most common types in this crate, re-exported for your convenience.
48
pub mod prelude {
49
#[doc(hidden)]
50
pub use crate::aabb::{AabbGizmoConfigGroup, ShowAabbGizmo};
51
pub use crate::frustum::{FrustumGizmoConfigGroup, ShowFrustumGizmo};
52
53
#[doc(hidden)]
54
#[cfg(feature = "bevy_mesh")]
55
pub use crate::skinned_mesh_bounds::{
56
ShowSkinnedMeshBoundsGizmo, SkinnedMeshBoundsGizmoConfigGroup,
57
};
58
59
#[doc(hidden)]
60
pub use crate::{
61
config::{
62
DefaultGizmoConfigGroup, GizmoConfig, GizmoConfigGroup, GizmoConfigStore,
63
GizmoLineConfig, GizmoLineJoint, GizmoLineStyle,
64
},
65
gizmos::Gizmos,
66
global::gizmo,
67
primitives::{dim2::GizmoPrimitive2d, dim3::GizmoPrimitive3d},
68
retained::Gizmo,
69
AppGizmoBuilder, GizmoAsset,
70
};
71
}
72
73
use bevy_app::{App, FixedFirst, FixedLast, Last, Plugin, RunFixedMainLoop};
74
use bevy_asset::{Asset, AssetApp, Assets, Handle};
75
use bevy_color::{Color, Oklcha};
76
use bevy_ecs::{
77
prelude::Entity,
78
resource::Resource,
79
schedule::{IntoScheduleConfigs, SystemSet},
80
system::{Res, ResMut},
81
};
82
use bevy_reflect::TypePath;
83
84
use crate::{config::ErasedGizmoConfigGroup, gizmos::GizmoBuffer};
85
86
use bevy_time::Fixed;
87
use bevy_utils::TypeIdMap;
88
use config::{DefaultGizmoConfigGroup, GizmoConfig, GizmoConfigGroup, GizmoConfigStore};
89
use core::{any::TypeId, marker::PhantomData, mem};
90
use gizmos::{GizmoStorage, Swap};
91
92
#[cfg(feature = "bevy_mesh")]
93
use crate::skinned_mesh_bounds::SkinnedMeshBoundsGizmoPlugin;
94
95
/// A [`Plugin`] that provides an immediate mode drawing api for visual debugging.
96
#[derive(Default)]
97
pub struct GizmoPlugin;
98
99
impl Plugin for GizmoPlugin {
100
fn build(&self, app: &mut App) {
101
app.init_asset::<GizmoAsset>()
102
.init_resource::<GizmoHandles>()
103
// We insert the Resource GizmoConfigStore into the world implicitly here if it does not exist.
104
.init_gizmo_group::<DefaultGizmoConfigGroup>();
105
106
app.add_plugins((
107
aabb::AabbGizmoPlugin,
108
frustum::FrustumGizmoPlugin,
109
global::GlobalGizmosPlugin,
110
));
111
112
#[cfg(feature = "bevy_mesh")]
113
app.add_plugins(SkinnedMeshBoundsGizmoPlugin);
114
}
115
}
116
117
/// A extension trait adding `App::init_gizmo_group` and `App::insert_gizmo_config`.
118
pub trait AppGizmoBuilder {
119
/// Registers [`GizmoConfigGroup`] in the app enabling the use of [Gizmos&lt;Config&gt;](crate::gizmos::Gizmos).
120
///
121
/// Configurations can be set using the [`GizmoConfigStore`] [`Resource`].
122
fn init_gizmo_group<Config: GizmoConfigGroup>(&mut self) -> &mut Self;
123
124
/// Insert a [`GizmoConfig`] into a specific [`GizmoConfigGroup`].
125
///
126
/// This method should be preferred over [`AppGizmoBuilder::init_gizmo_group`] if and only if you need to configure fields upon initialization.
127
fn insert_gizmo_config<Config: GizmoConfigGroup>(
128
&mut self,
129
group: Config,
130
config: GizmoConfig,
131
) -> &mut Self;
132
}
133
134
impl AppGizmoBuilder for App {
135
fn init_gizmo_group<Config: GizmoConfigGroup>(&mut self) -> &mut Self {
136
if self.world().contains_resource::<GizmoStorage<Config, ()>>() {
137
return self;
138
}
139
140
self.world_mut()
141
.get_resource_or_init::<GizmoConfigStore>()
142
.register::<Config>();
143
144
let mut handles = self.world_mut().get_resource_or_init::<GizmoHandles>();
145
146
handles.handles.insert(TypeId::of::<Config>(), None);
147
148
// These handles are safe to mutate in any order
149
self.allow_ambiguous_resource::<GizmoHandles>();
150
151
self.init_resource::<GizmoStorage<Config, ()>>()
152
.init_resource::<GizmoStorage<Config, Fixed>>()
153
.init_resource::<GizmoStorage<Config, Swap<Fixed>>>()
154
.add_systems(
155
RunFixedMainLoop,
156
start_gizmo_context::<Config, Fixed>
157
.in_set(bevy_app::RunFixedMainLoopSystems::BeforeFixedMainLoop),
158
)
159
.add_systems(FixedFirst, clear_gizmo_context::<Config, Fixed>)
160
.add_systems(FixedLast, collect_requested_gizmos::<Config, Fixed>)
161
.add_systems(
162
RunFixedMainLoop,
163
end_gizmo_context::<Config, Fixed>
164
.in_set(bevy_app::RunFixedMainLoopSystems::AfterFixedMainLoop),
165
)
166
.add_systems(
167
Last,
168
(
169
propagate_gizmos::<Config, Fixed>.before(GizmoMeshSystems),
170
update_gizmo_meshes::<Config>.in_set(GizmoMeshSystems),
171
),
172
);
173
174
self
175
}
176
177
fn insert_gizmo_config<Config: GizmoConfigGroup>(
178
&mut self,
179
group: Config,
180
config: GizmoConfig,
181
) -> &mut Self {
182
self.init_gizmo_group::<Config>();
183
184
self.world_mut()
185
.get_resource_or_init::<GizmoConfigStore>()
186
.insert(config, group);
187
188
self
189
}
190
}
191
192
/// Holds handles to the line gizmos for each gizmo configuration group
193
// As `TypeIdMap` iteration order depends on the order of insertions and deletions, this uses
194
// `Option<Handle>` to be able to reserve the slot when creating the gizmo configuration group.
195
// That way iteration order is stable across executions and depends on the order of configuration
196
// group creation.
197
#[derive(Resource, Default)]
198
pub struct GizmoHandles {
199
handles: TypeIdMap<Option<Handle<GizmoAsset>>>,
200
}
201
202
impl GizmoHandles {
203
/// The handles to the gizmo assets of each gizmo configuration group.
204
pub fn handles(&self) -> &TypeIdMap<Option<Handle<GizmoAsset>>> {
205
&self.handles
206
}
207
}
208
209
/// Start a new gizmo clearing context.
210
///
211
/// Internally this pushes the parent default context into a swap buffer.
212
/// Gizmo contexts should be handled like a stack, so if you push a new context,
213
/// you must pop the context before the parent context ends.
214
pub fn start_gizmo_context<Config, Clear>(
215
mut swap: ResMut<GizmoStorage<Config, Swap<Clear>>>,
216
mut default: ResMut<GizmoStorage<Config, ()>>,
217
) where
218
Config: GizmoConfigGroup,
219
Clear: 'static + Send + Sync,
220
{
221
default.swap(&mut *swap);
222
}
223
224
/// End this gizmo clearing context.
225
///
226
/// Pop the default gizmos context out of the [`Swap<Clear>`] gizmo storage.
227
///
228
/// This must be called before [`GizmoMeshSystems`] in the [`Last`] schedule.
229
pub fn end_gizmo_context<Config, Clear>(
230
mut swap: ResMut<GizmoStorage<Config, Swap<Clear>>>,
231
mut default: ResMut<GizmoStorage<Config, ()>>,
232
) where
233
Config: GizmoConfigGroup,
234
Clear: 'static + Send + Sync,
235
{
236
default.clear();
237
default.swap(&mut *swap);
238
}
239
240
/// Collect the requested gizmos into a specific clear context.
241
pub fn collect_requested_gizmos<Config, Clear>(
242
mut update: ResMut<GizmoStorage<Config, ()>>,
243
mut context: ResMut<GizmoStorage<Config, Clear>>,
244
) where
245
Config: GizmoConfigGroup,
246
Clear: 'static + Send + Sync,
247
{
248
context.append_storage(&update);
249
update.clear();
250
}
251
252
/// Clear out the contextual gizmos.
253
pub fn clear_gizmo_context<Config, Clear>(mut context: ResMut<GizmoStorage<Config, Clear>>)
254
where
255
Config: GizmoConfigGroup,
256
Clear: 'static + Send + Sync,
257
{
258
context.clear();
259
}
260
261
/// Propagate the contextual gizmo into the `Update` storage for rendering.
262
///
263
/// This should be before [`GizmoMeshSystems`].
264
pub fn propagate_gizmos<Config, Clear>(
265
mut update_storage: ResMut<GizmoStorage<Config, ()>>,
266
contextual_storage: Res<GizmoStorage<Config, Clear>>,
267
) where
268
Config: GizmoConfigGroup,
269
Clear: 'static + Send + Sync,
270
{
271
update_storage.append_storage(&*contextual_storage);
272
}
273
274
/// System set for updating the rendering meshes for drawing gizmos.
275
#[derive(SystemSet, Clone, Debug, PartialEq, Eq, Hash)]
276
pub struct GizmoMeshSystems;
277
278
/// Prepare gizmos for rendering.
279
///
280
/// This also clears the default `GizmoStorage`.
281
fn update_gizmo_meshes<Config: GizmoConfigGroup>(
282
mut gizmo_assets: ResMut<Assets<GizmoAsset>>,
283
mut handles: ResMut<GizmoHandles>,
284
mut storage: ResMut<GizmoStorage<Config, ()>>,
285
) {
286
if storage.list_positions.is_empty() && storage.strip_positions.is_empty() {
287
handles.handles.insert(TypeId::of::<Config>(), None);
288
} else if let Some(handle) = handles.handles.get_mut(&TypeId::of::<Config>()) {
289
if let Some(handle) = handle {
290
let mut gizmo = gizmo_assets.get_mut(handle.id()).unwrap();
291
292
gizmo.buffer.list_positions = mem::take(&mut storage.list_positions);
293
gizmo.buffer.list_colors = mem::take(&mut storage.list_colors);
294
gizmo.buffer.strip_positions = mem::take(&mut storage.strip_positions);
295
gizmo.buffer.strip_colors = mem::take(&mut storage.strip_colors);
296
} else {
297
let gizmo = GizmoAsset {
298
config_ty: TypeId::of::<Config>(),
299
buffer: GizmoBuffer {
300
enabled: true,
301
list_positions: mem::take(&mut storage.list_positions),
302
list_colors: mem::take(&mut storage.list_colors),
303
strip_positions: mem::take(&mut storage.strip_positions),
304
strip_colors: mem::take(&mut storage.strip_colors),
305
marker: PhantomData,
306
},
307
};
308
309
*handle = Some(gizmo_assets.add(gizmo));
310
}
311
}
312
}
313
314
/// A collection of gizmos.
315
///
316
/// Has the same gizmo drawing API as [`Gizmos`](crate::gizmos::Gizmos).
317
#[derive(Asset, Debug, Clone, TypePath)]
318
pub struct GizmoAsset {
319
/// vertex buffers.
320
buffer: GizmoBuffer<ErasedGizmoConfigGroup, ()>,
321
config_ty: TypeId,
322
}
323
324
impl GizmoAsset {
325
/// A reference to the gizmo's vertex buffer.
326
pub fn buffer(&self) -> &GizmoBuffer<ErasedGizmoConfigGroup, ()> {
327
&self.buffer
328
}
329
}
330
331
impl GizmoAsset {
332
/// Create a new [`GizmoAsset`].
333
pub fn new() -> Self {
334
GizmoAsset {
335
buffer: GizmoBuffer::default(),
336
config_ty: TypeId::of::<ErasedGizmoConfigGroup>(),
337
}
338
}
339
340
/// The type of the gizmo's configuration group.
341
pub fn config_typeid(&self) -> TypeId {
342
self.config_ty
343
}
344
}
345
346
impl Default for GizmoAsset {
347
fn default() -> Self {
348
GizmoAsset::new()
349
}
350
}
351
352
/// Generates a random, well-dispersed color seeded by the provided `Entity`.
353
pub fn color_from_entity(entity: Entity) -> Color {
354
Oklcha::sequential_dispersed(entity.index_u32()).into()
355
}
356
357