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

pmp-library / pmp-library / 21355628778

26 Jan 2026 11:12AM UTC coverage: 90.99% (-0.03%) from 91.017%
21355628778

push

github

mbotsch
Merge branch 'main' of github.com:pmp-library/pmp-library

5241 of 5760 relevant lines covered (90.99%)

642838.37 hits per line

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

70.24
/src/pmp/io/read_obj.cpp
1
// Copyright 2011-2022 the Polygon Mesh Processing Library developers.
2
// SPDX-License-Identifier: MIT
3

4
#include "pmp/io/read_obj.h"
5
#include "pmp/exceptions.h"
6

7
namespace pmp {
8

9
void read_obj(SurfaceMesh& mesh, const std::filesystem::path& file)
1✔
10
{
11
    std::array<char, 200> s;
12
    float x, y, z, r, g, b;
13
    std::vector<Vertex> vertices;
1✔
14
    std::vector<TexCoord> all_tex_coords; //individual texture coordinates
1✔
15
    std::vector<int>
16
        halfedge_tex_idx; //texture coordinates sorted for halfedges
1✔
17
    HalfedgeProperty<TexCoord> tex_coords =
18
        mesh.halfedge_property<TexCoord>("h:tex");
3✔
19
    bool with_tex_coord = false;
1✔
20
    VertexProperty<Color> colors;
1✔
21

22
    // open file (in ASCII mode)
23
    FILE* in = fopen(file.string().c_str(), "r");
1✔
24
    if (!in)
1✔
25
        throw IOException("Failed to open file: " + file.string());
×
26

27
    // clear line once
28
    memset(s.data(), 0, 200);
1✔
29

30
    // parse line by line (currently only supports vertex positions & faces
31
    while (in && !feof(in) && fgets(s.data(), 200, in))
12✔
32
    {
33
        // comment
34
        if (s[0] == '#' || isspace(s[0]))
5✔
35
            continue;
1✔
36

37
        // vertex
38
        else if (strncmp(s.data(), "v ", 2) == 0)
4✔
39
        {
40
            int n = sscanf(s.data(), "v %f %f %f %f %f %f", &x, &y, &z, &r, &g, &b);
3✔
41
            if (n >= 3)
3✔
42
            {
43
                Vertex v = mesh.add_vertex(Point(x, y, z));
3✔
44
                if (n >= 6)
3✔
45
                {
46
                    if (!colors) colors = mesh.vertex_property<Color>("v:color");
×
47
                    colors[v] = Color(r,g,b);
×
48
                }
49
            }
50
        }
51

52
        // normal
53
        else if (strncmp(s.data(), "vn ", 3) == 0)
1✔
54
        {
55
            if (sscanf(s.data(), "vn %f %f %f", &x, &y, &z))
×
56
            {
57
                // problematic as it can be either a vertex property when interpolated
58
                // or a halfedge property for hard edges
59
            }
60
        }
61

62
        // texture coordinate
63
        else if (strncmp(s.data(), "vt ", 3) == 0)
1✔
64
        {
65
            if (sscanf(s.data(), "vt %f %f", &x, &y))
×
66
            {
67
                all_tex_coords.emplace_back(x, y);
×
68
            }
69
        }
70

71
        // face
72
        else if (strncmp(s.data(), "f ", 2) == 0)
1✔
73
        {
74
            int component(0);
1✔
75
            bool end_of_vertex(false);
1✔
76
            char *p0, *p1(s.data() + 1);
1✔
77

78
            vertices.clear();
1✔
79
            halfedge_tex_idx.clear();
1✔
80

81
            // skip white-spaces
82
            while (*p1 == ' ')
2✔
83
                ++p1;
1✔
84

85
            while (p1)
4✔
86
            {
87
                p0 = p1;
3✔
88

89
                // overwrite next separator
90

91
                // skip '/', '\n', ' ', '\0', '\r' <-- don't forget Windows
92
                while (*p1 != '/' && *p1 != '\r' && *p1 != '\n' && *p1 != ' ' &&
6✔
93
                       *p1 != '\0')
3✔
94
                    ++p1;
3✔
95

96
                // detect end of vertex
97
                if (*p1 != '/')
3✔
98
                {
99
                    end_of_vertex = true;
3✔
100
                }
101

102
                // replace separator by '\0'
103
                if (*p1 != '\0')
3✔
104
                {
105
                    *p1 = '\0';
3✔
106
                    p1++; // point to next token
3✔
107
                }
108

109
                // detect end of line and break
110
                if (*p1 == '\0' || *p1 == '\n')
3✔
111
                {
112
                    p1 = nullptr;
1✔
113
                }
114

115
                // read next vertex component
116
                if (*p0 != '\0')
3✔
117
                {
118
                    switch (component)
3✔
119
                    {
120
                        case 0: // vertex
3✔
121
                        {
122
                            int idx = atoi(p0);
3✔
123
                            if (idx < 0)
3✔
124
                                idx = mesh.n_vertices() + idx + 1;
×
125
                            vertices.emplace_back(idx - 1);
3✔
126
                            break;
3✔
127
                        }
128
                        case 1: // texture coord
×
129
                        {
130
                            const int idx = atoi(p0) - 1;
×
131
                            halfedge_tex_idx.push_back(idx);
×
132
                            with_tex_coord = true;
×
133
                            break;
×
134
                        }
135
                        case 2: // normal
×
136
                            break;
×
137
                    }
138
                }
139

140
                ++component;
3✔
141

142
                if (end_of_vertex)
3✔
143
                {
144
                    component = 0;
3✔
145
                    end_of_vertex = false;
3✔
146
                }
147
            }
148

149
            Face f;
1✔
150
            try
151
            {
152
                f = mesh.add_face(vertices);
1✔
153
            }
154
            catch (const TopologyException& e)
×
155
            {
156
                std::cerr << e.what();
×
157
            }
×
158

159
            // add texture coordinates
160
            if (with_tex_coord && f.is_valid())
1✔
161
            {
162
                auto h_fit = mesh.halfedges(f);
×
163
                auto h_end = h_fit;
×
164
                unsigned v_idx = 0;
×
165
                do
166
                {
167
                    tex_coords[*h_fit] =
×
168
                        all_tex_coords.at(halfedge_tex_idx.at(v_idx));
×
169
                    ++v_idx;
×
170
                    ++h_fit;
×
171
                } while (h_fit != h_end);
×
172
            }
173
        }
174
        // clear line
175
        memset(s.data(), 0, 200);
4✔
176
    }
177

178
    // if there are no textures, delete texture property!
179
    if (!with_tex_coord)
1✔
180
    {
181
        mesh.remove_halfedge_property(tex_coords);
1✔
182
    }
183

184
    fclose(in);
1✔
185
}
1✔
186

187
} // namespace pmp
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