23#ifndef VCL_LOAD_SAVE_OFF_LOAD_H
24#define VCL_LOAD_SAVE_OFF_LOAD_H
26#include <vclib/algorithms/mesh/face_topology.h>
27#include <vclib/exceptions/io.h>
28#include <vclib/io/file_info.h>
29#include <vclib/io/read.h>
30#include <vclib/load_save/settings.h>
31#include <vclib/misc/logger.h>
32#include <vclib/misc/tokenizer.h>
33#include <vclib/space/complex/mesh_info.h>
39constexpr float OFF_GEOMVIEW_COLOR_MAP[148][4] = {
40 {1.0f, 1.0f, 1.0f, 1.0f },
41 {1.0f, 1.0f, 1.0f, 1.0f },
42 {1.0f, 1.0f, 1.0f, 1.0f },
43 {1.0f, 1.0f, 1.0f, 1.0f },
45 {1.0f, 1.0f, 1.0f, 1.0f },
46 {1.0f, 1.0f, 1.0f, 1.0f },
47 {0.7f, 0.7f, 0.7f, 0.7f },
48 {0.2f, 0.2f, 0.2f, 0.2f },
50 {0.9f, 0.9f, 0.9f, 0.9f },
51 {0.1f, 0.1f, 0.1f, 0.1f },
52 {0.1f, 0.1f, 0.1f, 0.1f },
53 {0.8f, 0.8f, 0.8f, 0.8f },
55 {0.7f, 0.7f, 0.7f, 0.7f },
56 {0.7f, 0.7f, 0.7f, 0.7f },
57 {0.0f, 0.0f, 0.0f, 0.0f },
58 {0.9f, 0.9f, 0.9f, 0.9f },
60 {0.2f, 0.2f, 0.2f, 0.2f },
61 {0.0f, 0.0f, 0.0f, 0.0f },
62 {0.75f, 0.75f, 0.75f, 0.75f},
63 {0.8f, 0.8f, 0.8f, 0.8f },
65 {0.8f, 0.8f, 0.8f, 0.8f },
66 {0.0f, 0.0f, 0.0f, 0.0f },
67 {0.0f, 0.0f, 0.0f, 0.0f },
68 {0.0f, 0.0f, 0.0f, 0.0f },
70 {0.0f, 0.0f, 0.0f, 0.0f },
71 {0.4f, 0.4f, 0.4f, 0.4f },
72 {0.4f, 0.4f, 0.4f, 0.4f },
73 {0.8f, 0.8f, 0.8f, 0.8f },
75 {0.8f, 0.8f, 0.8f, 0.8f },
76 {0.7f, 0.7f, 0.7f, 0.7f },
77 {0.7f, 0.7f, 0.7f, 0.7f },
78 {0.7f, 0.7f, 0.7f, 0.7f },
80 {0.7f, 0.7f, 0.7f, 0.7f },
81 {0.0f, 0.0f, 0.0f, 0.0f },
82 {0.9f, 0.9f, 0.9f, 0.9f },
83 {0.0f, 0.0f, 0.0f, 0.0f },
85 {0.0f, 0.0f, 0.0f, 0.0f },
86 {0.75f, 0.75f, 0.75f, 0.75f},
87 {0.8f, 0.8f, 0.8f, 0.8f },
88 {0.4f, 0.4f, 0.4f, 0.4f },
90 {0.0f, 0.0f, 0.0f, 0.0f },
91 {0.0f, 0.0f, 0.0f, 0.0f },
92 {0.4f, 0.4f, 0.4f, 0.4f },
93 {0.8f, 0.8f, 0.8f, 0.8f },
95 {0.7f, 0.7f, 0.7f, 0.7f },
96 {0.7f, 0.7f, 0.7f, 0.7f },
97 {0.0f, 0.0f, 0.0f, 0.0f },
98 {0.9f, 0.9f, 0.9f, 0.9f },
100 {0.0f, 0.0f, 0.0f, 0.0f },
101 {0.0f, 0.0f, 0.0f, 0.0f },
102 {0.75f, 0.75f, 0.75f, 0.75f},
103 {0.8f, 0.8f, 0.8f, 0.8f },
105 {0.4f, 0.4f, 0.4f, 0.4f },
106 {0.0f, 0.0f, 0.0f, 0.0f },
107 {0.0f, 0.0f, 0.0f, 0.0f },
108 {0.4f, 0.4f, 0.4f, 0.4f },
110 {0.8f, 0.8f, 0.8f, 0.8f },
111 {0.7f, 0.7f, 0.7f, 0.7f },
112 {0.7f, 0.7f, 0.7f, 0.7f },
113 {0.0f, 0.0f, 0.0f, 0.0f },
115 {0.9f, 0.9f, 0.9f, 0.9f },
116 {0.0f, 0.0f, 0.0f, 0.0f },
117 {0.0f, 0.0f, 0.0f, 0.0f },
118 {0.75f, 0.75f, 0.75f, 0.75f},
120 {0.8f, 0.8f, 0.8f, 0.8f },
121 {0.4f, 0.4f, 0.4f, 0.4f },
122 {0.0f, 0.0f, 0.0f, 0.0f },
123 {0.0f, 0.0f, 0.0f, 0.0f },
125 {0.4f, 0.4f, 0.4f, 0.4f },
126 {0.8f, 0.8f, 0.8f, 0.8f },
127 {1.0f, 1.0f, 1.0f, 1.0f },
128 {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 {1.0f, 1.0f, 1.0f, 1.0f },
133 {1.0f, 1.0f, 1.0f, 1.0f },
135 {0.05f, 0.05f, 0.05f, 0.05f},
136 {0.7f, 0.7f, 0.7f, 0.7f },
137 {0.2f, 0.2f, 0.2f, 0.2f },
138 {0.9f, 0.9f, 0.9f, 0.9f },
140 {0.0f, 0.0f, 0.0f, 0.0f },
141 {0.1f, 0.1f, 0.1f, 0.1f },
142 {0.8f, 0.8f, 0.8f, 0.8f },
143 {0.7f, 0.7f, 0.7f, 0.7f },
145 {0.7f, 0.7f, 0.7f, 0.7f },
146 {0.7f, 0.7f, 0.7f, 0.7f },
147 {0.7f, 0.7f, 0.7f, 0.7f },
148 {0.0f, 0.0f, 0.0f, 0.0f },
150 {0.0f, 0.0f, 0.0f, 0.0f },
151 {0.9f, 0.9f, 0.9f, 0.9f },
152 {0.9f, 0.9f, 0.9f, 0.9f },
153 {0.0f, 0.0f, 0.0f, 0.0f },
155 {0.0f, 0.0f, 0.0f, 0.0f },
156 {0.0f, 0.0f, 0.0f, 0.0f },
157 {0.0f, 0.0f, 0.0f, 0.0f },
158 {0.75f, 0.75f, 0.75f, 0.75f},
160 {0.75f, 0.75f, 0.75f, 0.75f},
161 {0.8f, 0.8f, 0.8f, 0.8f },
162 {0.8f, 0.8f, 0.8f, 0.8f },
163 {0.0f, 0.0f, 0.0f, 0.0f },
165 {0.0f, 0.0f, 0.0f, 0.0f },
166 {0.0f, 0.0f, 0.0f, 0.0f },
167 {0.0f, 0.0f, 0.0f, 0.0f },
168 {0.4f, 0.4f, 0.4f, 0.4f },
170 {0.4f, 0.4f, 0.4f, 0.4f },
171 {0.8f, 0.8f, 0.8f, 0.8f },
172 {0.8f, 0.8f, 0.8f, 0.8f },
173 {0.7f, 0.7f, 0.7f, 0.7f },
175 {0.7f, 0.7f, 0.7f, 0.7f },
176 {0.7f, 0.7f, 0.7f, 0.7f },
177 {0.7f, 0.7f, 0.7f, 0.7f },
178 {0.0f, 0.0f, 0.0f, 0.0f },
180 {0.9f, 0.9f, 0.9f, 0.9f },
181 {0.0f, 0.0f, 0.0f, 0.0f },
182 {0.0f, 0.0f, 0.0f, 0.0f },
183 {0.75f, 0.75f, 0.75f, 0.75f},
185 {0.8f, 0.8f, 0.8f, 0.8f },
186 {0.4f, 0.4f, 0.4f, 0.4f },
187 {0.0f, 0.0f, 0.0f, 0.0f },
188 {0.0f, 0.0f, 0.0f, 0.0f },
190 {0.4f, 0.4f, 0.4f, 0.4f },
191 {0.8f, 0.8f, 0.8f, 0.8f },
192 {0.7f, 0.7f, 0.7f, 0.7f },
193 {0.7f, 0.7f, 0.7f, 0.7f },
195 {0.0f, 0.0f, 0.0f, 0.0f },
196 {0.9f, 0.9f, 0.9f, 0.9f },
197 {0.0f, 0.0f, 0.0f, 0.0f },
198 {0.0f, 0.0f, 0.0f, 0.0f },
200 {0.75f, 0.75f, 0.75f, 0.75f},
201 {0.8f, 0.8f, 0.8f, 0.8f },
202 {0.4f, 0.4f, 0.4f, 0.4f },
203 {0.0f, 0.0f, 0.0f, 0.0f },
205 {0.0f, 0.0f, 0.0f, 0.0f },
206 {0.4f, 0.4f, 0.4f, 0.4f },
207 {0.8f, 0.8f, 0.8f, 0.8f },
208 {0.7f, 0.7f, 0.7f, 0.7f },
210 {0.7f, 0.7f, 0.7f, 0.7f },
211 {0.0f, 0.0f, 0.0f, 0.0f },
212 {0.9f, 0.9f, 0.9f, 0.9f },
213 {0.0f, 0.0f, 0.0f, 0.0f },
215 {0.0f, 0.0f, 0.0f, 0.0f },
216 {0.75f, 0.75f, 0.75f, 0.75f},
217 {0.8f, 0.8f, 0.8f, 0.8f },
218 {0.4f, 0.4f, 0.4f, 0.4f },
220 {0.0f, 0.0f, 0.0f, 0.0f },
221 {0.0f, 0.0f, 0.0f, 0.0f },
222 {0.4f, 0.4f, 0.4f, 0.4f },
223 {0.8f, 0.8f, 0.8f, 0.8f }
226inline void readOffHeader(
234 Tokenizer tokens = readAndTokenizeNextNonEmptyLine(file);
235 Tokenizer::iterator token = tokens.begin();
236 std::string header = *token;
239 if (header.rfind(
"OFF") != std::basic_string<char>::npos) {
240 for (
int u = header.rfind(
"OFF") - 1; u >= 0; u--) {
241 if (header[u] ==
'C')
242 fileInfo.setVertexColors();
243 else if (header[u] ==
'N')
244 fileInfo.setVertexNormals();
245 else if (u > 0 && header[u - 1] ==
'S' && header[u] ==
'T')
246 fileInfo.setVertexTexCoords();
247 else if (header[u] ==
'4')
248 throw MalformedFileException(
249 "Unsupported Homogeneus components in OFF.");
250 else if (header[u] ==
'n')
251 throw MalformedFileException(
"Unsupported High Dimension OFF.");
255 throw MalformedFileException(
"Missing OFF header in file.");
259 if (tokens.size() == 1) {
260 tokens = readAndTokenizeNextNonEmptyLine(file);
261 token = tokens.begin();
266 nv = io::readUInt<uint>(token);
267 nf = io::readUInt<uint>(token);
268 ne = io::readUInt<uint>(token);
270 fileInfo.setVertices();
277inline Color readOffColor(Tokenizer::iterator& token,
int nColorComponents)
279 uint red, green, blue, alpha = 255;
281 if (nColorComponents == 1) {
282 uint k = io::readUInt<uint>(token);
284 red = OFF_GEOMVIEW_COLOR_MAP[k][0] * 255;
285 green = OFF_GEOMVIEW_COLOR_MAP[k][1] * 255;
286 blue = OFF_GEOMVIEW_COLOR_MAP[k][2] * 255;
287 alpha = OFF_GEOMVIEW_COLOR_MAP[k][3] * 255;
290 double r = io::readDouble<double>(token);
291 double g = io::readDouble<double>(token);
292 double b = io::readDouble<double>(token);
294 if (nColorComponents == 4) {
295 a = io::readDouble<double>(token);
297 if (r > 1 || g > 1 || b > 1) {
301 alpha = a != -1 ? a : alpha;
307 alpha = a != -1 ? a * 255 : alpha;
310 return Color(red, green, blue, alpha);
313template<MeshConcept MeshType, LoggerConcept LogType>
317 const MeshInfo& fileInfo,
321 using VertexType = MeshType::VertexType;
323 const uint nTexCoords = fileInfo.hasVertexTexCoords() ? 2 : 0;
325 log.startProgress(
"Reading vertices", nv);
326 mesh.addVertices(nv);
327 for (uint i = 0; i < nv; i++) {
328 VertexType& v = mesh.vertex(i);
330 Tokenizer tokens = readAndTokenizeNextNonEmptyLine(file);
331 Tokenizer::iterator token = tokens.begin();
334 for (
unsigned int j = 0; j < 3; j++) {
336 v.coord()[j] = io::readDouble<double>(token);
339 if constexpr (HasPerVertexNormal<MeshType>) {
340 if (isPerVertexNormalAvailable(mesh) &&
341 fileInfo.hasVertexNormals()) {
343 for (
unsigned int j = 0; j < 3; j++) {
344 v.normal()[j] = io::readDouble<double>(token);
349 else if (fileInfo.hasVertexNormals()) {
350 for (
unsigned int j = 0; j < 3; j++) {
351 io::readDouble<double>(token);
355 const uint nReadComponents = token - tokens.begin();
356 const int nColorComponents =
357 (int) tokens.size() - nReadComponents - nTexCoords;
359 if constexpr (HasPerVertexColor<MeshType>) {
360 if (isPerVertexColorAvailable(mesh) && fileInfo.hasVertexColors()) {
361 if (nColorComponents != 1 && nColorComponents != 3 &&
362 nColorComponents != 4)
363 throw MalformedFileException(
364 "Wrong number of components in line.");
365 v.color() = readOffColor(token, nColorComponents);
369 else if (fileInfo.hasVertexColors()) {
370 if (nColorComponents != 1 && nColorComponents != 3 &&
371 nColorComponents != 4)
372 throw MalformedFileException(
373 "Wrong number of components in line.");
374 readOffColor(token, nColorComponents);
377 if constexpr (HasPerVertexTexCoord<MeshType>) {
378 if (isPerVertexTexCoordAvailable(mesh) &&
379 fileInfo.hasVertexTexCoords()) {
381 for (
unsigned int j = 0; j < 2; j++) {
382 v.texCoord()[j] = io::readDouble<double>(token);
387 else if (fileInfo.hasVertexTexCoords()) {
388 for (
unsigned int j = 0; j < 2; j++) {
389 io::readDouble<double>(token);
398template<FaceMeshConcept MeshType, LoggerConcept LogType>
402 MeshInfo& loadedInfo,
404 const LoadSettings& settings,
407 if constexpr (HasFaces<MeshType>) {
408 using FaceType = MeshType::FaceType;
410 mesh.reserveFaces(nf);
411 log.startProgress(
"Reading faces", nf);
413 for (uint fid = 0; fid < nf; ++fid) {
414 Tokenizer tokens = readAndTokenizeNextNonEmptyLine(file);
415 Tokenizer::iterator token = tokens.begin();
417 FaceType& f = mesh.face(mesh.faceNumber() - 1);
420 uint fSize = io::readUInt<uint>(token);
421 loadedInfo.updateMeshType(fSize);
423 std::vector<uint> vids;
426 for (uint i = 0; i < fSize; ++i) {
427 vids[i] = io::readUInt<uint>(token);
431 bool splitFace =
false;
433 if constexpr (FaceType::VERTEX_NUMBER < 0) {
435 f.resizeVertices(vids.size());
437 else if (FaceType::VERTEX_NUMBER != vids.size()) {
444 for (uint i = 0; i < vids.size(); ++i) {
445 if (vids[i] >= mesh.vertexNumber()) {
446 throw MalformedFileException(
447 "Bad vertex index for face " + std::to_string(i));
449 f.setVertex(i, vids[i]);
453 addTriangleFacesFromPolygon(mesh, f, vids);
457 if (token != tokens.end()) {
458 if constexpr (HasPerFaceColor<MeshType>) {
460 (settings.enableOptionalComponents &&
462 loadedInfo.setFaceColors();
463 f.color() = readOffColor(
464 token, tokens.size() - (token - tokens.begin()));
467 for (uint ff = mesh.index(f); ff < mesh.faceNumber();
469 mesh.face(ff).color() = f.color();
479 for (uint i = 0; i < nf; ++i)
480 readAndTokenizeNextNonEmptyLine(file);
510template<MeshConcept MeshType, LoggerConcept LogType = NullLogger>
524 if (settings.enableOptionalComponents)
528 log.log(
"The file has no vertices", LogType::WARNING_LOG);
537 log.endTask(
"Reading vertices");
540 detail::readOffFaces(
542 log.endTask(
"Reading faces");
545 log.log(100,
"Ignored faces reading");
547 if (settings.enableOptionalComponents)
570template<MeshConcept MeshType, LoggerConcept LogType = NullLogger>
605template<MeshConcept MeshType, LoggerConcept LogType = NullLogger>
636template<MeshConcept MeshType, LoggerConcept LogType = NullLogger>
670template<MeshConcept MeshType, LoggerConcept LogType = NullLogger>
706template<MeshConcept MeshType, LoggerConcept LogType = NullLogger>
741template<MeshConcept MeshType, LoggerConcept LogType = NullLogger>
772template<MeshConcept MeshType, LoggerConcept LogType = NullLogger>
static std::string fileNameWithoutExtension(const std::string &fullpath)
Get the file name without extension of a file.
Definition file_info.h:217
A simple class that allows to store which elements and their components have been imported/loaded or ...
Definition mesh_info.h:76
A class representing a line segment in n-dimensional space. The class is parameterized by a PointConc...
Definition segment.h:43
HasFaces concepts is satisfied when at least one of its template types is (or inherits from) a vcl::m...
Definition face_container.h:133
Concept that checks if a Mesh has the Name component.
Definition per_mesh.h:95
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:214
bool enableIfPerFaceColorOptional(MeshType &m)
If the input mesh has a FaceContainer, and the Face Element has a Color Component,...
Definition face_requirements.h:238
void loadOff(MeshType &m, std::istream &inputOffStream, MeshInfo &loadedInfo, LogType &log=nullLogger, const LoadSettings &settings=LoadSettings())
Loads from the given input off stream and puts the content into the mesh m.
Definition load.h:511
NullLogger nullLogger
The nullLogger object is an object of type NullLogger that is used as default argument in the functio...
Definition null_logger.h:125
The LoadSettings structure contains the settings that can be used to load a mesh from a stream/file.
Definition settings.h:35