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
7extern crate self as bevy_gizmos;
24
25pub mod aabb;
26pub mod arcs;
27pub mod arrows;
28pub mod circles;
29pub mod config;
30pub mod cross;
31pub mod curves;
32pub mod gizmos;
33mod global;
34pub mod grid;
35pub mod primitives;
36pub mod retained;
37pub mod rounded_box;
38
39#[cfg(feature = "bevy_light")]
40pub mod light;
41
42pub mod prelude {
46 #[doc(hidden)]
47 pub use crate::aabb::{AabbGizmoConfigGroup, ShowAabbGizmo};
48
49 #[doc(hidden)]
50 pub use crate::{
51 config::{
52 DefaultGizmoConfigGroup, GizmoConfig, GizmoConfigGroup, GizmoConfigStore,
53 GizmoLineConfig, GizmoLineJoint, GizmoLineStyle,
54 },
55 gizmos::Gizmos,
56 global::gizmo,
57 primitives::{dim2::GizmoPrimitive2d, dim3::GizmoPrimitive3d},
58 retained::Gizmo,
59 AppGizmoBuilder, GizmoAsset,
60 };
61
62 #[doc(hidden)]
63 #[cfg(feature = "bevy_light")]
64 pub use crate::light::{LightGizmoColor, LightGizmoConfigGroup, ShowLightGizmo};
65}
66
67use bevy_app::{App, FixedFirst, FixedLast, Last, Plugin, RunFixedMainLoop};
68use bevy_asset::{Asset, AssetApp, Assets, Handle};
69use bevy_ecs::{
70 resource::Resource,
71 schedule::{IntoScheduleConfigs, SystemSet},
72 system::{Res, ResMut},
73};
74use bevy_reflect::TypePath;
75
76use crate::{config::ErasedGizmoConfigGroup, gizmos::GizmoBuffer};
77
78use bevy_time::Fixed;
79use bevy_utils::TypeIdMap;
80use config::{DefaultGizmoConfigGroup, GizmoConfig, GizmoConfigGroup, GizmoConfigStore};
81use core::{any::TypeId, marker::PhantomData, mem};
82use gizmos::{GizmoStorage, Swap};
83#[cfg(feature = "bevy_light")]
84use light::LightGizmoPlugin;
85
86#[derive(Default)]
88pub struct GizmoPlugin;
89
90impl Plugin for GizmoPlugin {
91 fn build(&self, app: &mut App) {
92 app.init_asset::<GizmoAsset>()
93 .init_resource::<GizmoHandles>()
94 .init_gizmo_group::<DefaultGizmoConfigGroup>();
96
97 app.add_plugins((aabb::AabbGizmoPlugin, global::GlobalGizmosPlugin));
98
99 #[cfg(feature = "bevy_light")]
100 app.add_plugins(LightGizmoPlugin);
101 }
102}
103
104pub trait AppGizmoBuilder {
106 fn init_gizmo_group<Config: GizmoConfigGroup>(&mut self) -> &mut Self;
110
111 fn insert_gizmo_config<Config: GizmoConfigGroup>(
115 &mut self,
116 group: Config,
117 config: GizmoConfig,
118 ) -> &mut Self;
119}
120
121impl AppGizmoBuilder for App {
122 fn init_gizmo_group<Config: GizmoConfigGroup>(&mut self) -> &mut Self {
123 if self.world().contains_resource::<GizmoStorage<Config, ()>>() {
124 return self;
125 }
126
127 self.world_mut()
128 .get_resource_or_init::<GizmoConfigStore>()
129 .register::<Config>();
130
131 let mut handles = self.world_mut().get_resource_or_init::<GizmoHandles>();
132
133 handles.handles.insert(TypeId::of::<Config>(), None);
134
135 self.allow_ambiguous_resource::<GizmoHandles>();
137
138 self.init_resource::<GizmoStorage<Config, ()>>()
139 .init_resource::<GizmoStorage<Config, Fixed>>()
140 .init_resource::<GizmoStorage<Config, Swap<Fixed>>>()
141 .add_systems(
142 RunFixedMainLoop,
143 start_gizmo_context::<Config, Fixed>
144 .in_set(bevy_app::RunFixedMainLoopSystems::BeforeFixedMainLoop),
145 )
146 .add_systems(FixedFirst, clear_gizmo_context::<Config, Fixed>)
147 .add_systems(FixedLast, collect_requested_gizmos::<Config, Fixed>)
148 .add_systems(
149 RunFixedMainLoop,
150 end_gizmo_context::<Config, Fixed>
151 .in_set(bevy_app::RunFixedMainLoopSystems::AfterFixedMainLoop),
152 )
153 .add_systems(
154 Last,
155 (
156 propagate_gizmos::<Config, Fixed>.before(GizmoMeshSystems),
157 update_gizmo_meshes::<Config>.in_set(GizmoMeshSystems),
158 ),
159 );
160
161 self
162 }
163
164 fn insert_gizmo_config<Config: GizmoConfigGroup>(
165 &mut self,
166 group: Config,
167 config: GizmoConfig,
168 ) -> &mut Self {
169 self.init_gizmo_group::<Config>();
170
171 self.world_mut()
172 .get_resource_or_init::<GizmoConfigStore>()
173 .insert(config, group);
174
175 self
176 }
177}
178
179#[derive(Resource, Default)]
185pub struct GizmoHandles {
186 handles: TypeIdMap<Option<Handle<GizmoAsset>>>,
187}
188
189impl GizmoHandles {
190 pub fn handles(&self) -> &TypeIdMap<Option<Handle<GizmoAsset>>> {
192 &self.handles
193 }
194}
195
196pub fn start_gizmo_context<Config, Clear>(
202 mut swap: ResMut<GizmoStorage<Config, Swap<Clear>>>,
203 mut default: ResMut<GizmoStorage<Config, ()>>,
204) where
205 Config: GizmoConfigGroup,
206 Clear: 'static + Send + Sync,
207{
208 default.swap(&mut *swap);
209}
210
211pub fn end_gizmo_context<Config, Clear>(
217 mut swap: ResMut<GizmoStorage<Config, Swap<Clear>>>,
218 mut default: ResMut<GizmoStorage<Config, ()>>,
219) where
220 Config: GizmoConfigGroup,
221 Clear: 'static + Send + Sync,
222{
223 default.clear();
224 default.swap(&mut *swap);
225}
226
227pub fn collect_requested_gizmos<Config, Clear>(
229 mut update: ResMut<GizmoStorage<Config, ()>>,
230 mut context: ResMut<GizmoStorage<Config, Clear>>,
231) where
232 Config: GizmoConfigGroup,
233 Clear: 'static + Send + Sync,
234{
235 context.append_storage(&update);
236 update.clear();
237}
238
239pub fn clear_gizmo_context<Config, Clear>(mut context: ResMut<GizmoStorage<Config, Clear>>)
241where
242 Config: GizmoConfigGroup,
243 Clear: 'static + Send + Sync,
244{
245 context.clear();
246}
247
248pub fn propagate_gizmos<Config, Clear>(
252 mut update_storage: ResMut<GizmoStorage<Config, ()>>,
253 contextual_storage: Res<GizmoStorage<Config, Clear>>,
254) where
255 Config: GizmoConfigGroup,
256 Clear: 'static + Send + Sync,
257{
258 update_storage.append_storage(&*contextual_storage);
259}
260
261#[derive(SystemSet, Clone, Debug, PartialEq, Eq, Hash)]
263pub struct GizmoMeshSystems;
264
265fn update_gizmo_meshes<Config: GizmoConfigGroup>(
269 mut gizmo_assets: ResMut<Assets<GizmoAsset>>,
270 mut handles: ResMut<GizmoHandles>,
271 mut storage: ResMut<GizmoStorage<Config, ()>>,
272) {
273 if storage.list_positions.is_empty() && storage.strip_positions.is_empty() {
274 handles.handles.insert(TypeId::of::<Config>(), None);
275 } else if let Some(handle) = handles.handles.get_mut(&TypeId::of::<Config>()) {
276 if let Some(handle) = handle {
277 let gizmo = gizmo_assets.get_mut(handle.id()).unwrap();
278
279 gizmo.buffer.list_positions = mem::take(&mut storage.list_positions);
280 gizmo.buffer.list_colors = mem::take(&mut storage.list_colors);
281 gizmo.buffer.strip_positions = mem::take(&mut storage.strip_positions);
282 gizmo.buffer.strip_colors = mem::take(&mut storage.strip_colors);
283 } else {
284 let gizmo = GizmoAsset {
285 config_ty: TypeId::of::<Config>(),
286 buffer: GizmoBuffer {
287 enabled: true,
288 list_positions: mem::take(&mut storage.list_positions),
289 list_colors: mem::take(&mut storage.list_colors),
290 strip_positions: mem::take(&mut storage.strip_positions),
291 strip_colors: mem::take(&mut storage.strip_colors),
292 marker: PhantomData,
293 },
294 };
295
296 *handle = Some(gizmo_assets.add(gizmo));
297 }
298 }
299}
300
301#[derive(Asset, Debug, Clone, TypePath)]
305pub struct GizmoAsset {
306 buffer: GizmoBuffer<ErasedGizmoConfigGroup, ()>,
308 config_ty: TypeId,
309}
310
311impl GizmoAsset {
312 pub fn buffer(&self) -> &GizmoBuffer<ErasedGizmoConfigGroup, ()> {
314 &self.buffer
315 }
316}
317
318impl GizmoAsset {
319 pub fn new() -> Self {
321 GizmoAsset {
322 buffer: GizmoBuffer::default(),
323 config_ty: TypeId::of::<ErasedGizmoConfigGroup>(),
324 }
325 }
326
327 pub fn config_typeid(&self) -> TypeId {
329 self.config_ty
330 }
331}
332
333impl Default for GizmoAsset {
334 fn default() -> Self {
335 GizmoAsset::new()
336 }
337}