DGtal  1.4.beta
MeshReader.ih
1 /**
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.
6  *
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.
11  *
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/>.
14  *
15  **/
16 
17 /**
18  * @file MeshReader.ih
19  * @author Bertrand Kerautret (\c kerautre@loria.fr )
20  * LORIA (CNRS, UMR 7503), University of Nancy, France
21  *
22  * @date 2012/06/29
23  *
24  * Implementation of inline methods defined in MeshReader.h
25  *
26  * This file is part of the DGtal library.
27  */
28 
29 ///////////////////////////////////////////////////////////////////////////////
30 // IMPLEMENTATION of inline methods.
31 ///////////////////////////////////////////////////////////////////////////////
32 
33 //////////////////////////////////////////////////////////////////////////////
34 #include <cstdlib>
35 #include <cstdlib>
36 #include <iostream>
37 #include <fstream>
38 #include <sstream>
39 #include <map>
40 #include <string>
41 //////////////////////////////////////////////////////////////////////////////
42 
43 #include "DGtal/helpers/StdDefs.h"
44 #include "DGtal/io/readers/SurfaceMeshReader.h"
45 
46 ///////////////////////////////////////////////////////////////////////////////
47 // Implementation of inline methods //
48 
49 
50 
51 
52 template <typename TPoint>
53 inline
54 bool
55 DGtal::MeshReader<TPoint>::importOFFFile(const std::string & aFilename,
56  DGtal::Mesh<TPoint> & aMesh,
57  bool invertVertexOrder, bool onlyFaceVertex)
58 {
59  std::ifstream infile;
60  DGtal::IOException dgtalio;
61  try
62  {
63  infile.open (aFilename.c_str(), std::ifstream::in);
64  }
65  catch( ... )
66  {
67  trace.error() << "MeshReader : can't open " << aFilename << std::endl;
68  throw dgtalio;
69  }
70  std::string str;
71  getline( infile, str );
72 
73  if ( ! infile.good() )
74  {
75  trace.error() << "MeshReader : can't read " << aFilename << std::endl;
76  throw dgtalio;
77  }
78  if ( str.substr(0,3) != "OFF" && str.substr(0,4) != "NOFF" && str.substr(0,5) != "CNOFF")
79  {
80  std::cerr <<"*" <<str<<"*"<< std::endl;
81  trace.error() << "MeshReader : No OFF, NOFF or CNOFF format in " << aFilename << std::endl;
82  throw dgtalio;
83  }
84  if ( str.substr(0,4) == "NOFF")
85  {
86  trace.warning() << "MeshReader : reading NOFF format from importOFFFile (normal vectors will be ignored)..." << std::endl;
87  }
88 
89  // Processing comments
90  do
91  {
92  getline( infile, str );
93  if ( ! infile.good() )
94  {
95  trace.error() << "MeshReader : Invalid format in " << aFilename << std::endl;
96  throw dgtalio;
97  }
98  }
99  while ( str[ 0 ] == '#' || str=="" || str=="\r" || str=="\n" );
100  std::istringstream str_in( str );
101  int nbPoints, nbFaces, nbEdges;
102  str_in >> nbPoints;
103  str_in >> nbFaces;
104  str_in >> nbEdges;
105  do
106  {
107  getline( infile, str );
108  if ( ! infile.good() )
109  {
110  trace.error() << "MeshReader : Invalid format in " << aFilename << std::endl;
111  throw dgtalio;
112  }
113  }
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++)
118  {
119  TPoint p;
120  str_in >> p[0];
121  str_in >> p[1];
122  str_in >> p[2];
123  aMesh.addVertex(p);
124  // Needed since a line can also contain vertex colors
125  getline(infile, str);
126  str_in = std::istringstream ( str );
127  }
128 
129  // Reading mesh faces
130  for(int i=0; i<nbFaces; i++)
131  {
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++)
137  {
138  unsigned int anIndex;
139  str_in >> anIndex;
140  aFace.push_back(anIndex);
141  }
142  if( invertVertexOrder )
143  {
144  for(unsigned int j=0; j < aFace.size()/2; j++)
145  {
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;
149  }
150  }
151 
152  // Contains colors:
153  bool findValidColor=true;
154 
155  if(str_in.good())
156  {
157  double colorR, colorG, colorB, colorT;
158  findValidColor=str_in.good();
159  if(findValidColor && str_in.good())
160  {
161  str_in >> colorR;
162  }
163  findValidColor &=!str_in.fail();
164  if(findValidColor && str_in.good())
165  {
166  str_in >> colorG;
167  }
168  findValidColor &=!str_in.fail();
169  if(findValidColor && str_in.good())
170  {
171  str_in >> colorB;
172  }
173  findValidColor &=!str_in.fail();
174 
175  if(findValidColor && str_in.good()){
176  str_in >> colorT;
177  // Since alpha is optional:
178  if(str_in.fail()){
179  colorT=1.0;
180  }
181  }
182  else
183  {
184  colorT=1.0;
185  }
186  if(findValidColor)
187  {
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);
191  }
192  else
193  {
194  aMesh.addFace(aFace);
195  }
196  }
197  else
198  {
199  aMesh.addFace(aFace);
200  }
201  getline(infile, str);
202  str_in = std::istringstream ( str );
203  }
204  if (onlyFaceVertex)
205  {
206  aMesh.removeIsolatedVertices();
207  }
208 
209  return true;
210 }
211 
212 
213 
214 
215 
216 
217 template <typename TPoint>
218 inline
219 bool
220 DGtal::MeshReader<TPoint>::importOFSFile(const std::string & aFilename,
221  DGtal::Mesh<TPoint> & aMesh,
222  bool invertVertexOrder, double scale)
223 {
224  std::ifstream infile;
225  DGtal::IOException dgtalio;
226  try
227  {
228  infile.open (aFilename.c_str(), std::ifstream::in);
229  }
230  catch( ... )
231  {
232  trace.error() << "MeshReader : can't open " << aFilename << std::endl;
233  throw dgtalio;
234  }
235  std::string str;
236  getline( infile, str );
237 
238  if ( ! infile.good() )
239  {
240  trace.error() << "MeshReader : can't read " << aFilename << std::endl;
241  throw dgtalio;
242  }
243  if ( str.substr(0,3) != "OFS")
244  {
245  trace.error() << "MeshReader : No OFS format in " << aFilename << std::endl;
246  throw dgtalio;
247  }
248 
249  // Processing comments
250  do
251  {
252  getline( infile, str );
253  if ( ! infile.good() ){
254  trace.error() << "MeshReader : Invalid format in " << aFilename << std::endl;
255  throw dgtalio;
256  }
257  }
258  while ( str[ 0 ] == '#' || str=="");
259  std::istringstream str_in( str );
260  int nbPoints;
261  str_in >> nbPoints;
262 
263  // Reading mesh vertex
264  for(int i=0; i<nbPoints; i++)
265  {
266  TPoint p;
267  infile >> p[0];
268  infile >> p[1];
269  infile >> p[2];
270  p[0]*=scale;
271  p[1]*=scale;
272  p[2]*=scale;
273  aMesh.addVertex(p);
274  // Needed since a line can also contain vertex colors
275  getline(infile, str);
276  }
277  do
278  {
279  getline( infile, str );
280  if ( ! infile.good() ){
281  trace.error() << "MeshReader : Invalid format in " << aFilename << std::endl;
282  throw dgtalio;
283  }
284  }
285  while ( str[ 0 ] == '#' || str=="");
286  std::istringstream str_in2( str );
287  unsigned int nbFaces;
288  str_in2 >> nbFaces;
289  // Reading mesh faces
290  for(unsigned int i=0; i<nbFaces; i++)
291  {
292  // Reading the number of face vertex
293  std::vector<typename Mesh<TPoint>::Index> aFace;
294  for (unsigned int j=0; j< 3; j++)
295  {
296  unsigned int anIndex;
297  infile >> anIndex;
298  aFace.push_back(anIndex);
299  }
300  if( invertVertexOrder )
301  {
302  const auto tmp=aFace.at(0);
303  aFace.at(0)=aFace.at(2);
304  aFace.at(2)=tmp;
305  }
306  aMesh.addFace(aFace);
307  getline(infile, str);
308  }
309  return true;
310 }
311 
312 //-----------------------------------------------------------------------------
313 template <typename TPoint>
314 inline
315 bool
316 DGtal::MeshReader<TPoint>::
317 importOBJFile( const std::string & filename, DGtal::Mesh<TPoint> & mesh,
318  bool onlyFaceVertex )
319 {
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;
325  std::string linestr;
326  std::string keyword;
327  std::string indices;
328  bool useMtllib = false;
329  DGtal::Color currentMtlCol = DGtal::Color::White;
330  TPoint p;
331  std::ifstream input;
332  DGtal::IOException dgtalio;
333  try
334  {
335  input.open (filename.c_str(), std::ifstream::in);
336  }
337  catch( ... )
338  {
339  trace.error() << "MeshReader : can't open " << filename << std::endl;
340  throw dgtalio;
341  }
342  material = MeshReader<TPoint>::readMaterial(input);
343  useMtllib = !material.empty();
344  input.close();
345  input.open (filename.c_str(), std::ifstream::in);
346  std::getline( input, linestr );
347  Index l = 0;
348  for ( ; input.good() && ! input.eof(); std::getline( input, linestr ), l++ )
349  {
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" )
355  {
356  lineinput >> p[ 0 ] >> p[ 1 ] >> p[ 2 ];
357  vertices.push_back( p );
358  }
359  else if ( keyword == "f" )
360  {
361  std::vector< Index > face;
362  while ( ! lineinput.eof() )
363  {
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;
369  if (v < 0 )
370  { // special case of relative indices (ie negative index);
371  v = (int)vertices.size() + v+1;
372  }
373  face.push_back( v);
374  indices = "";
375  }
376  if ( ! face.empty() && verifyIndicesUniqueness( face ) )
377  {
378  faces.push_back( face );
379  if (useMtllib)
380  {
381  colors.push_back(currentMtlCol );
382  }
383  }
384 
385  } else if (keyword == "mtllib")
386  {
387  std::string name;
388  std::operator>>( lineinput, name );
389  std::string base = name.substr(0,name.find_last_of("."));
390  auto iSep = filename.find_last_of('/');
391  if ((int)iSep == -1)
392  { // special for windows.
393  iSep = filename.find_last_of('\\');
394  }
395  std::string path = filename.substr(0, iSep+1);
396  std::stringstream matPathName ;
397  matPathName << path << name;
398  std::ifstream is (matPathName.str());
399  if (is.good()){
400  material = MeshReader<TPoint>::readMaterial(is);
401  useMtllib = true;
402  is.close();
403  }else {
404  // Path of material is probably outdated, trying to open same directroy as source mesh.
405  iSep = name.find_last_of('/');
406  if ((int)iSep == -1)
407  { // special for windows.
408  iSep = name.find_last_of('\\');
409  }
410  std::string pathMesh = name.substr(iSep+1,name.size());
411  std::ifstream is2 (path+pathMesh);
412  if (is2.good()){
413  material = MeshReader<TPoint>::readMaterial(is2);
414  useMtllib = true;
415  }
416  is2.close();
417  }
418  }
419  else if (keyword == "usemtl")
420  {
421  std::string name;
422  std::operator>>( lineinput, name );
423  if (material.count(name) !=0 )
424  {
425  currentMtlCol = material[name];
426  }
427  }
428  // Weird: necessary to clear them.
429  keyword = ""; linestr = "";
430  }
431  // Creating Mesh
432  trace.info() << "[MeshReader::readOBJ] Read"
433  << " #lines=" << l
434  << " #V=" << vertices.size()
435  << " #F=" << faces.size() << std::endl;
436  if ( input.bad() )
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)
440  {
441  mesh.addVertex(s);
442  }
443  for (auto const &f : faces)
444  {
445  mesh.addFace(f);
446  }
447 
448  if (!colors.empty()){
449  for (Index i = 0; i < colors.size(); i++){
450  mesh.setFaceColor(i, colors[i]);
451  }
452  }
453  if (onlyFaceVertex && ! input.bad()){
454  mesh.removeIsolatedVertices();
455  }
456  return ( ! input.bad() );
457 }
458 
459 template <typename TPoint>
460 bool
461 DGtal::operator<< ( Mesh<TPoint> & mesh, const std::string &filename )
462 {
463  std::string extension = filename.substr(filename.find_last_of(".") + 1);
464  if(extension== "off")
465  {
466  DGtal::MeshReader< TPoint >::importOFFFile(filename, mesh);
467  return true;
468  }
469  else if(extension== "ofs")
470  {
471  DGtal::MeshReader< TPoint >::importOFSFile(filename, mesh);
472  return true;
473  }
474  else if(extension == "obj")
475  {
476  DGtal::MeshReader< TPoint >::importOBJFile(filename, mesh);
477  return true;
478  }
479  return false;
480 }
481 
482 
483 
484 //-----------------------------------------------------------------------------
485 template <typename TPoint>
486 bool
487 DGtal::MeshReader<TPoint>::
488 verifyIndicesUniqueness( const std::vector< typename DGtal::Mesh<TPoint>::Index > &indices )
489 {
490  std::unordered_set<typename DGtal::Mesh<TPoint>::Index> sindices( indices.begin(), indices.end() );
491  return sindices.size() == indices.size();
492 }
493 
494 //-----------------------------------------------------------------------------
495 template <typename TPoint>
496 std::map<std::string, DGtal::Color>
497 DGtal::MeshReader<TPoint>::readMaterial( std::istream & input)
498 {
499  typedef unsigned int Index;
500  std::string linestr;
501  std::string keyword;
502  std::map<std::string, DGtal::Color> resultMap;
503  std::getline( input, linestr );
504  double r, g, b;
505  Index l = 0;
506  std::string currentMtlName="";
507  for ( ; input.good() && ! input.eof(); std::getline( input, linestr ), l++ )
508  {
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" )
514  {
515  std::string nameMtl;
516  std::operator>>( lineinput, nameMtl );
517  if (nameMtl != "")
518  {
519  currentMtlName = nameMtl;
520  }
521  }
522  else if (keyword == "Kd" && currentMtlName != "" )
523  {
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;
529  currentMtlName = "";
530  }
531  }
532  return resultMap;
533 }
534 
535 
536 
537 // //
538 ///////////////////////////////////////////////////////////////////////////////
539 
540 
541 
542 
543