Developer Documentation

OBJReader.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 //== INCLUDES =================================================================
46
47
48 // OpenMesh
49 #include <OpenMesh/Core/IO/reader/OBJReader.hh>
50 #include <OpenMesh/Core/IO/IOManager.hh>
51 #include <OpenMesh/Core/Utils/vector_cast.hh>
52 #include <OpenMesh/Core/Utils/color_cast.hh>
53 // STL
54 #if defined(OM_CC_MIPS)
55 # include <ctype.h>
57 #elif defined(_STLPORT_VERSION) && (_STLPORT_VERSION==0x460)
58 # include <cctype>
59 #else
60 using std::isspace;
61 #endif
62
63 #ifndef WIN32
64 #endif
65
66 #include <fstream>
67
68 //=== NAMESPACES ==============================================================
69
70
71 namespace OpenMesh {
72 namespace IO {
73
74
75 //=== INSTANCIATE =============================================================
76
77
78 _OBJReader_ __OBJReaderInstance;
79 _OBJReader_& OBJReader() { return __OBJReaderInstance; }
80
81
82 //=== IMPLEMENTATION ==========================================================
83
84 //-----------------------------------------------------------------------------
85
86 void trimString( std::string& _string) {
87 // Trim Both leading and trailing spaces
88
89 size_t start = _string.find_first_not_of(" \t\r\n");
90 size_t end = _string.find_last_not_of(" \t\r\n");
91
92 if(( std::string::npos == start ) || ( std::string::npos == end))
93 _string = "";
94 else
95 _string = _string.substr( start, end-start+1 );
96 }
97
98 //-----------------------------------------------------------------------------
99
100 // remove duplicated indices from one face
101 void remove_duplicated_vertices(BaseImporter::VHandles& _indices)
102 {
103 BaseImporter::VHandles::iterator endIter = _indices.end();
104 for (BaseImporter::VHandles::iterator iter = _indices.begin(); iter != endIter; ++iter)
105 endIter = std::remove(iter+1, endIter, *(iter));
106
107 _indices.erase(endIter,_indices.end());
108 }
109
110 //-----------------------------------------------------------------------------
111
112 _OBJReader_::
113 _OBJReader_()
114 {
115 IOManager().register_module(this);
116 }
117
118
119 //-----------------------------------------------------------------------------
120
121
122 bool
123 _OBJReader_::
124 read(const std::string& _filename, BaseImporter& _bi, Options& _opt)
125 {
126 std::fstream in( _filename.c_str(), std::ios_base::in );
127
128 if (!in.is_open() || !in.good())
129 {
130 omerr() << "[OBJReader] : cannot not open file "
131 << _filename
132 << std::endl;
133 return false;
134 }
135
136 {
137 #if defined(WIN32)
138 std::string::size_type dot = _filename.find_last_of("\\/");
139 #else
140 std::string::size_type dot = _filename.rfind("/");
141 #endif
142 path_ = (dot == std::string::npos)
143 ? "./"
144 : std::string(_filename.substr(0,dot+1));
145 }
146
147 bool result = read(in, _bi, _opt);
148
149 in.close();
150 return result;
151 }
152
153 //-----------------------------------------------------------------------------
154
155 bool
156 _OBJReader_::
157 read_material(std::fstream& _in)
158 {
159 std::string line;
160 std::string keyWrd;
161 std::string textureName;
162
163 std::stringstream stream;
164
165 std::string key;
166 Material mat;
167 float f1,f2,f3;
168 bool indef = false;
169 int textureId = 1;
170
171
172 materials_.clear();
173 mat.cleanup();
174
175 while( _in && !_in.eof() )
176 {
177 std::getline(_in,line);
178 if ( _in.bad() ){
179 omerr() << " Warning! Could not read file properly!\n";
180 return false;
181 }
182
183 if ( line.empty() )
184 continue;
185
186 stream.str(line);
187 stream.clear();
188
189 stream >> keyWrd;
190
191 if( ( isspace(line[0]) && line[0] != '\t' ) || line[0] == '#' )
192 {
193 if (indef && !key.empty() && mat.is_valid())
194 {
195 materials_[key] = mat;
196 mat.cleanup();
197 }
198 }
199
200 else if (keyWrd == "newmtl") // begin new material definition
201 {
202 stream >> key;
203 indef = true;
204 }
205
206 else if (keyWrd == "Kd") // diffuse color
207 {
208 stream >> f1; stream >> f2; stream >> f3;
209
210 if( !stream.fail() )
211 mat.set_Kd(f1,f2,f3);
212 }
213
214 else if (keyWrd == "Ka") // ambient color
215 {
216 stream >> f1; stream >> f2; stream >> f3;
217
218 if( !stream.fail() )
219 mat.set_Ka(f1,f2,f3);
220 }
221
222 else if (keyWrd == "Ks") // specular color
223 {
224 stream >> f1; stream >> f2; stream >> f3;
225
226 if( !stream.fail() )
227 mat.set_Ks(f1,f2,f3);
228 }
229 #if 0
230 else if (keyWrd == "illum") // diffuse/specular shading model
231 {
232 ; // just skip this
233 }
234
235 else if (keyWrd == "Ns") // Shininess [0..200]
236 {
237 ; // just skip this
238 }
239
240 else if (keyWrd == "map_") // map images
241 {
242 // map_Ks, specular map
243 // map_Ka, ambient map
244 // map_Bump, bump map
245 // map_d, opacity map
246 ; // just skip this
247 }
248 #endif
249 else if (keyWrd == "map_Kd" ) {
250 // Get the rest of the line, removing leading or trailing spaces
251 // This will define the filename of the texture
252 std::getline(stream,textureName);
253 trimString(textureName);
254 if ( ! textureName.empty() )
255 mat.set_map_Kd( textureName, textureId++ );
256 }
257 else if (keyWrd == "Tr") // transparency value
258 {
259 stream >> f1;
260
261 if( !stream.fail() )
262 mat.set_Tr(f1);
263 }
264 else if (keyWrd == "d") // transparency value
265 {
266 stream >> f1;
267
268 if( !stream.fail() )
269 mat.set_Tr(f1);
270 }
271
272 if ( _in && indef && mat.is_valid() && !key.empty())
273 materials_[key] = mat;
274 }
275 return true;
276 }
277 //-----------------------------------------------------------------------------
278
279 bool
280 _OBJReader_::
281 read_vertices(std::istream& _in, BaseImporter& _bi, Options& _opt,
282 std::vector<Vec3f> & normals,
283 std::vector<Vec3f> & colors,
284 std::vector<Vec3f> & texcoords3d,
285 std::vector<Vec2f> & texcoords,
286 std::vector<VertexHandle> & vertexHandles,
287 Options & fileOptions)
288 {
289 float x, y, z, u, v, w;
290 float r, g, b;
291
292 std::string line;
293 std::string keyWrd;
294
295 std::stringstream stream;
296
297
298 // Options supplied by the user
299 const Options & userOptions = _opt;
300
301 while( _in && !_in.eof() )
302 {
303 std::getline(_in,line);
304 if ( _in.bad() ){
305 omerr() << " Warning! Could not read file properly!\n";
306 return false;
307 }
308
309 // Trim Both leading and trailing spaces
310 trimString(line);
311
312 // comment
313 if ( line.size() == 0 || line[0] == '#' || isspace(line[0]) ) {
314 continue;
315 }
316
317 stream.str(line);
318 stream.clear();
319
320 stream >> keyWrd;
321
322 // vertex
323 if (keyWrd == "v")
324 {
325 stream >> x; stream >> y; stream >> z;
326
327 if ( !stream.fail() )
328 {
329 vertexHandles.push_back(_bi.add_vertex(OpenMesh::Vec3f(x,y,z)));
330 stream >> r; stream >> g; stream >> b;
331
332 if ( !stream.fail() )
333 {
334 if ( userOptions.vertex_has_color() ) {
335 fileOptions += Options::VertexColor;
336 colors.push_back(OpenMesh::Vec3f(r,g,b));
337 }
338 }
339 }
340 }
341
342 // texture coord
343 else if (keyWrd == "vt")
344 {
345 stream >> u; stream >> v;
346
347 if ( !stream.fail() ){
348
349 if ( userOptions.vertex_has_texcoord() || userOptions.face_has_texcoord() ) {
350 texcoords.push_back(OpenMesh::Vec2f(u, v));
351
352 // Can be used for both!
353 fileOptions += Options::VertexTexCoord;
354 fileOptions += Options::FaceTexCoord;
355
356 // try to read the w component as it is optional
357 stream >> w;
358 if ( !stream.fail() )
359 texcoords3d.push_back(OpenMesh::Vec3f(u, v, w));
360
361 }
362
363 }else{
364 omerr() << "Only single 2D or 3D texture coordinate per vertex"
365 << "allowed!" << std::endl;
366 return false;
367 }
368 }
369
370 // color per vertex
371 else if (keyWrd == "vc")
372 {
373 stream >> r; stream >> g; stream >> b;
374
375 if ( !stream.fail() ){
376 if ( userOptions.vertex_has_color() ) {
377 colors.push_back(OpenMesh::Vec3f(r,g,b));
378 fileOptions += Options::VertexColor;
379 }
380 }
381 }
382
383 // normal
384 else if (keyWrd == "vn")
385 {
386 stream >> x; stream >> y; stream >> z;
387
388 if ( !stream.fail() ) {
389 if (userOptions.vertex_has_normal() ){
390 normals.push_back(OpenMesh::Vec3f(x,y,z));
391 fileOptions += Options::VertexNormal;
392 }
393 }
394 }
395 }
396
397 return true;
398 }
399
400 //-----------------------------------------------------------------------------
401
402 bool
403 _OBJReader_::
404 read(std::istream& _in, BaseImporter& _bi, Options& _opt)
405 {
406 std::string line;
407 std::string keyWrd;
408
409 std::vector<Vec3f> normals;
410 std::vector<Vec3f> colors;
411 std::vector<Vec3f> texcoords3d;
412 std::vector<Vec2f> texcoords;
413 std::vector<VertexHandle> vertexHandles;
414
415 BaseImporter::VHandles vhandles;
416 std::vector<Vec3f> face_texcoords3d;
417 std::vector<Vec2f> face_texcoords;
418
419 std::string matname;
420
421 std::stringstream stream, lineData, tmp;
422
423
424 // Options supplied by the user
425 Options userOptions = _opt;
426
427 // Options collected via file parsing
428 Options fileOptions;
429
430 // pass 1: read vertices
431 if ( !read_vertices(_in, _bi, _opt,
432 normals, colors, texcoords3d, texcoords,
433 vertexHandles, fileOptions) ){
434 return false;
435 }
436
437 // reset stream for second pass
438 _in.clear();
439 _in.seekg(0, std::ios::beg);
440
441 int nCurrentPositions = 0,
442 nCurrentTexcoords = 0,
443 nCurrentNormals = 0;
444
445 // pass 2: read faces
446 while( _in && !_in.eof() )
447 {
448 std::getline(_in,line);
449 if ( _in.bad() ){
450 omerr() << " Warning! Could not read file properly!\n";
451 return false;
452 }
453
454 // Trim Both leading and trailing spaces
455 trimString(line);
456
457 // comment
458 if ( line.size() == 0 || line[0] == '#' || isspace(line[0]) ) {
459 continue;
460 }
461
462 stream.str(line);
463 stream.clear();
464
465 stream >> keyWrd;
466
467 // material file
468 if (keyWrd == "mtllib")
469 {
470 std::string matFile;
471
472 // Get the rest of the line, removing leading or trailing spaces
473 // This will define the filename of the texture
474 std::getline(stream,matFile);
475 trimString(matFile);
476
477 matFile = path_ + matFile;
478
479 //omlog() << "Load material file " << matFile << std::endl;
480
481 std::fstream matStream( matFile.c_str(), std::ios_base::in );
482
483 if ( matStream ){
484
485 if ( !read_material( matStream ) )
486 omerr() << " Warning! Could not read file properly!\n";
487 matStream.close();
488
489 }else
490 omerr() << " Warning! Material file '" << matFile << "' not found!\n";
491
492 //omlog() << " " << materials_.size() << " materials loaded.\n";
493
494 for ( MaterialList::iterator material = materials_.begin(); material != materials_.end(); ++material )
495 {
496 // Save the texture information in a property
497 if ( (*material).second.has_map_Kd() )
498 _bi.add_texture_information( (*material).second.map_Kd_index() , (*material).second.map_Kd() );
499 }
500
501 }
502
503 // usemtl
504 else if (keyWrd == "usemtl")
505 {
506 stream >> matname;
507 if (materials_.find(matname)==materials_.end())
508 {
509 omerr() << "Warning! Material '" << matname
510 << "' not defined in material file.\n";
511 matname="";
512 }
513 }
514
515 // track current number of parsed vertex attributes,
516 // to allow for OBJs negative indices
517 else if (keyWrd == "v")
518 {
519 ++nCurrentPositions;
520 }
521 else if (keyWrd == "vt")
522 {
523 ++nCurrentTexcoords;
524 }
525 else if (keyWrd == "vn")
526 {
527 ++nCurrentNormals;
528 }
529
530 // faces
531 else if (keyWrd == "f")
532 {
533 int component(0), nV(0);
534 int value;
535
536 vhandles.clear();
537 face_texcoords.clear();
538 face_texcoords3d.clear();
539
540 // read full line after detecting a face
541 std::string faceLine;
542 std::getline(stream,faceLine);
543 lineData.str( faceLine );
544 lineData.clear();
545
546 FaceHandle fh;
547 BaseImporter::VHandles faceVertices;
548
549 // work on the line until nothing left to read
550 while ( !lineData.eof() )
551 {
552 // read one block from the line ( vertex/texCoord/normal )
553 std::string vertex;
554 lineData >> vertex;
555
556 do{
557
558 //get the component (vertex/texCoord/normal)
559 size_t found=vertex.find("/");
560
561 // parts are seperated by '/' So if no '/' found its the last component
562 if( found != std::string::npos ){
563
564 // read the index value
565 tmp.str( vertex.substr(0,found) );
566 tmp.clear();
567
568 // If we get an empty string this property is undefined in the file
569 if ( vertex.substr(0,found).empty() ) {
570 // Switch to next field
571 vertex = vertex.substr(found+1);
572
573 // Now we are at the next component
574 ++component;
575
576 // Skip further processing of this component
577 continue;
578 }
579
580 // Read current value
581 tmp >> value;
582
583 // remove the read part from the string
584 vertex = vertex.substr(found+1);
585
586 } else {
587
588 // last component of the vertex, read it.
589 tmp.str( vertex );
590 tmp.clear();
591 tmp >> value;
592
593 // Clear vertex after finished reading the line
594 vertex="";
595
596 // Nothing to read here ( garbage at end of line )
597 if ( tmp.fail() ) {
598 continue;
599 }
600 }
601
602 // store the component ( each component is referenced by the index here! )
603 switch (component)
604 {
605 case 0: // vertex
606 if ( value < 0 ) {
607 // Calculation of index :
608 // -1 is the last vertex in the list
609 // As obj counts from 1 and not zero add +1
610 value = nCurrentPositions + value + 1;
611 }
612 // Obj counts from 1 and not zero .. array counts from zero therefore -1
613 vhandles.push_back(VertexHandle(value-1));
614 faceVertices.push_back(VertexHandle(value-1));
615 if (fileOptions.vertex_has_color()) {
616 if ((unsigned int)(value - 1) < colors.size()) {
617 _bi.set_color(vhandles.back(), colors[value - 1]);
618 }
619 else {
620 omerr() << "Error setting vertex color" << std::endl;
621 }
622 }
623 break;
624
625 case 1: // texture coord
626 if ( value < 0 ) {
627 // Calculation of index :
628 // -1 is the last vertex in the list
629 // As obj counts from 1 and not zero add +1
630 value = nCurrentTexcoords + value + 1;
631 }
632 assert(!vhandles.empty());
633
634
635 if ( fileOptions.vertex_has_texcoord() && userOptions.vertex_has_texcoord() ) {
636
637 if (!texcoords.empty() && (unsigned int) (value - 1) < texcoords.size()) {
638 // Obj counts from 1 and not zero .. array counts from zero therefore -1
639 _bi.set_texcoord(vhandles.back(), texcoords[value - 1]);
640 if(!texcoords3d.empty() && (unsigned int) (value -1) < texcoords3d.size())
641 _bi.set_texcoord(vhandles.back(), texcoords3d[value - 1]);
642 } else {
643 omerr() << "Error setting Texture coordinates" << std::endl;
644 }
645
646 }
647
648 if (fileOptions.face_has_texcoord() && userOptions.face_has_texcoord() ) {
649
650 if (!texcoords.empty() && (unsigned int) (value - 1) < texcoords.size()) {
651 face_texcoords.push_back( texcoords[value-1] );
652 if(!texcoords3d.empty() && (unsigned int) (value -1) < texcoords3d.size())
653 face_texcoords3d.push_back( texcoords3d[value-1] );
654 } else {
655 omerr() << "Error setting Texture coordinates" << std::endl;
656 }
657 }
658
659
660 break;
661
662 case 2: // normal
663 if ( value < 0 ) {
664 // Calculation of index :
665 // -1 is the last vertex in the list
666 // As obj counts from 1 and not zero add +1
667 value = nCurrentNormals + value + 1;
668 }
669
670 // Obj counts from 1 and not zero .. array counts from zero therefore -1
671 if (fileOptions.vertex_has_normal() ) {
672 assert(!vhandles.empty());
673 if ((unsigned int)(value - 1) < normals.size()) {
674 _bi.set_normal(vhandles.back(), normals[value - 1]);
675 }
676 else {
677 omerr() << "Error setting vertex normal" << std::endl;
678 }
679 }
680 break;
681 }
682
683 // Prepare for reading next component
684 ++component;
685
686 // Read until line does not contain any other info
687 } while ( !vertex.empty() );
688
689 component = 0;
690 nV++;
691
692 }
693
694 // note that add_face can possibly triangulate the faces, which is why we have to
695 // store the current number of faces first
696 size_t n_faces = _bi.n_faces();
697 remove_duplicated_vertices(faceVertices);
698
699 //A minimum of three vertices are required.
700 if (faceVertices.size() > 2)
701 fh = _bi.add_face(faceVertices);
702
703 if (!vhandles.empty() && fh.is_valid() )
704 {
705 _bi.add_face_texcoords(fh, vhandles[0], face_texcoords);
706 _bi.add_face_texcoords(fh, vhandles[0], face_texcoords3d);
707 }
708
709 if ( !matname.empty() )
710 {
711 std::vector<FaceHandle> newfaces;
712
713 for( size_t i=0; i < _bi.n_faces()-n_faces; ++i )
714 newfaces.push_back(FaceHandle(int(n_faces+i)));
715
716 Material& mat = materials_[matname];
717
718 if ( mat.has_Kd() ) {
719 Vec3uc fc = color_cast<Vec3uc, Vec3f>(mat.Kd());
720
721 if ( userOptions.face_has_color()) {
722
723 for (std::vector<FaceHandle>::iterator it = newfaces.begin(); it != newfaces.end(); ++it)
724 _bi.set_color(*it, fc);
725
726 fileOptions += Options::FaceColor;
727 }
728 }
729
730 // Set the texture index in the face index property
731 if ( mat.has_map_Kd() ) {
732
733 if (userOptions.face_has_texcoord()) {
734
735 for (std::vector<FaceHandle>::iterator it = newfaces.begin(); it != newfaces.end(); ++it)
736 _bi.set_face_texindex(*it, mat.map_Kd_index());
737
738 fileOptions += Options::FaceTexCoord;
739
740 }
741
742 } else {
743
744 // If we don't have the info, set it to no texture
745 if (userOptions.face_has_texcoord()) {
746
747 for (std::vector<FaceHandle>::iterator it = newfaces.begin(); it != newfaces.end(); ++it)
748 _bi.set_face_texindex(*it, 0);
749
750 }
751 }
752
753 } else {
754 std::vector<FaceHandle> newfaces;
755
756 for( size_t i=0; i < _bi.n_faces()-n_faces; ++i )
757 newfaces.push_back(FaceHandle(int(n_faces+i)));
758
759 // Set the texture index to zero as we don't have any information
760 if ( userOptions.face_has_texcoord() )
761 for (std::vector<FaceHandle>::iterator it = newfaces.begin(); it != newfaces.end(); ++it)
762 _bi.set_face_texindex(*it, 0);
763 }
764
765 }
766
767 }
768
769 // If we do not have any faces,
770 // assume this is a point cloud and read the normals and colors directly
771 if (_bi.n_faces() == 0)
772 {
773 int i = 0;
774 // add normal per vertex
775
776 if (normals.size() == _bi.n_vertices()) {
777 if ( fileOptions.vertex_has_normal() && userOptions.vertex_has_normal() ) {
778 for (std::vector<VertexHandle>::iterator it = vertexHandles.begin(); it != vertexHandles.end(); ++it, i++)
779 _bi.set_normal(*it, normals[i]);
780 }
781 }
782
783 // add color per vertex
784 i = 0;
785 if (colors.size() >= _bi.n_vertices())
786 if (fileOptions.vertex_has_color() && userOptions.vertex_has_color()) {
787 for (std::vector<VertexHandle>::iterator it = vertexHandles.begin(); it != vertexHandles.end(); ++it, i++)
788 _bi.set_color(*it, colors[i]);
789 }
790
791 }
792
793 // Return, what we actually read
794 _opt = fileOptions;
795
796 return true;
797 }
798
799
800 //=============================================================================
801 } // namespace IO
802 } // namespace OpenMesh
803 //=============================================================================
OpenMesh::IO::Options::VertexColor
Has (r) / store (w) vertex colors.
Definition: Options.hh:105
OpenMesh::VertexHandle
Handle for a vertex entity.
Definition: Handles.hh:120
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::IOManager
_IOManager_ & IOManager()
Definition: IOManager.cc:72
OpenMesh::IO::_OBJReader_::read
bool read(const std::string &_filename, BaseImporter &_bi, Options &_opt)
Definition: OBJReader.cc:124
OpenMesh::dot
osg::Vec3f::ValueType dot(const osg::Vec3f &_v1, const osg::Vec3f &_v2)
Adapter for osg vector member computing a scalar product.
Definition: VectorAdapter.hh:176
OpenMesh::Vec3uc
VectorT< unsigned char, 3 > Vec3uc
Definition: Vector11T.hh:801
OpenMesh::IO::Options::VertexTexCoord
Has (r) / store (w) texture coordinates.
Definition: Options.hh:106
OpenMesh::IO::Options::FaceTexCoord
Has (r) / store (w) face texture coordinates.
Definition: Options.hh:110
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
Set options for reader/writer modules.
Definition: Options.hh:90
OpenMesh::VectorT< float, 3 >
OpenMesh::IO::Options::VertexNormal
Has (r) / store (w) vertex normals.
Definition: Options.hh:104
OpenMesh::BaseHandle::is_valid
bool is_valid() const
The handle is valid iff the index is not negative.
Definition: Handles.hh:72
OpenMesh::IO::BaseImporter
Definition: BaseImporter.hh:83
OpenMesh
Definition: MeshItems.hh:59