Developer Documentation

- Main Page
- Related Pages
- Modules
- Namespaces
- Classes
- Files

- File List
- File Members

All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
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
128 _OBJReader_::
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
285 bool
286 _OBJReader_::
287 read(std::istream& _in, BaseImporter& _bi, Options& _opt)
288 {
289
290 std::string line;
291 std::string keyWrd;
292
293 float x, y, z, u, v, w;
294 float r, g, b;
295 BaseImporter::VHandles vhandles;
296 std::vector<Vec3f> normals;
297 std::vector<Vec3f> colors;
298 std::vector<Vec3f> texcoords3d, face_texcoords3d;
299 std::vector<Vec2f> texcoords, face_texcoords;
300 std::vector<VertexHandle> vertexHandles;
301
302 std::string matname;
303
304 std::stringstream stream, lineData, tmp;
305
306
307 // Options supplied by the user
308 Options userOptions = _opt;
309
310 // Options collected via file parsing
311 Options fileOptions;
312
313
314 while( _in && !_in.eof() )
315 {
316 std::getline(_in,line);
317 if ( _in.bad() ){
318 omerr() << " Warning! Could not read file properly!\n";
319 return false;
320 }
321
322 // Trim Both leading and trailing spaces
323 trimString(line);
324
325 // comment
326 if ( line.size() == 0 || line[0] == '#' || isspace(line[0]) ) {
327 continue;
328 }
329
330 stream.str(line);
331 stream.clear();
332
333 stream >> keyWrd;
334
335 // material file
336 if (keyWrd == "mtllib")
337 {
338 std::string matFile;
339
340 // Get the rest of the line, removing leading or trailing spaces
341 // This will define the filename of the texture
342 std::getline(stream,matFile);
343 trimString(matFile);
344
345 matFile = path_ + matFile;
346
347 //omlog() << "Load material file " << matFile << std::endl;
348
349 std::fstream matStream( matFile.c_str(), std::ios_base::in );
350
351 if ( matStream ){
352
353 if ( !read_material( matStream ) )
354 omerr() << " Warning! Could not read file properly!\n";
355 matStream.close();
356
357 }else
358 omerr() << " Warning! Material file '" << matFile << "' not found!\n";
359
360 //omlog() << " " << materials_.size() << " materials loaded.\n";
361
362 for ( MaterialList::iterator material = materials_.begin(); material != materials_.end(); ++material )
363 {
364 // Save the texture information in a property
365 if ( (*material).second.has_map_Kd() )
366 _bi.add_texture_information( (*material).second.map_Kd_index() , (*material).second.map_Kd() );
367 }
368
369 }
370
371 // usemtl
372 else if (keyWrd == "usemtl")
373 {
374 stream >> matname;
375 if (materials_.find(matname)==materials_.end())
376 {
377 omerr() << "Warning! Material '" << matname
378 << "' not defined in material file.\n";
379 matname="";
380 }
381 }
382
383 // vertex
384 else if (keyWrd == "v")
385 {
386 stream >> x; stream >> y; stream >> z;
387
388 if ( !stream.fail() )
389 {
390 vertexHandles.push_back(_bi.add_vertex(OpenMesh::Vec3f(x,y,z)));
391 stream >> r; stream >> g; stream >> b;
392
393 if ( !stream.fail() )
394 {
395 if ( userOptions.vertex_has_color() ) {
396 fileOptions += Options::VertexColor;
397 colors.push_back(OpenMesh::Vec3f(r,g,b));
398 }
399 }
400 }
401 }
402
403 // texture coord
404 else if (keyWrd == "vt")
405 {
406 stream >> u; stream >> v;
407
408 if ( !stream.fail() ){
409
410 if ( userOptions.vertex_has_texcoord() || userOptions.face_has_texcoord() ) {
411 texcoords.push_back(OpenMesh::Vec2f(u, v));
412
413 // Can be used for both!
414 fileOptions += Options::VertexTexCoord;
415 fileOptions += Options::FaceTexCoord;
416
417 // try to read the w component as it is optional
418 stream >> w;
419 if ( !stream.fail() )
420 texcoords3d.push_back(OpenMesh::Vec3f(u, v, w));
421
422 }
423
424 }else{
425 omerr() << "Only single 2D or 3D texture coordinate per vertex"
426 << "allowed!" << std::endl;
427 return false;
428 }
429 }
430
431 // color per vertex
432 else if (keyWrd == "vc")
433 {
434 stream >> r; stream >> g; stream >> b;
435
436 if ( !stream.fail() ){
437 if ( userOptions.vertex_has_color() ) {
438 colors.push_back(OpenMesh::Vec3f(r,g,b));
439 fileOptions += Options::VertexColor;
440 }
441 }
442 }
443
444 // normal
445 else if (keyWrd == "vn")
446 {
447 stream >> x; stream >> y; stream >> z;
448
449 if ( !stream.fail() ) {
450 if (userOptions.vertex_has_normal() ){
451 normals.push_back(OpenMesh::Vec3f(x,y,z));
452 fileOptions += Options::VertexNormal;
453 }
454 }
455 }
456
457
458 // face
459 else if (keyWrd == "f")
460 {
461 int component(0), nV(0);
462 int value;
463
464 vhandles.clear();
465 face_texcoords.clear();
466
467 // read full line after detecting a face
468 std::string faceLine;
469 std::getline(stream,faceLine);
470 lineData.str( faceLine );
471 lineData.clear();
472
473 FaceHandle fh;
474 BaseImporter::VHandles faceVertices;
475
476 // work on the line until nothing left to read
477 while ( !lineData.eof() )
478 {
479 // read one block from the line ( vertex/texCoord/normal )
480 std::string vertex;
481 lineData >> vertex;
482
483 do{
484
485 //get the component (vertex/texCoord/normal)
486 size_t found=vertex.find("/");
487
488 // parts are seperated by '/' So if no '/' found its the last component
489 if( found != std::string::npos ){
490
491 // read the index value
492 tmp.str( vertex.substr(0,found) );
493 tmp.clear();
494
495 // If we get an empty string this property is undefined in the file
496 if ( vertex.substr(0,found).empty() ) {
497 // Switch to next field
498 vertex = vertex.substr(found+1);
499
500 // Now we are at the next component
501 ++component;
502
503 // Skip further processing of this component
504 continue;
505 }
506
507 // Read current value
508 tmp >> value;
509
510 // remove the read part from the string
511 vertex = vertex.substr(found+1);
512
513 } else {
514
515 // last component of the vertex, read it.
516 tmp.str( vertex );
517 tmp.clear();
518 tmp >> value;
519
520 // Clear vertex after finished reading the line
521 vertex="";
522
523 // Nothing to read here ( garbage at end of line )
524 if ( tmp.fail() ) {
525 continue;
526 }
527 }
528
529 // store the component ( each component is referenced by the index here! )
530 switch (component)
531 {
532 case 0: // vertex
533 if ( value < 0 ) {
534 // Calculation of index :
535 // -1 is the last vertex in the list
536 // As obj counts from 1 and not zero add +1
537 value = int(_bi.n_vertices() + value + 1);
538 }
539 // Obj counts from 1 and not zero .. array counts from zero therefore -1
540 vhandles.push_back(VertexHandle(value-1));
541 faceVertices.push_back(VertexHandle(value-1));
542 if (fileOptions.vertex_has_color() )
543 _bi.set_color(vhandles.back(), colors[value-1]);
544 break;
545
546 case 1: // texture coord
547 if ( value < 0 ) {
548 // Calculation of index :
549 // -1 is the last vertex in the list
550 // As obj counts from 1 and not zero add +1
551 value = int(texcoords.size()) + value + 1;
552 }
553 assert(!vhandles.empty());
554
555
556 if ( fileOptions.vertex_has_texcoord() && userOptions.vertex_has_texcoord() ) {
557
558 if (!texcoords.empty() && (unsigned int) (value - 1) < texcoords.size()) {
559 // Obj counts from 1 and not zero .. array counts from zero therefore -1
560 _bi.set_texcoord(vhandles.back(), texcoords[value - 1]);
561 if(!texcoords3d.empty() && (unsigned int) (value -1) < texcoords3d.size())
562 _bi.set_texcoord(vhandles.back(), texcoords3d[value - 1]);
563 } else {
564 omerr() << "Error setting Texture coordinates" << std::endl;
565 }
566
567 }
568
569 if (fileOptions.face_has_texcoord() && userOptions.face_has_texcoord() ) {
570
571 if (!texcoords.empty() && (unsigned int) (value - 1) < texcoords.size()) {
572 face_texcoords.push_back( texcoords[value-1] );
573 if(!texcoords3d.empty() && (unsigned int) (value -1) < texcoords3d.size())
574 face_texcoords3d.push_back( texcoords3d[value-1] );
575 } else {
576 omerr() << "Error setting Texture coordinates" << std::endl;
577 }
578 }
579
580
581 break;
582
583 case 2: // normal
584 if ( value < 0 ) {
585 // Calculation of index :
586 // -1 is the last vertex in the list
587 // As obj counts from 1 and not zero add +1
588 value = int(normals.size()) + value + 1;
589 }
590
591 // Obj counts from 1 and not zero .. array counts from zero therefore -1
592 if (fileOptions.vertex_has_normal() ) {
593 assert(!vhandles.empty());
594 assert((unsigned int)(value-1) < normals.size());
595 _bi.set_normal(vhandles.back(), normals[value-1]);
596 }
597 break;
598 }
599
600 // Prepare for reading next component
601 ++component;
602
603 // Read until line does not contain any other info
604 } while ( !vertex.empty() );
605
606 component = 0;
607 nV++;
608
609 }
610
611 // note that add_face can possibly triangulate the faces, which is why we have to
612 // store the current number of faces first
613 size_t n_faces = _bi.n_faces();
614 remove_duplicated_vertices(faceVertices);
615
616 //A minimum of three vertices are required.
617 if (faceVertices.size() > 2)
618 fh = _bi.add_face(faceVertices);
619
620 if (!vhandles.empty() && fh.is_valid() )
621 {
622 _bi.add_face_texcoords(fh, vhandles[0], face_texcoords);
623 _bi.add_face_texcoords(fh, vhandles[0], face_texcoords3d);
624 }
625
626 if ( !matname.empty() )
627 {
628 std::vector<FaceHandle> newfaces;
629
630 for( size_t i=0; i < _bi.n_faces()-n_faces; ++i )
631 newfaces.push_back(FaceHandle(int(n_faces+i)));
632
633 Material& mat = materials_[matname];
634
635 if ( mat.has_Kd() ) {
636 Vec3uc fc = color_cast<Vec3uc, Vec3f>(mat.Kd());
637
638 if ( userOptions.face_has_color()) {
639
640 for (std::vector<FaceHandle>::iterator it = newfaces.begin(); it != newfaces.end(); ++it)
641 _bi.set_color(*it, fc);
642
643 fileOptions += Options::FaceColor;
644 }
645 }
646
647 // Set the texture index in the face index property
648 if ( mat.has_map_Kd() ) {
649
650 if (userOptions.face_has_texcoord()) {
651
652 for (std::vector<FaceHandle>::iterator it = newfaces.begin(); it != newfaces.end(); ++it)
653 _bi.set_face_texindex(*it, mat.map_Kd_index());
654
655 fileOptions += Options::FaceTexCoord;
656
657 }
658
659 } else {
660
661 // If we don't have the info, set it to no texture
662 if (userOptions.face_has_texcoord()) {
663
664 for (std::vector<FaceHandle>::iterator it = newfaces.begin(); it != newfaces.end(); ++it)
665 _bi.set_face_texindex(*it, 0);
666
667 }
668 }
669
670 } else {
671 std::vector<FaceHandle> newfaces;
672
673 for( size_t i=0; i < _bi.n_faces()-n_faces; ++i )
674 newfaces.push_back(FaceHandle(int(n_faces+i)));
675
676 // Set the texture index to zero as we don't have any information
677 if ( userOptions.face_has_texcoord() )
678 for (std::vector<FaceHandle>::iterator it = newfaces.begin(); it != newfaces.end(); ++it)
679 _bi.set_face_texindex(*it, 0);
680 }
681
682 }
683
684 }
685
686 // If we do not have any faces,
687 // assume this is a point cloud and read the normals and colors directly
688 if (_bi.n_faces() == 0)
689 {
690 int i = 0;
691 // add normal per vertex
692
693 if (normals.size() == _bi.n_vertices()) {
694 if ( fileOptions.vertex_has_normal() && userOptions.vertex_has_normal() ) {
695 for (std::vector<VertexHandle>::iterator it = vertexHandles.begin(); it != vertexHandles.end(); ++it, i++)
696 _bi.set_normal(*it, normals[i]);
697 }
698 }
699
700 // add color per vertex
701 i = 0;
702 if (colors.size() >= _bi.n_vertices())
703 if (fileOptions.vertex_has_color() && userOptions.vertex_has_color()) {
704 for (std::vector<VertexHandle>::iterator it = vertexHandles.begin(); it != vertexHandles.end(); ++it, i++)
705 _bi.set_color(*it, colors[i]);
706 }
707
708 }
709
710 // Return, what we actually read
711 _opt = fileOptions;
712
713 return true;
714 }
715
716
717 //=============================================================================
718 } // namespace IO
719 } // namespace OpenMesh
720 //=============================================================================
OpenMesh::IO::Options::VertexNormal
Has (r) / store (w) vertex normals.
Definition: Options.hh:109
OpenMesh
Definition: MeshItems.hh:64
OpenMesh::IO::_IOManager_::register_module
bool register_module(BaseReader *_bl)
Definition: IOManager.hh:222
OpenMesh::IO::Options::FaceColor
Has (r) / store (w) face colors.
Definition: Options.hh:114
OpenMesh::BaseHandle::is_valid
bool is_valid() const
The handle is valid iff the index is not equal to -1.
Definition: Handles.hh:77
OpenMesh::IO::Options::VertexTexCoord
Has (r) / store (w) texture coordinates.
Definition: Options.hh:111
OpenMesh::IO::Options
Set options for reader/writer modules.
Definition: Options.hh:95
OpenMesh::IO::_OBJReader_::read
bool read(const std::string &_filename, BaseImporter &_bi, Options &_opt)
Definition: OBJReader.cc:129
OpenMesh::IO::Options::FaceTexCoord
Has (r) / store (w) face texture coordinates.
Definition: Options.hh:115
OpenMesh::VectorT< float, 3 >
OpenMesh::VertexHandle
Handle for a vertex entity.
Definition: Handles.hh:125
OpenMesh::FaceHandle
Handle for a face entity.
Definition: Handles.hh:146
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:181
OpenMesh::Vec3uc
VectorT< unsigned char, 3 > Vec3uc
Definition: Vector11T.hh:759
OpenMesh::IO::Options::VertexColor
Has (r) / store (w) vertex colors.
Definition: Options.hh:110
OpenMesh::IO::IOManager
_IOManager_ & IOManager()
Definition: IOManager.cc:77
OpenMesh::IO::BaseImporter
Definition: BaseImporter.hh:88