bevy_gizmos/
skinned_mesh_bounds.rs1use bevy_app::{Plugin, PostUpdate};
4use bevy_asset::Assets;
5use bevy_camera::visibility::DynamicSkinnedMeshBounds;
6use bevy_color::Color;
7use bevy_ecs::{
8 component::Component,
9 query::{With, Without},
10 reflect::ReflectComponent,
11 schedule::IntoScheduleConfigs,
12 system::{Query, Res},
13};
14use bevy_math::Affine3A;
15use bevy_mesh::{
16 mark_3d_meshes_as_changed_if_their_assets_changed,
17 skinning::{SkinnedMesh, SkinnedMeshInverseBindposes},
18 Mesh, Mesh3d,
19};
20use bevy_reflect::{std_traits::ReflectDefault, Reflect};
21use bevy_transform::{components::GlobalTransform, TransformSystems};
22
23use crate::{
24 config::{GizmoConfigGroup, GizmoConfigStore},
25 gizmos::Gizmos,
26 AppGizmoBuilder,
27};
28
29pub struct SkinnedMeshBoundsGizmoPlugin;
31
32impl Plugin for SkinnedMeshBoundsGizmoPlugin {
33 fn build(&self, app: &mut bevy_app::App) {
34 app.init_gizmo_group::<SkinnedMeshBoundsGizmoConfigGroup>()
35 .add_systems(
36 PostUpdate,
37 (
38 draw_skinned_mesh_bounds,
39 draw_all_skinned_mesh_bounds.run_if(|config: Res<GizmoConfigStore>| {
40 config
41 .config::<SkinnedMeshBoundsGizmoConfigGroup>()
42 .1
43 .draw_all
44 }),
45 )
46 .after(TransformSystems::Propagate)
47 .ambiguous_with(mark_3d_meshes_as_changed_if_their_assets_changed),
48 );
49 }
50}
51#[derive(Clone, Reflect, GizmoConfigGroup)]
53#[reflect(Clone, Default)]
54pub struct SkinnedMeshBoundsGizmoConfigGroup {
55 pub draw_all: bool,
62 pub default_color: Color,
64}
65
66impl Default for SkinnedMeshBoundsGizmoConfigGroup {
67 fn default() -> Self {
68 Self {
69 draw_all: false,
70 default_color: Color::WHITE,
71 }
72 }
73}
74
75#[derive(Component, Reflect, Default, Debug)]
77#[reflect(Component, Default, Debug)]
78pub struct ShowSkinnedMeshBoundsGizmo {
79 pub color: Option<Color>,
83}
84
85fn draw(
86 color: Color,
87 mesh: &Mesh3d,
88 mesh_assets: &Res<Assets<Mesh>>,
89 skinned_mesh: &SkinnedMesh,
90 joint_entities: &Query<&GlobalTransform>,
91 inverse_bindposes_assets: &Res<Assets<SkinnedMeshInverseBindposes>>,
92 gizmos: &mut Gizmos<SkinnedMeshBoundsGizmoConfigGroup>,
93) {
94 if let Some(mesh_asset) = mesh_assets.get(mesh)
95 && let Some(bounds) = mesh_asset.skinned_mesh_bounds()
96 && let Some(inverse_bindposes_asset) =
97 inverse_bindposes_assets.get(&skinned_mesh.inverse_bindposes)
98 {
99 for (&joint_index, &joint_aabb) in bounds.iter() {
100 let joint_index = joint_index.0 as usize;
101
102 if let Some(&joint_entity) = skinned_mesh.joints.get(joint_index)
103 && let Ok(&world_from_joint) = joint_entities.get(joint_entity)
104 && let Some(&joint_from_mesh) = inverse_bindposes_asset.get(joint_index)
105 {
106 let world_from_mesh =
107 world_from_joint.affine() * Affine3A::from_mat4(joint_from_mesh);
108
109 gizmos.aabb_3d(joint_aabb, world_from_mesh, color);
110 }
111 }
112 }
113}
114
115fn draw_skinned_mesh_bounds(
116 mesh_entities: Query<
117 (&Mesh3d, &SkinnedMesh, &ShowSkinnedMeshBoundsGizmo),
118 With<DynamicSkinnedMeshBounds>,
119 >,
120 joint_entities: Query<&GlobalTransform>,
121 mesh_assets: Res<Assets<Mesh>>,
122 inverse_bindposes_assets: Res<Assets<SkinnedMeshInverseBindposes>>,
123 mut gizmos: Gizmos<SkinnedMeshBoundsGizmoConfigGroup>,
124) {
125 for (mesh, skinned_mesh, gizmo) in mesh_entities {
126 let color = gizmo.color.unwrap_or(gizmos.config_ext.default_color);
127
128 draw(
129 color,
130 mesh,
131 &mesh_assets,
132 skinned_mesh,
133 &joint_entities,
134 &inverse_bindposes_assets,
135 &mut gizmos,
136 );
137 }
138}
139
140fn draw_all_skinned_mesh_bounds(
141 mesh_entities: Query<
142 (&Mesh3d, &SkinnedMesh),
143 (
144 With<DynamicSkinnedMeshBounds>,
145 Without<ShowSkinnedMeshBoundsGizmo>,
146 ),
147 >,
148 joint_entities: Query<&GlobalTransform>,
149 mesh_assets: Res<Assets<Mesh>>,
150 inverse_bindposes_assets: Res<Assets<SkinnedMeshInverseBindposes>>,
151 mut gizmos: Gizmos<SkinnedMeshBoundsGizmoConfigGroup>,
152) {
153 for (mesh, skinned_mesh) in mesh_entities {
154 draw(
155 gizmos.config_ext.default_color,
156 mesh,
157 &mesh_assets,
158 skinned_mesh,
159 &joint_entities,
160 &inverse_bindposes_assets,
161 &mut gizmos,
162 );
163 }
164}