PolyFEM
Loading...
Searching...
No Matches
OBJReader.cpp
Go to the documentation of this file.
1// Modified version of read_obj from libigl to include reading polyline elements
2// as edges.
3//
4// Copyright (C) 2013 Alec Jacobson <alecjacobson@gmail.com>
5//
6// This Source Code Form is subject to the terms of the Mozilla Public License
7// v. 2.0. If a copy of the MPL was not distributed with this file, You can
8// obtain one at http://mozilla.org/MPL/2.0/.
9
10#include "OBJReader.hpp"
11
12#include <cstdio>
13#include <fstream>
14#include <iterator>
15
16#include <igl/edges.h>
17#include <igl/list_to_matrix.h>
18
20
21namespace polyfem::io
22{
23 namespace
24 {
25 std::string remove_newline(std::string s)
26 {
27 s.erase(std::remove(s.begin(), s.end(), '\n'), s.end());
28 return s;
29 }
30 } // namespace
31
33 const std::string obj_file_name,
34 std::vector<std::vector<double>> &V,
35 std::vector<std::vector<double>> &TC,
36 std::vector<std::vector<double>> &N,
37 std::vector<std::vector<int>> &F,
38 std::vector<std::vector<int>> &FTC,
39 std::vector<std::vector<int>> &FN,
40 std::vector<std::vector<int>> &L)
41 {
42 // Open file, and check for error
43 FILE *obj_file = fopen(obj_file_name.c_str(), "r");
44 if (obj_file == NULL)
45 {
46 logger().error("OBJReader::read: {:s} could not be opened!", obj_file_name);
47 return false;
48 }
49 return read(obj_file, V, TC, N, F, FTC, FN, L);
50 }
51
53 FILE *obj_file,
54 std::vector<std::vector<double>> &V,
55 std::vector<std::vector<double>> &TC,
56 std::vector<std::vector<double>> &N,
57 std::vector<std::vector<int>> &F,
58 std::vector<std::vector<int>> &FTC,
59 std::vector<std::vector<int>> &FN,
60 std::vector<std::vector<int>> &L)
61 {
62 // File open was successful so clear outputs
63 V.clear();
64 TC.clear();
65 N.clear();
66 F.clear();
67 FTC.clear();
68 FN.clear();
69 L.clear();
70
71 // variables and constants to assist parsing the .obj file
72 // Constant strings to compare against
73 std::string v("v");
74 std::string vn("vn");
75 std::string vt("vt");
76 std::string f("f");
77 std::string l("l");
78 std::string tic_tac_toe("#");
79
80 const int LINE_MAX_LEN = 2048;
81
82 char line[LINE_MAX_LEN];
83 int line_no = 1;
84 while (fgets(line, LINE_MAX_LEN, obj_file) != NULL)
85 {
86 char type[LINE_MAX_LEN];
87 // Read first word containing type
88 if (sscanf(line, "%s", type) == 1)
89 {
90 // Get pointer to rest of line right after type
91 char *rest_of_line = &line[strlen(type)];
92 if (type == v)
93 {
94 std::istringstream ls(&line[1]);
95 std::vector<double> vertex{std::istream_iterator<double>(ls),
96 std::istream_iterator<double>()};
97
98 // if (vertex.size() < 3) {
99 // logger().error(
100 // "OBJReader::read: vertex on line {:d} should have at "
101 // "least 3 coordinates",
102 // line_no);
103 // fclose(obj_file);
104 // return false;
105 // }
106
107 V.push_back(vertex);
108 }
109 else if (type == vn)
110 {
111 double x[3];
112 int count =
113 sscanf(rest_of_line, "%lf %lf %lf\n", &x[0], &x[1], &x[2]);
114 if (count != 3)
115 {
116 logger().error(
117 "OBJReader::read: normal on line {:d} should have 3 "
118 "coordinates",
119 line_no);
120 fclose(obj_file);
121 return false;
122 }
123 std::vector<double> normal(count);
124 for (int i = 0; i < count; i++)
125 {
126 normal[i] = x[i];
127 }
128 N.push_back(normal);
129 }
130 else if (type == vt)
131 {
132 double x[3];
133 int count =
134 sscanf(rest_of_line, "%lf %lf %lf\n", &x[0], &x[1], &x[2]);
135 if (count != 2 && count != 3)
136 {
137 logger().error(
138 "OBJReader::read: texture coords on line {:d} should have "
139 "2 or 3 coordinates (has {:d})",
140 line_no, count);
141 fclose(obj_file);
142 return false;
143 }
144 std::vector<double> tex(count);
145 for (int i = 0; i < count; i++)
146 {
147 tex[i] = x[i];
148 }
149 TC.push_back(tex);
150 }
151 else if (type == f)
152 {
153 const auto &shift = [&V](const int i) -> int {
154 return i < 0 ? i + V.size() : i - 1;
155 };
156 const auto &shift_t = [&TC](const int i) -> int {
157 return i < 0 ? i + TC.size() : i - 1;
158 };
159 const auto &shift_n = [&N](const int i) -> int {
160 return i < 0 ? i + N.size() : i - 1;
161 };
162 std::vector<int> f;
163 std::vector<int> ftc;
164 std::vector<int> fn;
165 // Read each "word" after type
166 char word[LINE_MAX_LEN];
167 int offset;
168 while (sscanf(rest_of_line, "%s%n", word, &offset) == 1)
169 {
170 // adjust offset
171 rest_of_line += offset;
172 // Process word
173 long int i, it, in;
174 if (sscanf(word, "%ld/%ld/%ld", &i, &it, &in) == 3)
175 {
176 f.push_back(shift(i));
177 ftc.push_back(shift_t(it));
178 fn.push_back(shift_n(in));
179 }
180 else if (sscanf(word, "%ld/%ld", &i, &it) == 2)
181 {
182 f.push_back(shift(i));
183 ftc.push_back(shift_t(it));
184 }
185 else if (sscanf(word, "%ld//%ld", &i, &in) == 2)
186 {
187 f.push_back(shift(i));
188 fn.push_back(shift_n(in));
189 }
190 else if (sscanf(word, "%ld", &i) == 1)
191 {
192 f.push_back(shift(i));
193 }
194 else
195 {
196 logger().error(
197 "OBJReader::read: face on line {:d} has invalid "
198 "element format",
199 line_no);
200 fclose(obj_file);
201 return false;
202 }
203 }
204 if ((f.size() > 0 && fn.size() == 0 && ftc.size() == 0)
205 || (f.size() > 0 && fn.size() == f.size()
206 && ftc.size() == 0)
207 || (f.size() > 0 && fn.size() == 0
208 && ftc.size() == f.size())
209 || (f.size() > 0 && fn.size() == f.size()
210 && ftc.size() == f.size()))
211 {
212 // No matter what add each type to lists so that lists
213 // are the correct lengths
214 F.push_back(f);
215 FTC.push_back(ftc);
216 FN.push_back(fn);
217 }
218 else
219 {
220 logger().error(
221 "OBJReader::read: face on line {:d} has invalid format",
222 line_no);
223 fclose(obj_file);
224 return false;
225 }
226 }
227 else if (type == l)
228 {
229 std::istringstream ls(&line[1]);
230 std::vector<int> polyline{std::istream_iterator<int>(ls),
231 std::istream_iterator<int>()};
232
233 if (polyline.size() < 2)
234 {
235 logger().error(
236 "OBJReader::read: line element on line {:d} should have "
237 "at least 2 vertices",
238 line_no);
239 fclose(obj_file);
240 return false;
241 }
242
243 for (int i = 0; i < polyline.size(); i++)
244 {
245 polyline[i] = polyline[i] < 0 ? polyline[i] + V.size()
246 : polyline[i] - 1;
247 }
248
249 L.push_back(polyline);
250 }
251 else if (
252 strlen(type) >= 1
253 && (type[0] == '#' || type[0] == 'g' || type[0] == 's'
254 || strcmp("usemtl", type) == 0
255 || strcmp("mtllib", type) == 0))
256 {
257 // ignore comments or other stuff
258 }
259 else
260 {
261 // ignore any other lines
262 std::string line_no_newline = remove_newline(line);
263 logger().warn(
264 "OBJReader::read: ignored non-comment line {:d}: {:s}", line_no,
265 line_no_newline);
266 }
267 }
268 else
269 {
270 // ignore empty line
271 }
272 line_no++;
273 }
274 fclose(obj_file);
275
276 assert(F.size() == FN.size());
277 assert(F.size() == FTC.size());
278
279 return true;
280 }
281
283 const std::string obj_file_name,
284 std::vector<std::vector<double>> &V,
285 std::vector<std::vector<int>> &F,
286 std::vector<std::vector<int>> &L)
287 {
288 std::vector<std::vector<double>> TC, N;
289 std::vector<std::vector<int>> FTC, FN;
290 return read(obj_file_name, V, TC, N, F, FTC, FN, L);
291 }
292
294 const std::string str,
295 Eigen::MatrixXd &V,
296 Eigen::MatrixXi &E,
297 Eigen::MatrixXi &F)
298 {
299 std::vector<std::vector<double>> vV, vTC, vN;
300 std::vector<std::vector<int>> vF, vFTC, vFN, vL;
301 bool success = read(str, vV, vTC, vN, vF, vFTC, vFN, vL);
302 if (!success)
303 {
304 // read(str,vV,vTC,vN,vF,vFTC,vFN) should have already printed
305 // an error message
306 return false;
307 }
308 bool V_rect = igl::list_to_matrix(vV, V);
309 if (!V_rect)
310 {
311 // igl::list_to_matrix(vV,V) already printed error message
312 return false;
313 }
314 bool F_rect = igl::list_to_matrix(vF, F);
315 if (!F_rect)
316 {
317 // igl::list_to_matrix(vF,F) already printed error message
318 return false;
319 }
320 std::vector<std::vector<int>> vE;
321 for (const std::vector<int> &polyline : vL)
322 {
323 for (int i = 1; i < polyline.size(); i++)
324 {
325 vE.push_back({{polyline[i - 1], polyline[i]}});
326 }
327 }
328 bool E_rect = igl::list_to_matrix(vE, E);
329 if (!E_rect)
330 {
331 logger().error("OBJReader::read: edges not rectangular matrix!");
332 return false;
333 }
334 // if (F.size())
335 // {
336 // Eigen::MatrixXi faceE;
337 // igl::edges(F, faceE);
338 // E.conservativeResize(E.rows() + faceE.rows(), 2);
339 // E.bottomRows(faceE.rows()) = faceE;
340 // }
341
342 return true;
343 }
344} // namespace polyfem::io
int V
int x
static bool read(const std::string obj_file_name, std::vector< std::vector< double > > &V, std::vector< std::vector< double > > &TC, std::vector< std::vector< double > > &N, std::vector< std::vector< int > > &F, std::vector< std::vector< int > > &FTC, std::vector< std::vector< int > > &FN, std::vector< std::vector< int > > &L)
Read a mesh from an ascii obj file.
Definition OBJReader.cpp:32
spdlog::logger & logger()
Retrieves the current logger.
Definition Logger.cpp:42