Developer Documentation
OFFReader.cc
1 /* ========================================================================= *
2  * *
3  * OpenMesh *
4  * Copyright (c) 2001-2015, 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
70 using std::isspace;
71 #endif
72 
73 #ifndef WIN32
74 #endif
75 
76 //=== NAMESPACES ==============================================================
77 
78 
79 namespace OpenMesh {
80 namespace 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 {
98  IOManager().register_module(this);
99 }
100 
101 
102 //-----------------------------------------------------------------------------
103 
104 
105 bool
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 
132 bool
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 = _opt.check( Options::Swap );
144 
145 
146  userOptions_ = _opt;
147 
148  // build options to be returned
149  _opt.clear();
150 
151  if (options_.vertex_has_normal() && userOptions_.vertex_has_normal()) _opt += Options::VertexNormal;
152  if (options_.vertex_has_texcoord() && userOptions_.vertex_has_texcoord()) _opt += Options::VertexTexCoord;
153  if (options_.vertex_has_color() && userOptions_.vertex_has_color()) _opt += Options::VertexColor;
154  if (options_.face_has_color() && userOptions_.face_has_color()) _opt += Options::FaceColor;
155  if (options_.is_binary()) _opt += Options::Binary;
156 
157  //force user-choice for the alpha value when reading binary
158  if ( options_.is_binary() && userOptions_.color_has_alpha() )
159  options_ += Options::ColorAlpha;
160 
161  return (options_.is_binary() ?
162  read_binary(_in, _bi, _opt, swap) :
163  read_ascii(_in, _bi, _opt));
164 
165 }
166 
167 
168 
169 //-----------------------------------------------------------------------------
170 
171 bool
172 _OFFReader_::read_ascii(std::istream& _in, BaseImporter& _bi, Options& _opt) const
173 {
174 
175 
176  unsigned int i, j, k, l, idx;
177  unsigned int nV, nF, dummy;
178  OpenMesh::Vec3f v, n;
179  OpenMesh::Vec2f t;
180  OpenMesh::Vec3i c3;
181  OpenMesh::Vec3f c3f;
182  OpenMesh::Vec4i c4;
183  OpenMesh::Vec4f c4f;
184  BaseImporter::VHandles vhandles;
185  VertexHandle vh;
186  std::stringstream stream;
187  std::string trash;
188 
189  // read header line
190  std::string header;
191  std::getline(_in,header);
192 
193  // + #Vertice, #Faces, #Edges
194  _in >> nV;
195  _in >> nF;
196  _in >> dummy;
197 
198  _bi.reserve(nV, 3*nV, nF);
199 
200  // read vertices: coord [hcoord] [normal] [color] [texcoord]
201  for (i=0; i<nV && !_in.eof(); ++i)
202  {
203  // Always read VERTEX
204  _in >> v[0]; _in >> v[1]; _in >> v[2];
205 
206  vh = _bi.add_vertex(v);
207 
208  //perhaps read NORMAL
209  if ( options_.vertex_has_normal() ){
210 
211  _in >> n[0]; _in >> n[1]; _in >> n[2];
212 
213  if ( userOptions_.vertex_has_normal() )
214  _bi.set_normal(vh, n);
215  }
216 
217  //take the rest of the line and check how colors are defined
218  std::string line;
219  std::getline(_in,line);
220 
221  int colorType = getColorType(line, options_.vertex_has_texcoord() );
222 
223  stream.str(line);
224  stream.clear();
225 
226  //perhaps read COLOR
227  if ( options_.vertex_has_color() ){
228 
229  switch (colorType){
230  case 0 : break; //no color
231  case 1 : stream >> trash; break; //one int (isn't handled atm)
232  case 2 : stream >> trash; stream >> trash; break; //corrupt format (ignore)
233  // rgb int
234  case 3 : stream >> c3[0]; stream >> c3[1]; stream >> c3[2];
235  if ( userOptions_.vertex_has_color() )
236  _bi.set_color( vh, Vec3uc( c3 ) );
237  break;
238  // rgba int
239  case 4 : stream >> c4[0]; stream >> c4[1]; stream >> c4[2]; stream >> c4[3];
240  if ( userOptions_.vertex_has_color() )
241  _bi.set_color( vh, Vec4uc( c4 ) );
242  break;
243  // rgb floats
244  case 5 : stream >> c3f[0]; stream >> c3f[1]; stream >> c3f[2];
245  if ( userOptions_.vertex_has_color() ) {
246  _bi.set_color( vh, c3f );
247  _opt += Options::ColorFloat;
248  }
249  break;
250  // rgba floats
251  case 6 : stream >> c4f[0]; stream >> c4f[1]; stream >> c4f[2]; stream >> c4f[3];
252  if ( userOptions_.vertex_has_color() ) {
253  _bi.set_color( vh, c4f );
254  _opt += Options::ColorFloat;
255  }
256  break;
257 
258  default:
259  std::cerr << "Error in file format (colorType = " << colorType << ")\n";
260  break;
261  }
262  }
263  //perhaps read TEXTURE COORDs
264  if ( options_.vertex_has_texcoord() ){
265  stream >> t[0]; stream >> t[1];
266  if ( userOptions_.vertex_has_texcoord() )
267  _bi.set_texcoord(vh, t);
268  }
269  }
270 
271  // faces
272  // #N <v1> <v2> .. <v(n-1)> [color spec]
273  for (i=0; i<nF; ++i)
274  {
275  // nV = number of Vertices for current face
276  _in >> nV;
277 
278  if (nV == 3)
279  {
280  vhandles.resize(3);
281  _in >> j;
282  _in >> k;
283  _in >> l;
284 
285  vhandles[0] = VertexHandle(j);
286  vhandles[1] = VertexHandle(k);
287  vhandles[2] = VertexHandle(l);
288  }
289  else
290  {
291  vhandles.clear();
292  for (j=0; j<nV; ++j)
293  {
294  _in >> idx;
295  vhandles.push_back(VertexHandle(idx));
296  }
297  }
298 
299  FaceHandle fh = _bi.add_face(vhandles);
300 
301  //perhaps read face COLOR
302  if ( options_.face_has_color() ){
303 
304  //take the rest of the line and check how colors are defined
305  std::string line;
306  std::getline(_in,line);
307 
308  int colorType = getColorType(line, false );
309 
310  stream.str(line);
311  stream.clear();
312 
313  switch (colorType){
314  case 0 : break; //no color
315  case 1 : stream >> trash; break; //one int (isn't handled atm)
316  case 2 : stream >> trash; stream >> trash; break; //corrupt format (ignore)
317  // rgb int
318  case 3 : stream >> c3[0]; stream >> c3[1]; stream >> c3[2];
319  if ( userOptions_.face_has_color() )
320  _bi.set_color( fh, Vec3uc( c3 ) );
321  break;
322  // rgba int
323  case 4 : stream >> c4[0]; stream >> c4[1]; stream >> c4[2]; stream >> c4[3];
324  if ( userOptions_.face_has_color() )
325  _bi.set_color( fh, Vec4uc( c4 ) );
326  break;
327  // rgb floats
328  case 5 : stream >> c3f[0]; stream >> c3f[1]; stream >> c3f[2];
329  if ( userOptions_.face_has_color() ) {
330  _bi.set_color( fh, c3f );
331  _opt += Options::ColorFloat;
332  }
333  break;
334  // rgba floats
335  case 6 : stream >> c4f[0]; stream >> c4f[1]; stream >> c4f[2]; stream >> c4f[3];
336  if ( userOptions_.face_has_color() ) {
337  _bi.set_color( fh, c4f );
338  _opt += Options::ColorFloat;
339  }
340  break;
341 
342  default:
343  std::cerr << "Error in file format (colorType = " << colorType << ")\n";
344  break;
345  }
346  }
347  }
348 
349  // File was successfully parsed.
350  return true;
351 }
352 
353 
354 //-----------------------------------------------------------------------------
355 
356 int _OFFReader_::getColorType(std::string& _line, bool _texCoordsAvailable) const
357 {
358 /*
359  0 : no Color
360  1 : one int (e.g colormap index)
361  2 : two items (error!)
362  3 : 3 ints
363  4 : 3 ints
364  5 : 3 floats
365  6 : 4 floats
366 
367 */
368  // Check if we have any additional information here
369  if ( _line.size() < 1 )
370  return 0;
371 
372  //first remove spaces at start/end of the line
373  while (_line.size() > 0 && std::isspace(_line[0]))
374  _line = _line.substr(1);
375  while (_line.size() > 0 && std::isspace(_line[ _line.length()-1 ]))
376  _line = _line.substr(0, _line.length()-1);
377 
378  //count the remaining items in the line
379  size_t found;
380  int count = 0;
381 
382  found=_line.find_first_of(" ");
383  while (found!=std::string::npos){
384  count++;
385  found=_line.find_first_of(" ",found+1);
386  }
387 
388  if (!_line.empty()) count++;
389 
390  if (_texCoordsAvailable) count -= 2;
391 
392  if (count == 3 || count == 4){
393  //get first item
394  found = _line.find(" ");
395  std::string c1 = _line.substr (0,found);
396 
397  if (c1.find(".") != std::string::npos){
398  if (count == 3)
399  count = 5;
400  else
401  count = 6;
402  }
403  }
404  return count;
405 }
406 
407 void _OFFReader_::readValue(std::istream& _in, float& _value) const{
408  float32_t tmp;
409 
410  restore( _in , tmp, false ); //assuming LSB byte order
411  _value = tmp;
412 }
413 
414 void _OFFReader_::readValue(std::istream& _in, int& _value) const{
415  uint32_t tmp;
416 
417  restore( _in , tmp, false ); //assuming LSB byte order
418  _value = tmp;
419 }
420 
421 void _OFFReader_::readValue(std::istream& _in, unsigned int& _value) const{
422  uint32_t tmp;
423 
424  restore( _in , tmp, false ); //assuming LSB byte order
425  _value = tmp;
426 }
427 
428 bool
429 _OFFReader_::read_binary(std::istream& _in, BaseImporter& _bi, Options& _opt, bool /*_swap*/) const
430 {
431  unsigned int i, j, k, l, idx;
432  unsigned int nV, nF, dummy;
433  OpenMesh::Vec3f v, n;
434  OpenMesh::Vec3i c;
435  OpenMesh::Vec4i cA;
436  OpenMesh::Vec3f cf;
437  OpenMesh::Vec4f cAf;
438  OpenMesh::Vec2f t;
439  BaseImporter::VHandles vhandles;
440  VertexHandle vh;
441 
442  // read header line
443  std::string header;
444  std::getline(_in,header);
445 
446  // + #Vertice, #Faces, #Edges
447  readValue(_in, nV);
448  readValue(_in, nF);
449  readValue(_in, dummy);
450 
451  _bi.reserve(nV, 3*nV, nF);
452 
453  // read vertices: coord [hcoord] [normal] [color] [texcoord]
454  for (i=0; i<nV && !_in.eof(); ++i)
455  {
456  // Always read Vertex
457  readValue(_in, v[0]);
458  readValue(_in, v[1]);
459  readValue(_in, v[2]);
460 
461  vh = _bi.add_vertex(v);
462 
463  if ( options_.vertex_has_normal() ) {
464  readValue(_in, n[0]);
465  readValue(_in, n[1]);
466  readValue(_in, n[2]);
467 
468  if ( userOptions_.vertex_has_normal() )
469  _bi.set_normal(vh, n);
470  }
471 
472  if ( options_.vertex_has_color() ) {
473  if ( userOptions_.color_is_float() ) {
474  _opt += Options::ColorFloat;
475  //with alpha
476  if ( options_.color_has_alpha() ){
477  readValue(_in, cAf[0]);
478  readValue(_in, cAf[1]);
479  readValue(_in, cAf[2]);
480  readValue(_in, cAf[3]);
481 
482  if ( userOptions_.vertex_has_color() )
483  _bi.set_color( vh, cAf );
484  }else{
485 
486  //without alpha
487  readValue(_in, cf[0]);
488  readValue(_in, cf[1]);
489  readValue(_in, cf[2]);
490 
491  if ( userOptions_.vertex_has_color() )
492  _bi.set_color( vh, cf );
493  }
494  } else {
495  //with alpha
496  if ( options_.color_has_alpha() ){
497  readValue(_in, cA[0]);
498  readValue(_in, cA[1]);
499  readValue(_in, cA[2]);
500  readValue(_in, cA[3]);
501 
502  if ( userOptions_.vertex_has_color() )
503  _bi.set_color( vh, Vec4uc( cA ) );
504  }else{
505  //without alpha
506  readValue(_in, c[0]);
507  readValue(_in, c[1]);
508  readValue(_in, c[2]);
509 
510  if ( userOptions_.vertex_has_color() )
511  _bi.set_color( vh, Vec3uc( c ) );
512  }
513  }
514  }
515 
516  if ( options_.vertex_has_texcoord()) {
517  readValue(_in, t[0]);
518  readValue(_in, t[1]);
519 
520  if ( userOptions_.vertex_has_texcoord() )
521  _bi.set_texcoord(vh, t);
522  }
523  }
524 
525  // faces
526  // #N <v1> <v2> .. <v(n-1)> [color spec]
527  // So far color spec is unsupported!
528  for (i=0; i<nF; ++i)
529  {
530  readValue(_in, nV);
531 
532  if (nV == 3)
533  {
534  vhandles.resize(3);
535  readValue(_in, j);
536  readValue(_in, k);
537  readValue(_in, l);
538 
539  vhandles[0] = VertexHandle(j);
540  vhandles[1] = VertexHandle(k);
541  vhandles[2] = VertexHandle(l);
542  } else {
543  vhandles.clear();
544  for (j=0; j<nV; ++j)
545  {
546  readValue(_in, idx);
547  vhandles.push_back(VertexHandle(idx));
548  }
549  }
550 
551  FaceHandle fh = _bi.add_face(vhandles);
552 
553  //face color
554  if ( _opt.face_has_color() ) {
555  if ( userOptions_.color_is_float() ) {
556  _opt += Options::ColorFloat;
557  //with alpha
558  if ( options_.color_has_alpha() ){
559  readValue(_in, cAf[0]);
560  readValue(_in, cAf[1]);
561  readValue(_in, cAf[2]);
562  readValue(_in, cAf[3]);
563 
564  if ( userOptions_.face_has_color() )
565  _bi.set_color( fh , cAf );
566  }else{
567  //without alpha
568  readValue(_in, cf[0]);
569  readValue(_in, cf[1]);
570  readValue(_in, cf[2]);
571 
572  if ( userOptions_.face_has_color() )
573  _bi.set_color( fh, cf );
574  }
575  } else {
576  //with alpha
577  if ( options_.color_has_alpha() ){
578  readValue(_in, cA[0]);
579  readValue(_in, cA[1]);
580  readValue(_in, cA[2]);
581  readValue(_in, cA[3]);
582 
583  if ( userOptions_.face_has_color() )
584  _bi.set_color( fh , Vec4uc( cA ) );
585  }else{
586  //without alpha
587  readValue(_in, c[0]);
588  readValue(_in, c[1]);
589  readValue(_in, c[2]);
590 
591  if ( userOptions_.face_has_color() )
592  _bi.set_color( fh, Vec3uc( c ) );
593  }
594  }
595  }
596 
597  }
598 
599  // File was successfully parsed.
600  return true;
601 
602 }
603 
604 
605 //-----------------------------------------------------------------------------
606 
607 
608 bool _OFFReader_::can_u_read(const std::string& _filename) const
609 {
610  // !!! Assuming BaseReader::can_u_parse( std::string& )
611  // does not call BaseReader::read_magic()!!!
612  if (BaseReader::can_u_read(_filename))
613  {
614  std::ifstream ifs(_filename.c_str());
615  if (ifs.is_open() && can_u_read(ifs))
616  {
617  ifs.close();
618  return true;
619  }
620  }
621  return false;
622 }
623 
624 
625 //-----------------------------------------------------------------------------
626 
627 
628 bool _OFFReader_::can_u_read(std::istream& _is) const
629 {
630  options_.cleanup();
631 
632  // read 1st line
633  char line[LINE_LEN], *p;
634  _is.getline(line, LINE_LEN);
635  p = line;
636 
637  std::streamsize remainingChars = _is.gcount();
638 
639  bool vertexDimensionTooHigh = false;
640 
641  // check header: [ST][C][N][4][n]OFF BINARY
642 
643  if ( ( remainingChars > 1 ) && ( p[0] == 'S' && p[1] == 'T') )
644  { options_ += Options::VertexTexCoord; p += 2; remainingChars -= 2; }
645 
646  if ( ( remainingChars > 0 ) && ( p[0] == 'C') )
647  { options_ += Options::VertexColor;
648  options_ += Options::FaceColor; ++p; --remainingChars; }
649 
650  if ( ( remainingChars > 0 ) && ( p[0] == 'N') )
651  { options_ += Options::VertexNormal; ++p; --remainingChars; }
652 
653  if ( ( remainingChars > 0 ) && (p[0] == '4' ) )
654  { vertexDimensionTooHigh = true; ++p; --remainingChars; }
655 
656  if ( ( remainingChars > 0 ) && ( p[0] == 'n') )
657  { vertexDimensionTooHigh = true; ++p; --remainingChars; }
658 
659  if ( ( remainingChars < 3 ) || (!(p[0] == 'O' && p[1] == 'F' && p[2] == 'F') ) )
660  return false;
661 
662  p += 4;
663 
664  // Detect possible garbage and make sure, we don't have an underflow
665  if ( remainingChars >= 4 )
666  remainingChars -= 4;
667  else
668  remainingChars = 0;
669 
670  if ( ( remainingChars >= 6 ) && ( strncmp(p, "BINARY", 6) == 0 ) )
671  options_+= Options::Binary;
672 
673  // vertex Dimensions != 3 are currently not supported
674  if (vertexDimensionTooHigh)
675  return false;
676 
677  return true;
678 }
679 
680 
681 //=============================================================================
682 } // namespace IO
683 } // namespace OpenMesh
684 //=============================================================================
Swap byte order in binary mode.
Definition: Options.hh:103
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
Handle for a face entity.
Definition: Handles.hh:141
Has (r) / store (w) vertex colors.
Definition: Options.hh:105
void clear(void)
Clear all bits.
Definition: Options.hh:147
Has (r) / store (w) face colors.
Definition: Options.hh:109
Has (r) / store (w) float values for colors (currently only implemented for PLY and OFF files) ...
Definition: Options.hh:112
float float32_t
Definition: SR_types.hh:92
Handle for a vertex entity.
Definition: Handles.hh:120
Has (r) / store (w) alpha values for colors.
Definition: Options.hh:111
bool register_module(BaseReader *_bl)
Definition: IOManager.hh:217
bool read(const std::string &_filename, BaseImporter &_bi, Options &_opt) override
Definition: OFFReader.cc:106
void cleanup(void)
Restore state after default constructor.
Definition: Options.hh:143
unsigned int uint32_t
Definition: SR_types.hh:85
Set options for reader/writer modules.
Definition: Options.hh:90
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:608
Has (r) / store (w) vertex normals.
Definition: Options.hh:104
_IOManager_ & IOManager()
Definition: IOManager.cc:72
_OFFReader_ __OFFReaderInstance
Declare the single entity of the OFF reader.
Definition: OFFReader.cc:88
Set binary mode for r/w.
Definition: Options.hh:100
Has (r) / store (w) texture coordinates.
Definition: Options.hh:106