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