nom_obj/parser/
mtl.rs

1/// http://paulbourke.net/dataformats/mtl/
2///
3use 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), // unknown, but blender writes it
23
24    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}