1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
use crate::BlenderArmature;
use serde_json;
use std::collections::hash_map::Entry;
use std::collections::HashMap;
pub type ArmaturesByFilename = HashMap<String, ArmaturesByArmatureName>;
pub type ArmaturesByArmatureName = HashMap<String, BlenderArmature>;
pub fn parse_armatures_from_blender_stdout(blender_stdout: &str) -> ArmaturesByFilename {
let mut filenames_to_armatures = HashMap::new();
let mut index = 0;
while let Some((filename_to_armatures, next_start_index)) =
find_first_armature_after_index(blender_stdout, index)
{
for (filename, armatures) in filename_to_armatures.into_iter() {
match filenames_to_armatures.entry(filename) {
Entry::Vacant(v) => {
v.insert(armatures);
}
Entry::Occupied(ref mut o) => {
o.get_mut().extend(armatures);
}
}
}
index = next_start_index;
}
filenames_to_armatures
}
pub fn flatten_exported_armatures(
armatures_by_filename: &ArmaturesByFilename,
) -> Result<HashMap<&str, &BlenderArmature>, FlattenArmatureError> {
let mut flattened_armatures = HashMap::new();
let mut duplicate_armatures: HashMap<String, Vec<String>> = HashMap::new();
for (source_filename, armatures) in armatures_by_filename.iter() {
for (armature_name, armature) in armatures.iter() {
flattened_armatures.insert(armature_name.as_str(), armature);
match duplicate_armatures.entry(armature_name.to_string()) {
Entry::Occupied(mut duplicates) => {
duplicates.get_mut().push(source_filename.to_string());
}
Entry::Vacant(filenames) => {
filenames.insert(vec![source_filename.to_string()]);
}
};
}
}
let duplicate_armatures: HashMap<String, Vec<String>> = duplicate_armatures
.into_iter()
.filter(|(_armature_name, filenames)| filenames.len() > 1)
.collect();
if duplicate_armatures.len() > 0 {
return Err(FlattenArmatureError::DuplicateArmatureNamesAcrossFiles {
duplicates: duplicate_armatures,
});
}
Ok(flattened_armatures)
}
fn find_first_armature_after_index(
blender_stdout: &str,
index: usize,
) -> Option<(ArmaturesByFilename, usize)> {
let blender_stdout = &blender_stdout[index as usize..];
if let Some(armature_start_index) = blender_stdout.find("START_ARMATURE_JSON") {
let mut filenames_to_armature = HashMap::new();
let mut armature_name_to_data = HashMap::new();
let armature_end_index = blender_stdout.find("END_ARMATURE_JSON").unwrap();
let armature_data = &blender_stdout[armature_start_index..armature_end_index];
let mut lines = armature_data.lines();
let first_line = lines.next().unwrap();
let armature_filename: Vec<&str> = first_line.split(" ").collect();
let armature_filename = armature_filename[1].to_string();
let armature_name = first_line.split(" ").last().unwrap().to_string();
let armature_data: String = lines.collect();
let armature_data: BlenderArmature = serde_json::from_str(&armature_data).expect(&format!(
"Could not deserialize Blender Armature data{}",
&armature_data
));
armature_name_to_data.insert(armature_name, armature_data);
filenames_to_armature.insert(armature_filename, armature_name_to_data);
return Some((filenames_to_armature, index + armature_end_index + 1));
}
return None;
}
#[derive(Debug, thiserror::Error)]
pub enum FlattenArmatureError {
#[error("Duplicate armatures found: {:#?}", duplicates)]
DuplicateArmatureNamesAcrossFiles {
duplicates: HashMap<String, Vec<String>>,
},
}