Visual Computing Library  devel
Loading...
Searching...
No Matches
load.h
1/*****************************************************************************
2 * VCLib *
3 * Visual Computing Library *
4 * *
5 * Copyright(C) 2021-2025 *
6 * Visual Computing Lab *
7 * ISTI - Italian National Research Council *
8 * *
9 * All rights reserved. *
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the Mozilla Public License Version 2.0 as published *
13 * by the Mozilla Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 * This program is distributed in the hope that it will be useful, *
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
19 * Mozilla Public License Version 2.0 *
20 * (https://www.mozilla.org/en-US/MPL/2.0/) for more details. *
21 ****************************************************************************/
22
23#ifndef VCL_IO_MESH_OFF_LOAD_H
24#define VCL_IO_MESH_OFF_LOAD_H
25
26#include <vclib/io/file_info.h>
27#include <vclib/io/mesh/settings.h>
28#include <vclib/io/read.h>
29
30#include <vclib/algorithms/mesh.h>
31#include <vclib/space/complex.h>
32
33namespace vcl {
34
35namespace detail {
36
37constexpr float OFF_GEOMVIEW_COLOR_MAP[148][4] = {
38 {1.0f, 1.0f, 1.0f, 1.0f },
39 {1.0f, 1.0f, 1.0f, 1.0f },
40 {1.0f, 1.0f, 1.0f, 1.0f },
41 {1.0f, 1.0f, 1.0f, 1.0f },
42
43 {1.0f, 1.0f, 1.0f, 1.0f },
44 {1.0f, 1.0f, 1.0f, 1.0f },
45 {0.7f, 0.7f, 0.7f, 0.7f },
46 {0.2f, 0.2f, 0.2f, 0.2f },
47
48 {0.9f, 0.9f, 0.9f, 0.9f },
49 {0.1f, 0.1f, 0.1f, 0.1f },
50 {0.1f, 0.1f, 0.1f, 0.1f },
51 {0.8f, 0.8f, 0.8f, 0.8f },
52
53 {0.7f, 0.7f, 0.7f, 0.7f },
54 {0.7f, 0.7f, 0.7f, 0.7f },
55 {0.0f, 0.0f, 0.0f, 0.0f },
56 {0.9f, 0.9f, 0.9f, 0.9f },
57
58 {0.2f, 0.2f, 0.2f, 0.2f },
59 {0.0f, 0.0f, 0.0f, 0.0f },
60 {0.75f, 0.75f, 0.75f, 0.75f},
61 {0.8f, 0.8f, 0.8f, 0.8f },
62
63 {0.8f, 0.8f, 0.8f, 0.8f },
64 {0.0f, 0.0f, 0.0f, 0.0f },
65 {0.0f, 0.0f, 0.0f, 0.0f },
66 {0.0f, 0.0f, 0.0f, 0.0f },
67
68 {0.0f, 0.0f, 0.0f, 0.0f },
69 {0.4f, 0.4f, 0.4f, 0.4f },
70 {0.4f, 0.4f, 0.4f, 0.4f },
71 {0.8f, 0.8f, 0.8f, 0.8f },
72
73 {0.8f, 0.8f, 0.8f, 0.8f },
74 {0.7f, 0.7f, 0.7f, 0.7f },
75 {0.7f, 0.7f, 0.7f, 0.7f },
76 {0.7f, 0.7f, 0.7f, 0.7f },
77
78 {0.7f, 0.7f, 0.7f, 0.7f },
79 {0.0f, 0.0f, 0.0f, 0.0f },
80 {0.9f, 0.9f, 0.9f, 0.9f },
81 {0.0f, 0.0f, 0.0f, 0.0f },
82
83 {0.0f, 0.0f, 0.0f, 0.0f },
84 {0.75f, 0.75f, 0.75f, 0.75f},
85 {0.8f, 0.8f, 0.8f, 0.8f },
86 {0.4f, 0.4f, 0.4f, 0.4f },
87
88 {0.0f, 0.0f, 0.0f, 0.0f },
89 {0.0f, 0.0f, 0.0f, 0.0f },
90 {0.4f, 0.4f, 0.4f, 0.4f },
91 {0.8f, 0.8f, 0.8f, 0.8f },
92
93 {0.7f, 0.7f, 0.7f, 0.7f },
94 {0.7f, 0.7f, 0.7f, 0.7f },
95 {0.0f, 0.0f, 0.0f, 0.0f },
96 {0.9f, 0.9f, 0.9f, 0.9f },
97
98 {0.0f, 0.0f, 0.0f, 0.0f },
99 {0.0f, 0.0f, 0.0f, 0.0f },
100 {0.75f, 0.75f, 0.75f, 0.75f},
101 {0.8f, 0.8f, 0.8f, 0.8f },
102
103 {0.4f, 0.4f, 0.4f, 0.4f },
104 {0.0f, 0.0f, 0.0f, 0.0f },
105 {0.0f, 0.0f, 0.0f, 0.0f },
106 {0.4f, 0.4f, 0.4f, 0.4f },
107
108 {0.8f, 0.8f, 0.8f, 0.8f },
109 {0.7f, 0.7f, 0.7f, 0.7f },
110 {0.7f, 0.7f, 0.7f, 0.7f },
111 {0.0f, 0.0f, 0.0f, 0.0f },
112
113 {0.9f, 0.9f, 0.9f, 0.9f },
114 {0.0f, 0.0f, 0.0f, 0.0f },
115 {0.0f, 0.0f, 0.0f, 0.0f },
116 {0.75f, 0.75f, 0.75f, 0.75f},
117
118 {0.8f, 0.8f, 0.8f, 0.8f },
119 {0.4f, 0.4f, 0.4f, 0.4f },
120 {0.0f, 0.0f, 0.0f, 0.0f },
121 {0.0f, 0.0f, 0.0f, 0.0f },
122
123 {0.4f, 0.4f, 0.4f, 0.4f },
124 {0.8f, 0.8f, 0.8f, 0.8f },
125 {1.0f, 1.0f, 1.0f, 1.0f },
126 {1.0f, 1.0f, 1.0f, 1.0f },
127
128 {1.0f, 1.0f, 1.0f, 1.0f },
129 {1.0f, 1.0f, 1.0f, 1.0f },
130 {1.0f, 1.0f, 1.0f, 1.0f },
131 {1.0f, 1.0f, 1.0f, 1.0f },
132
133 {0.05f, 0.05f, 0.05f, 0.05f},
134 {0.7f, 0.7f, 0.7f, 0.7f },
135 {0.2f, 0.2f, 0.2f, 0.2f },
136 {0.9f, 0.9f, 0.9f, 0.9f },
137
138 {0.0f, 0.0f, 0.0f, 0.0f },
139 {0.1f, 0.1f, 0.1f, 0.1f },
140 {0.8f, 0.8f, 0.8f, 0.8f },
141 {0.7f, 0.7f, 0.7f, 0.7f },
142
143 {0.7f, 0.7f, 0.7f, 0.7f },
144 {0.7f, 0.7f, 0.7f, 0.7f },
145 {0.7f, 0.7f, 0.7f, 0.7f },
146 {0.0f, 0.0f, 0.0f, 0.0f },
147
148 {0.0f, 0.0f, 0.0f, 0.0f },
149 {0.9f, 0.9f, 0.9f, 0.9f },
150 {0.9f, 0.9f, 0.9f, 0.9f },
151 {0.0f, 0.0f, 0.0f, 0.0f },
152
153 {0.0f, 0.0f, 0.0f, 0.0f },
154 {0.0f, 0.0f, 0.0f, 0.0f },
155 {0.0f, 0.0f, 0.0f, 0.0f },
156 {0.75f, 0.75f, 0.75f, 0.75f},
157
158 {0.75f, 0.75f, 0.75f, 0.75f},
159 {0.8f, 0.8f, 0.8f, 0.8f },
160 {0.8f, 0.8f, 0.8f, 0.8f },
161 {0.0f, 0.0f, 0.0f, 0.0f },
162
163 {0.0f, 0.0f, 0.0f, 0.0f },
164 {0.0f, 0.0f, 0.0f, 0.0f },
165 {0.0f, 0.0f, 0.0f, 0.0f },
166 {0.4f, 0.4f, 0.4f, 0.4f },
167
168 {0.4f, 0.4f, 0.4f, 0.4f },
169 {0.8f, 0.8f, 0.8f, 0.8f },
170 {0.8f, 0.8f, 0.8f, 0.8f },
171 {0.7f, 0.7f, 0.7f, 0.7f },
172
173 {0.7f, 0.7f, 0.7f, 0.7f },
174 {0.7f, 0.7f, 0.7f, 0.7f },
175 {0.7f, 0.7f, 0.7f, 0.7f },
176 {0.0f, 0.0f, 0.0f, 0.0f },
177
178 {0.9f, 0.9f, 0.9f, 0.9f },
179 {0.0f, 0.0f, 0.0f, 0.0f },
180 {0.0f, 0.0f, 0.0f, 0.0f },
181 {0.75f, 0.75f, 0.75f, 0.75f},
182
183 {0.8f, 0.8f, 0.8f, 0.8f },
184 {0.4f, 0.4f, 0.4f, 0.4f },
185 {0.0f, 0.0f, 0.0f, 0.0f },
186 {0.0f, 0.0f, 0.0f, 0.0f },
187
188 {0.4f, 0.4f, 0.4f, 0.4f },
189 {0.8f, 0.8f, 0.8f, 0.8f },
190 {0.7f, 0.7f, 0.7f, 0.7f },
191 {0.7f, 0.7f, 0.7f, 0.7f },
192
193 {0.0f, 0.0f, 0.0f, 0.0f },
194 {0.9f, 0.9f, 0.9f, 0.9f },
195 {0.0f, 0.0f, 0.0f, 0.0f },
196 {0.0f, 0.0f, 0.0f, 0.0f },
197
198 {0.75f, 0.75f, 0.75f, 0.75f},
199 {0.8f, 0.8f, 0.8f, 0.8f },
200 {0.4f, 0.4f, 0.4f, 0.4f },
201 {0.0f, 0.0f, 0.0f, 0.0f },
202
203 {0.0f, 0.0f, 0.0f, 0.0f },
204 {0.4f, 0.4f, 0.4f, 0.4f },
205 {0.8f, 0.8f, 0.8f, 0.8f },
206 {0.7f, 0.7f, 0.7f, 0.7f },
207
208 {0.7f, 0.7f, 0.7f, 0.7f },
209 {0.0f, 0.0f, 0.0f, 0.0f },
210 {0.9f, 0.9f, 0.9f, 0.9f },
211 {0.0f, 0.0f, 0.0f, 0.0f },
212
213 {0.0f, 0.0f, 0.0f, 0.0f },
214 {0.75f, 0.75f, 0.75f, 0.75f},
215 {0.8f, 0.8f, 0.8f, 0.8f },
216 {0.4f, 0.4f, 0.4f, 0.4f },
217
218 {0.0f, 0.0f, 0.0f, 0.0f },
219 {0.0f, 0.0f, 0.0f, 0.0f },
220 {0.4f, 0.4f, 0.4f, 0.4f },
221 {0.8f, 0.8f, 0.8f, 0.8f }
222};
223
224inline void readOffHeader(
225 std::istream& file,
226 MeshInfo& fileInfo,
227 uint& nv,
228 uint& nf,
229 uint& ne)
230{
231 fileInfo.clear();
232 Tokenizer tokens = readAndTokenizeNextNonEmptyLine(file);
233 Tokenizer::iterator token = tokens.begin();
234 std::string header = *token;
235
236 // the OFF string is in the header go on parsing it.
237 if (header.rfind("OFF") != std::basic_string<char>::npos) {
238 for (int u = header.rfind("OFF") - 1; u >= 0; u--) {
239 if (header[u] == 'C')
240 fileInfo.setPerVertexColor();
241 else if (header[u] == 'N')
242 fileInfo.setPerVertexNormal();
243 else if (u > 0 && header[u - 1] == 'S' && header[u] == 'T')
244 fileInfo.setPerVertexTexCoord();
245 else if (header[u] == '4')
246 throw MalformedFileException(
247 "Unsupported Homogeneus components in OFF.");
248 else if (header[u] == 'n')
249 throw MalformedFileException("Unsupported High Dimension OFF.");
250 }
251 }
252 else
253 throw MalformedFileException("Missing OFF header in file.");
254
255 // If the file is slightly malformed and it has nvert and nface AFTER the
256 // OFF string instead of in the next line we manage it here...
257 if (tokens.size() == 1) {
258 tokens = readAndTokenizeNextNonEmptyLine(file);
259 token = tokens.begin();
260 }
261 else
262 ++token;
263
264 nv = io::readUInt<uint>(token);
265 nf = io::readUInt<uint>(token);
266 ne = io::readUInt<uint>(token);
267 if (nv > 0)
268 fileInfo.setVertices();
269 if (nf > 0)
270 fileInfo.setFaces();
271 // if (ne > 0)
272 // loadedInfo.setEdges();
273}
274
275inline Color readOffColor(Tokenizer::iterator& token, int nColorComponents)
276{
277 uint red, green, blue, alpha = 255;
278
279 if (nColorComponents == 1) {
280 uint k = io::readUInt<uint>(token);
281
282 red = OFF_GEOMVIEW_COLOR_MAP[k][0] * 255;
283 green = OFF_GEOMVIEW_COLOR_MAP[k][1] * 255;
284 blue = OFF_GEOMVIEW_COLOR_MAP[k][2] * 255;
285 alpha = OFF_GEOMVIEW_COLOR_MAP[k][3] * 255;
286 }
287 else {
288 double r = io::readDouble<double>(token);
289 double g = io::readDouble<double>(token);
290 double b = io::readDouble<double>(token);
291 double a = -1;
292 if (nColorComponents == 4) {
293 a = io::readDouble<double>(token);
294 }
295 if (r > 1 || g > 1 || b > 1) {
296 red = r;
297 green = g;
298 blue = b;
299 alpha = a != -1 ? a : alpha;
300 }
301 else {
302 red = r * 255;
303 green = g * 255;
304 blue = b * 255;
305 alpha = a != -1 ? a * 255 : alpha;
306 }
307 }
308 return Color(red, green, blue, alpha);
309}
310
311template<MeshConcept MeshType, LoggerConcept LogType>
312void readOffVertices(
313 MeshType& mesh,
314 std::istream& file,
315 const MeshInfo& fileInfo,
316 uint nv,
317 LogType& log)
318{
319 using VertexType = MeshType::VertexType;
320
321 const uint nTexCoords = fileInfo.hasPerVertexTexCoord() ? 2 : 0;
322
323 log.startProgress("Reading vertices", nv);
324 mesh.addVertices(nv);
325 for (uint i = 0; i < nv; i++) {
326 VertexType& v = mesh.vertex(i);
327
328 Tokenizer tokens = readAndTokenizeNextNonEmptyLine(file);
329 Tokenizer::iterator token = tokens.begin();
330
331 // Read 3 vertex coordinates
332 for (unsigned int j = 0; j < 3; j++) {
333 // Read vertex coordinate
334 v.position()[j] = io::readDouble<double>(token);
335 }
336
337 if constexpr (HasPerVertexNormal<MeshType>) {
338 if (isPerVertexNormalAvailable(mesh) &&
339 fileInfo.hasPerVertexNormal()) {
340 // Read 3 normal coordinates
341 for (unsigned int j = 0; j < 3; j++) {
342 v.normal()[j] = io::readDouble<double>(token);
343 }
344 }
345 }
346 // need to read and throw away data
347 else if (fileInfo.hasPerVertexNormal()) {
348 for (unsigned int j = 0; j < 3; j++) {
349 io::readDouble<double>(token);
350 }
351 }
352
353 const uint nReadComponents = token - tokens.begin();
354 const int nColorComponents =
355 (int) tokens.size() - nReadComponents - nTexCoords;
356
357 if constexpr (HasPerVertexColor<MeshType>) {
358 if (isPerVertexColorAvailable(mesh) &&
359 fileInfo.hasPerVertexColor()) {
360 if (nColorComponents != 1 && nColorComponents != 3 &&
361 nColorComponents != 4)
362 throw MalformedFileException(
363 "Wrong number of components in line.");
364 v.color() = readOffColor(token, nColorComponents);
365 }
366 }
367 // need to read and throw away data
368 else if (fileInfo.hasPerVertexColor()) {
369 if (nColorComponents != 1 && nColorComponents != 3 &&
370 nColorComponents != 4)
371 throw MalformedFileException(
372 "Wrong number of components in line.");
373 readOffColor(token, nColorComponents);
374 }
375
376 if constexpr (HasPerVertexTexCoord<MeshType>) {
377 if (isPerVertexTexCoordAvailable(mesh) &&
378 fileInfo.hasPerVertexTexCoord()) {
379 // Read 2 tex coordinates
380 for (unsigned int j = 0; j < 2; j++) {
381 v.texCoord()[j] = io::readDouble<double>(token);
382 }
383 }
384 }
385 // need to read and throw away data
386 else if (fileInfo.hasPerVertexTexCoord()) {
387 for (unsigned int j = 0; j < 2; j++) {
388 io::readDouble<double>(token);
389 }
390 }
391
392 log.progress(i);
393 }
394 log.endProgress();
395}
396
397template<FaceMeshConcept MeshType, LoggerConcept LogType>
398void readOffFaces(
399 MeshType& mesh,
400 std::istream& file,
401 MeshInfo& loadedInfo,
402 uint nf,
403 const LoadSettings& settings,
404 LogType& log)
405{
406 if constexpr (HasFaces<MeshType>) {
407 using FaceType = MeshType::FaceType;
408
409 mesh.reserveFaces(nf);
410 log.startProgress("Reading faces", nf);
411
412 for (uint fid = 0; fid < nf; ++fid) {
413 Tokenizer tokens = readAndTokenizeNextNonEmptyLine(file);
414 Tokenizer::iterator token = tokens.begin();
415 mesh.addFace();
416 FaceType& f = mesh.face(mesh.faceNumber() - 1);
417
418 // read vertex indices
419 uint fSize = io::readUInt<uint>(token);
420 loadedInfo.updateMeshType(fSize);
421 // contains the vertex ids of the actual face
422 std::vector<uint> vids;
423 vids.resize(fSize);
424 // read vertex indices
425 for (uint i = 0; i < fSize; ++i) {
426 vids[i] = io::readUInt<uint>(token);
427 }
428
429 // load vertex indices into face
430 bool splitFace = false;
431 // we have a polygonal mesh
432 if constexpr (FaceType::VERTEX_NUMBER < 0) {
433 // need to resize to the right number of verts
434 f.resizeVertices(vids.size());
435 }
436 else if (FaceType::VERTEX_NUMBER != vids.size()) {
437 // we have faces with static sizes (triangles), but we are
438 // loading faces with number of verts > 3. Need to split the
439 // face we are loading in n faces!
440 splitFace = true;
441 }
442 if (!splitFace) { // classic load, no split needed
443 for (uint i = 0; i < vids.size(); ++i) {
444 if (vids[i] >= mesh.vertexNumber()) {
445 throw MalformedFileException(
446 "Bad vertex index for face " + std::to_string(i));
447 }
448 f.setVertex(i, vids[i]);
449 }
450 }
451 else { // split needed
452 addTriangleFacesFromPolygon(mesh, f, vids);
453 }
454
455 // read face color
456 if (token != tokens.end()) { // there are colors to read
457 if constexpr (HasPerFaceColor<MeshType>) {
458 if (isPerFaceColorAvailable(mesh) ||
459 (settings.enableOptionalComponents &&
461 loadedInfo.setPerFaceColor();
462 f.color() = readOffColor(
463 token, tokens.size() - (token - tokens.begin()));
464 // in case the loaded polygon has been triangulated in
465 // the last n triangles
466 for (uint ff = mesh.index(f); ff < mesh.faceNumber();
467 ++ff) {
468 mesh.face(ff).color() = f.color();
469 }
470 }
471 }
472 }
473 log.progress(fid);
474 }
475 log.endProgress();
476 }
477 else { // mesh does not have face, read nf lines and throw them away
478 for (uint i = 0; i < nf; ++i)
479 readAndTokenizeNextNonEmptyLine(file);
480 }
481}
482
483} // namespace detail
484
509template<MeshConcept MeshType, LoggerConcept LogType = NullLogger>
511 MeshType& m,
512 std::istream& inputOffStream,
514 const LoadSettings& settings = LoadSettings(),
515 LogType& log = nullLogger)
516{
517 uint nVertices, nFaces, nEdges;
518
519 MeshInfo fileInfo; // data that needs to be read from the file
520
521 detail::readOffHeader(inputOffStream, fileInfo, nVertices, nFaces, nEdges);
522 loadedInfo = fileInfo; // data that will be stored in the mesh!
523 if (settings.enableOptionalComponents)
524 enableOptionalComponentsFromInfo(loadedInfo, m);
525
526 if (nVertices == 0) {
527 log.log("The file has no vertices", LogType::WARNING_LOG);
528 return;
529 }
530
531 int percVertices = nVertices / (nVertices + nFaces) * 100;
532 int percFaces = 100 - percVertices;
533
534 log.startNewTask(0, percVertices, "Reading vertices");
535 detail::readOffVertices(m, inputOffStream, fileInfo, nVertices, log);
536 log.endTask("Reading vertices");
537 if constexpr (HasFaces<MeshType>) {
538 log.startNewTask(percVertices, 100, "Reading faces");
539 detail::readOffFaces(
540 m, inputOffStream, fileInfo, nFaces, settings, log);
541 log.endTask("Reading faces");
542 }
543 else {
544 log.log(100, "Ignored faces reading");
545 }
546 if (settings.enableOptionalComponents)
548}
549
574template<MeshConcept MeshType, LoggerConcept LogType = NullLogger>
576 MeshType& m,
577 const std::string& filename,
579 const LoadSettings& settings = LoadSettings(),
580 LogType& log = nullLogger)
581{
582 std::ifstream file = openInputFileStream(filename);
583
584 if constexpr (HasName<MeshType>) {
586 }
587
588 loadOff(m, file, loadedInfo, settings, log);
589}
590
591} // namespace vcl
592
593#endif // VCL_IO_MESH_OFF_LOAD_H
A class representing a box in N-dimensional space.
Definition box.h:46
PointT size() const
Computes the size of the box.
Definition box.h:267
static std::string fileNameWithoutExtension(const std::string &fullpath)
Get the file name without extension of a file.
Definition file_info.h:220
A simple class that allows to store which elements and their components have been imported/loaded or ...
Definition mesh_info.h:76
HasFaces concepts is satisfied when at least one of its template types is (or inherits from) a vcl::m...
Definition face_container.h:1389
Concept that checks if a Mesh has the Name component.
Definition mesh_requirements.h:88
NullLogger nullLogger
The nullLogger object is an object of type NullLogger that is used as default argument in the functio...
Definition null_logger.h:123
bool isPerFaceColorAvailable(const MeshType &m)
Returns true if the Color component is available (enabled) in the Face element of the input mesh m.
Definition face_requirements.h:476
bool enableIfPerFaceColorOptional(MeshType &m)
If the input mesh has a FaceContainer, and the Face Element has a Color Component,...
Definition face_requirements.h:500
void loadOff(MeshType &m, std::istream &inputOffStream, MeshInfo &loadedInfo, const LoadSettings &settings=LoadSettings(), LogType &log=nullLogger)
Loads from the given input off stream and puts the content into the mesh m.
Definition load.h:510
The LoadSettings structure contains the settings that can be used to load a mesh from a stream/file.
Definition settings.h:35