1use crate::BlenderMesh;
2use failure::Fail;
3use std::collections::hash_map::Entry;
4use std::collections::HashMap;
5
6pub type MeshesByFilename = HashMap<String, MeshesByMeshName>;
7pub type MeshesByMeshName = HashMap<String, BlenderMesh>;
8
9pub fn parse_meshes_from_blender_stdout(blender_stdout: &str) -> MeshesByFilename {
20 let mut filenames_to_meshes = HashMap::new();
21
22 let mut index = 0;
23
24 while let Some((filename_to_mesh, next_start_index)) =
25 find_first_mesh_after_index(blender_stdout, index)
26 {
27 for (filename, meshes) in filename_to_mesh.into_iter() {
28 match filenames_to_meshes.entry(filename) {
29 Entry::Vacant(v) => {
30 v.insert(meshes);
31 }
32 Entry::Occupied(ref mut o) => {
33 o.get_mut().extend(meshes);
34 }
35 }
36 }
37
38 index = next_start_index;
39 }
40
41 filenames_to_meshes
42}
43
44pub type FlattenedExportedMeshes = HashMap<String, BlenderMesh>;
45
46pub fn flatten_exported_meshes_owned(
51 meshes_by_filename: MeshesByFilename,
52) -> Result<FlattenedExportedMeshes, FlattenMeshError> {
53 let mut flattened_meshes = HashMap::new();
54
55 let mut duplicate_meshes: HashMap<String, Vec<String>> = HashMap::new();
56
57 for (source_filename, meshes) in meshes_by_filename.into_iter() {
58 for (mesh_name, mesh) in meshes.into_iter() {
59 match duplicate_meshes.entry(mesh_name.to_string()) {
60 Entry::Occupied(mut duplicates) => {
61 duplicates.get_mut().push(source_filename.to_string());
62 }
63 Entry::Vacant(filenames) => {
64 filenames.insert(vec![source_filename.to_string()]);
65 }
66 };
67
68 flattened_meshes.insert(mesh_name, mesh);
69 }
70 }
71
72 validate_no_duplicates(duplicate_meshes)?;
73
74 Ok(flattened_meshes)
75}
76
77pub fn flatten_exported_meshes(
82 meshes_by_filename: &MeshesByFilename,
83) -> Result<HashMap<&str, &BlenderMesh>, FlattenMeshError> {
84 let mut flattened_meshes = HashMap::new();
85
86 let mut duplicate_meshes: HashMap<String, Vec<String>> = HashMap::new();
87
88 for (source_filename, meshes) in meshes_by_filename.iter() {
89 for (mesh_name, mesh) in meshes.iter() {
90 flattened_meshes.insert(mesh_name.as_str(), mesh);
91
92 match duplicate_meshes.entry(mesh_name.to_string()) {
93 Entry::Occupied(mut duplicates) => {
94 duplicates.get_mut().push(source_filename.to_string());
95 }
96 Entry::Vacant(filenames) => {
97 filenames.insert(vec![source_filename.to_string()]);
98 }
99 };
100 }
101 }
102
103 validate_no_duplicates(duplicate_meshes)?;
104
105 Ok(flattened_meshes)
106}
107
108fn validate_no_duplicates(
109 duplicate_meshes: HashMap<String, Vec<String>>,
110) -> Result<(), FlattenMeshError> {
111 let duplicate_meshes: HashMap<String, Vec<String>> = duplicate_meshes
112 .into_iter()
113 .filter(|(_mesh_name, filenames)| filenames.len() > 1)
114 .collect();
115
116 if duplicate_meshes.len() > 0 {
117 return Err(FlattenMeshError::DuplicateMeshNamesAcrossFiles {
118 duplicates: duplicate_meshes,
119 });
120 }
121
122 Ok(())
123}
124
125#[derive(Debug, Fail)]
128pub enum FlattenMeshError {
129 #[fail(display = "Duplicate meshes found: {:#?}", duplicates)]
130 DuplicateMeshNamesAcrossFiles {
131 duplicates: HashMap<String, Vec<String>>,
133 },
134}
135
136fn find_first_mesh_after_index(
137 blender_stdout: &str,
138 index: usize,
139) -> Option<(MeshesByFilename, usize)> {
140 let blender_stdout = &blender_stdout[index as usize..];
141
142 if let Some(mesh_start_index) = blender_stdout.find("START_MESH_JSON") {
143 let mut filenames_to_meshes = HashMap::new();
144 let mut mesh_name_to_data = HashMap::new();
145
146 let mesh_end_index = blender_stdout.find("END_MESH_JSON").unwrap();
147
148 let mesh_data = &blender_stdout[mesh_start_index..mesh_end_index];
149
150 let mut lines = mesh_data.lines();
151
152 let first_line = lines.next().unwrap();
153
154 let mesh_filename: Vec<&str> = first_line.split(" ").collect();
155 let mesh_filename = mesh_filename[1].to_string();
156
157 let mesh_name = first_line.split(" ").last().unwrap().to_string();
158
159 let mesh_data: String = lines.collect();
160 let mesh_data: BlenderMesh = serde_json::from_str(&mesh_data).unwrap();
161
162 mesh_name_to_data.insert(mesh_name, mesh_data);
163 filenames_to_meshes.insert(mesh_filename, mesh_name_to_data);
164
165 return Some((filenames_to_meshes, index + mesh_end_index + 1));
166 }
167
168 return None;
169}