1use parser::common::*;
4
5use std::str;
6
7use std::io::BufRead;
8
9#[derive(PartialEq, Debug)]
10pub struct FaceIndex(pub u32, pub Option<u32>, pub Option<u32>);
11
12#[derive(PartialEq, Debug)]
13pub enum ObjLine {
14 Comment(String),
15 ObjectName(String),
16 GroupName(String),
17 MtlLib(String),
18 UseMtl(String),
19 SmoothShading(String),
20 Vertex(f32, f32, f32, Option<f32>), VertexParam(f32, f32, f32),
22 Normal(f32, f32, f32),
23 Face(FaceIndex, FaceIndex, FaceIndex),
24 TextureUVW(f32, f32, Option<f32>), }
26
27def_string_line!(object_line, "o", ObjLine, ObjectName);
28def_string_line!(group_line, "g", ObjLine, GroupName);
29def_string_line!(mtllib_line, "mtllib", ObjLine, MtlLib);
30def_string_line!(usemtl_line, "usemtl", ObjLine, UseMtl);
31def_string_line!(s_line, "s", ObjLine, SmoothShading);
32
33named!( vertex_line< &[u8], ObjLine >, map!(
34 sp!( delimited!( tag!("v"), float_triple_opt_4th, end_of_line )),
35 |(x,y,z,w)| ObjLine::Vertex(x,y,z,w)
36));
37
38named!( normal_line< &[u8], ObjLine >, map!(
39 sp!( delimited!( tag!("vn"), float_triple, end_of_line )),
40 |(x,y,z)| ObjLine::Normal(x,y,z)
41));
42
43named!( texcoord_line< &[u8], ObjLine >, map!(
44 sp!( delimited!( tag!("vt"), float_pair_opt_3rd, end_of_line )),
45 |(u,v,w)| ObjLine::TextureUVW(u,v,w)
46));
47
48named!( vertex_param_line< &[u8], ObjLine >, map!(
49 sp!(delimited!( tag!("vp"), float_triple, end_of_line )),
50 |(x,y,z)| ObjLine::VertexParam(x,y,z)
51));
52
53named!( face_triple< &[u8], FaceIndex >, map!(
54 tuple!(
55 uint,
56 delimited!( tag!("/"), opt!(uint), tag!("/") ),
57 opt!(uint)
58 ),
59 |(v, vt, vn)| FaceIndex(v, vt, vn)
60));
61
62named!( face_pair< &[u8], FaceIndex >, map!(
63 separated_pair!(
64 uint,
65 tag!("/"),
66 opt!(uint)
67 ),
68 |(v,vt)| FaceIndex(v, vt, None)
69));
70
71named!( face_line< &[u8], ObjLine >, delimited!(
72 sp!(tag!("f")),
73 alt!(
74 sp!(tuple!(uint, uint, uint)) => {|(u1,u2,u3)| ObjLine::Face(
75 FaceIndex(u1, None, None),
76 FaceIndex(u2, None, None),
77 FaceIndex(u3, None, None)
78 )
79 }
80 |
81 sp!(tuple!(face_pair, face_pair, face_pair)) => {|(a,b,c)| ObjLine::Face(a,b,c)}
82 |
83 sp!(tuple!(face_triple, face_triple, face_triple)) => {|(a,b,c)| ObjLine::Face(a,b,c)}
84 ),
85 end_of_line
86 )
87);
88
89named!(
90 comment_line<ObjLine>,
91 map!(sp!(comment), |s| ObjLine::Comment(
92 str::from_utf8(s).unwrap().trim().to_string()
93 ))
94);
95
96named!(
97 parse_obj_line<ObjLine>,
98 alt!(
99 vertex_line
100 | normal_line
101 | vertex_param_line
102 | texcoord_line
103 | face_line
104 | object_line
105 | group_line
106 | mtllib_line
107 | usemtl_line
108 | s_line
109 | comment_line
110 )
111);
112
113pub struct ObjParser<R> {
114 reader: R,
115}
116
117impl<R> ObjParser<R>
118where
119 R: BufRead,
120{
121 pub fn new(reader: R) -> Self {
122 ObjParser { reader }
123 }
124}
125
126impl<R> Iterator for ObjParser<R>
127where
128 R: BufRead,
129{
130 type Item = ObjLine;
131
132 fn next(&mut self) -> Option<Self::Item> {
133 use nom::IResult;
134 let mut line = String::new();
135 let read_result = self.reader.read_line(&mut line);
136 match read_result {
137 Ok(len) => {
138 if len > 0 {
139 let result = parse_obj_line(line.as_bytes());
140 match result {
141 IResult::Done(_, o) => Some(o),
142 IResult::Error(_e) => None,
143 IResult::Incomplete(_) => self.next(),
144 }
145 } else {
146 None
147 }
148 }
149 Err(_o) => None,
150 }
151 }
152}
153
154#[cfg(test)]
155mod tests {
156
157 use super::*;
158 use std::fs::File;
159 use std::io::BufReader;
160
161 #[test]
162 fn parser_can_read_from_file() -> Result<(), Box<dyn std::error::Error>> {
163 let file = File::open("assets/cube.obj")?;
164 let parser = ObjParser::new(BufReader::new(file));
165 let parsed_lines = parser.collect::<Vec<_>>();
166 assert_eq!(parsed_lines.len(), 51);
167 Ok(())
168 }
169
170 #[test]
171 fn can_parse_any_line() {
172 let result =
173 parse_obj_line("f 1/11/4 1/3/4 1/11/4 #this is an important face \n".as_bytes());
174 let (_, line) = result.unwrap();
175 assert_eq!(
176 line,
177 ObjLine::Face(
178 FaceIndex(1, Some(11), Some(4)),
179 FaceIndex(1, Some(3), Some(4)),
180 FaceIndex(1, Some(11), Some(4))
181 )
182 );
183 }
184
185 #[test]
186 fn can_ignore_comment_at_eol() {
187 let ff = face_line("f 1/11/4 1/3/4 1/11/4 #this is an important face \n".as_bytes());
188 let (_, b) = ff.unwrap();
189 assert_eq!(
190 b,
191 ObjLine::Face(
192 FaceIndex(1, Some(11), Some(4)),
193 FaceIndex(1, Some(3), Some(4)),
194 FaceIndex(1, Some(11), Some(4))
195 )
196 );
197 }
198
199 #[test]
200 fn can_parse_face_triple() {
201 named!(sp_face<FaceIndex>, sp!(face_triple));
202 let ff = face_triple("1/11/4".as_bytes());
203 let (_, b) = ff.unwrap();
204 assert_eq!(b, FaceIndex(1, Some(11), Some(4)));
205 }
206
207 #[test]
208 fn can_parse_face_line_1() {
209 let ff = face_line("f 1/11/4 1/3/4 1/11/4 \n".as_bytes());
210 let (_, b) = ff.unwrap();
211 assert_eq!(
212 b,
213 ObjLine::Face(
214 FaceIndex(1, Some(11), Some(4)),
215 FaceIndex(1, Some(3), Some(4)),
216 FaceIndex(1, Some(11), Some(4))
217 )
218 );
219 }
220
221 #[test]
222 fn can_parse_face_line_2() {
223 let ff = face_line("f 1/3 2/62 4/3\n".as_bytes());
225 let (_, b) = ff.unwrap();
226 assert_eq!(
227 b,
228 ObjLine::Face(
229 FaceIndex(1, Some(3), None),
230 FaceIndex(2, Some(62), None),
231 FaceIndex(4, Some(3), None),
232 )
233 );
234 }
235
236 #[test]
237 fn can_parse_face_line_3() {
238 let ff = face_line("f 1//4 1//4 1//11 \n".as_bytes());
239 let (_, b) = ff.unwrap();
240 assert_eq!(
241 b,
242 ObjLine::Face(
243 FaceIndex(1, None, Some(4)),
244 FaceIndex(1, None, Some(4)),
245 FaceIndex(1, None, Some(11))
246 )
247 );
248 }
249
250 #[test]
251 fn can_parse_face_line_4() {
252 let ff = face_line("f 42 1 11 \n".as_bytes());
253 let (_, b) = ff.unwrap();
254 assert_eq!(
255 b,
256 ObjLine::Face(
257 FaceIndex(42, None, None),
258 FaceIndex(1, None, None),
259 FaceIndex(11, None, None)
260 )
261 );
262 }
263
264 #[test]
265 fn can_parse_face_line_5() {
266 let ff = face_line("f 42/ 1/ 11/ \n".as_bytes());
267 let (_, b) = ff.unwrap();
268 assert_eq!(
269 b,
270 ObjLine::Face(
271 FaceIndex(42, None, None),
272 FaceIndex(1, None, None),
273 FaceIndex(11, None, None)
274 )
275 );
276 }
277
278 #[test]
279 fn can_parse_face_line_6() {
280 let ff = face_line("f 42// 1// 11// \t \n".as_bytes());
281 let (_, b) = ff.unwrap();
282 assert_eq!(
283 b,
284 ObjLine::Face(
285 FaceIndex(42, None, None),
286 FaceIndex(1, None, None),
287 FaceIndex(11, None, None)
288 )
289 );
290 }
291
292 #[test]
293 fn can_parse_texcoord_line() {
294 let vline = "vt -1.000000 -1.000000 \r\n".as_bytes();
295 let v = texcoord_line(vline);
296 let (_a, b) = v.unwrap();
297 assert_eq!(b, ObjLine::TextureUVW(-1.0, -1.0, None));
298 }
299
300 #[test]
301 fn can_parse_normal_line() {
302 let vline = "vn -1.000000 -1.000000 1.000000 \r\n".as_bytes();
303 let v = normal_line(vline);
304 let (_, b) = v.unwrap();
305 assert_eq!(b, ObjLine::Normal(-1.0, -1.0, 1.0));
306 }
307
308 #[test]
309 #[should_panic]
310 fn invalid_vertex_line_fails() {
311 let vline = "vZZ -1.000000 -1.000000 1.000000 \r\n".as_bytes();
312 let v = vertex_line(vline);
313 let (_, b) = v.unwrap();
314 assert_eq!(b, ObjLine::Vertex(-1.0, -1.0, 1.0, None));
315 }
316
317 #[test]
318 fn can_parse_vertex_parameter_line() {
319 let vline = "vp -1.000000 -1.000000 1.000000 \r\n".as_bytes();
320 let v = vertex_param_line(vline);
321 let (_, b) = v.unwrap();
322 assert_eq!(b, ObjLine::VertexParam(-1.0, -1.0, 1.0));
323 }
324
325 #[test]
326 fn can_parse_vertex_line_with_optional_w_value() {
327 let vline = "v -1.000000 -1.000000 1.000000 42.000\r\n".as_bytes();
328 let v = vertex_line(vline);
329 let (_, b) = v.unwrap();
330 assert_eq!(b, ObjLine::Vertex(-1.0, -1.0, 1.0, Some(42.0)));
331 }
332
333 #[test]
334 fn can_parse_vertex_line() {
335 let vline = "v -1.000000 -1.000000 1.000000 \r\n".as_bytes();
336 let v = vertex_line(vline);
337 let (_, b) = v.unwrap();
338 assert_eq!(b, ObjLine::Vertex(-1.0, -1.0, 1.0, None));
339 }
340
341 #[test]
342 fn can_parse_object_line() {
343 let cmt = object_line("o someobject.999asdf.7 \n".as_bytes());
344 let (_, b) = cmt.unwrap();
345 assert_eq!(b, ObjLine::ObjectName("someobject.999asdf.7".to_string()));
346 }
347
348 #[test]
349 fn can_parse_mtllib_line() {
350 let cmt = mtllib_line("mtllib somelib \n".as_bytes());
351 let (_, b) = cmt.unwrap();
352 assert_eq!(b, ObjLine::MtlLib("somelib".to_string()));
353 }
354
355 #[test]
356 fn can_parse_usemtl_line() {
357 let cmt = usemtl_line("usemtl SomeMaterial\n".as_bytes());
358 let (_, b) = cmt.unwrap();
359 assert_eq!(b, ObjLine::UseMtl("SomeMaterial".to_string()));
360 }
361
362 #[test]
363 fn can_parse_s_line() {
364 let cmt = s_line("s off\n".as_bytes());
365 let (_, b) = cmt.unwrap();
366 assert_eq!(b, ObjLine::SmoothShading("off".to_string()));
367 }
368
369 const CUBE_MODEL: &'static str = "
370# Blender v3.78 (sub 0) OBJ File: 'untitled.blend'
371# www.blender.org
372mtllib cube.mtl
373o Cube_Cube.001
374v -1.000000 -1.000000 1.000000
375v -1.000000 1.000000 1.000000
376v -1.000000 -1.000000 -1.000000
377v -1.000000 1.000000 -1.000000
378v 1.000000 -1.000000 1.000000
379v 1.000000 1.000000 1.000000
380v 1.000000 -1.000000 -1.000000
381v 1.000000 1.000000 -1.000000
382vt 0.0000 0.0000
383vt 1.0000 0.0000
384vt 1.0000 1.0000
385vt 0.0000 0.0000
386vt 1.0000 0.0000
387vt 1.0000 1.0000
388vt 0.0000 0.0000
389vt 1.0000 0.0000
390vt 1.0000 1.0000
391vt 0.0000 0.0000
392vt 1.0000 0.0000
393vt 1.0000 1.0000
394vt 0.0000 0.0000
395vt 1.0000 0.0000
396vt 1.0000 1.0000
397vt 1.0000 0.0000
398vt 1.0000 0.0000
399vt 1.0000 0.0000
400vt 1.0000 1.0000
401vn -1.0000 0.0000 0.0000
402vn 0.0000 0.0000 -1.0000
403vn 1.0000 0.0000 0.0000
404vn 0.0000 0.0000 1.0000
405vn 0.0000 -1.0000 0.0000
406vn 0.0000 1.0000 0.0000
407usemtl None
408s off
409f 2/1/1 3/2/1 1/3/1
410f 4/4/2 7/5/2 3/6/2
411f 8/7/3 5/8/3 7/9/3
412f 6/10/4 1/11/4 5/12/4
413f 7/13/5 1/11/5 3/6/5
414f 4/4/6 6/14/6 8/15/6
415f 2/1/1 4/16/1 3/6/1
416f 4/4/2 8/17/2 7/9/2
417f 8/7/3 6/14/3 5/12/3
418f 6/10/4 2/18/4 1/3/4
419f 7/13/5 5/8/5 1/3/5
420f 4/4/6 2/18/6 6/19/6
421";
422}