#![cfg_attr(docsrs, feature(doc_cfg))]
#![doc(
html_logo_url = "https://bevy.org/assets/icon.png",
html_favicon_url = "https://bevy.org/assets/icon.png"
)]
extern crate self as bevy_gizmos;
pub mod aabb;
pub mod arcs;
pub mod arrows;
pub mod circles;
pub mod config;
pub mod cross;
pub mod curves;
pub mod frustum;
pub mod gizmos;
mod global;
pub mod grid;
pub mod primitives;
pub mod retained;
pub mod rounded_box;
#[cfg(feature = "bevy_mesh")]
pub mod skinned_mesh_bounds;
pub mod prelude {
#[doc(hidden)]
pub use crate::aabb::{AabbGizmoConfigGroup, ShowAabbGizmo};
pub use crate::frustum::{FrustumGizmoConfigGroup, ShowFrustumGizmo};
#[doc(hidden)]
#[cfg(feature = "bevy_mesh")]
pub use crate::skinned_mesh_bounds::{
ShowSkinnedMeshBoundsGizmo, SkinnedMeshBoundsGizmoConfigGroup,
};
#[doc(hidden)]
pub use crate::{
config::{
DefaultGizmoConfigGroup, GizmoConfig, GizmoConfigGroup, GizmoConfigStore,
GizmoLineConfig, GizmoLineJoint, GizmoLineStyle,
},
gizmos::Gizmos,
global::gizmo,
primitives::{dim2::GizmoPrimitive2d, dim3::GizmoPrimitive3d},
retained::Gizmo,
AppGizmoBuilder, GizmoAsset,
};
}
use bevy_app::{App, FixedFirst, FixedLast, Last, Plugin, RunFixedMainLoop};
use bevy_asset::{Asset, AssetApp, Assets, Handle};
use bevy_color::{Color, Oklcha};
use bevy_ecs::{
prelude::Entity,
resource::Resource,
schedule::{IntoScheduleConfigs, SystemSet},
system::{Res, ResMut},
};
use bevy_reflect::TypePath;
use crate::{config::ErasedGizmoConfigGroup, gizmos::GizmoBuffer};
use bevy_time::Fixed;
use bevy_utils::TypeIdMap;
use config::{DefaultGizmoConfigGroup, GizmoConfig, GizmoConfigGroup, GizmoConfigStore};
use core::{any::TypeId, marker::PhantomData, mem};
use gizmos::{GizmoStorage, Swap};
#[cfg(feature = "bevy_mesh")]
use crate::skinned_mesh_bounds::SkinnedMeshBoundsGizmoPlugin;
#[derive(Default)]
pub struct GizmoPlugin;
impl Plugin for GizmoPlugin {
fn build(&self, app: &mut App) {
app.init_asset::<GizmoAsset>()
.init_resource::<GizmoHandles>()
.init_gizmo_group::<DefaultGizmoConfigGroup>();
app.add_plugins((
aabb::AabbGizmoPlugin,
frustum::FrustumGizmoPlugin,
global::GlobalGizmosPlugin,
));
#[cfg(feature = "bevy_mesh")]
app.add_plugins(SkinnedMeshBoundsGizmoPlugin);
}
}
pub trait AppGizmoBuilder {
fn init_gizmo_group<Config: GizmoConfigGroup>(&mut self) -> &mut Self;
fn insert_gizmo_config<Config: GizmoConfigGroup>(
&mut self,
group: Config,
config: GizmoConfig,
) -> &mut Self;
}
impl AppGizmoBuilder for App {
fn init_gizmo_group<Config: GizmoConfigGroup>(&mut self) -> &mut Self {
if self.world().contains_resource::<GizmoStorage<Config, ()>>() {
return self;
}
self.world_mut()
.get_resource_or_init::<GizmoConfigStore>()
.register::<Config>();
let mut handles = self.world_mut().get_resource_or_init::<GizmoHandles>();
handles.handles.insert(TypeId::of::<Config>(), None);
self.allow_ambiguous_resource::<GizmoHandles>();
self.init_resource::<GizmoStorage<Config, ()>>()
.init_resource::<GizmoStorage<Config, Fixed>>()
.init_resource::<GizmoStorage<Config, Swap<Fixed>>>()
.add_systems(
RunFixedMainLoop,
start_gizmo_context::<Config, Fixed>
.in_set(bevy_app::RunFixedMainLoopSystems::BeforeFixedMainLoop),
)
.add_systems(FixedFirst, clear_gizmo_context::<Config, Fixed>)
.add_systems(FixedLast, collect_requested_gizmos::<Config, Fixed>)
.add_systems(
RunFixedMainLoop,
end_gizmo_context::<Config, Fixed>
.in_set(bevy_app::RunFixedMainLoopSystems::AfterFixedMainLoop),
)
.add_systems(
Last,
(
propagate_gizmos::<Config, Fixed>.before(GizmoMeshSystems),
update_gizmo_meshes::<Config>.in_set(GizmoMeshSystems),
),
);
self
}
fn insert_gizmo_config<Config: GizmoConfigGroup>(
&mut self,
group: Config,
config: GizmoConfig,
) -> &mut Self {
self.init_gizmo_group::<Config>();
self.world_mut()
.get_resource_or_init::<GizmoConfigStore>()
.insert(config, group);
self
}
}
#[derive(Resource, Default)]
pub struct GizmoHandles {
handles: TypeIdMap<Option<Handle<GizmoAsset>>>,
}
impl GizmoHandles {
pub fn handles(&self) -> &TypeIdMap<Option<Handle<GizmoAsset>>> {
&self.handles
}
}
pub fn start_gizmo_context<Config, Clear>(
mut swap: ResMut<GizmoStorage<Config, Swap<Clear>>>,
mut default: ResMut<GizmoStorage<Config, ()>>,
) where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
{
default.swap(&mut *swap);
}
pub fn end_gizmo_context<Config, Clear>(
mut swap: ResMut<GizmoStorage<Config, Swap<Clear>>>,
mut default: ResMut<GizmoStorage<Config, ()>>,
) where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
{
default.clear();
default.swap(&mut *swap);
}
pub fn collect_requested_gizmos<Config, Clear>(
mut update: ResMut<GizmoStorage<Config, ()>>,
mut context: ResMut<GizmoStorage<Config, Clear>>,
) where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
{
context.append_storage(&update);
update.clear();
}
pub fn clear_gizmo_context<Config, Clear>(mut context: ResMut<GizmoStorage<Config, Clear>>)
where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
{
context.clear();
}
pub fn propagate_gizmos<Config, Clear>(
mut update_storage: ResMut<GizmoStorage<Config, ()>>,
contextual_storage: Res<GizmoStorage<Config, Clear>>,
) where
Config: GizmoConfigGroup,
Clear: 'static + Send + Sync,
{
update_storage.append_storage(&*contextual_storage);
}
#[derive(SystemSet, Clone, Debug, PartialEq, Eq, Hash)]
pub struct GizmoMeshSystems;
fn update_gizmo_meshes<Config: GizmoConfigGroup>(
mut gizmo_assets: ResMut<Assets<GizmoAsset>>,
mut handles: ResMut<GizmoHandles>,
mut storage: ResMut<GizmoStorage<Config, ()>>,
) {
if storage.list_positions.is_empty() && storage.strip_positions.is_empty() {
handles.handles.insert(TypeId::of::<Config>(), None);
} else if let Some(handle) = handles.handles.get_mut(&TypeId::of::<Config>()) {
if let Some(handle) = handle {
let gizmo = gizmo_assets.get_mut(handle.id()).unwrap();
gizmo.buffer.list_positions = mem::take(&mut storage.list_positions);
gizmo.buffer.list_colors = mem::take(&mut storage.list_colors);
gizmo.buffer.strip_positions = mem::take(&mut storage.strip_positions);
gizmo.buffer.strip_colors = mem::take(&mut storage.strip_colors);
} else {
let gizmo = GizmoAsset {
config_ty: TypeId::of::<Config>(),
buffer: GizmoBuffer {
enabled: true,
list_positions: mem::take(&mut storage.list_positions),
list_colors: mem::take(&mut storage.list_colors),
strip_positions: mem::take(&mut storage.strip_positions),
strip_colors: mem::take(&mut storage.strip_colors),
marker: PhantomData,
},
};
*handle = Some(gizmo_assets.add(gizmo));
}
}
}
#[derive(Asset, Debug, Clone, TypePath)]
pub struct GizmoAsset {
buffer: GizmoBuffer<ErasedGizmoConfigGroup, ()>,
config_ty: TypeId,
}
impl GizmoAsset {
pub fn buffer(&self) -> &GizmoBuffer<ErasedGizmoConfigGroup, ()> {
&self.buffer
}
}
impl GizmoAsset {
pub fn new() -> Self {
GizmoAsset {
buffer: GizmoBuffer::default(),
config_ty: TypeId::of::<ErasedGizmoConfigGroup>(),
}
}
pub fn config_typeid(&self) -> TypeId {
self.config_ty
}
}
impl Default for GizmoAsset {
fn default() -> Self {
GizmoAsset::new()
}
}
pub fn color_from_entity(entity: Entity) -> Color {
Oklcha::sequential_dispersed(entity.index_u32()).into()
}