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