Developer Documentation
OFFReader.cc
1/* ========================================================================= *
2 * *
3 * OpenMesh *
4 * Copyright (c) 2001-2025, RWTH-Aachen University *
5 * Department of Computer Graphics and Multimedia *
6 * All rights reserved. *
7 * www.openmesh.org *
8 * *
9 *---------------------------------------------------------------------------*
10 * This file is part of OpenMesh. *
11 *---------------------------------------------------------------------------*
12 * *
13 * Redistribution and use in source and binary forms, with or without *
14 * modification, are permitted provided that the following conditions *
15 * are met: *
16 * *
17 * 1. Redistributions of source code must retain the above copyright notice, *
18 * this list of conditions and the following disclaimer. *
19 * *
20 * 2. Redistributions in binary form must reproduce the above copyright *
21 * notice, this list of conditions and the following disclaimer in the *
22 * documentation and/or other materials provided with the distribution. *
23 * *
24 * 3. Neither the name of the copyright holder nor the names of its *
25 * contributors may be used to endorse or promote products derived from *
26 * this software without specific prior written permission. *
27 * *
28 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS *
29 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED *
30 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
31 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER *
32 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, *
33 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, *
34 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR *
35 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF *
36 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING *
37 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS *
38 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *
39 * *
40 * ========================================================================= */
41
42
43
44
45#define LINE_LEN 4096
46
47
48//== INCLUDES =================================================================
49
50// OpenMesh
53#include <OpenMesh/Core/IO/reader/OFFReader.hh>
54#include <OpenMesh/Core/IO/IOManager.hh>
55#include <OpenMesh/Core/Utils/color_cast.hh>
56// #include <OpenMesh/Core/IO/BinaryHelper.hh>
57
58//STL
59#include <iostream>
60#include <fstream>
61#include <memory>
62#include <cstring>
63
64#if defined(OM_CC_MIPS)
65# include <ctype.h>
67#elif defined(_STLPORT_VERSION) && (_STLPORT_VERSION==0x460)
68# include <cctype>
69#else
70using std::isspace;
71#endif
72
73#ifndef WIN32
74#endif
75
76//=== NAMESPACES ==============================================================
77
78
79namespace OpenMesh {
80namespace IO {
81
82
83//=============================================================================
84
85//=== INSTANCIATE =============================================================
86
87
89_OFFReader_& OFFReader() { return __OFFReaderInstance; }
90
91
92//=== IMPLEMENTATION ==========================================================
93
94
95
96_OFFReader_::_OFFReader_()
97{
99}
100
101
102//-----------------------------------------------------------------------------
103
104
105bool
106_OFFReader_::read(const std::string& _filename, BaseImporter& _bi,
107 Options& _opt)
108{
109 std::ifstream ifile(_filename.c_str(), (options_.is_binary() ? std::ios::binary | std::ios::in
110 : std::ios::in) );
111
112 if (!ifile.is_open() || !ifile.good())
113 {
114 omerr() << "[OFFReader] : cannot not open file "
115 << _filename
116 << std::endl;
117
118 return false;
119 }
120
121 assert(ifile);
122
123 bool result = read(ifile, _bi, _opt);
124
125 ifile.close();
126 return result;
127}
128
129//-----------------------------------------------------------------------------
130
131
132bool
133_OFFReader_::read(std::istream& _in, BaseImporter& _bi, Options& _opt )
134{
135 if (!_in.good())
136 {
137 omerr() << "[OMReader] : cannot not use stream "
138 << std::endl;
139 return false;
140 }
141
142 // filter relevant options for reading
143 bool swap_required = _opt.check( Options::Swap );
144
145 userOptions_ = _opt;
146
147 // build options to be returned
148 _opt.clear();
149
150 if (options_.vertex_has_normal() && userOptions_.vertex_has_normal()) _opt += Options::VertexNormal;
151 if (options_.vertex_has_texcoord() && userOptions_.vertex_has_texcoord()) _opt += Options::VertexTexCoord;
152 if (options_.vertex_has_color() && userOptions_.vertex_has_color()) _opt += Options::VertexColor;
153 if (options_.face_has_color() && userOptions_.face_has_color()) _opt += Options::FaceColor;
154 if (options_.is_binary()) _opt += Options::Binary;
155
156 //force user-choice for the alpha value when reading binary
157 if ( options_.is_binary() && userOptions_.color_has_alpha() )
158 options_ += Options::ColorAlpha;
159
160 return (options_.is_binary() ?
161 read_binary(_in, _bi, _opt, swap_required) :
162 read_ascii(_in, _bi, _opt));
163
164}
165
166
167
168//-----------------------------------------------------------------------------
169
170bool
171_OFFReader_::read_ascii(std::istream& _in, BaseImporter& _bi, Options& _opt) const
172{
173
174
175 unsigned int i, j, k, l, idx;
176 unsigned int nV, nF, dummy;
177 OpenMesh::Vec3f v, n;
180 OpenMesh::Vec3f c3f;
182 OpenMesh::Vec4f c4f;
183 BaseImporter::VHandles vhandles;
184 VertexHandle vh;
185 std::stringstream stream;
186 std::string trash;
187
188 // read header line
189 std::string header;
190 std::getline(_in,header);
191
192 // + #Vertice, #Faces, #Edges
193 _in >> nV;
194 _in >> nF;
195 _in >> dummy;
196
197 _bi.reserve(nV, 3*nV, nF);
198
199 // read vertices: coord [hcoord] [normal] [color] [texcoord]
200 for (i=0; i<nV && !_in.eof(); ++i)
201 {
202 // Always read VERTEX
203 _in >> v[0]; _in >> v[1]; _in >> v[2];
204
205 vh = _bi.add_vertex(v);
206
207 //perhaps read NORMAL
208 if ( options_.vertex_has_normal() ){
209
210 _in >> n[0]; _in >> n[1]; _in >> n[2];
211
212 if ( userOptions_.vertex_has_normal() )
213 _bi.set_normal(vh, n);
214 }
215
216 //take the rest of the line and check how colors are defined
217 std::string line;
218 std::getline(_in,line);
219
220 int colorType = getColorType(line, options_.vertex_has_texcoord() );
221
222 stream.str(line);
223 stream.clear();
224
225 //perhaps read COLOR
226 if ( options_.vertex_has_color() ){
227
228 switch (colorType){
229 case 0 : break; //no color
230 case 1 : stream >> trash; break; //one int (isn't handled atm)
231 case 2 : stream >> trash; stream >> trash; break; //corrupt format (ignore)
232 // rgb int
233 case 3 : stream >> c3[0]; stream >> c3[1]; stream >> c3[2];
234 if ( userOptions_.vertex_has_color() )
235 _bi.set_color( vh, Vec3uc( c3 ) );
236 break;
237 // rgba int
238 case 4 : stream >> c4[0]; stream >> c4[1]; stream >> c4[2]; stream >> c4[3];
239 if ( userOptions_.vertex_has_color() )
240 _bi.set_color( vh, Vec4uc( c4 ) );
241 break;
242 // rgb floats
243 case 5 : stream >> c3f[0]; stream >> c3f[1]; stream >> c3f[2];
244 if ( userOptions_.vertex_has_color() ) {
245 _bi.set_color( vh, c3f );
246 _opt += Options::ColorFloat;
247 }
248 break;
249 // rgba floats
250 case 6 : stream >> c4f[0]; stream >> c4f[1]; stream >> c4f[2]; stream >> c4f[3];
251 if ( userOptions_.vertex_has_color() ) {
252 _bi.set_color( vh, c4f );
253 _opt += Options::ColorFloat;
254 }
255 break;
256
257 default:
258 std::cerr << "Error in file format (colorType = " << colorType << ")\n";
259 break;
260 }
261 }
262 //perhaps read TEXTURE COORDs
263 if ( options_.vertex_has_texcoord() ){
264 stream >> t[0]; stream >> t[1];
265 if ( userOptions_.vertex_has_texcoord() )
266 _bi.set_texcoord(vh, t);
267 }
268 }
269
270 // faces
271 // #N <v1> <v2> .. <v(n-1)> [color spec]
272 for (i=0; i<nF; ++i)
273 {
274 // nV = number of Vertices for current face
275 _in >> nV;
276
277 if (nV == 3)
278 {
279 vhandles.resize(3);
280 _in >> j;
281 _in >> k;
282 _in >> l;
283
284 vhandles[0] = VertexHandle(j);
285 vhandles[1] = VertexHandle(k);
286 vhandles[2] = VertexHandle(l);
287 }
288 else
289 {
290 vhandles.clear();
291 for (j=0; j<nV; ++j)
292 {
293 _in >> idx;
294 vhandles.push_back(VertexHandle(idx));
295 }
296 }
297
298 FaceHandle fh = _bi.add_face(vhandles);
299
300 //perhaps read face COLOR
301 if ( options_.face_has_color() ){
302
303 //take the rest of the line and check how colors are defined
304 std::string line;
305 std::getline(_in,line);
306
307 int colorType = getColorType(line, false );
308
309 stream.str(line);
310 stream.clear();
311
312 switch (colorType){
313 case 0 : break; //no color
314 case 1 : stream >> trash; break; //one int (isn't handled atm)
315 case 2 : stream >> trash; stream >> trash; break; //corrupt format (ignore)
316 // rgb int
317 case 3 : stream >> c3[0]; stream >> c3[1]; stream >> c3[2];
318 if ( userOptions_.face_has_color() )
319 _bi.set_color( fh, Vec3uc( c3 ) );
320 break;
321 // rgba int
322 case 4 : stream >> c4[0]; stream >> c4[1]; stream >> c4[2]; stream >> c4[3];
323 if ( userOptions_.face_has_color() )
324 _bi.set_color( fh, Vec4uc( c4 ) );
325 break;
326 // rgb floats
327 case 5 : stream >> c3f[0]; stream >> c3f[1]; stream >> c3f[2];
328 if ( userOptions_.face_has_color() ) {
329 _bi.set_color( fh, c3f );
330 _opt += Options::ColorFloat;
331 }
332 break;
333 // rgba floats
334 case 6 : stream >> c4f[0]; stream >> c4f[1]; stream >> c4f[2]; stream >> c4f[3];
335 if ( userOptions_.face_has_color() ) {
336 _bi.set_color( fh, c4f );
337 _opt += Options::ColorFloat;
338 }
339 break;
340
341 default:
342 std::cerr << "Error in file format (colorType = " << colorType << ")\n";
343 break;
344 }
345 }
346 }
347
348 // File was successfully parsed.
349 return true;
350}
351
352
353//-----------------------------------------------------------------------------
354
355int _OFFReader_::getColorType(std::string& _line, bool _texCoordsAvailable) const
356{
357/*
358 0 : no Color
359 1 : one int (e.g colormap index)
360 2 : two items (error!)
361 3 : 3 ints
362 4 : 3 ints
363 5 : 3 floats
364 6 : 4 floats
365
366*/
367 // Check if we have any additional information here
368 if ( _line.size() < 1 )
369 return 0;
370
371 //first remove spaces at start/end of the line
372 while (_line.size() > 0 && std::isspace(_line[0]))
373 _line = _line.substr(1);
374 while (_line.size() > 0 && std::isspace(_line[ _line.length()-1 ]))
375 _line = _line.substr(0, _line.length()-1);
376
377 //count the remaining items in the line
378 size_t found;
379 int count = 0;
380
381 found=_line.find_first_of(" ");
382 while (found!=std::string::npos){
383 count++;
384 found=_line.find_first_of(" ",found+1);
385 }
386
387 if (!_line.empty()) count++;
388
389 if (_texCoordsAvailable) count -= 2;
390
391 if (count == 3 || count == 4){
392 //get first item
393 found = _line.find(" ");
394 std::string c1 = _line.substr (0,found);
395
396 if (c1.find(".") != std::string::npos){
397 if (count == 3)
398 count = 5;
399 else
400 count = 6;
401 }
402 }
403 return count;
404}
405
406void _OFFReader_::readValue(std::istream& _in, float& _value) const{
407 float32_t tmp;
408
409 restore( _in , tmp, false ); //assuming LSB byte order
410 _value = tmp;
411}
412
413void _OFFReader_::readValue(std::istream& _in, int& _value) const{
414 uint32_t tmp;
415
416 restore( _in , tmp, false ); //assuming LSB byte order
417 _value = tmp;
418}
419
420void _OFFReader_::readValue(std::istream& _in, unsigned int& _value) const{
421 uint32_t tmp;
422
423 restore( _in , tmp, false ); //assuming LSB byte order
424 _value = tmp;
425}
426
427bool
428_OFFReader_::read_binary(std::istream& _in, BaseImporter& _bi, Options& _opt, bool /*_swap*/) const
429{
430 unsigned int i, j, k, l, idx;
431 unsigned int nV, nF, dummy;
432 OpenMesh::Vec3f v, n;
436 OpenMesh::Vec4f cAf;
438 BaseImporter::VHandles vhandles;
439 VertexHandle vh;
440
441 // read header line
442 std::string header;
443 std::getline(_in,header);
444
445 // + #Vertice, #Faces, #Edges
446 readValue(_in, nV);
447 readValue(_in, nF);
448 readValue(_in, dummy);
449
450 _bi.reserve(nV, 3*nV, nF);
451
452 // read vertices: coord [hcoord] [normal] [color] [texcoord]
453 for (i=0; i<nV && !_in.eof(); ++i)
454 {
455 // Always read Vertex
456 readValue(_in, v[0]);
457 readValue(_in, v[1]);
458 readValue(_in, v[2]);
459
460 vh = _bi.add_vertex(v);
461
462 if ( options_.vertex_has_normal() ) {
463 readValue(_in, n[0]);
464 readValue(_in, n[1]);
465 readValue(_in, n[2]);
466
467 if ( userOptions_.vertex_has_normal() )
468 _bi.set_normal(vh, n);
469 }
470
471 if ( options_.vertex_has_color() ) {
472 if ( userOptions_.color_is_float() ) {
473 _opt += Options::ColorFloat;
474 //with alpha
475 if ( options_.color_has_alpha() ){
476 readValue(_in, cAf[0]);
477 readValue(_in, cAf[1]);
478 readValue(_in, cAf[2]);
479 readValue(_in, cAf[3]);
480
481 if ( userOptions_.vertex_has_color() )
482 _bi.set_color( vh, cAf );
483 }else{
484
485 //without alpha
486 readValue(_in, cf[0]);
487 readValue(_in, cf[1]);
488 readValue(_in, cf[2]);
489
490 if ( userOptions_.vertex_has_color() )
491 _bi.set_color( vh, cf );
492 }
493 } else {
494 //with alpha
495 if ( options_.color_has_alpha() ){
496 readValue(_in, cA[0]);
497 readValue(_in, cA[1]);
498 readValue(_in, cA[2]);
499 readValue(_in, cA[3]);
500
501 if ( userOptions_.vertex_has_color() )
502 _bi.set_color( vh, Vec4uc( cA ) );
503 }else{
504 //without alpha
505 readValue(_in, c[0]);
506 readValue(_in, c[1]);
507 readValue(_in, c[2]);
508
509 if ( userOptions_.vertex_has_color() )
510 _bi.set_color( vh, Vec3uc( c ) );
511 }
512 }
513 }
514
515 if ( options_.vertex_has_texcoord()) {
516 readValue(_in, t[0]);
517 readValue(_in, t[1]);
518
519 if ( userOptions_.vertex_has_texcoord() )
520 _bi.set_texcoord(vh, t);
521 }
522 }
523
524 // faces
525 // #N <v1> <v2> .. <v(n-1)> [color spec]
526 // So far color spec is unsupported!
527 for (i=0; i<nF; ++i)
528 {
529 readValue(_in, nV);
530
531 if (nV == 3)
532 {
533 vhandles.resize(3);
534 readValue(_in, j);
535 readValue(_in, k);
536 readValue(_in, l);
537
538 vhandles[0] = VertexHandle(j);
539 vhandles[1] = VertexHandle(k);
540 vhandles[2] = VertexHandle(l);
541 } else {
542 vhandles.clear();
543 for (j=0; j<nV; ++j)
544 {
545 readValue(_in, idx);
546 vhandles.push_back(VertexHandle(idx));
547 }
548 }
549
550 FaceHandle fh = _bi.add_face(vhandles);
551
552 //face color
553 if ( _opt.face_has_color() ) {
554 if ( userOptions_.color_is_float() ) {
555 _opt += Options::ColorFloat;
556 //with alpha
557 if ( options_.color_has_alpha() ){
558 readValue(_in, cAf[0]);
559 readValue(_in, cAf[1]);
560 readValue(_in, cAf[2]);
561 readValue(_in, cAf[3]);
562
563 if ( userOptions_.face_has_color() )
564 _bi.set_color( fh , cAf );
565 }else{
566 //without alpha
567 readValue(_in, cf[0]);
568 readValue(_in, cf[1]);
569 readValue(_in, cf[2]);
570
571 if ( userOptions_.face_has_color() )
572 _bi.set_color( fh, cf );
573 }
574 } else {
575 //with alpha
576 if ( options_.color_has_alpha() ){
577 readValue(_in, cA[0]);
578 readValue(_in, cA[1]);
579 readValue(_in, cA[2]);
580 readValue(_in, cA[3]);
581
582 if ( userOptions_.face_has_color() )
583 _bi.set_color( fh , Vec4uc( cA ) );
584 }else{
585 //without alpha
586 readValue(_in, c[0]);
587 readValue(_in, c[1]);
588 readValue(_in, c[2]);
589
590 if ( userOptions_.face_has_color() )
591 _bi.set_color( fh, Vec3uc( c ) );
592 }
593 }
594 }
595
596 }
597
598 // File was successfully parsed.
599 return true;
600
601}
602
603
604//-----------------------------------------------------------------------------
605
606
607bool _OFFReader_::can_u_read(const std::string& _filename) const
608{
609 // !!! Assuming BaseReader::can_u_parse( std::string& )
610 // does not call BaseReader::read_magic()!!!
611 if (BaseReader::can_u_read(_filename))
612 {
613 std::ifstream ifs(_filename.c_str());
614 if (ifs.is_open() && can_u_read(ifs))
615 {
616 ifs.close();
617 return true;
618 }
619 }
620 return false;
621}
622
623
624//-----------------------------------------------------------------------------
625
626
627bool _OFFReader_::can_u_read(std::istream& _is) const
628{
629 options_.cleanup();
630
631 // read 1st line
632 char line[LINE_LEN], *p;
633 _is.getline(line, LINE_LEN);
634 p = line;
635
636 std::streamsize remainingChars = _is.gcount();
637
638 bool vertexDimensionTooHigh = false;
639
640 // check header: [ST][C][N][4][n]OFF BINARY
641
642 if ( ( remainingChars > 1 ) && ( p[0] == 'S' && p[1] == 'T') )
643 { options_ += Options::VertexTexCoord; p += 2; remainingChars -= 2; }
644
645 if ( ( remainingChars > 0 ) && ( p[0] == 'C') )
646 { options_ += Options::VertexColor;
647 options_ += Options::FaceColor; ++p; --remainingChars; }
648
649 if ( ( remainingChars > 0 ) && ( p[0] == 'N') )
650 { options_ += Options::VertexNormal; ++p; --remainingChars; }
651
652 if ( ( remainingChars > 0 ) && (p[0] == '4' ) )
653 { vertexDimensionTooHigh = true; ++p; --remainingChars; }
654
655 if ( ( remainingChars > 0 ) && ( p[0] == 'n') )
656 { vertexDimensionTooHigh = true; ++p; --remainingChars; }
657
658 if ( ( remainingChars < 3 ) || (!(p[0] == 'O' && p[1] == 'F' && p[2] == 'F') ) )
659 return false;
660
661 p += 4;
662
663 // Detect possible garbage and make sure, we don't have an underflow
664 if ( remainingChars >= 4 )
665 remainingChars -= 4;
666 else
667 remainingChars = 0;
668
669 if ( ( remainingChars >= 6 ) && ( strncmp(p, "BINARY", 6) == 0 ) )
670 options_+= Options::Binary;
671
672 // vertex Dimensions != 3 are currently not supported
673 if (vertexDimensionTooHigh)
674 return false;
675
676 return true;
677}
678
679
680//=============================================================================
681} // namespace IO
682} // namespace OpenMesh
683//=============================================================================
virtual bool can_u_read(const std::string &_filename) const
Returns true if writer can parse _filename (checks extension). _filename can also provide an extensio...
Definition: BaseReader.cc:77
Set options for reader/writer modules.
Definition: Options.hh:92
void cleanup(void)
Restore state after default constructor.
Definition: Options.hh:139
void clear(void)
Clear all bits.
Definition: Options.hh:143
@ ColorFloat
Has (r) / store (w) float values for colors (currently only implemented for PLY and OFF files)
Definition: Options.hh:113
@ Swap
Swap byte order in binary mode.
Definition: Options.hh:104
@ FaceColor
Has (r) / store (w) face colors.
Definition: Options.hh:110
@ Binary
Set binary mode for r/w.
Definition: Options.hh:101
@ ColorAlpha
Has (r) / store (w) alpha values for colors.
Definition: Options.hh:112
@ VertexNormal
Has (r) / store (w) vertex normals.
Definition: Options.hh:105
@ VertexTexCoord
Has (r) / store (w) texture coordinates.
Definition: Options.hh:107
@ VertexColor
Has (r) / store (w) vertex colors.
Definition: Options.hh:106
bool register_module(BaseReader *_bl)
Definition: IOManager.hh:217
bool can_u_read(const std::string &_filename) const override
Returns true if writer can parse _filename (checks extension). _filename can also provide an extensio...
Definition: OFFReader.cc:607
bool read(const std::string &_filename, BaseImporter &_bi, Options &_opt) override
Definition: OFFReader.cc:106
_OFFReader_ __OFFReaderInstance
Declare the single entity of the OFF reader.
Definition: OFFReader.cc:88
unsigned int uint32_t
Definition: SR_types.hh:85
_IOManager_ & IOManager()
Definition: IOManager.cc:72
float float32_t
Definition: SR_types.hh:92
VectorT< unsigned char, 3 > Vec3uc
Definition: Vector11T.hh:841
VectorT< unsigned char, 4 > Vec4uc
Definition: Vector11T.hh:860
Handle for a vertex entity.
Definition: Handles.hh:121