DGtal  1.5.beta
SurfaceMeshReader.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 SurfaceMeshReader.ih
19  * @author Jacques-Olivier Lachaud (\c jacques-olivier.lachaud@univ-savoie.fr )
20  * Laboratory of Mathematics (CNRS, UMR 5127), University of Savoie, France
21  *
22  * @date 2020/02/18
23  *
24  * Implementation of inline methods defined in SurfaceMeshReader.h
25  *
26  * This file is part of the DGtal library.
27  */
28 
29 
30 //////////////////////////////////////////////////////////////////////////////
31 #include <cstdlib>
32 #include <limits>
33 //////////////////////////////////////////////////////////////////////////////
34 
35 
36 ///////////////////////////////////////////////////////////////////////////////
37 // IMPLEMENTATION of inline methods.
38 ///////////////////////////////////////////////////////////////////////////////
39 
40 //-----------------------------------------------------------------------------
41 template <typename TRealPoint, typename TRealVector>
42 bool
43 DGtal::SurfaceMeshReader<TRealPoint, TRealVector>::
44 verifyIndicesUniqueness( const std::vector< Index > &indices )
45 {
46  std::set<Index> sindices( indices.begin(), indices.end() );
47  return sindices.size() == indices.size();
48 }
49 
50 //-----------------------------------------------------------------------------
51 template <typename TRealPoint, typename TRealVector>
52 std::vector< std::string >
53 DGtal::SurfaceMeshReader<TRealPoint, TRealVector>::
54 split( const std::string& str, char delim )
55 {
56  std::stringstream ss(str);
57  std::string token;
58  std::vector< std::string > cont;
59  while ( std::getline( ss, token, delim ) ) cont.push_back(token);
60  return cont;
61 }
62 
63 //-----------------------------------------------------------------------------
64 template <typename TRealPoint, typename TRealVector>
65 bool
66 DGtal::SurfaceMeshReader<TRealPoint, TRealVector>::
67 readOBJ( std::istream & input, SurfaceMesh & smesh )
68 {
69  std::vector<RealPoint> vertices;
70  std::vector<RealVector> normals;
71  std::vector< std::vector< Index > > faces;
72  std::vector< std::vector< Index > > faces_normals_idx;
73  std::string linestr;
74  std::string keyword;
75  std::string indices;
76  RealPoint p;
77  RealVector n;
78  std::getline( input, linestr );
79  Index l = 0;
80  for ( ; input.good() && ! input.eof(); std::getline( input, linestr ), l++ )
81  {
82  if ( linestr.empty() ) continue; // skip empty line
83  if ( linestr[0] == '#' ) continue; // skip comment line
84  std::istringstream lineinput( linestr );
85  std::operator>>( lineinput, keyword ); // lineinput >> keyword;
86  if ( keyword == "v" ) {
87  lineinput >> p[ 0 ] >> p[ 1 ] >> p[ 2 ];
88  vertices.push_back( p );
89  } else if ( keyword == "vn" ) {
90  lineinput >> n[ 0 ] >> n[ 1 ] >> n[ 2 ];
91  normals.push_back( n );
92  } else if ( keyword == "f" ) {
93  std::vector< Index > face, face_normals;
94  while ( ! lineinput.eof() ) {
95  std::operator>>( lineinput, indices ); // lineinput >> indices;
96  if ( indices.empty() ) break;
97  auto vtxinfo = split( indices, '/' );
98  if ( vtxinfo.size() == 0 ) break;
99  Index v = static_cast<Index>((int)std::stoi( vtxinfo[ 0 ] )-1);
100  if (v < 0 ){ // special case of relative indices (ie negative index);
101  v = vertices.size() + v+1;
102  }
103  face.push_back(v);
104  Index vn = vtxinfo.size() >= 3 ? std::stoi( vtxinfo[ 2 ] ) : v-1;
105  face_normals.push_back( vn - 1 );
106  indices = "";
107  }
108  if ( ! face.empty() && verifyIndicesUniqueness( face ) )
109  {
110  faces.push_back( face );
111  faces_normals_idx.push_back( face_normals );
112  }
113  }
114  // Weird: necessary to clear them.
115  keyword = ""; linestr = "";
116  }
117  // Creating SurfaceMesh
118  trace.info() << "[SurfaceMeshReader::readOBJ] Read"
119  << " #lines=" << l
120  << " #V=" << vertices.size()
121  << " #VN=" << normals.size()
122  << " #F=" << faces.size() << std::endl;
123  if ( input.bad() )
124  trace.warning() << "[SurfaceMeshReader::readOBJ] Some I/O error occured."
125  << " Proceeding but the mesh may be damaged." << std::endl;
126  bool ok = smesh.init( vertices.begin(), vertices.end(),
127  faces.begin(), faces.end() );
128  if ( ! ok )
129  trace.warning() << "[SurfaceMeshReader::readOBJ]"
130  << " Error initializing mesh." << std::endl;
131  if ( ( ! normals.empty() ) && ( normals.size() == vertices.size() ) )
132  { // Build vertex normal map
133  bool ok_vtx_normals = smesh.setVertexNormals( normals.begin(), normals.end() );
134  if ( ! ok_vtx_normals )
135  trace.warning() << "[SurfaceMeshReader::readOBJ]"
136  << " Error setting vertex normals." << std::endl;
137  ok = ok && ok_vtx_normals;
138  }
139  if ( ! normals.empty() )
140  { // Build face normal map
141  std::vector< RealVector > faces_normals;
142  for ( auto face_n_indices : faces_normals_idx )
143  {
144  RealVector _n;
145  for ( auto k : face_n_indices ) _n += normals[ k ];
146  _n /= face_n_indices.size();
147  faces_normals.push_back( _n );
148  }
149  bool ok_face_normals = smesh.setFaceNormals( faces_normals.begin(),
150  faces_normals.end() );
151  if ( ! ok_face_normals )
152  trace.warning() << "[SurfaceMeshReader::readOBJ]"
153  << " Error setting face normals." << std::endl;
154  ok = ok && ok_face_normals;
155  }
156  return ( ! input.bad() ) && ok;
157 }
158 
159 ///////////////////////////////////////////////////////////////////////////////
160 ///////////////////////////////////////////////////////////////////////////////