DGtal 1.4.0
Loading...
Searching...
No Matches
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
52template <typename TPoint>
53inline
54bool
55DGtal::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
217template <typename TPoint>
218inline
219bool
220DGtal::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//-----------------------------------------------------------------------------
313template <typename TPoint>
314inline
315bool
316DGtal::MeshReader<TPoint>::
317importOBJFile( 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
459template <typename TPoint>
460bool
461DGtal::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//-----------------------------------------------------------------------------
485template <typename TPoint>
486bool
487DGtal::MeshReader<TPoint>::
488verifyIndicesUniqueness( 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//-----------------------------------------------------------------------------
495template <typename TPoint>
496std::map<std::string, DGtal::Color>
497DGtal::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