Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/tests/3d/test_skinned_mesh_bounds.rs
9356 views
1
//! Test `SkinnedMeshBounds` by showing the bounds of various animated meshes.
2
3
use bevy::{
4
asset::RenderAssetUsages,
5
camera::visibility::DynamicSkinnedMeshBounds,
6
mesh::{
7
skinning::{SkinnedMesh, SkinnedMeshInverseBindposes},
8
PrimitiveTopology, VertexAttributeValues,
9
},
10
prelude::*,
11
scene::SceneInstanceReady,
12
};
13
use std::f32::consts::{FRAC_PI_2, FRAC_PI_4};
14
15
fn main() {
16
App::new()
17
.add_plugins(DefaultPlugins.set(WindowPlugin {
18
primary_window: Some(Window {
19
title: "Test Skinned Mesh Bounds".into(),
20
..default()
21
}),
22
..default()
23
}))
24
.insert_gizmo_config(
25
SkinnedMeshBoundsGizmoConfigGroup {
26
draw_all: true,
27
..Default::default()
28
},
29
GizmoConfig::default(),
30
)
31
.insert_gizmo_config(
32
AabbGizmoConfigGroup {
33
draw_all: true,
34
..Default::default()
35
},
36
GizmoConfig::default(),
37
)
38
.insert_resource(GlobalAmbientLight {
39
brightness: 2000.0,
40
..Default::default()
41
})
42
.add_systems(Startup, setup)
43
.add_systems(Startup, load_scene)
44
.add_systems(Update, spawn_scene)
45
.add_systems(Startup, spawn_custom_meshes)
46
.add_systems(Update, update_custom_mesh_animation)
47
.run();
48
}
49
50
fn setup(mut commands: Commands) {
51
commands.spawn((
52
Camera3d::default(),
53
Transform::from_xyz(0.0, 7.5, 18.0).looking_at(Vec3::new(0.0, 5.5, 0.0), Vec3::Y),
54
));
55
}
56
57
#[derive(Component, Debug, Default)]
58
struct PendingScene(Handle<Gltf>);
59
60
#[derive(Component, Debug, Default)]
61
struct PendingAnimation((Handle<AnimationGraph>, AnimationNodeIndex));
62
63
fn load_scene(mut commands: Commands, asset_server: Res<AssetServer>) {
64
commands.spawn((
65
PendingScene(asset_server.load("models/animated/Fox.glb")),
66
Transform::from_xyz(1.3, 4.3, 0.0)
67
.with_scale(Vec3::splat(0.08))
68
.looking_to(-Vec3::X, Vec3::Y),
69
));
70
}
71
72
fn spawn_scene(
73
mut commands: Commands,
74
query: Query<(Entity, &PendingScene)>,
75
assets: Res<Assets<Gltf>>,
76
mut graphs: ResMut<Assets<AnimationGraph>>,
77
) {
78
for (entity, PendingScene(asset)) in query.iter() {
79
if let Some(gltf) = assets.get(asset)
80
&& let Some(scene_handle) = gltf.scenes.first()
81
&& let Some(animation_handle) = gltf.named_animations.get("Run")
82
{
83
let (graph, graph_node_index) = AnimationGraph::from_clip(animation_handle.clone());
84
85
commands
86
.entity(entity)
87
.remove::<PendingScene>()
88
.insert((
89
SceneRoot(scene_handle.clone()),
90
PendingAnimation((graphs.add(graph), graph_node_index)),
91
))
92
.observe(play_animation);
93
}
94
}
95
}
96
97
fn play_animation(
98
trigger: On<SceneInstanceReady>,
99
mut commands: Commands,
100
children: Query<&Children>,
101
animations: Query<&PendingAnimation>,
102
mut players: Query<&mut AnimationPlayer>,
103
) {
104
if let Ok(PendingAnimation((graph_handle, graph_node_index))) = animations.get(trigger.entity) {
105
for child in children.iter_descendants(trigger.entity) {
106
if let Ok(mut player) = players.get_mut(child) {
107
player.play(*graph_node_index).set_speed(0.6).repeat();
108
109
commands
110
.entity(child)
111
.insert(AnimationGraphHandle(graph_handle.clone()));
112
}
113
}
114
}
115
116
commands.entity(trigger.entity).remove::<PendingAnimation>();
117
}
118
119
type CustomAnimationId = i8;
120
121
#[derive(Component)]
122
struct CustomAnimation(CustomAnimationId);
123
124
fn spawn_custom_meshes(
125
mut commands: Commands,
126
mut mesh_assets: ResMut<Assets<Mesh>>,
127
mut material_assets: ResMut<Assets<StandardMaterial>>,
128
mut inverse_bindposes_assets: ResMut<Assets<SkinnedMeshInverseBindposes>>,
129
) {
130
let mesh_handle = mesh_assets.add(
131
Mesh::new(
132
PrimitiveTopology::TriangleStrip,
133
// Test that skinned mesh bounds work even if the mesh is render
134
// world only.
135
RenderAssetUsages::RENDER_WORLD,
136
)
137
.with_inserted_attribute(
138
Mesh::ATTRIBUTE_POSITION,
139
vec![
140
[-0.5, 0.0, 0.0],
141
[0.5, 0.0, 0.0],
142
[-0.5, 0.5, 0.0],
143
[0.5, 0.5, 0.0],
144
[-0.5, 1.0, 0.0],
145
[0.5, 1.0, 0.0],
146
[-0.5, 1.5, 0.0],
147
[0.5, 1.5, 0.0],
148
[-0.5, 2.0, 0.0],
149
[0.5, 2.0, 0.0],
150
],
151
)
152
.with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, vec![[0.0, 0.0, 1.0]; 10])
153
.with_inserted_attribute(
154
Mesh::ATTRIBUTE_JOINT_INDEX,
155
VertexAttributeValues::Uint16x4(vec![
156
[1, 0, 0, 0],
157
[1, 0, 0, 0],
158
[1, 2, 0, 0],
159
[1, 2, 0, 0],
160
[1, 2, 0, 0],
161
[1, 2, 0, 0],
162
[2, 1, 0, 0],
163
[2, 1, 0, 0],
164
[2, 0, 0, 0],
165
[2, 0, 0, 0],
166
]),
167
)
168
.with_inserted_attribute(
169
Mesh::ATTRIBUTE_JOINT_WEIGHT,
170
vec![
171
[1.00, 0.00, 0.0, 0.0],
172
[1.00, 0.00, 0.0, 0.0],
173
[0.75, 0.25, 0.0, 0.0],
174
[0.75, 0.25, 0.0, 0.0],
175
[0.50, 0.50, 0.0, 0.0],
176
[0.50, 0.50, 0.0, 0.0],
177
[0.75, 0.25, 0.0, 0.0],
178
[0.75, 0.25, 0.0, 0.0],
179
[1.00, 0.00, 0.0, 0.0],
180
[1.00, 0.00, 0.0, 0.0],
181
],
182
)
183
.with_generated_skinned_mesh_bounds()
184
.unwrap(),
185
);
186
187
let inverse_bindposes_handle = inverse_bindposes_assets.add(vec![
188
Mat4::from_translation(Vec3::new(0.0, 0.0, 0.0)),
189
Mat4::from_translation(Vec3::new(0.0, 0.0, 0.0)),
190
Mat4::from_translation(Vec3::new(0.0, -1.0, 0.0)),
191
]);
192
193
struct MeshInstance {
194
animations: [CustomAnimationId; 2],
195
}
196
197
let mesh_instances = [
198
// Simple cases. First joint is still, second joint is all rotation/translation/scale variations.
199
MeshInstance { animations: [0, 1] },
200
MeshInstance { animations: [0, 2] },
201
MeshInstance { animations: [0, 3] },
202
MeshInstance { animations: [0, 4] },
203
MeshInstance { animations: [0, 5] },
204
MeshInstance { animations: [0, 6] },
205
MeshInstance { animations: [0, 7] },
206
MeshInstance { animations: [0, 8] },
207
// Skewed cases. First joint is non-uniform scaling, second joint is rotation/translation variations.
208
MeshInstance { animations: [9, 1] },
209
MeshInstance { animations: [9, 2] },
210
MeshInstance { animations: [9, 3] },
211
MeshInstance { animations: [9, 4] },
212
MeshInstance { animations: [9, 5] },
213
];
214
215
for (i, mesh_instance) in mesh_instances.iter().enumerate() {
216
let x = ((i as f32) * 2.0) - ((mesh_instances.len() - 1) as f32);
217
218
let base_entity = commands
219
.spawn((Transform::from_xyz(x, 0.0, 0.0), Visibility::default()))
220
.id();
221
222
let joints = vec![
223
commands.spawn((Transform::IDENTITY,)).id(),
224
commands
225
.spawn((
226
CustomAnimation(mesh_instance.animations[0]),
227
Transform::IDENTITY,
228
))
229
.id(),
230
commands
231
.spawn((
232
CustomAnimation(mesh_instance.animations[1]),
233
Transform::IDENTITY,
234
))
235
.id(),
236
];
237
238
commands.entity(joints[0]).insert(ChildOf(base_entity));
239
240
commands.entity(joints[1]).insert(ChildOf(joints[0]));
241
commands.entity(joints[2]).insert(ChildOf(joints[1]));
242
243
let mesh_entity = commands
244
.spawn((
245
Transform::IDENTITY,
246
Mesh3d(mesh_handle.clone()),
247
MeshMaterial3d(material_assets.add(StandardMaterial {
248
base_color: Color::WHITE,
249
cull_mode: None,
250
..default()
251
})),
252
SkinnedMesh {
253
inverse_bindposes: inverse_bindposes_handle.clone(),
254
joints: joints.clone(),
255
},
256
DynamicSkinnedMeshBounds,
257
))
258
.id();
259
260
commands.entity(mesh_entity).insert(ChildOf(base_entity));
261
}
262
}
263
264
fn update_custom_mesh_animation(
265
time: Res<Time<Virtual>>,
266
mut query: Query<(&mut Transform, &CustomAnimation)>,
267
) {
268
let t = time.elapsed_secs();
269
let ts = ops::sin(t);
270
let tc = ops::cos(t);
271
let ots = ops::sin(t + FRAC_PI_4);
272
let otc = ops::cos(t + FRAC_PI_4);
273
274
for (mut transform, animation) in &mut query {
275
match animation.0 {
276
1 => transform.translation = Vec3::new(0.5 * ts, 0.3 + tc, 0.0),
277
2 => transform.translation = Vec3::new(0.0, 0.5 + ts, tc),
278
3 => transform.rotation = Quat::from_rotation_x(FRAC_PI_2 * ts),
279
4 => transform.rotation = Quat::from_rotation_y(FRAC_PI_2 * ts),
280
5 => transform.rotation = Quat::from_rotation_z(FRAC_PI_2 * ts),
281
6 => transform.scale.x = ts * 1.5,
282
7 => transform.scale.y = ts * 1.5,
283
8 => transform.scale = Vec3::new(ts * 1.5, otc * 1.5, 1.0),
284
9 => transform.scale = Vec3::new(ots, 1.0 + (tc * 0.3), 1.0 - (tc * 0.5)),
285
_ => (),
286
}
287
}
288
}
289
290