Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/examples/tools/scene_viewer/morph_viewer_plugin.rs
6598 views
1
//! Enable controls for morph targets detected in a loaded scene.
2
//!
3
//! Collect morph targets and assign keys to them,
4
//! shows on screen additional controls for morph targets.
5
//!
6
//! Illustrates how to access and modify individual morph target weights.
7
//! See the [`update_morphs`] system for details.
8
//!
9
//! Also illustrates how to read morph target names in [`detect_morphs`].
10
11
use crate::scene_viewer_plugin::SceneHandle;
12
use bevy::prelude::*;
13
use std::fmt;
14
15
const FONT_SIZE: f32 = 13.0;
16
17
const WEIGHT_PER_SECOND: f32 = 0.8;
18
const ALL_MODIFIERS: &[KeyCode] = &[KeyCode::ShiftLeft, KeyCode::ControlLeft, KeyCode::AltLeft];
19
const AVAILABLE_KEYS: [MorphKey; 56] = [
20
MorphKey::new("r", &[], KeyCode::KeyR),
21
MorphKey::new("t", &[], KeyCode::KeyT),
22
MorphKey::new("z", &[], KeyCode::KeyZ),
23
MorphKey::new("i", &[], KeyCode::KeyI),
24
MorphKey::new("o", &[], KeyCode::KeyO),
25
MorphKey::new("p", &[], KeyCode::KeyP),
26
MorphKey::new("f", &[], KeyCode::KeyF),
27
MorphKey::new("g", &[], KeyCode::KeyG),
28
MorphKey::new("h", &[], KeyCode::KeyH),
29
MorphKey::new("j", &[], KeyCode::KeyJ),
30
MorphKey::new("k", &[], KeyCode::KeyK),
31
MorphKey::new("y", &[], KeyCode::KeyY),
32
MorphKey::new("x", &[], KeyCode::KeyX),
33
MorphKey::new("c", &[], KeyCode::KeyC),
34
MorphKey::new("v", &[], KeyCode::KeyV),
35
MorphKey::new("b", &[], KeyCode::KeyB),
36
MorphKey::new("n", &[], KeyCode::KeyN),
37
MorphKey::new("m", &[], KeyCode::KeyM),
38
MorphKey::new("0", &[], KeyCode::Digit0),
39
MorphKey::new("1", &[], KeyCode::Digit1),
40
MorphKey::new("2", &[], KeyCode::Digit2),
41
MorphKey::new("3", &[], KeyCode::Digit3),
42
MorphKey::new("4", &[], KeyCode::Digit4),
43
MorphKey::new("5", &[], KeyCode::Digit5),
44
MorphKey::new("6", &[], KeyCode::Digit6),
45
MorphKey::new("7", &[], KeyCode::Digit7),
46
MorphKey::new("8", &[], KeyCode::Digit8),
47
MorphKey::new("9", &[], KeyCode::Digit9),
48
MorphKey::new("lshift-R", &[KeyCode::ShiftLeft], KeyCode::KeyR),
49
MorphKey::new("lshift-T", &[KeyCode::ShiftLeft], KeyCode::KeyT),
50
MorphKey::new("lshift-Z", &[KeyCode::ShiftLeft], KeyCode::KeyZ),
51
MorphKey::new("lshift-I", &[KeyCode::ShiftLeft], KeyCode::KeyI),
52
MorphKey::new("lshift-O", &[KeyCode::ShiftLeft], KeyCode::KeyO),
53
MorphKey::new("lshift-P", &[KeyCode::ShiftLeft], KeyCode::KeyP),
54
MorphKey::new("lshift-F", &[KeyCode::ShiftLeft], KeyCode::KeyF),
55
MorphKey::new("lshift-G", &[KeyCode::ShiftLeft], KeyCode::KeyG),
56
MorphKey::new("lshift-H", &[KeyCode::ShiftLeft], KeyCode::KeyH),
57
MorphKey::new("lshift-J", &[KeyCode::ShiftLeft], KeyCode::KeyJ),
58
MorphKey::new("lshift-K", &[KeyCode::ShiftLeft], KeyCode::KeyK),
59
MorphKey::new("lshift-Y", &[KeyCode::ShiftLeft], KeyCode::KeyY),
60
MorphKey::new("lshift-X", &[KeyCode::ShiftLeft], KeyCode::KeyX),
61
MorphKey::new("lshift-C", &[KeyCode::ShiftLeft], KeyCode::KeyC),
62
MorphKey::new("lshift-V", &[KeyCode::ShiftLeft], KeyCode::KeyV),
63
MorphKey::new("lshift-B", &[KeyCode::ShiftLeft], KeyCode::KeyB),
64
MorphKey::new("lshift-N", &[KeyCode::ShiftLeft], KeyCode::KeyN),
65
MorphKey::new("lshift-M", &[KeyCode::ShiftLeft], KeyCode::KeyM),
66
MorphKey::new("lshift-0", &[KeyCode::ShiftLeft], KeyCode::Digit0),
67
MorphKey::new("lshift-1", &[KeyCode::ShiftLeft], KeyCode::Digit1),
68
MorphKey::new("lshift-2", &[KeyCode::ShiftLeft], KeyCode::Digit2),
69
MorphKey::new("lshift-3", &[KeyCode::ShiftLeft], KeyCode::Digit3),
70
MorphKey::new("lshift-4", &[KeyCode::ShiftLeft], KeyCode::Digit4),
71
MorphKey::new("lshift-5", &[KeyCode::ShiftLeft], KeyCode::Digit5),
72
MorphKey::new("lshift-6", &[KeyCode::ShiftLeft], KeyCode::Digit6),
73
MorphKey::new("lshift-7", &[KeyCode::ShiftLeft], KeyCode::Digit7),
74
MorphKey::new("lshift-8", &[KeyCode::ShiftLeft], KeyCode::Digit8),
75
MorphKey::new("lshift-9", &[KeyCode::ShiftLeft], KeyCode::Digit9),
76
];
77
78
#[derive(Clone, Copy)]
79
enum WeightChange {
80
Increase,
81
Decrease,
82
}
83
84
impl WeightChange {
85
fn reverse(&mut self) {
86
*self = match *self {
87
WeightChange::Increase => WeightChange::Decrease,
88
WeightChange::Decrease => WeightChange::Increase,
89
}
90
}
91
fn sign(self) -> f32 {
92
match self {
93
WeightChange::Increase => 1.0,
94
WeightChange::Decrease => -1.0,
95
}
96
}
97
fn change_weight(&mut self, weight: f32, change: f32) -> f32 {
98
let mut change = change * self.sign();
99
let new_weight = weight + change;
100
if new_weight <= 0.0 || new_weight >= 1.0 {
101
self.reverse();
102
change = -change;
103
}
104
weight + change
105
}
106
}
107
108
struct Target {
109
entity_name: Option<String>,
110
entity: Entity,
111
name: Option<String>,
112
index: usize,
113
weight: f32,
114
change_dir: WeightChange,
115
}
116
117
impl fmt::Display for Target {
118
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
119
match (self.name.as_ref(), self.entity_name.as_ref()) {
120
(None, None) => write!(f, "animation{} of {}", self.index, self.entity),
121
(None, Some(entity)) => write!(f, "animation{} of {entity}", self.index),
122
(Some(target), None) => write!(f, "{target} of {}", self.entity),
123
(Some(target), Some(entity)) => write!(f, "{target} of {entity}"),
124
}?;
125
write!(f, ": {}", self.weight)
126
}
127
}
128
129
impl Target {
130
fn text_span(&self, key: &str, style: TextFont) -> (TextSpan, TextFont) {
131
(TextSpan::new(format!("[{key}] {self}\n")), style)
132
}
133
fn new(
134
entity_name: Option<&Name>,
135
weights: &[f32],
136
target_names: Option<&[String]>,
137
entity: Entity,
138
) -> Vec<Target> {
139
let get_name = |i| target_names.and_then(|names| names.get(i));
140
let entity_name = entity_name.map(Name::as_str);
141
weights
142
.iter()
143
.enumerate()
144
.map(|(index, weight)| Target {
145
entity_name: entity_name.map(ToOwned::to_owned),
146
entity,
147
name: get_name(index).cloned(),
148
index,
149
weight: *weight,
150
change_dir: WeightChange::Increase,
151
})
152
.collect()
153
}
154
}
155
156
#[derive(Resource)]
157
struct WeightsControl {
158
weights: Vec<Target>,
159
}
160
161
struct MorphKey {
162
name: &'static str,
163
modifiers: &'static [KeyCode],
164
key: KeyCode,
165
}
166
167
impl MorphKey {
168
const fn new(name: &'static str, modifiers: &'static [KeyCode], key: KeyCode) -> Self {
169
MorphKey {
170
name,
171
modifiers,
172
key,
173
}
174
}
175
fn active(&self, inputs: &ButtonInput<KeyCode>) -> bool {
176
let mut modifier = self.modifiers.iter();
177
let mut non_modifier = ALL_MODIFIERS.iter().filter(|m| !self.modifiers.contains(m));
178
179
let key = inputs.pressed(self.key);
180
let modifier = modifier.all(|m| inputs.pressed(*m));
181
let non_modifier = non_modifier.all(|m| !inputs.pressed(*m));
182
key && modifier && non_modifier
183
}
184
}
185
fn update_text(
186
controls: Option<ResMut<WeightsControl>>,
187
texts: Query<Entity, With<Text>>,
188
morphs: Query<&MorphWeights>,
189
mut writer: TextUiWriter,
190
) {
191
let Some(mut controls) = controls else {
192
return;
193
};
194
195
let Ok(text) = texts.single() else {
196
return;
197
};
198
199
for (i, target) in controls.weights.iter_mut().enumerate() {
200
let Ok(weights) = morphs.get(target.entity) else {
201
continue;
202
};
203
let Some(&actual_weight) = weights.weights().get(target.index) else {
204
continue;
205
};
206
if actual_weight != target.weight {
207
target.weight = actual_weight;
208
}
209
let key_name = &AVAILABLE_KEYS[i].name;
210
211
*writer.text(text, i + 3) = format!("[{key_name}] {target}\n");
212
}
213
}
214
fn update_morphs(
215
controls: Option<ResMut<WeightsControl>>,
216
mut morphs: Query<&mut MorphWeights>,
217
input: Res<ButtonInput<KeyCode>>,
218
time: Res<Time>,
219
) {
220
let Some(mut controls) = controls else {
221
return;
222
};
223
for (i, target) in controls.weights.iter_mut().enumerate() {
224
if !AVAILABLE_KEYS[i].active(&input) {
225
continue;
226
}
227
let Ok(mut weights) = morphs.get_mut(target.entity) else {
228
continue;
229
};
230
// To update individual morph target weights, get the `MorphWeights`
231
// component and call `weights_mut` to get access to the weights.
232
let weights_slice = weights.weights_mut();
233
let i = target.index;
234
let change = time.delta_secs() * WEIGHT_PER_SECOND;
235
let new_weight = target.change_dir.change_weight(weights_slice[i], change);
236
weights_slice[i] = new_weight;
237
target.weight = new_weight;
238
}
239
}
240
241
fn detect_morphs(
242
mut commands: Commands,
243
morphs: Query<(Entity, &MorphWeights, Option<&Name>)>,
244
meshes: Res<Assets<Mesh>>,
245
scene_handle: Res<SceneHandle>,
246
mut setup: Local<bool>,
247
) {
248
let no_morphing = morphs.iter().len() == 0;
249
if no_morphing {
250
return;
251
}
252
if scene_handle.is_loaded && !*setup {
253
*setup = true;
254
} else {
255
return;
256
}
257
let mut detected = Vec::new();
258
259
for (entity, weights, name) in &morphs {
260
let target_names = weights
261
.first_mesh()
262
.and_then(|h| meshes.get(h))
263
.and_then(|m| m.morph_target_names());
264
let targets = Target::new(name, weights.weights(), target_names, entity);
265
detected.extend(targets);
266
}
267
detected.truncate(AVAILABLE_KEYS.len());
268
let style = TextFont {
269
font_size: FONT_SIZE,
270
..default()
271
};
272
let mut spans = vec![
273
(TextSpan::new("Morph Target Controls\n"), style.clone()),
274
(TextSpan::new("---------------\n"), style.clone()),
275
];
276
let target_to_text =
277
|(i, target): (usize, &Target)| target.text_span(AVAILABLE_KEYS[i].name, style.clone());
278
spans.extend(detected.iter().enumerate().map(target_to_text));
279
commands.insert_resource(WeightsControl { weights: detected });
280
commands.spawn((
281
Text::default(),
282
Node {
283
position_type: PositionType::Absolute,
284
top: px(12),
285
left: px(12),
286
..default()
287
},
288
Children::spawn(spans),
289
));
290
}
291
292
pub struct MorphViewerPlugin;
293
294
impl Plugin for MorphViewerPlugin {
295
fn build(&self, app: &mut App) {
296
app.add_systems(
297
Update,
298
(
299
update_morphs,
300
detect_morphs,
301
update_text.after(update_morphs),
302
),
303
);
304
}
305
}
306
307