blender_mesh/
bone.rs

1use crate::vertex_attributes::{MultiIndexedVertexAttributes, VertexBoneInfluences};
2
3/// The number of bones that influence each uniform.
4///
5/// When exported from Blender this is non uniform, but becomes uniform when
6/// we call `.set_groups_per_vertex` to make every vertex have the same number
7/// of influences.
8///
9/// TODO: Remove this and use VertexAttribute with something like attribute_size: Varies(vec![])
10/// this allows us to handle all attributes the same way.
11#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
12pub(crate) enum BoneInfluencesPerVertex {
13    NonUniform(Vec<u8>),
14    Uniform(u8),
15}
16
17impl Default for BoneInfluencesPerVertex {
18    fn default() -> Self {
19        BoneInfluencesPerVertex::Uniform(0)
20    }
21}
22
23impl From<Vec<u8>> for BoneInfluencesPerVertex {
24    fn from(influences: Vec<u8>) -> Self {
25        BoneInfluencesPerVertex::NonUniform(influences)
26    }
27}
28
29impl MultiIndexedVertexAttributes {
30    /// Different vertices might have different numbers of bones that influence them.
31    /// A vertex near the shoulder might be influenced by the neck and upper arm and sternum,
32    /// while a vertex in a toe might only be influenced by a toe bone.
33    ///
34    /// When passing data to the GPU, each vertex needs the same number of bone attributes, so
35    /// we must add/remove bones from each vertex to get them equal.
36    ///
37    /// Say we're setting 3 groups per vertex:
38    ///  - If a vertex has one vertex group (bone) we will create two fake bones with 0.0 weight.
39    ///  - If a vertex has 5 bones we'll remove the one with the smallest weighting (influence).
40    ///
41    /// TODO: I wrote landon when I was a Rust noob and so a lot of things are just wrong. This
42    /// method doesn't work if you try to set the bone influences to two different constants in
43    /// a row - for example.
44    ///       Need to eventually rewrite a lot of the library with TDD - but we can clean up over
45    ///       time.
46    pub(crate) fn set_bone_influences_per_vertex(&mut self, count: u8) {
47        let mut normalized_group_indices = vec![];
48        let mut normalized_group_weights = vec![];
49
50        let mut current_index: u32 = 0;
51
52        // TODO: Error handling
53        if self.bone_influences.is_none() {
54            return;
55        }
56
57        {
58            let VertexBoneInfluences {
59                bones_per_vertex,
60                bone_indices,
61                bone_weights,
62            } = self.bone_influences.as_mut().unwrap();
63
64            if let BoneInfluencesPerVertex::NonUniform(bone_influences_per_vertex) =
65                &bones_per_vertex
66            {
67                for group_count in bone_influences_per_vertex.iter() {
68                    let mut vertex_indices = vec![];
69                    let mut vertex_weights = vec![];
70
71                    for index in current_index..(current_index + *group_count as u32) {
72                        vertex_indices.push(index);
73                        vertex_weights.push(bone_weights[index as usize]);
74                    }
75
76                    vertex_weights.sort_by(|a, b| b.partial_cmp(a).unwrap());
77                    vertex_indices.sort_by(|a, b| {
78                        bone_weights[*b as usize]
79                            .partial_cmp(&bone_weights[*a as usize])
80                            .unwrap()
81                    });
82
83                    let mut vertex_indices: Vec<u8> = vertex_indices
84                        .iter()
85                        .map(|i| bone_indices[*i as usize])
86                        .collect();
87
88                    vertex_indices.resize(count as usize, 0);
89                    vertex_weights.resize(count as usize, 0.0);
90
91                    normalized_group_indices.append(&mut vertex_indices);
92                    normalized_group_weights.append(&mut vertex_weights);
93
94                    current_index += *group_count as u32;
95                }
96            }
97        }
98
99        let mut bone_influences = self.bone_influences.as_mut().unwrap();
100
101        bone_influences.bones_per_vertex = BoneInfluencesPerVertex::Uniform(count);
102        bone_influences.bone_indices = normalized_group_indices;
103        bone_influences.bone_weights = normalized_group_weights;
104    }
105}
106
107#[cfg(test)]
108mod tests {
109    use super::*;
110    use crate::combine_indices::tests::TodoDeleteMeMultiConverter;
111    use crate::BlenderMesh;
112
113    #[test]
114    fn set_joints_per_vert() {
115        let mut start_mesh = BlenderMesh {
116            multi_indexed_vertex_attributes: TodoDeleteMeMultiConverter {
117                vertex_group_indices: Some(vec![0, 2, 3, 4, 0, 1, 3, 2]),
118                bone_influences_per_vertex: Some(vec![1, 3, 4].into()),
119                vertex_group_weights: Some(vec![1.0, 0.5, 0.2, 0.3, 0.6, 0.15, 0.1, 0.15]),
120                ..TodoDeleteMeMultiConverter::default()
121            }
122            .into(),
123            ..BlenderMesh::default()
124        };
125
126        start_mesh
127            .multi_indexed_vertex_attributes
128            .set_bone_influences_per_vertex(3);
129        let three_joints_per_vert = start_mesh;
130
131        let expected_mesh = BlenderMesh {
132            multi_indexed_vertex_attributes: TodoDeleteMeMultiConverter {
133                vertex_group_indices: Some(vec![0, 0, 0, 2, 4, 3, 0, 1, 2]),
134                bone_influences_per_vertex: Some(BoneInfluencesPerVertex::Uniform(3)),
135                vertex_group_weights: Some(vec![1.0, 0.0, 0.0, 0.5, 0.3, 0.2, 0.6, 0.15, 0.15]),
136                ..TodoDeleteMeMultiConverter::default()
137            }
138            .into(),
139            ..BlenderMesh::default()
140        };
141
142        assert_eq!(three_joints_per_vert, expected_mesh);
143    }
144}