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
51 #include <OpenMesh/Core/System/config.h>
52 #include <OpenMesh/Core/System/omstream.hh>
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
88 _OFFReader_ __OFFReaderInstance;
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 //=============================================================================
OpenMesh::IO::Options::VertexColor
Has (r) / store (w) vertex colors.
Definition: Options.hh:105
OpenMesh::IO::Options::Swap
Swap byte order in binary mode.
Definition: Options.hh:103
OpenMesh::VertexHandle
Handle for a vertex entity.
Definition: Handles.hh:120
OpenMesh::IO::BaseReader::can_u_read
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
OpenMesh::IO::_IOManager_::register_module
bool register_module(BaseReader *_bl)
Definition: IOManager.hh:217
OpenMesh::IO::Options::FaceColor
Has (r) / store (w) face colors.
Definition: Options.hh:109
OpenMesh::IO::uint32_t
unsigned int uint32_t
Definition: SR_types.hh:85
OpenMesh::IO::Options::ColorFloat
Has (r) / store (w) float values for colors (currently only implemented for PLY and OFF files) ...
Definition: Options.hh:112
OpenMesh::IO::IOManager
_IOManager_ & IOManager()
Definition: IOManager.cc:72
config.h
OpenMesh::IO::Options::VertexTexCoord
Has (r) / store (w) texture coordinates.
Definition: Options.hh:106
OpenMesh::IO::_OFFReader_::read
bool read(const std::string &_filename, BaseImporter &_bi, Options &_opt)
Definition: OFFReader.cc:106
VectorT< signed int, 4 >
OpenMesh::FaceHandle
Handle for a face entity.
Definition: Handles.hh:141
OpenMesh::IO::Options::clear
void clear(void)
Clear all bits.
Definition: Options.hh:147
OpenMesh::IO::Options::cleanup
void cleanup(void)
Restore state after default constructor.
Definition: Options.hh:143
OpenMesh::IO::_OFFReader_
Definition: OFFReader.hh:112
OpenMesh::IO::Options
Set options for reader/writer modules.
Definition: Options.hh:90
OpenMesh::IO::Options::ColorAlpha
Has (r) / store (w) alpha values for colors.
Definition: Options.hh:111
OpenMesh::VectorT< float, 3 >
OpenMesh::IO::Options::Binary
Set binary mode for r/w.
Definition: Options.hh:100
OpenMesh::IO::Options::VertexNormal
Has (r) / store (w) vertex normals.
Definition: Options.hh:104
OpenMesh::IO::_OFFReader_::can_u_read
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: OFFReader.cc:608
OpenMesh::IO::BaseImporter
Definition: BaseImporter.hh:83
OpenMesh::IO::float32_t
float float32_t
Definition: SR_types.hh:92
OpenMesh
Definition: MeshItems.hh:59
omstream.hh
OpenMesh::IO::__OFFReaderInstance
_OFFReader_ __OFFReaderInstance
Declare the single entity of the OFF reader.
Definition: OFFReader.cc:88