2 * This program is free software: you can redistribute it and/or modify
3 * it under the terms of the GNU Lesser General Public License as
4 * published by the Free Software Foundation, either version 3 of the
5 * License, or (at your option) any later version.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 * @author Bertrand Kerautret (\c kerautre@loria.fr )
20 * LORIA (CNRS, UMR 7503), University of Nancy, France
24 * Implementation of inline methods defined in MeshReader.h
26 * This file is part of the DGtal library.
29///////////////////////////////////////////////////////////////////////////////
30// IMPLEMENTATION of inline methods.
31///////////////////////////////////////////////////////////////////////////////
33//////////////////////////////////////////////////////////////////////////////
41//////////////////////////////////////////////////////////////////////////////
43#include "DGtal/helpers/StdDefs.h"
44#include "DGtal/io/readers/SurfaceMeshReader.h"
46///////////////////////////////////////////////////////////////////////////////
47// Implementation of inline methods //
52template <typename TPoint>
55DGtal::MeshReader<TPoint>::importOFFFile(const std::string & aFilename,
56 DGtal::Mesh<TPoint> & aMesh,
57 bool invertVertexOrder, bool onlyFaceVertex)
60 DGtal::IOException dgtalio;
63 infile.open (aFilename.c_str(), std::ifstream::in);
67 trace.error() << "MeshReader : can't open " << aFilename << std::endl;
71 getline( infile, str );
73 if ( ! infile.good() )
75 trace.error() << "MeshReader : can't read " << aFilename << std::endl;
78 if ( str.substr(0,3) != "OFF" && str.substr(0,4) != "NOFF" && str.substr(0,5) != "CNOFF")
80 std::cerr <<"*" <<str<<"*"<< std::endl;
81 trace.error() << "MeshReader : No OFF, NOFF or CNOFF format in " << aFilename << std::endl;
84 if ( str.substr(0,4) == "NOFF")
86 trace.warning() << "MeshReader : reading NOFF format from importOFFFile (normal vectors will be ignored)..." << std::endl;
89 // Processing comments
92 getline( infile, str );
93 if ( ! infile.good() )
95 trace.error() << "MeshReader : Invalid format in " << aFilename << std::endl;
99 while ( str[ 0 ] == '#' || str=="" || str=="\r" || str=="\n" );
100 std::istringstream str_in( str );
101 int nbPoints, nbFaces, nbEdges;
107 getline( infile, str );
108 if ( ! infile.good() )
110 trace.error() << "MeshReader : Invalid format in " << aFilename << std::endl;
114 while ( str[ 0 ] == '#' || str=="" || str=="\r" || str=="\n" );
115 str_in = std::istringstream ( str );
116 // Reading mesh vertex
117 for(int i=0; i<nbPoints; i++)
124 // Needed since a line can also contain vertex colors
125 getline(infile, str);
126 str_in = std::istringstream ( str );
129 // Reading mesh faces
130 for(int i=0; i<nbFaces; i++)
132 // Reading the number of face vertex
133 unsigned int aNbFaceVertex;
134 str_in >> aNbFaceVertex;
135 std::vector<typename Mesh<TPoint>::Index> aFace;
136 for (unsigned int j=0; j< aNbFaceVertex; j++)
138 unsigned int anIndex;
140 aFace.push_back(anIndex);
142 if( invertVertexOrder )
144 for(unsigned int j=0; j < aFace.size()/2; j++)
146 const auto tmp=aFace.at(j);
147 aFace.at(j)=aFace.at(aFace.size()-1-j);
148 aFace.at(aFace.size()-1-j)=tmp;
153 bool findValidColor=true;
157 double colorR, colorG, colorB, colorT;
158 findValidColor=str_in.good();
159 if(findValidColor && str_in.good())
163 findValidColor &=!str_in.fail();
164 if(findValidColor && str_in.good())
168 findValidColor &=!str_in.fail();
169 if(findValidColor && str_in.good())
173 findValidColor &=!str_in.fail();
175 if(findValidColor && str_in.good()){
177 // Since alpha is optional:
188 DGtal::Color c((unsigned int)(colorR*255.0), (unsigned int)(colorG*255.0),
189 (unsigned int)(colorB*255.0), (unsigned int)(colorT*255.0));
190 aMesh.addFace(aFace, c);
194 aMesh.addFace(aFace);
199 aMesh.addFace(aFace);
201 getline(infile, str);
202 str_in = std::istringstream ( str );
206 aMesh.removeIsolatedVertices();
217template <typename TPoint>
220DGtal::MeshReader<TPoint>::importOFSFile(const std::string & aFilename,
221 DGtal::Mesh<TPoint> & aMesh,
222 bool invertVertexOrder, double scale)
224 std::ifstream infile;
225 DGtal::IOException dgtalio;
228 infile.open (aFilename.c_str(), std::ifstream::in);
232 trace.error() << "MeshReader : can't open " << aFilename << std::endl;
236 getline( infile, str );
238 if ( ! infile.good() )
240 trace.error() << "MeshReader : can't read " << aFilename << std::endl;
243 if ( str.substr(0,3) != "OFS")
245 trace.error() << "MeshReader : No OFS format in " << aFilename << std::endl;
249 // Processing comments
252 getline( infile, str );
253 if ( ! infile.good() ){
254 trace.error() << "MeshReader : Invalid format in " << aFilename << std::endl;
258 while ( str[ 0 ] == '#' || str=="");
259 std::istringstream str_in( str );
263 // Reading mesh vertex
264 for(int i=0; i<nbPoints; i++)
274 // Needed since a line can also contain vertex colors
275 getline(infile, str);
279 getline( infile, str );
280 if ( ! infile.good() ){
281 trace.error() << "MeshReader : Invalid format in " << aFilename << std::endl;
285 while ( str[ 0 ] == '#' || str=="");
286 std::istringstream str_in2( str );
287 unsigned int nbFaces;
289 // Reading mesh faces
290 for(unsigned int i=0; i<nbFaces; i++)
292 // Reading the number of face vertex
293 std::vector<typename Mesh<TPoint>::Index> aFace;
294 for (unsigned int j=0; j< 3; j++)
296 unsigned int anIndex;
298 aFace.push_back(anIndex);
300 if( invertVertexOrder )
302 const auto tmp=aFace.at(0);
303 aFace.at(0)=aFace.at(2);
306 aMesh.addFace(aFace);
307 getline(infile, str);
312//-----------------------------------------------------------------------------
313template <typename TPoint>
316DGtal::MeshReader<TPoint>::
317importOBJFile( const std::string & filename, DGtal::Mesh<TPoint> & mesh,
318 bool onlyFaceVertex )
320 typedef typename Mesh<TPoint>::Index Index;
321 std::vector<TPoint> vertices;
322 std::vector<DGtal::Color> colors;
323 std::vector< std::vector< Index > > faces;
324 std::map<std::string, DGtal::Color> material;
328 bool useMtllib = false;
329 DGtal::Color currentMtlCol = DGtal::Color::White;
332 DGtal::IOException dgtalio;
335 input.open (filename.c_str(), std::ifstream::in);
339 trace.error() << "MeshReader : can't open " << filename << std::endl;
342 material = MeshReader<TPoint>::readMaterial(input);
343 useMtllib = !material.empty();
345 input.open (filename.c_str(), std::ifstream::in);
346 std::getline( input, linestr );
348 for ( ; input.good() && ! input.eof(); std::getline( input, linestr ), l++ )
350 if ( linestr.empty() ) continue; // skip empty line
351 if ( linestr[0] == '#' ) continue; // skip comment line
352 std::istringstream lineinput( linestr );
353 std::operator>>( lineinput, keyword ); // lineinput >> keyword;
354 if ( keyword == "v" )
356 lineinput >> p[ 0 ] >> p[ 1 ] >> p[ 2 ];
357 vertices.push_back( p );
359 else if ( keyword == "f" )
361 std::vector< Index > face;
362 while ( ! lineinput.eof() )
364 std::operator>>( lineinput, indices ); // lineinput >> indices;
365 if ( indices.empty() ) break;
366 auto vtxinfo = SurfaceMeshReader<TPoint, Z3i::RealVector >::split( indices, '/' );
367 if ( vtxinfo.size() == 0 ) break;
368 int v = std::stoi( vtxinfo[ 0 ] )-1;
370 { // special case of relative indices (ie negative index);
371 v = (int)vertices.size() + v+1;
376 if ( ! face.empty() && verifyIndicesUniqueness( face ) )
378 faces.push_back( face );
381 colors.push_back(currentMtlCol );
385 } else if (keyword == "mtllib")
388 std::operator>>( lineinput, name );
389 std::string base = name.substr(0,name.find_last_of("."));
390 auto iSep = filename.find_last_of('/');
392 { // special for windows.
393 iSep = filename.find_last_of('\\');
395 std::string path = filename.substr(0, iSep+1);
396 std::stringstream matPathName ;
397 matPathName << path << name;
398 std::ifstream is (matPathName.str());
400 material = MeshReader<TPoint>::readMaterial(is);
404 // Path of material is probably outdated, trying to open same directroy as source mesh.
405 iSep = name.find_last_of('/');
407 { // special for windows.
408 iSep = name.find_last_of('\\');
410 std::string pathMesh = name.substr(iSep+1,name.size());
411 std::ifstream is2 (path+pathMesh);
413 material = MeshReader<TPoint>::readMaterial(is2);
419 else if (keyword == "usemtl")
422 std::operator>>( lineinput, name );
423 if (material.count(name) !=0 )
425 currentMtlCol = material[name];
428 // Weird: necessary to clear them.
429 keyword = ""; linestr = "";
432 trace.info() << "[MeshReader::readOBJ] Read"
434 << " #V=" << vertices.size()
435 << " #F=" << faces.size() << std::endl;
437 trace.warning() << "[MeshReader::readOBJ] Some I/O error occured."
438 << " Proceeding but the mesh may be damaged." << std::endl;
439 for (auto const &s : vertices)
443 for (auto const &f : faces)
448 if (!colors.empty()){
449 for (Index i = 0; i < colors.size(); i++){
450 mesh.setFaceColor(i, colors[i]);
453 if (onlyFaceVertex && ! input.bad()){
454 mesh.removeIsolatedVertices();
456 return ( ! input.bad() );
459template <typename TPoint>
461DGtal::operator<< ( Mesh<TPoint> & mesh, const std::string &filename )
463 std::string extension = filename.substr(filename.find_last_of(".") + 1);
464 if(extension== "off")
466 DGtal::MeshReader< TPoint >::importOFFFile(filename, mesh);
469 else if(extension== "ofs")
471 DGtal::MeshReader< TPoint >::importOFSFile(filename, mesh);
474 else if(extension == "obj")
476 DGtal::MeshReader< TPoint >::importOBJFile(filename, mesh);
484//-----------------------------------------------------------------------------
485template <typename TPoint>
487DGtal::MeshReader<TPoint>::
488verifyIndicesUniqueness( const std::vector< typename DGtal::Mesh<TPoint>::Index > &indices )
490 std::unordered_set<typename DGtal::Mesh<TPoint>::Index> sindices( indices.begin(), indices.end() );
491 return sindices.size() == indices.size();
494//-----------------------------------------------------------------------------
495template <typename TPoint>
496std::map<std::string, DGtal::Color>
497DGtal::MeshReader<TPoint>::readMaterial( std::istream & input)
499 typedef unsigned int Index;
502 std::map<std::string, DGtal::Color> resultMap;
503 std::getline( input, linestr );
506 std::string currentMtlName="";
507 for ( ; input.good() && ! input.eof(); std::getline( input, linestr ), l++ )
509 if ( linestr.empty() ) continue; // skip empty line
510 if ( linestr[0] == '#' ) continue; // skip comment line
511 std::istringstream lineinput( linestr );
512 std::operator>>( lineinput, keyword ); // lineinput >> keyword;
513 if ( keyword == "newmtl" )
516 std::operator>>( lineinput, nameMtl );
519 currentMtlName = nameMtl;
522 else if (keyword == "Kd" && currentMtlName != "" )
524 lineinput >> r >> g >> b;
525 DGtal::Color c (static_cast<unsigned char>(r*255.0),
526 static_cast<unsigned char>(g*255),
527 static_cast<unsigned char>(b*255));
528 resultMap[currentMtlName] = c;
538///////////////////////////////////////////////////////////////////////////////