23#ifndef VCL_IO_MESH_OFF_LOAD_H
24#define VCL_IO_MESH_OFF_LOAD_H
26#include <vclib/io/file_info.h>
27#include <vclib/io/mesh/settings.h>
28#include <vclib/io/read.h>
30#include <vclib/algorithms/mesh.h>
31#include <vclib/space/complex.h>
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 },
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 },
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 },
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 },
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 },
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 },
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 },
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 },
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 },
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 },
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 },
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 },
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 },
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 },
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 },
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},
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 },
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 },
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 },
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 },
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 },
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 },
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 },
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},
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 },
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 },
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 },
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 },
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},
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 },
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 },
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 },
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 },
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 },
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 },
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 },
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 }
224inline void readOffHeader(
232 Tokenizer tokens = readAndTokenizeNextNonEmptyLine(file);
233 Tokenizer::iterator token = tokens.begin();
234 std::string header = *token;
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.");
253 throw MalformedFileException(
"Missing OFF header in file.");
257 if (tokens.size() == 1) {
258 tokens = readAndTokenizeNextNonEmptyLine(file);
259 token = tokens.begin();
264 nv = io::readUInt<uint>(token);
265 nf = io::readUInt<uint>(token);
266 ne = io::readUInt<uint>(token);
268 fileInfo.setVertices();
275inline Color readOffColor(Tokenizer::iterator& token,
int nColorComponents)
277 uint red, green, blue, alpha = 255;
279 if (nColorComponents == 1) {
280 uint k = io::readUInt<uint>(token);
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;
288 double r = io::readDouble<double>(token);
289 double g = io::readDouble<double>(token);
290 double b = io::readDouble<double>(token);
292 if (nColorComponents == 4) {
293 a = io::readDouble<double>(token);
295 if (r > 1 || g > 1 || b > 1) {
299 alpha = a != -1 ? a : alpha;
305 alpha = a != -1 ? a * 255 : alpha;
308 return Color(red, green, blue, alpha);
311template<MeshConcept MeshType, LoggerConcept LogType>
315 const MeshInfo& fileInfo,
319 using VertexType = MeshType::VertexType;
321 const uint nTexCoords = fileInfo.hasPerVertexTexCoord() ? 2 : 0;
323 log.startProgress(
"Reading vertices", nv);
324 mesh.addVertices(nv);
325 for (uint i = 0; i < nv; i++) {
326 VertexType& v = mesh.vertex(i);
328 Tokenizer tokens = readAndTokenizeNextNonEmptyLine(file);
329 Tokenizer::iterator token = tokens.begin();
332 for (
unsigned int j = 0; j < 3; j++) {
334 v.position()[j] = io::readDouble<double>(token);
337 if constexpr (HasPerVertexNormal<MeshType>) {
338 if (isPerVertexNormalAvailable(mesh) &&
339 fileInfo.hasPerVertexNormal()) {
341 for (
unsigned int j = 0; j < 3; j++) {
342 v.normal()[j] = io::readDouble<double>(token);
347 else if (fileInfo.hasPerVertexNormal()) {
348 for (
unsigned int j = 0; j < 3; j++) {
349 io::readDouble<double>(token);
353 const uint nReadComponents = token - tokens.begin();
354 const int nColorComponents =
355 (int) tokens.
size() - nReadComponents - nTexCoords;
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);
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);
376 if constexpr (HasPerVertexTexCoord<MeshType>) {
377 if (isPerVertexTexCoordAvailable(mesh) &&
378 fileInfo.hasPerVertexTexCoord()) {
380 for (
unsigned int j = 0; j < 2; j++) {
381 v.texCoord()[j] = io::readDouble<double>(token);
386 else if (fileInfo.hasPerVertexTexCoord()) {
387 for (
unsigned int j = 0; j < 2; j++) {
388 io::readDouble<double>(token);
397template<FaceMeshConcept MeshType, LoggerConcept LogType>
401 MeshInfo& loadedInfo,
403 const LoadSettings& settings,
406 if constexpr (HasFaces<MeshType>) {
407 using FaceType = MeshType::FaceType;
409 mesh.reserveFaces(nf);
410 log.startProgress(
"Reading faces", nf);
412 for (uint fid = 0; fid < nf; ++fid) {
413 Tokenizer tokens = readAndTokenizeNextNonEmptyLine(file);
414 Tokenizer::iterator token = tokens.begin();
416 FaceType& f = mesh.face(mesh.faceNumber() - 1);
419 uint fSize = io::readUInt<uint>(token);
420 loadedInfo.updateMeshType(fSize);
422 std::vector<uint> vids;
425 for (uint i = 0; i < fSize; ++i) {
426 vids[i] = io::readUInt<uint>(token);
430 bool splitFace =
false;
432 if constexpr (FaceType::VERTEX_NUMBER < 0) {
434 f.resizeVertices(vids.size());
436 else if (FaceType::VERTEX_NUMBER != vids.size()) {
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));
448 f.setVertex(i, vids[i]);
452 addTriangleFacesFromPolygon(mesh, f, vids);
456 if (token != tokens.end()) {
457 if constexpr (HasPerFaceColor<MeshType>) {
459 (settings.enableOptionalComponents &&
461 loadedInfo.setPerFaceColor();
462 f.color() = readOffColor(
463 token, tokens.size() - (token - tokens.begin()));
466 for (uint ff = mesh.index(f); ff < mesh.faceNumber();
468 mesh.face(ff).color() = f.color();
478 for (uint i = 0; i < nf; ++i)
479 readAndTokenizeNextNonEmptyLine(file);
509template<MeshConcept MeshType, LoggerConcept LogType = NullLogger>
523 if (settings.enableOptionalComponents)
527 log.log(
"The file has no vertices", LogType::WARNING_LOG);
536 log.endTask(
"Reading vertices");
539 detail::readOffFaces(
541 log.endTask(
"Reading faces");
544 log.log(100,
"Ignored faces reading");
546 if (settings.enableOptionalComponents)
574template<MeshConcept MeshType, LoggerConcept LogType = NullLogger>
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