use bevy_camera::visibility::RenderLayers;
pub use bevy_gizmos_macros::GizmoConfigGroup;
use {crate::GizmoAsset, bevy_asset::Handle, bevy_ecs::component::Component};
use bevy_ecs::{reflect::ReflectResource, resource::Resource, template::FromTemplate};
use bevy_reflect::{std_traits::ReflectDefault, Reflect, TypePath};
use bevy_utils::TypeIdHashMap;
use core::{
any::TypeId,
hash::Hash,
ops::{Deref, DerefMut},
panic,
};
#[derive(Debug, Default, Copy, Clone, Reflect, PartialEq, Eq, Hash)]
#[reflect(Default, PartialEq, Hash, Clone)]
pub enum GizmoLineJoint {
#[default]
None,
Miter,
Round(u32),
Bevel,
}
#[derive(Copy, Clone, Debug, Default, PartialEq, Reflect)]
#[reflect(Default, PartialEq, Hash, Clone)]
#[non_exhaustive]
pub enum GizmoLineStyle {
#[default]
Solid,
Dotted,
Dashed {
gap_scale: f32,
line_scale: f32,
},
}
impl Eq for GizmoLineStyle {}
impl Hash for GizmoLineStyle {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
match self {
Self::Solid => {
0u64.hash(state);
}
Self::Dotted => 1u64.hash(state),
Self::Dashed {
gap_scale,
line_scale,
} => {
2u64.hash(state);
gap_scale.to_bits().hash(state);
line_scale.to_bits().hash(state);
}
}
}
}
pub trait GizmoConfigGroup: Reflect + TypePath + Default {}
#[derive(Default, Reflect, GizmoConfigGroup)]
#[reflect(Default)]
pub struct DefaultGizmoConfigGroup;
#[derive(Default, Reflect, GizmoConfigGroup, Debug, Clone)]
#[reflect(Default, Clone)]
pub struct ErasedGizmoConfigGroup;
#[derive(Reflect, Resource, Default)]
#[reflect(Resource, Default)]
pub struct GizmoConfigStore {
#[reflect(ignore)]
store: TypeIdHashMap<(GizmoConfig, Box<dyn Reflect>)>,
}
impl GizmoConfigStore {
pub fn get_config_dyn(&self, config_type_id: &TypeId) -> Option<(&GizmoConfig, &dyn Reflect)> {
let (config, ext) = self.store.get(config_type_id)?;
Some((config, ext.deref()))
}
pub fn config<T: GizmoConfigGroup>(&self) -> (&GizmoConfig, &T) {
let Some(configs) = self.get_config() else {
panic!("Requested config {} does not exist in `GizmoConfigStore`! Did you forget to add it using `app.init_gizmo_group<T>()`?", T::type_path());
};
configs
}
pub fn get_config<T: GizmoConfigGroup>(&self) -> Option<(&GizmoConfig, &T)> {
let (config, ext) = self.get_config_dyn(&TypeId::of::<T>())?;
let ext = ext.as_any().downcast_ref().unwrap();
Some((config, ext))
}
pub fn get_config_mut_dyn(
&mut self,
config_type_id: &TypeId,
) -> Option<(&mut GizmoConfig, &mut dyn Reflect)> {
let (config, ext) = self.store.get_mut(config_type_id)?;
Some((config, ext.deref_mut()))
}
pub fn config_mut<T: GizmoConfigGroup>(&mut self) -> (&mut GizmoConfig, &mut T) {
let Some(configs) = self.get_config_mut() else {
panic!("Requested config {} does not exist in `GizmoConfigStore`! Did you forget to add it using `app.init_gizmo_group<T>()`?", T::type_path());
};
configs
}
pub fn get_config_mut<T: GizmoConfigGroup>(&mut self) -> Option<(&mut GizmoConfig, &mut T)> {
let (config, ext) = self.get_config_mut_dyn(&TypeId::of::<T>())?;
let ext = ext.as_any_mut().downcast_mut().unwrap();
Some((config, ext))
}
pub fn iter(&self) -> impl Iterator<Item = (&TypeId, &GizmoConfig, &dyn Reflect)> + '_ {
self.store
.iter()
.map(|(id, (config, ext))| (id, config, ext.deref()))
}
pub fn iter_mut(
&mut self,
) -> impl Iterator<Item = (&TypeId, &mut GizmoConfig, &mut dyn Reflect)> + '_ {
self.store
.iter_mut()
.map(|(id, (config, ext))| (id, config, ext.deref_mut()))
}
pub fn insert<T: GizmoConfigGroup>(&mut self, config: GizmoConfig, ext_config: T) {
self.store
.insert(TypeId::of::<T>(), (config, Box::new(ext_config)));
}
pub(crate) fn register<T: GizmoConfigGroup>(&mut self) {
self.insert(GizmoConfig::default(), T::default());
}
}
#[derive(Clone, Reflect, Debug)]
#[reflect(Clone, Default)]
pub struct GizmoConfig {
pub enabled: bool,
pub line: GizmoLineConfig,
pub depth_bias: f32,
pub render_layers: RenderLayers,
}
impl Default for GizmoConfig {
fn default() -> Self {
Self {
enabled: true,
line: Default::default(),
depth_bias: 0.,
render_layers: Default::default(),
}
}
}
#[derive(Clone, Reflect, Debug)]
#[reflect(Clone, Default)]
pub struct GizmoLineConfig {
pub width: f32,
pub perspective: bool,
pub style: GizmoLineStyle,
pub joints: GizmoLineJoint,
pub animation_offset: f32,
}
impl Default for GizmoLineConfig {
fn default() -> Self {
Self {
width: 2.,
perspective: false,
style: GizmoLineStyle::Solid,
joints: GizmoLineJoint::None,
animation_offset: 0.0,
}
}
}
#[derive(Component, FromTemplate)]
pub struct GizmoMeshConfig {
pub line_perspective: bool,
pub line_style: GizmoLineStyle,
pub line_joints: GizmoLineJoint,
pub render_layers: RenderLayers,
pub handle: Handle<GizmoAsset>,
}