//! A module for the [`GizmoConfig<T>`] [`Resource`].12pub use bevy_gizmos_macros::GizmoConfigGroup;34#[cfg(all(5feature = "bevy_render",6any(feature = "bevy_pbr", feature = "bevy_sprite")7))]8use {crate::GizmoAsset, bevy_asset::Handle, bevy_ecs::component::Component};910use bevy_ecs::{reflect::ReflectResource, resource::Resource};11use bevy_reflect::{std_traits::ReflectDefault, Reflect, TypePath};12use bevy_utils::TypeIdMap;13use core::{14any::TypeId,15hash::Hash,16ops::{Deref, DerefMut},17panic,18};1920/// An enum configuring how line joints will be drawn.21#[derive(Debug, Default, Copy, Clone, Reflect, PartialEq, Eq, Hash)]22#[reflect(Default, PartialEq, Hash, Clone)]23pub enum GizmoLineJoint {24/// Does not draw any line joints.25#[default]26None,27/// Extends both lines at the joining point until they meet in a sharp point.28Miter,29/// Draws a round corner with the specified resolution between the two lines.30///31/// The resolution determines the amount of triangles drawn per joint,32/// e.g. `GizmoLineJoint::Round(4)` will draw 4 triangles at each line joint.33Round(u32),34/// Draws a bevel, a straight line in this case, to connect the ends of both lines.35Bevel,36}3738/// An enum used to configure the style of gizmo lines, similar to CSS line-style39#[derive(Copy, Clone, Debug, Default, PartialEq, Reflect)]40#[reflect(Default, PartialEq, Hash, Clone)]41#[non_exhaustive]42pub enum GizmoLineStyle {43/// A solid line without any decorators44#[default]45Solid,46/// A dotted line47Dotted,48/// A dashed line with configurable gap and line sizes49Dashed {50/// The length of the gap in `line_width`s51gap_scale: f32,52/// The length of the visible line in `line_width`s53line_scale: f32,54},55}5657impl Eq for GizmoLineStyle {}5859impl Hash for GizmoLineStyle {60fn hash<H: core::hash::Hasher>(&self, state: &mut H) {61match self {62Self::Solid => {630u64.hash(state);64}65Self::Dotted => 1u64.hash(state),66Self::Dashed {67gap_scale,68line_scale,69} => {702u64.hash(state);71gap_scale.to_bits().hash(state);72line_scale.to_bits().hash(state);73}74}75}76}7778/// A trait used to create gizmo configs groups.79///80/// Here you can store additional configuration for you gizmo group not covered by [`GizmoConfig`]81///82/// Make sure to derive [`Default`] + [`Reflect`] and register in the app using `app.init_gizmo_group::<T>()`83pub trait GizmoConfigGroup: Reflect + TypePath + Default {}8485/// The default gizmo config group.86#[derive(Default, Reflect, GizmoConfigGroup)]87#[reflect(Default)]88pub struct DefaultGizmoConfigGroup;8990/// Used when the gizmo config group needs to be type-erased.91/// Also used for retained gizmos, which can't have a gizmo config group.92#[derive(Default, Reflect, GizmoConfigGroup, Debug, Clone)]93#[reflect(Default, Clone)]94pub struct ErasedGizmoConfigGroup;9596/// A [`Resource`] storing [`GizmoConfig`] and [`GizmoConfigGroup`] structs97///98/// Use `app.init_gizmo_group::<T>()` to register a custom config group.99#[derive(Reflect, Resource, Default)]100#[reflect(Resource, Default)]101pub struct GizmoConfigStore {102// INVARIANT: must map TypeId::of::<T>() to correct type T103#[reflect(ignore)]104store: TypeIdMap<(GizmoConfig, Box<dyn Reflect>)>,105}106107impl GizmoConfigStore {108/// Returns [`GizmoConfig`] and [`GizmoConfigGroup`] associated with [`TypeId`] of a [`GizmoConfigGroup`]109pub fn get_config_dyn(&self, config_type_id: &TypeId) -> Option<(&GizmoConfig, &dyn Reflect)> {110let (config, ext) = self.store.get(config_type_id)?;111Some((config, ext.deref()))112}113114/// Returns [`GizmoConfig`] and [`GizmoConfigGroup`] associated with [`GizmoConfigGroup`] `T`115pub fn config<T: GizmoConfigGroup>(&self) -> (&GizmoConfig, &T) {116let Some((config, ext)) = self.get_config_dyn(&TypeId::of::<T>()) else {117panic!("Requested config {} does not exist in `GizmoConfigStore`! Did you forget to add it using `app.init_gizmo_group<T>()`?", T::type_path());118};119// hash map invariant guarantees that &dyn Reflect is of correct type T120let ext = ext.as_any().downcast_ref().unwrap();121(config, ext)122}123124/// Returns mutable [`GizmoConfig`] and [`GizmoConfigGroup`] associated with [`TypeId`] of a [`GizmoConfigGroup`]125pub fn get_config_mut_dyn(126&mut self,127config_type_id: &TypeId,128) -> Option<(&mut GizmoConfig, &mut dyn Reflect)> {129let (config, ext) = self.store.get_mut(config_type_id)?;130Some((config, ext.deref_mut()))131}132133/// Returns mutable [`GizmoConfig`] and [`GizmoConfigGroup`] associated with [`GizmoConfigGroup`] `T`134pub fn config_mut<T: GizmoConfigGroup>(&mut self) -> (&mut GizmoConfig, &mut T) {135let Some((config, ext)) = self.get_config_mut_dyn(&TypeId::of::<T>()) else {136panic!("Requested config {} does not exist in `GizmoConfigStore`! Did you forget to add it using `app.init_gizmo_group<T>()`?", T::type_path());137};138// hash map invariant guarantees that &dyn Reflect is of correct type T139let ext = ext.as_any_mut().downcast_mut().unwrap();140(config, ext)141}142143/// Returns an iterator over all [`GizmoConfig`]s.144pub fn iter(&self) -> impl Iterator<Item = (&TypeId, &GizmoConfig, &dyn Reflect)> + '_ {145self.store146.iter()147.map(|(id, (config, ext))| (id, config, ext.deref()))148}149150/// Returns an iterator over all [`GizmoConfig`]s, by mutable reference.151pub fn iter_mut(152&mut self,153) -> impl Iterator<Item = (&TypeId, &mut GizmoConfig, &mut dyn Reflect)> + '_ {154self.store155.iter_mut()156.map(|(id, (config, ext))| (id, config, ext.deref_mut()))157}158159/// Inserts [`GizmoConfig`] and [`GizmoConfigGroup`] replacing old values160pub fn insert<T: GizmoConfigGroup>(&mut self, config: GizmoConfig, ext_config: T) {161// INVARIANT: hash map must correctly map TypeId::of::<T>() to &dyn Reflect of type T162self.store163.insert(TypeId::of::<T>(), (config, Box::new(ext_config)));164}165166pub(crate) fn register<T: GizmoConfigGroup>(&mut self) {167self.insert(GizmoConfig::default(), T::default());168}169}170171/// A struct that stores configuration for gizmos.172#[derive(Clone, Reflect, Debug)]173#[reflect(Clone, Default)]174pub struct GizmoConfig {175/// Set to `false` to stop drawing gizmos.176///177/// Defaults to `true`.178pub enabled: bool,179/// Line settings.180pub line: GizmoLineConfig,181/// How closer to the camera than real geometry the gizmos should be.182///183/// In 2D this setting has no effect and is effectively always -1.184///185/// Value between -1 and 1 (inclusive).186/// * 0 means that there is no change to the line position when rendering187/// * 1 means it is furthest away from camera as possible188/// * -1 means that it will always render in front of other things.189///190/// This is typically useful if you are drawing wireframes on top of polygons191/// and your wireframe is z-fighting (flickering on/off) with your main model.192/// You would set this value to a negative number close to 0.193pub depth_bias: f32,194/// Describes which rendering layers gizmos will be rendered to.195///196/// Gizmos will only be rendered to cameras with intersecting layers.197#[cfg(feature = "bevy_render")]198pub render_layers: bevy_camera::visibility::RenderLayers,199}200201impl Default for GizmoConfig {202fn default() -> Self {203Self {204enabled: true,205line: Default::default(),206depth_bias: 0.,207#[cfg(feature = "bevy_render")]208render_layers: Default::default(),209}210}211}212213/// A struct that stores configuration for gizmos.214#[derive(Clone, Reflect, Debug)]215#[reflect(Clone, Default)]216pub struct GizmoLineConfig {217/// Line width specified in pixels.218///219/// If `perspective` is `true` then this is the size in pixels at the camera's near plane.220///221/// Defaults to `2.0`.222pub width: f32,223/// Apply perspective to gizmo lines.224///225/// This setting only affects 3D, non-orthographic cameras.226///227/// Defaults to `false`.228pub perspective: bool,229/// Determine the style of gizmo lines.230pub style: GizmoLineStyle,231/// Describe how lines should join.232pub joints: GizmoLineJoint,233}234235impl Default for GizmoLineConfig {236fn default() -> Self {237Self {238width: 2.,239perspective: false,240style: GizmoLineStyle::Solid,241joints: GizmoLineJoint::None,242}243}244}245246#[cfg(all(247feature = "bevy_render",248any(feature = "bevy_pbr", feature = "bevy_sprite")249))]250#[derive(Component)]251pub(crate) struct GizmoMeshConfig {252pub line_perspective: bool,253pub line_style: GizmoLineStyle,254pub line_joints: GizmoLineJoint,255pub render_layers: bevy_camera::visibility::RenderLayers,256pub handle: Handle<GizmoAsset>,257}258259260