1use parser::common::*;
4
5use nom::eol;
6
7use std::io::BufRead;
8use std::str;
9
10#[derive(PartialEq, Debug)]
11pub enum MtlLine {
12 Comment(String),
13 NewMtl(String),
14 AmbientMap(String),
15 DiffuseMap(String),
16 SpecularMap(String),
17 BumpMap(String),
18
19 AmbientColor(f32, f32, f32),
20 DiffuseColor(f32, f32, f32),
21 SpecularColor(f32, f32, f32),
22 KeColor(f32, f32, f32), TransmissionFilter(f32, f32, f32),
25
26 OpticalDensity(f32),
27 SpecularExponent(f32),
28 TransparencyD(f32),
29 TransparencyTr(f32),
30 IlluminationModel(u32),
31 Sharpness(u32),
32 Blank,
33}
34
35def_string_line!(newmtl_line, "newmtl", MtlLine, NewMtl);
36def_string_line!(ambient_map_line, "map_Ka", MtlLine, AmbientMap);
37def_string_line!(diffuse_map_line, "map_Kd", MtlLine, DiffuseMap);
38def_string_line!(specular_map_line, "map_Ks", MtlLine, SpecularMap);
39def_string_line!(bump_map_line, "map_bump", MtlLine, BumpMap);
40
41named!(pub ka_ambient_line< &[u8], MtlLine >, map!(
42 delimited!(tag!("Ka"), float_triple, end_of_line), |(r,g,b)| MtlLine::AmbientColor(r,g,b)
43));
44
45named!(pub transmission_filter_line< &[u8], MtlLine >, map!(
46 delimited!(tag!("Tf"), float_triple, end_of_line), |(r,g,b)| MtlLine::TransmissionFilter(r,g,b)
47));
48
49named!(pub kd_diffuse_line< &[u8], MtlLine >, map!(
50 delimited!(tag!("Kd"), float_triple, end_of_line), |(r,g,b)| MtlLine::DiffuseColor(r,g,b)
51));
52
53named!(pub ks_specular_line< &[u8], MtlLine >, map!(
54 delimited!(tag!("Ks"), float_triple, end_of_line), |(r,g,b)| MtlLine::SpecularColor(r,g,b)
55));
56
57named!(pub ke_line< &[u8], MtlLine >, map!(
58 delimited!(tag!("Ke"), float_triple, end_of_line), |(r,g,b)| MtlLine::KeColor(r,g,b)
59));
60
61named!(pub transparency_line_d< &[u8], MtlLine >, map!(
62 sp!(delimited!(tag!("d"), float, end_of_line)), |t| MtlLine::TransparencyD(t)
63));
64
65named!(pub transparency_line_tr< &[u8], MtlLine >, map!(
66 sp!(delimited!(tag!("Tr"), float, end_of_line)), |t| MtlLine::TransparencyTr(t)
67));
68
69named!(pub optical_density_line< &[u8], MtlLine >, map!(
70 sp!(delimited!(tag!("Ni"), float, end_of_line)), |t| MtlLine::OpticalDensity(t)
71));
72
73named!(pub illum_line< &[u8], MtlLine >, map!(
74 sp!(delimited!(tag!("illum"), uint, end_of_line)), |t| MtlLine::IlluminationModel(t)
75));
76
77named!(pub sharpness_line< &[u8], MtlLine >, map!(
78 sp!(delimited!(tag!("sharpness"), uint, end_of_line)), |t| MtlLine::Sharpness(t)
79));
80
81named!(pub specular_exponent_line< &[u8], MtlLine >, map!(
82 sp!(delimited!(tag!("Ns"), float, end_of_line)), |t| MtlLine::SpecularExponent(t)
83));
84
85named!(
86 comment_line<MtlLine>,
87 map!(sp!(comment), |s| MtlLine::Comment(
88 str::from_utf8(s).unwrap().trim().to_string()
89 ))
90);
91
92named!(blank_line<MtlLine>, map!(sp!(eol), |_| MtlLine::Blank));
93
94named!(
95 parse_mtl_line<MtlLine>,
96 alt!(
97 newmtl_line
98 | ambient_map_line
99 | diffuse_map_line
100 | specular_map_line
101 | bump_map_line
102 | ka_ambient_line
103 | kd_diffuse_line
104 | ks_specular_line
105 | ke_line
106 | transparency_line_d
107 | transparency_line_tr
108 | optical_density_line
109 | illum_line
110 | sharpness_line
111 | specular_exponent_line
112 | comment_line
113 | blank_line
114 )
115);
116
117pub struct MtlParser<R> {
118 reader: R,
119}
120
121impl<R> MtlParser<R>
122where
123 R: BufRead,
124{
125 pub fn new(reader: R) -> Self {
126 MtlParser { reader }
127 }
128}
129
130impl<R> Iterator for MtlParser<R>
131where
132 R: BufRead,
133{
134 type Item = MtlLine;
135
136 fn next(&mut self) -> Option<Self::Item> {
137 use nom::IResult;
138 let mut line = String::new();
139 let read_result = self.reader.read_line(&mut line);
140 match read_result {
141 Ok(len) => {
142 if len > 0 {
143 let result = parse_mtl_line(line.as_bytes());
144 match result {
145 IResult::Done(_, o) => Some(o),
146 IResult::Error(_e) => None,
147 IResult::Incomplete(_) => self.next(),
148 }
149 } else {
150 None
151 }
152 }
153 Err(_o) => None,
154 }
155 }
156}
157
158#[cfg(test)]
159mod tests {
160 use super::*;
161
162 use std::error::Error;
163 use std::fs::File;
164 use std::io::BufReader;
165 pub fn read_file(filename: &str) -> Result<MtlParser<BufReader<File>>, Box<dyn Error>> {
166 let file = File::open(filename)?;
167 let reader = BufReader::new(file);
168 Ok(MtlParser { reader })
169 }
170
171 #[test]
172 fn mtl_parser_can_load_from_file() -> Result<(), Box<dyn Error>> {
173 let parser = read_file("assets/transparent_blue_cube.mtl")?;
174 let parsed_lines = parser.collect::<Vec<_>>();
175 println!("{:?}", parsed_lines);
176 assert_eq!(parsed_lines.len(), 12);
177 Ok(())
178 }
179
180 #[test]
181 fn can_parse_newmtl_line() {
182 let (_, b) = newmtl_line("newmtl material/name\n".as_bytes()).unwrap();
183 assert_eq!(b, MtlLine::NewMtl("material/name".to_string()));
184 }
185
186 #[test]
187 fn can_parse_ambient_map_line() {
188 let (_, b) = ambient_map_line("map_Ka sometexture.png\n".as_bytes()).unwrap();
189 assert_eq!(b, MtlLine::AmbientMap("sometexture.png".to_string()));
190 }
191
192 #[test]
193 fn can_parse_diffuse_map_line() {
194 let (_, b) = diffuse_map_line("map_Kd sometexture.png\n".as_bytes()).unwrap();
195 assert_eq!(b, MtlLine::DiffuseMap("sometexture.png".to_string()));
196 }
197
198 #[test]
199 fn can_parse_specular_map_line() {
200 let (_, b) = specular_map_line("map_Ks sometexture.png\n".as_bytes()).unwrap();
201 assert_eq!(b, MtlLine::SpecularMap("sometexture.png".to_string()));
202 }
203
204 #[test]
205 fn can_parse_transparency_d_line() {
206 let (_, b) = transparency_line_d("d 0.5\n".as_bytes()).unwrap();
207 assert_eq!(b, MtlLine::TransparencyD(0.5));
208 }
209
210 #[test]
211 fn can_parse_transparency_tr_line() {
212 let (_, b) = transparency_line_tr("Tr 0.5\n".as_bytes()).unwrap();
213 assert_eq!(b, MtlLine::TransparencyTr(0.5));
214 }
215
216 #[test]
217 fn can_parse_illumination_model_line() {
218 let (_, b) = illum_line("illum 2\n".as_bytes()).unwrap();
219 assert_eq!(b, MtlLine::IlluminationModel(2));
220 }
221
222 #[test]
223 fn can_parse_specular_exponent_line() {
224 let (_, b) = specular_exponent_line("Ns 2\n".as_bytes()).unwrap();
225 assert_eq!(b, MtlLine::SpecularExponent(2.0));
226 }
227
228 #[test]
229 fn can_parse_ka_ambient_line() {
230 let vline = "Ka 1.000 1.000 1.000 \r\n".as_bytes();
231 let v = ka_ambient_line(vline);
232 let (_, b) = v.unwrap();
233 assert_eq!(b, MtlLine::AmbientColor(1.0, 1.0, 1.0));
234 }
235 #[test]
236 fn can_parse_ka_diffuse_line() {
237 let vline = "Kd 1.000 1.000 1.000 \r\n".as_bytes();
238 let v = kd_diffuse_line(vline);
239 let (_, b) = v.unwrap();
240 assert_eq!(b, MtlLine::DiffuseColor(1.0, 1.0, 1.0));
241 }
242 #[test]
243 fn can_parse_ka_specular_line() {
244 let vline = "Ks 1.000 1.000 1.000 \r\n".as_bytes();
245 let v = ks_specular_line(vline);
246 let (_, b) = v.unwrap();
247 assert_eq!(b, MtlLine::SpecularColor(1.0, 1.0, 1.0));
248 }
249
250 const MTL_FILE: &'static str = "
251# Blender MTL File: 'None'
252# Material Count: 1
253
254newmtl Material.002
255Ns 96.078431
256Ka 1.000000 1.000000 1.000000
257Kd 0.000000 0.003667 0.640000
258Ks 0.500000 0.500000 0.500000
259Ke 0.000000 0.000000 0.000000
260Ni 1.000000
261d 0.600000
262illum 2
263";
264}