Skip to content

Commit 2fd6806

Browse files
committed
symmetry for colliders and bodies
1 parent 31be518 commit 2fd6806

9 files changed

Lines changed: 314 additions & 38 deletions

File tree

crates/bevy_animation_graph/src/core/ragdoll/definition.rs

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,16 @@ use bevy::{
88
use serde::{Deserialize, Serialize};
99
use uuid::Uuid;
1010

11-
use crate::core::colliders::core::ColliderShape;
11+
use crate::{core::colliders::core::ColliderShape, prelude::config::SymmertryMode};
1212

1313
#[derive(Asset, Debug, Clone, Reflect, Serialize, Deserialize)]
1414
pub struct Ragdoll {
1515
pub bodies: HashMap<BodyId, Body>,
1616
pub colliders: HashMap<ColliderId, Collider>,
1717
pub joints: HashMap<JointId, Joint>,
18+
19+
#[serde(default)]
20+
pub suffixes: SymmetrySuffixes,
1821
}
1922

2023
impl Ragdoll {
@@ -54,11 +57,39 @@ impl Ragdoll {
5457
self.bodies.values()
5558
}
5659

60+
pub fn iter_bodies_mut(&mut self) -> impl Iterator<Item = &mut Body> {
61+
self.bodies.values_mut()
62+
}
63+
64+
pub fn iter_body_ids(&self) -> impl Iterator<Item = BodyId> {
65+
self.bodies.keys().copied()
66+
}
67+
68+
pub fn iter_colliders(&self) -> impl Iterator<Item = &Collider> {
69+
self.colliders.values()
70+
}
71+
5772
pub fn iter_joints(&self) -> impl Iterator<Item = &Joint> {
5873
self.joints.values()
5974
}
6075
}
6176

77+
/// Determines which suffixes to apply to labels of elements that make use of symmetry
78+
#[derive(Debug, Clone, Reflect, Serialize, Deserialize)]
79+
pub struct SymmetrySuffixes {
80+
pub original: String,
81+
pub mirror: String,
82+
}
83+
84+
impl Default for SymmetrySuffixes {
85+
fn default() -> Self {
86+
Self {
87+
original: ".R".into(),
88+
mirror: ".L".into(),
89+
}
90+
}
91+
}
92+
6293
#[derive(Reflect, Debug, Clone, Serialize, Deserialize)]
6394
pub struct Body {
6495
pub id: BodyId,
@@ -67,6 +98,13 @@ pub struct Body {
6798
pub isometry: Isometry3d,
6899
pub colliders: Vec<ColliderId>,
69100
pub default_mode: BodyMode,
101+
102+
#[serde(default)]
103+
pub use_symmetry: bool,
104+
/// If symmetry is enabled and this body was created as the image under the symmetry of another
105+
/// body, which body is it?
106+
#[serde(default)]
107+
pub created_from: Option<BodyId>,
70108
}
71109

72110
impl Body {
@@ -79,8 +117,39 @@ impl Body {
79117
isometry: default(),
80118
colliders: default(),
81119
default_mode: BodyMode::Kinematic,
120+
121+
use_symmetry: false,
122+
created_from: None,
82123
}
83124
}
125+
126+
pub fn mirror_onto(
127+
&self,
128+
target: &mut Self,
129+
suffixes: &SymmetrySuffixes,
130+
mode: &SymmertryMode,
131+
) {
132+
let original_label = self
133+
.label
134+
.strip_suffix(&suffixes.original)
135+
.unwrap_or(&self.label)
136+
.to_owned();
137+
138+
let mirrored_label = format!("{}{}", original_label, suffixes.mirror);
139+
target.label = mirrored_label;
140+
141+
let mirrored_isometry = Isometry3d {
142+
rotation: mode.apply_quat(self.isometry.rotation),
143+
translation: mode.apply_position(self.isometry.translation.into()).into(),
144+
};
145+
target.isometry = mirrored_isometry;
146+
147+
todo!("What do we do about colliders?");
148+
149+
target.default_mode = self.default_mode;
150+
target.use_symmetry = false;
151+
target.created_from = Some(self.id);
152+
}
84153
}
85154

86155
#[derive(Reflect, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
@@ -100,6 +169,10 @@ pub struct Collider {
100169
pub override_layers: bool,
101170
/// Label that will be attached to the created collider in a [`ColliderLabel`] component.
102171
pub label: String,
172+
/// If symmetry is enabled and this body was created as the image under the symmetry of another
173+
/// body, which body is it?
174+
#[serde(default)]
175+
pub created_from: Option<ColliderId>,
103176
}
104177

105178
impl Collider {
@@ -114,6 +187,7 @@ impl Collider {
114187
layer_filter: 1,
115188
override_layers: false,
116189
label: "New collider".into(),
190+
created_from: None,
117191
}
118192
}
119193
}

crates/bevy_animation_graph_editor/src/ui/actions/clip_preview.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ use bevy_animation_graph::{
1313
prelude::{AnimatedScene, AnimationGraph, AnimationNode, DataSpec, GraphClip},
1414
};
1515

16+
use crate::ui::actions::ActionContext;
17+
1618
use super::{DynamicAction, run_handler};
1719

1820
#[derive(Resource, Default)]
@@ -42,7 +44,7 @@ pub struct CreateClipPreview {
4244
}
4345

4446
impl DynamicAction for CreateClipPreview {
45-
fn handle(self: Box<Self>, world: &mut World) {
47+
fn handle(self: Box<Self>, world: &mut World, _: &mut ActionContext) {
4648
run_handler(world, "Could not create clip preview")(Self::system, *self)
4749
}
4850
}
@@ -97,7 +99,7 @@ pub struct CreateTrackNodePreview {
9799
}
98100

99101
impl DynamicAction for CreateTrackNodePreview {
100-
fn handle(self: Box<Self>, world: &mut World) {
102+
fn handle(self: Box<Self>, world: &mut World, _: &mut ActionContext) {
101103
run_handler(world, "Could not create clip preview")(Self::system, *self)
102104
}
103105
}

crates/bevy_animation_graph_editor/src/ui/actions/colliders.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,15 @@ use bevy_animation_graph::{
1313
prelude::config::SymmetryConfig,
1414
};
1515

16-
use crate::ui::actions::{DynamicAction, run_handler, saving::DirtyAssets};
16+
use crate::ui::actions::{ActionContext, DynamicAction, run_handler, saving::DirtyAssets};
1717

1818
pub struct CreateOrEditCollider {
1919
pub colliders: Handle<SkeletonColliders>,
2020
pub config: ColliderConfig,
2121
}
2222

2323
impl DynamicAction for CreateOrEditCollider {
24-
fn handle(self: Box<Self>, world: &mut World) {
24+
fn handle(self: Box<Self>, world: &mut World, _: &mut ActionContext) {
2525
run_handler(world, "Could not create collider")(Self::system, *self)
2626
}
2727
}
@@ -56,7 +56,7 @@ pub struct DeleteCollider {
5656
}
5757

5858
impl DynamicAction for DeleteCollider {
59-
fn handle(self: Box<Self>, world: &mut World) {
59+
fn handle(self: Box<Self>, world: &mut World, _: &mut ActionContext) {
6060
run_handler(world, "Could not create collider")(Self::system, *self)
6161
}
6262
}
@@ -83,7 +83,7 @@ pub struct UpdateSymmetryConfig {
8383
}
8484

8585
impl DynamicAction for UpdateSymmetryConfig {
86-
fn handle(self: Box<Self>, world: &mut World) {
86+
fn handle(self: Box<Self>, world: &mut World, _: &mut ActionContext) {
8787
run_handler(world, "Could not update symmetry config")(Self::system, *self)
8888
}
8989
}
@@ -109,7 +109,7 @@ pub struct UpdateSymmetryEnabled {
109109
}
110110

111111
impl DynamicAction for UpdateSymmetryEnabled {
112-
fn handle(self: Box<Self>, world: &mut World) {
112+
fn handle(self: Box<Self>, world: &mut World, _: &mut ActionContext) {
113113
run_handler(world, "Could not update symmetry config")(Self::system, *self)
114114
}
115115
}
@@ -136,7 +136,7 @@ pub struct UpdateDefaultLayers {
136136
}
137137

138138
impl DynamicAction for UpdateDefaultLayers {
139-
fn handle(self: Box<Self>, world: &mut World) {
139+
fn handle(self: Box<Self>, world: &mut World, _: &mut ActionContext) {
140140
run_handler(world, "Could not update default physics layers config")(Self::system, *self)
141141
}
142142
}
@@ -164,7 +164,7 @@ pub struct UpdateSuffixes {
164164
}
165165

166166
impl DynamicAction for UpdateSuffixes {
167-
fn handle(self: Box<Self>, world: &mut World) {
167+
fn handle(self: Box<Self>, world: &mut World, _: &mut ActionContext) {
168168
run_handler(world, "Could not update suffixes config")(Self::system, *self)
169169
}
170170
}

crates/bevy_animation_graph_editor/src/ui/actions/graph.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use bevy_animation_graph::{
1919

2020
use crate::{
2121
graph_show::{GraphIndicesMap, make_graph_indices},
22-
ui::egui_inspector_impls::OrderedMap,
22+
ui::{actions::ActionContext, egui_inspector_impls::OrderedMap},
2323
};
2424

2525
use super::{DynamicAction, run_handler, saving::DirtyAssets};
@@ -398,7 +398,7 @@ impl GraphAndContext<'_> {
398398
pub struct CreateGraphAction;
399399

400400
impl DynamicAction for CreateGraphAction {
401-
fn handle(self: Box<Self>, world: &mut World) {
401+
fn handle(self: Box<Self>, world: &mut World, _: &mut ActionContext) {
402402
run_handler(world, "Could not create clip preview")(
403403
|In(_),
404404
mut graph_assets: ResMut<Assets<AnimationGraph>>,

crates/bevy_animation_graph_editor/src/ui/actions/mod.rs

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ pub mod ragdoll;
1212
pub mod saving;
1313
pub mod window;
1414

15-
use std::{any::Any, cmp::Ordering, fmt::Display};
15+
use std::{any::Any, cmp::Ordering, collections::VecDeque, fmt::Display};
1616

1717
use bevy::{
1818
ecs::{
@@ -40,18 +40,26 @@ pub struct PendingActions {
4040
pub actions: PushQueue<EditorAction>,
4141
}
4242

43+
#[derive(Default)]
44+
pub struct ActionContext {
45+
/// Queue further actions.
46+
///
47+
/// Be careful not to create an inifinite loop
48+
actions: PushQueue<EditorAction>,
49+
}
50+
4351
/// A "push-only" queue
44-
pub struct PushQueue<T>(Vec<T>);
52+
pub struct PushQueue<T>(VecDeque<T>);
4553

4654
impl<T> Default for PushQueue<T> {
4755
fn default() -> Self {
48-
Self(Vec::new())
56+
Self(VecDeque::new())
4957
}
5058
}
5159

5260
impl<T> PushQueue<T> {
5361
pub fn push(&mut self, item: T) {
54-
self.0.push(item);
62+
self.0.push_back(item);
5563
}
5664
}
5765

@@ -65,12 +73,17 @@ pub enum EditorAction {
6573
}
6674

6775
pub fn handle_editor_action_queue(world: &mut World, actions: impl Iterator<Item = EditorAction>) {
76+
let mut ctx = ActionContext::default();
6877
for action in actions {
69-
handle_editor_action(world, action);
78+
handle_editor_action(world, action, &mut ctx);
79+
}
80+
81+
while let Some(action) = ctx.actions.0.pop_front() {
82+
handle_editor_action(world, action, &mut ctx);
7083
}
7184
}
7285

73-
pub fn handle_editor_action(world: &mut World, action: EditorAction) {
86+
pub fn handle_editor_action(world: &mut World, action: EditorAction, ctx: &mut ActionContext) {
7487
match action {
7588
EditorAction::View(action) => {
7689
if let Err(err) = world.run_system_cached_with(handle_view_action, action) {
@@ -81,7 +94,7 @@ pub fn handle_editor_action(world: &mut World, action: EditorAction) {
8194
EditorAction::EventTrack(action) => handle_event_track_action(world, action),
8295
EditorAction::Graph(action) => handle_graph_action(world, action),
8396
EditorAction::Fsm(action) => handle_fsm_action(world, action),
84-
EditorAction::Dynamic(action) => action.handle(world),
97+
EditorAction::Dynamic(action) => action.handle(world, ctx),
8598
}
8699
}
87100

@@ -147,5 +160,5 @@ where
147160
}
148161

149162
pub trait DynamicAction: Send + Sync + 'static {
150-
fn handle(self: Box<Self>, world: &mut World);
163+
fn handle(self: Box<Self>, world: &mut World, ctx: &mut ActionContext);
151164
}

0 commit comments

Comments
 (0)