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

ekarpp / lumo / 5843175784

pending completion
5843175784

push

github

web-flow
0.3.0 (#26)

* add BDPT draft

* refactor hit to contain material instead of object. how to assert that correct materials in scene add methods?

* make BDPT "work"

* add comment to BDPT

* add thin lens approximation for depth of field to cameras

* refactor BDPT

* connect all paths in BDPT

* add light source sampling to BDPT

* add handling of full camera paths to BDPT

* fix rays from camera straight to light in BDPT

* add MIS skeleton to BDPT

* use shading normals in geometry term of BDPT

* fix vertex handling in walk of BDPT

* fix borrow checker issues in BDPT

* fixes light sampling in BDPT

* tune cornell box

* change box image dimensions

* comment current status of BDPT

* add untested .mtl parser

* add parsing multiple meshes from one .obj file

* add test for planar kdtrees. does not pass

* first draft of watertight triangle intersection

* fixes triangle intersection

* add permutations to avoid division by zero in triangle intersection

* compute error bounds for triangles and spheres

* comment triangle intersection

* flatten meshes in kdtree tests and teapot

* make cones aligned with y axis

* fix cone texture mapping

* add cone tests

* make cylinder base at y=0

* make cone texture mapping like in cylinder

* add cylinder tests

* add assertions to cone constructor

* add reprojection to cylinder hit

* fix error bounds in AABB

* add quadratic solving to efloat

* add error bounds to cylinder

* add epsilon to t check in triangle hit

* fix offset update in hit generate ray

* add 'proper' error bounds to hits

* make diffuse work properly on backfaces

* tuning examples

* fixes to tests and cylinder in nefe

* fix self intersection test in triangle

* add triangle behind hit test. cleanup cylinder tests

* use t_min instead of t_start in recursive kdtree subtree hits

* add floating point error bound checks to triangle hit

*... (continued)

2049 of 2049 new or added lines in 45 files covered. (100.0%)

2147 of 4336 relevant lines covered (49.52%)

7281164.49 hits per line

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

54.48
/src/parser/obj.rs
1
use super::*;
2

3
/// https://github.com/ekzhang/rpt/blob/master/src/io.rs
4
/// https://www.cs.cmu.edu/~mbz/personal/graphics/obj.html
5
pub fn load_file(file: File, material: Material) -> Result<Mesh> {
4✔
6
    let mut vertices: Vec<DVec3> = Vec::new();
4✔
7
    let mut normals: Vec<DVec3> = Vec::new();
4✔
8
    let mut uvs: Vec<DVec2> = Vec::new();
4✔
9
    let mut faces: Vec<Face> = Vec::new();
4✔
10

4✔
11
    let reader = BufReader::new(file);
4✔
12
    for line in reader.lines() {
75,120✔
13
        let line = line?.trim().to_string();
75,120✔
14
        if line.starts_with('#') || line.is_empty() {
75,120✔
15
            continue;
65✔
16
        }
75,055✔
17
        let tokens: Vec<&str> = line.split_ascii_whitespace().collect();
75,055✔
18

75,055✔
19
        parse_tokens(tokens, &mut vertices, &mut normals, &mut uvs, &mut faces)?;
75,055✔
20

21
                /*
22
                if !triangles.is_empty() {
23
                    meshes.push(triangles);
24
                    triangles = Vec::new();
25
                }
26
                 */
27
    }
28

29
    Ok(TriangleMesh::new(vertices, faces, normals, uvs, material))
4✔
30
}
4✔
31

32

33
pub fn load_scene(file: File, materials: HashMap<String, MtlConfig>) -> Result<Scene> {
×
34
    let mut scene = Scene::default();
×
35
    let mut vertices: Vec<DVec3> = Vec::new();
×
36
    let mut normals: Vec<DVec3> = Vec::new();
×
37
    let mut uvs: Vec<DVec2> = Vec::new();
×
38
    let mut faces: Vec<Face> = Vec::new();
×
39
    let mut meshes: Vec<(Vec<Face>, Material)> = Vec::new();
×
40
    let mut material = Material::Blank;
×
41

×
42
    let reader = BufReader::new(file);
×
43
    for line in reader.lines() {
×
44
        let line = line?.trim().to_string();
×
45
        if line.starts_with('#') || line.is_empty() {
×
46
            continue;
×
47
        }
×
48
        let tokens: Vec<&str> = line.split_ascii_whitespace().collect();
×
49

×
50
        match tokens[0] {
×
51
            "g" => {
×
52
                if !faces.is_empty() {
×
53
                    meshes.push((faces, material));
×
54
                    faces = Vec::new();
×
55
                    material = Material::Blank;
×
56
                }
×
57
            }
58
            "usemtl" => {
×
59
                match materials.get(tokens[1]) {
×
60
                    None => {
61
                        return Err(obj_error(
×
62
                            &format!("Could not find material {}", tokens[1])
×
63
                        ));
×
64
                    }
65
                    Some(mtl_cfg) => material = mtl_cfg.build_material(),
×
66
                }
67
            }
68
            _ => {
69
                parse_tokens(
×
70
                    tokens,
×
71
                    &mut vertices,
×
72
                    &mut normals,
×
73
                    &mut uvs,
×
74
                    &mut faces
×
75
                )?
×
76
            }
77
        }
78
    }
79

80
    meshes.push((faces, material));
×
81

×
82
    let triangle_mesh = Arc::new(TriangleMesh {
×
83
        vertices,
×
84
        normals,
×
85
        uvs,
×
86
    });
×
87

88
    for (faces, mtl) in meshes {
×
89
        let is_light = matches!(mtl, Material::Light(_));
×
90
        let object = Box::new(TriangleMesh::new_from_faces(
×
91
            triangle_mesh.clone(),
×
92
            faces,
×
93
            mtl,
×
94
        ));
×
95

×
96
        if is_light {
×
97
            scene.add_light(object);
×
98
        } else {
×
99
            scene.add(object);
×
100
        }
×
101
    }
102

103
    Ok(scene)
×
104
}
×
105

106
fn parse_tokens(
75,055✔
107
    tokens: Vec<&str>,
75,055✔
108
    vertices: &mut Vec<DVec3>,
75,055✔
109
    normals: &mut Vec<DVec3>,
75,055✔
110
    uvs: &mut Vec<DVec2>,
75,055✔
111
    faces: &mut Vec<Face>,
75,055✔
112
) -> Result<()> {
75,055✔
113
    match tokens[0] {
75,055✔
114
        "v" => {
75,055✔
115
            let vertex = parse_vec3(&tokens)?;
23,712✔
116
            vertices.push(vertex);
23,712✔
117
        }
118
        "vn" => {
51,343✔
119
            let normal = parse_vec3(&tokens)?;
960✔
120
            normals.push(normal);
960✔
121
        }
122
        "vt" => {
50,383✔
123
            let uv = parse_vec2(&tokens)?;
25,962✔
124
            uvs.push(uv);
25,962✔
125
        }
126
        "f" => {
24,421✔
127
            let face = parse_face(&tokens, vertices, normals, uvs)?;
24,404✔
128
            faces.extend(face);
24,404✔
129
        }
130
        _ => (),
17✔
131
    }
132
    Ok(())
75,055✔
133
}
75,055✔
134

135
/// Parses a face from a .obj file
136
fn parse_face(
24,404✔
137
    tokens: &[&str],
24,404✔
138
    vertices: &[DVec3],
24,404✔
139
    normals: &[DVec3],
24,404✔
140
    uvs: &[DVec2],
24,404✔
141
) -> Result<Vec<Face>> {
24,404✔
142
    let mut vidxs: Vec<usize> = Vec::new();
24,404✔
143
    let mut nidxs: Vec<usize> = Vec::new();
24,404✔
144
    let mut tidxs: Vec<usize> = Vec::new();
24,404✔
145

146
    for token in &tokens[1..] {
96,240✔
147
        let arguments: Vec<&str> = token.split('/').collect();
96,240✔
148

149
        let vidx = parse_idx(arguments[0], vertices.len())?;
96,240✔
150
        vidxs.push(vidx);
96,240✔
151

96,240✔
152
        if arguments.len() > 1 && !arguments[1].is_empty() {
96,240✔
153
            let tidx = parse_idx(arguments[1], uvs.len())?;
96,240✔
154
            tidxs.push(tidx);
96,240✔
155
        }
×
156

157
        if arguments.len() > 2 {
96,240✔
158
            let nidx = parse_idx(arguments[2], normals.len())?;
960✔
159
            nidxs.push(nidx);
960✔
160
        }
95,280✔
161
    }
162

163
    let mut faces: Vec<Face> = Vec::new();
24,404✔
164

165
    for i in 1..vidxs.len() - 1 {
47,432✔
166
        let (a, b, c) = (0, i, i + 1);
47,432✔
167
        let vidx = vec![vidxs[a], vidxs[b], vidxs[c]];
47,432✔
168

169
        let nidx = if nidxs.is_empty() {
47,432✔
170
            Vec::new()
47,112✔
171
        } else {
172
            vec![nidxs[a], nidxs[b], nidxs[c]]
320✔
173
        };
174

175
        let tidx = if tidxs.is_empty() {
47,432✔
176
            Vec::new()
×
177
        } else {
178
            vec![tidxs[a], tidxs[b], tidxs[c]]
47,432✔
179
        };
180

181
        faces.push(Face::new(vidx, nidx, tidx));
47,432✔
182
    }
183

184
    Ok(faces)
24,404✔
185
}
24,404✔
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