Skip to content

Commit 31be518

Browse files
committed
body mapping UX more or less complete
1 parent 82ef95e commit 31be518

24 files changed

Lines changed: 941 additions & 117 deletions

File tree

Lines changed: 134 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,142 @@
11
(
22
bones_from_bodies: {
3-
["metarig", "spine", "spine.001", "spine.002", "spine.003", "shoulder.R", "upper_arm.R"]: [
4-
(
5-
body: (uuid: "505e7d48-bbdb-440b-ad3f-5886d332b295"),
6-
weight: 1.,
7-
offset: (translation: (0.,0.2,0.), rotation: (0.,0.,1.,0.)),
8-
),
9-
],
10-
["metarig", "spine", "spine.001", "spine.002", "spine.003", "shoulder.R", "upper_arm.R", "forearm.R"]: [
11-
(
12-
body: (uuid: "505e7d48-bbdb-440b-ad3f-5886d332b294"),
13-
weight: 1.,
14-
offset: (translation: (0.,0.2,0.), rotation: (0.,0.,1.,0.)),
15-
),
16-
],
3+
[
4+
"metarig",
5+
"spine",
6+
"spine.001",
7+
"spine.002",
8+
"spine.003",
9+
"shoulder.R",
10+
"upper_arm.R",
11+
]: (
12+
bone_id: [
13+
"metarig",
14+
"spine",
15+
"spine.001",
16+
"spine.002",
17+
"spine.003",
18+
"shoulder.R",
19+
"upper_arm.R",
20+
],
21+
bodies: [
22+
(
23+
body: (
24+
uuid: "505e7d48-bbdb-440b-ad3f-5886d332b295",
25+
),
26+
weight: 1.0,
27+
offset: (
28+
rotation: (-0.018490374, -0.009419799, 0.683723, 0.7294465),
29+
translation: (0.15469998, -0.01540041, -0.0067000575),
30+
),
31+
override_offset: false,
32+
),
33+
],
34+
),
35+
[
36+
"metarig",
37+
"spine",
38+
"spine.001",
39+
"spine.002",
40+
"spine.003",
41+
"shoulder.R",
42+
"upper_arm.R",
43+
"forearm.R",
44+
]: (
45+
bone_id: [
46+
"metarig",
47+
"spine",
48+
"spine.001",
49+
"spine.002",
50+
"spine.003",
51+
"shoulder.R",
52+
"upper_arm.R",
53+
"forearm.R",
54+
],
55+
bodies: [
56+
(
57+
body: (
58+
uuid: "505e7d48-bbdb-440b-ad3f-5886d332b294",
59+
),
60+
weight: 1.0,
61+
offset: (
62+
rotation: (0.019352648, 0.020627335, 0.7121179, 0.70148987),
63+
translation: (0.17322606, 0.0059952736, -0.009949485),
64+
),
65+
override_offset: false,
66+
),
67+
],
68+
),
1769
},
1870
bodies_from_bones: {
19-
(uuid: "505e7d48-bbdb-440b-ad3f-5886d332b295"):
20-
(
21-
bone: ["metarig", "spine", "spine.001", "spine.002", "spine.003", "shoulder.R", "upper_arm.R"],
22-
weight: 1.,
23-
offset: (translation: (0.,0.2,0.), rotation: (0.,0.,1.,0.)),
71+
(
72+
uuid: "505e7d48-bbdb-440b-ad3f-5886d332b291",
73+
): (
74+
body_id: (
75+
uuid: "505e7d48-bbdb-440b-ad3f-5886d332b291",
76+
),
77+
bone: (
78+
bone: [
79+
"metarig",
80+
"spine",
81+
"spine.001",
82+
"spine.002",
83+
"spine.003",
84+
],
85+
offset: (
86+
rotation: (0.014281374, 0.0, 0.0, 0.9998981),
87+
translation: (0.0, 0.09409344, 0.008590829),
88+
),
89+
override_offset: false,
90+
),
91+
),
92+
(
93+
uuid: "505e7d48-bbdb-440b-ad3f-5886d332b294",
94+
): (
95+
body_id: (
96+
uuid: "505e7d48-bbdb-440b-ad3f-5886d332b294",
97+
),
98+
bone: (
99+
bone: [
100+
"metarig",
101+
"spine",
102+
"spine.001",
103+
"spine.002",
104+
"spine.003",
105+
"shoulder.R",
106+
"upper_arm.R",
107+
"forearm.R",
108+
],
109+
offset: (
110+
rotation: (0.01935265, 0.020627337, 0.7121179, -0.7014899),
111+
translation: (-0.003396988, 0.17358181, 0.00013250858),
112+
),
113+
override_offset: false,
24114
),
25-
(uuid: "505e7d48-bbdb-440b-ad3f-5886d332b294"):
26-
(
27-
bone: ["metarig", "spine", "spine.001", "spine.002", "spine.003", "shoulder.R", "upper_arm.R", "forearm.R"],
28-
weight: 1.,
29-
offset: (translation: (0.,0.2,0.), rotation: (0.,0.,1.,0.)),
115+
),
116+
(
117+
uuid: "505e7d48-bbdb-440b-ad3f-5886d332b295",
118+
): (
119+
body_id: (
120+
uuid: "505e7d48-bbdb-440b-ad3f-5886d332b295",
30121
),
31-
(uuid: "505e7d48-bbdb-440b-ad3f-5886d332b291"):
32-
(
33-
bone: ["metarig", "spine", "spine.001", "spine.002", "spine.003"],
34-
weight: 1.,
35-
offset: (translation: (0.,0.1,0.), rotation: (0.,0.,0.,1.)),
122+
bone: (
123+
bone: [
124+
"metarig",
125+
"spine",
126+
"spine.001",
127+
"spine.002",
128+
"spine.003",
129+
"shoulder.R",
130+
"upper_arm.R",
131+
],
132+
offset: (
133+
rotation: (0.018490378, 0.009419801, -0.683723, 0.7294466),
134+
translation: (0.0052546263, 0.1549803, 0.012948836),
135+
),
136+
override_offset: false,
36137
),
37-
138+
),
38139
},
39-
)
140+
skeleton: "skeletons/human.skn.ron",
141+
ragdoll: "ragdolls/human.rag.ron",
142+
)

crates/bevy_animation_graph/src/core/colliders/core.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,9 +94,9 @@ impl ColliderConfig {
9494
match self.offset_mode {
9595
ColliderOffsetMode::Local => Transform::from_isometry(self.offset),
9696
ColliderOffsetMode::Global => Transform {
97-
translation: default_transforms.global.rotation.inverse()
97+
translation: default_transforms.character.rotation.inverse()
9898
* Vec3::from(self.offset.translation),
99-
rotation: default_transforms.global.rotation.inverse() * self.offset.rotation,
99+
rotation: default_transforms.character.rotation.inverse() * self.offset.rotation,
100100
scale: Vec3::ONE,
101101
},
102102
}

crates/bevy_animation_graph/src/core/id.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ impl Hash for BoneId {
2424
}
2525

2626
impl BoneId {
27+
pub fn from_uuid(uuid: Uuid) -> Self {
28+
Self { id: uuid }
29+
}
30+
2731
pub fn id(&self) -> Uuid {
2832
self.id
2933
}
Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,56 @@
1-
use bevy::{asset::Asset, math::Isometry3d, platform::collections::HashMap, reflect::Reflect};
1+
use bevy::{
2+
asset::{Asset, Handle},
3+
math::Isometry3d,
4+
platform::collections::HashMap,
5+
reflect::Reflect,
6+
};
27
use serde::{Deserialize, Serialize};
38

4-
use crate::core::{animation_clip::EntityPath, ragdoll::definition::BodyId};
9+
use crate::core::{
10+
animation_clip::EntityPath,
11+
ragdoll::definition::{BodyId, Ragdoll},
12+
skeleton::Skeleton,
13+
};
514

615
// TODO: Replace EntityPath with bone ids, and add a serial proxy that allows the loader to map
716

8-
#[derive(Asset, Debug, Clone, Reflect, Serialize, Deserialize)]
17+
#[derive(Asset, Debug, Clone, Reflect)]
918
pub struct RagdollBoneMap {
1019
/// We assume the invariant that the sum of all body weights for a bone is always 1.
11-
pub bones_from_bodies: HashMap<EntityPath, Vec<BodyWeight>>,
20+
pub bones_from_bodies: HashMap<EntityPath, BoneMapping>,
1221
/// We assume the invariant that the sum of all body weights for a bone is always 1.
13-
pub bodies_from_bones: HashMap<BodyId, BoneWeight>,
22+
pub bodies_from_bones: HashMap<BodyId, BodyMapping>,
23+
pub skeleton: Handle<Skeleton>,
24+
pub ragdoll: Handle<Ragdoll>,
1425
}
1526

27+
/// How to get a bone's position from rigidbodies
1628
#[derive(Debug, Clone, Reflect, Serialize, Deserialize)]
29+
pub struct BoneMapping {
30+
pub bone_id: EntityPath,
31+
pub bodies: Vec<BodyWeight>,
32+
}
33+
34+
/// How to get a rigibody's position from bones
35+
#[derive(Debug, Clone, Reflect, Serialize, Deserialize)]
36+
pub struct BodyMapping {
37+
pub body_id: BodyId,
38+
pub bone: BoneWeight,
39+
}
40+
41+
#[derive(Debug, Clone, Default, Reflect, Serialize, Deserialize)]
1742
pub struct BodyWeight {
1843
pub body: BodyId,
1944
pub weight: f32,
2045
pub offset: Isometry3d,
46+
#[serde(default)]
47+
pub override_offset: bool,
2148
}
2249

23-
#[derive(Debug, Clone, Reflect, Serialize, Deserialize)]
50+
#[derive(Debug, Clone, Default, Reflect, Serialize, Deserialize)]
2451
pub struct BoneWeight {
2552
pub bone: EntityPath,
2653
pub offset: Isometry3d,
54+
#[serde(default)]
55+
pub override_offset: bool,
2756
}

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

Lines changed: 53 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,18 @@
1-
use bevy::asset::{AssetLoader, LoadContext, io::Reader};
1+
use bevy::{
2+
asset::{AssetLoader, AssetPath, LoadContext, io::Reader},
3+
platform::collections::HashMap,
4+
reflect::Reflect,
5+
};
6+
use serde::{Deserialize, Serialize};
27

3-
use crate::core::{errors::AssetLoaderError, ragdoll::bone_mapping::RagdollBoneMap};
8+
use crate::core::{
9+
animation_clip::EntityPath,
10+
errors::AssetLoaderError,
11+
ragdoll::{
12+
bone_mapping::{BodyMapping, BoneMapping, RagdollBoneMap},
13+
definition::BodyId,
14+
},
15+
};
416

517
#[derive(Default)]
618
pub struct RagdollBoneMapLoader;
@@ -14,16 +26,52 @@ impl AssetLoader for RagdollBoneMapLoader {
1426
&self,
1527
reader: &mut dyn Reader,
1628
_settings: &Self::Settings,
17-
_load_context: &mut LoadContext<'_>,
29+
load_context: &mut LoadContext<'_>,
1830
) -> Result<Self::Asset, Self::Error> {
1931
let mut bytes = vec![];
2032
reader.read_to_end(&mut bytes).await?;
21-
let map: RagdollBoneMap = ron::de::from_bytes(&bytes)?;
33+
let RagdollBoneMapSerial {
34+
bones_from_bodies,
35+
bodies_from_bones,
36+
skeleton,
37+
ragdoll,
38+
} = ron::de::from_bytes(&bytes)?;
2239

23-
Ok(map)
40+
Ok(RagdollBoneMap {
41+
bones_from_bodies,
42+
bodies_from_bones,
43+
skeleton: load_context.load(skeleton),
44+
ragdoll: load_context.load(ragdoll),
45+
})
2446
}
2547

2648
fn extensions(&self) -> &[&str] {
2749
&["bm.ron"]
2850
}
2951
}
52+
53+
#[derive(Debug, Clone, Reflect, Serialize, Deserialize)]
54+
pub struct RagdollBoneMapSerial {
55+
pub bones_from_bodies: HashMap<EntityPath, BoneMapping>,
56+
pub bodies_from_bones: HashMap<BodyId, BodyMapping>,
57+
pub skeleton: AssetPath<'static>,
58+
pub ragdoll: AssetPath<'static>,
59+
}
60+
61+
impl RagdollBoneMapSerial {
62+
pub fn from_value(ragdoll_bone_map: &RagdollBoneMap) -> Option<Self> {
63+
let RagdollBoneMap {
64+
bones_from_bodies,
65+
bodies_from_bones,
66+
skeleton,
67+
ragdoll,
68+
} = ragdoll_bone_map;
69+
70+
Some(Self {
71+
bones_from_bodies: bones_from_bodies.clone(),
72+
bodies_from_bones: bodies_from_bones.clone(),
73+
skeleton: skeleton.path()?.to_owned(),
74+
ragdoll: ragdoll.path()?.to_owned(),
75+
})
76+
}
77+
}

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ pub fn read_pose(
2828
let mut bones_from_bodies = bone_map.bones_from_bodies.iter().collect::<Vec<_>>();
2929
bones_from_bodies.sort_by_key(|(path, _)| path.parts.len());
3030

31-
for (bone_path, bodies) in bones_from_bodies {
31+
for (bone_path, bone_mapping) in bones_from_bodies {
3232
let parent_bone_transform = bone_path
3333
.parent()
3434
.map(|parent_bone_path| {
@@ -39,7 +39,8 @@ pub fn read_pose(
3939
let inverse_parent_transform =
4040
Transform::from_matrix(parent_bone_transform.compute_affine().inverse().into());
4141

42-
let Some(mut weighted_transforms) = bodies
42+
let Some(mut weighted_transforms) = bone_mapping
43+
.bodies
4344
.iter()
4445
.filter_map(|body_weight| {
4546
let Some(body_entity) = spawned_ragdoll.bodies.get(&body_weight.body) else {

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,17 +38,17 @@ pub fn write_pose_to_ragdoll(
3838
};
3939

4040
for body in ragdoll.bodies.values() {
41-
let Some(bone_weight) = mapping.bodies_from_bones.get(&body.id) else {
41+
let Some(body_mapping) = mapping.bodies_from_bones.get(&body.id) else {
4242
continue;
4343
};
4444

4545
// TODO: Quaternion interpolation for more than 1 bone weights
4646
let character_transform =
47-
convert.character_transform_of_bone(pose, skeleton, bone_weight.bone.id());
47+
convert.character_transform_of_bone(pose, skeleton, body_mapping.bone.bone.id());
4848

4949
targets.bodies.push(BodyTarget {
5050
body_id: body.id,
51-
character_space_isometry: character_transform.to_isometry() * bone_weight.offset,
51+
character_space_isometry: character_transform.to_isometry() * body_mapping.bone.offset,
5252
})
5353
}
5454

crates/bevy_animation_graph/src/core/skeleton/loader.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,11 +64,11 @@ fn build_skeleton(world: &World) -> Result<Skeleton, AssetLoaderError> {
6464
let mut pending_children: Vec<(Entity, EntityPath, Transform)> =
6565
vec![(root, EntityPath::default(), Transform::IDENTITY)];
6666

67-
while let Some((cur_entity, parent_path, parent_global_transform)) = pending_children.pop() {
67+
while let Some((cur_entity, parent_path, parent_character_transform)) = pending_children.pop() {
6868
let (maybe_children, cur_name, cur_transform) = query.get(world, cur_entity).unwrap();
6969
let cur_path = parent_path.child(cur_name.clone());
7070

71-
let cur_global_transform = parent_global_transform * *cur_transform;
71+
let cur_global_transform = parent_character_transform * *cur_transform;
7272

7373
skeleton.add_bone(cur_path.clone(), *cur_transform, cur_global_transform);
7474

0 commit comments

Comments
 (0)