• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ekarpp / lumo / 6054336964

01 Sep 2023 10:00PM UTC coverage: 47.535% (-0.9%) from 48.466%
6054336964

push

github

web-flow
0.3.2 (#33)

- Rewrites sample gathering to use `FilmTiles`
- Fixes splat samples in BDPT
- Some BDPT medium bug fixes (Progresses #31)

Closes #32

180 of 180 new or added lines in 17 files covered. (100.0%)

2179 of 4584 relevant lines covered (47.53%)

6887198.38 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

51.56
/src/parser.rs
1
use crate::{Vec2, Vec3, Image, Float, Normal, Point};
2
use crate::tracer::{
3
    Scene, Material, Texture,
4
    TriangleMesh, Face, Mesh
5
};
6
use std::fs::File;
7
use std::sync::Arc;
8
use std::io::{
9
    self, BufRead, BufReader, Result,
10
    Cursor, Read, Seek, Write
11
};
12
use std::collections::HashMap;
13
use zip::ZipArchive;
14
use regex::Regex;
15
use mtl::MtlConfig;
16

17
/// .obj parser
18
mod obj;
19
/// .mtl parser
20
mod mtl;
21

22
/*
23
 * BEWARE WHO ENTERS! HERE BE DRAGONS!
24
 */
25

26
/// Function to create io::Error
27
fn obj_error(message: &str) -> io::Error {
×
28
    io::Error::new(io::ErrorKind::InvalidData, message)
×
29
}
×
30

31
/// For .obj and .mtl parsers
32
fn parse_double(token: &str) -> Result<Float> {
125,940✔
33
    token
125,940✔
34
        .parse()
125,940✔
35
        .map_err(|_| obj_error("Could not parse double in file"))
125,940✔
36
}
125,940✔
37

38
/// For .obj and .mtl parsers
39
fn parse_vec2(tokens: &[&str]) -> Result<Vec2> {
25,962✔
40
    Ok(Vec2::new(
25,962✔
41
        parse_double(tokens[1])?,
25,962✔
42
        parse_double(tokens[2])?,
25,962✔
43
    ))
44
}
25,962✔
45

46
/// For .obj and .mtl parsers
47
fn parse_vec3(tokens: &[&str]) -> Result<Vec3> {
24,672✔
48
    Ok(Vec3::new(
24,672✔
49
        parse_double(tokens[1])?,
24,672✔
50
        parse_double(tokens[2])?,
24,672✔
51
        parse_double(tokens[3])?,
24,672✔
52
    ))
53
}
24,672✔
54

55
/// For .obj and .mtl parsers
56
fn parse_idx(token: &str, vec_len: usize) -> Result<usize> {
193,440✔
57
    token
193,440✔
58
        .parse::<i32>()
193,440✔
59
        .map(|idx| {
193,440✔
60
            if idx > 0 {
193,440✔
61
                (idx - 1) as usize
193,440✔
62
            } else {
63
                (vec_len as i32 + idx) as usize
×
64
            }
65
        })
193,440✔
66
        .map_err(|_| obj_error("Could not parse index in file"))
193,440✔
67
}
193,440✔
68

69
/* these thigs below could be optimized alot...
70
 * but its boring work for little? gain */
71

72
fn _get_url(url: &str) -> Result<Vec<u8>> {
4✔
73
    let mut bytes = Vec::new();
4✔
74

4✔
75
    ureq::get(url)
4✔
76
        .call()
4✔
77
        .map_err(|_| obj_error("Error during HTTP, error parsing not implemented"))?
4✔
78
        .into_reader()
4✔
79
        .read_to_end(&mut bytes)?;
4✔
80

81
    Ok(bytes)
4✔
82
}
4✔
83

84
/// Extracts file matching `re` from zip file in `bytes`
85
fn _extract_zip(bytes: Vec<u8>, re: Regex) -> Result<Vec<u8>> {
3✔
86
    println!("Reading .zip");
3✔
87
    let mut zip = ZipArchive::new(Cursor::new(bytes))?;
3✔
88
    let mut data = Vec::new();
3✔
89

90
    for i in 0..zip.len() {
9✔
91
        let mut file = zip.by_index(i)?;
9✔
92

93
        if re.is_match(file.name()) {
9✔
94
            println!("Extracting \"{}\"", file.name());
3✔
95
            file.read_to_end(&mut data)?;
3✔
96
            break;
3✔
97
        }
6✔
98
    }
99

100
    if data.is_empty() {
3✔
101
        Err(obj_error("Could not find file in the archive"))
×
102
    } else {
103
        Ok(data)
3✔
104
    }
105
}
3✔
106

107
/// Maps `Vec<u8>` to `File`
108
fn _bytes_to_file(bytes: Vec<u8>) -> Result<File> {
4✔
109
    let mut tmp_file = tempfile::tempfile()?;
4✔
110

111
    tmp_file.write_all(&bytes)?;
4✔
112
    tmp_file.rewind()?;
4✔
113

114
    Ok(tmp_file)
4✔
115
}
4✔
116

117
/// Loads `tex_name` from `zip` to an `Image`
118
fn _img_from_zip(zip: Vec<u8>, tex_name: &str) -> Result<Image> {
×
119
    let file_bytes = _extract_zip(zip, Regex::new(tex_name).unwrap())?;
×
120
    let file = _bytes_to_file(file_bytes)?;
×
121

122
    println!("Decoding texture");
×
123
    Image::from_file(file)
×
124
        .map_err(|decode_error| obj_error(&decode_error.to_string()))
×
125
}
×
126

127
/// Loads a .OBJ file at the given path
128
pub fn mesh_from_path(path: &str, material: Material) -> Result<Mesh> {
×
129
    println!("Loading .OBJ file \"{}\"", path);
×
130
    obj::load_file(File::open(path)?, material)
×
131
}
×
132

133
/// Loads .OBJ file from resource at an URL. Supports direct .OBJ files and
134
/// .OBJ files within a zip archive.
135
pub fn mesh_from_url(url: &str, material: Material) -> Result<Mesh> {
4✔
136
    println!("Loading .OBJ from \"{}\"", url);
4✔
137
    let mut bytes = _get_url(url)?;
4✔
138

139
    if url.ends_with(".zip") {
4✔
140
        println!("Found zip archive, searching for .OBJ files");
3✔
141
        bytes = _extract_zip(bytes, Regex::new(r".+\.obj$").unwrap())?;
3✔
142
    } else if !url.ends_with(".obj") {
1✔
143
        return Err(obj_error(
×
144
            "Bad URL, or at least does not end with .zip or .obj",
×
145
        ));
×
146
    }
1✔
147

148
    let obj_file = _bytes_to_file(bytes)?;
4✔
149
    obj::load_file(obj_file, material)
4✔
150
}
4✔
151

152
/// Loads `tex_name` from .zip at `url`
153
pub fn texture_from_url(url: &str, tex_name: &str) -> Result<Image> {
×
154
    if !tex_name.ends_with(".png") {
×
155
        return Err(obj_error("Can only load .png files"));
×
156
    }
×
157
    if !url.ends_with(".zip") {
×
158
        return Err(obj_error("Can only extract textures from zip archives"));
×
159
    }
×
160

×
161
    println!("Loading texture \"{}\" from \"{}\"", tex_name, url);
×
162

163
    let resp = _get_url(url)?;
×
164

165
    _img_from_zip(resp, tex_name)
×
166
}
×
167

168
/// Parses a whole scene from a .obj file specified by `name`
169
/// in a .zip archive at `url`
170
#[allow(clippy::single_match)]
171
pub fn scene_from_url(url: &str, obj_name: &str) -> Result<Scene> {
×
172
    if !url.ends_with(".zip") {
×
173
        return Err(obj_error("Can only load scenes from .zip"));
×
174
    }
×
175
    if !obj_name.ends_with(".obj") {
×
176
        return Err(obj_error("Can only parse .obj files"));
×
177
    }
×
178

×
179
    println!("Loading scene \"{}\" from \"{}\"", obj_name, url);
×
180

181
    let resp = _get_url(url)?;
×
182

183
    let obj_bytes = _extract_zip(resp.clone(), Regex::new(obj_name).unwrap())?;
×
184

185
    let obj_file = _bytes_to_file(obj_bytes.clone())?;
×
186

187
    // parse materials first
188
    let mut materials = HashMap::<String, MtlConfig>::new();
×
189
    let reader = BufReader::new(obj_file);
×
190
    for line in reader.lines() {
×
191
        let line = line?.trim().to_string();
×
192
        if line.starts_with('#') || line.is_empty() {
×
193
            continue;
×
194
        }
×
195
        let tokens: Vec<&str> = line.split_ascii_whitespace().collect();
×
196

×
197
        match tokens[0] {
×
198
            "mtllib" => {
×
199
                let mtllib_name = tokens[1];
×
200
                let mtl_bytes = _extract_zip(resp.clone(), Regex::new(mtllib_name).unwrap())?;
×
201
                let mtl_file = _bytes_to_file(mtl_bytes)?;
×
202

203
                mtl::load_file(mtl_file, Some(resp.clone()), &mut materials)?;
×
204
            }
205
            _ => (),
×
206
        }
207
    }
208

209
    let obj_file = _bytes_to_file(obj_bytes)?;
×
210

211
    obj::load_scene(obj_file, materials)
×
212
}
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc