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