aimsdata  5.1.2
Neuroimaging data handling
wavefrontmeshR.h
Go to the documentation of this file.
1 /* This software and supporting documentation are distributed by
2  * Institut Federatif de Recherche 49
3  * CEA/NeuroSpin, Batiment 145,
4  * 91191 Gif-sur-Yvette cedex
5  * France
6  *
7  * This software is governed by the CeCILL-B license under
8  * French law and abiding by the rules of distribution of free software.
9  * You can use, modify and/or redistribute the software under the
10  * terms of the CeCILL-B license as circulated by CEA, CNRS
11  * and INRIA at the following URL "http://www.cecill.info".
12  *
13  * As a counterpart to the access to the source code and rights to copy,
14  * modify and redistribute granted by the license, users are provided only
15  * with a limited warranty and the software's author, the holder of the
16  * economic rights, and the successive licensors have only limited
17  * liability.
18  *
19  * In this respect, the user's attention is drawn to the risks associated
20  * with loading, using, modifying and/or developing or reproducing the
21  * software by the user in light of its specific status of free software,
22  * that may mean that it is complicated to manipulate, and that also
23  * therefore means that it is reserved for developers and experienced
24  * professionals having in-depth computer knowledge. Users are therefore
25  * encouraged to load and test the software's suitability as regards their
26  * requirements in conditions enabling the security of their systems and/or
27  * data to be ensured and, more generally, to use and operate it in the
28  * same conditions as regards security.
29  *
30  * The fact that you are presently reading this means that you have had
31  * knowledge of the CeCILL-B license and that you accept its terms.
32  */
33 
34 #ifndef AIMS_IO_WAVEFRONTMESHR_H
35 #define AIMS_IO_WAVEFRONTMESHR_H
36 
37 #include <aims/data/pheader.h>
38 #include <aims/io/datatypecode.h>
40 #include <aims/io/reader.h>
41 #include <aims/rgb/rgb.h>
46 
47 #include <aims/mesh/texture.h>
48 #include <aims/io/writer.h>
49 
54 
55 
56 namespace
57 {
58 
59  template <typename T>
60  class _readTexture
61  {
62  public:
63  inline static void r( std::istream & s, T & tex )
64  {
65  s >> tex;
66  }
67  };
68 
69 
70  template <typename T, int D>
71  class _readTexture<AimsVector<T, D> >
72  {
73  public:
74  inline static
75  void r( std::istream & s, AimsVector<T, D> & tex )
76  {
77  int i;
78  for( i=0; i<3 && i<D; ++i )
79  {
80  s >> tex[i];
81  }
82  for( ; i<D; ++i )
83  tex[i] = 0;
84  }
85  };
86 
87 }
88 
89 namespace aims
90 {
91 
92  template<long D, typename T>
94  {
95  public:
96  WavefrontMeshReader( const std::string& name ) : _name( name ) {}
98 
99  void read( AimsTimeSurface<D,T>& thing,
100  const carto::AllocatorContext & context,
101  carto::Object options );
102 
103  private:
104  void storeMesh( AimsTimeSurface<D,T> & thing,
105  const std::vector<Point3df> & vertices,
106  const std::vector<Point3df> &normals,
107  const std::vector<AimsVector<uint, D> > & polygons,
108  const std::vector<T> & texture,
109  const std::vector<int> & normals_ind,
110  const std::vector<int> & texture_ind, int timestep );
111  inline static
112  carto::Object readMtlFle( std::string & mtl_filename );
113  inline static
114  void setMtl( carto::Object mtl_dict, const std::string & mtl_objname,
115  PythonHeader & hdr );
116 
117  std::string _name;
118  };
119 
120 
121  template <long D, typename T>
122  inline WavefrontMeshReader<D,T> &
124  AimsTimeSurface<D,T> & thing )
125  {
126  reader.read( thing, thing.allocator(),
128  return reader;
129  }
130 
131 
132  template <long D, typename T>
133  inline
134  carto::Object WavefrontMeshReader<D,T>::readMtlFle(
135  std::string & mtl_filename )
136  {
138 
139  std::ifstream ms( mtl_filename.c_str() );
140  if( !ms )
141  {
142  std::cerr << "warning: could not open " << mtl_filename << std::endl;
143  return mtl_dict;
144  }
145 
146  ms.setf( std::ios::skipws );
147  soma::IStreamDataSource ds( ms );
148  int line = 0;
149  carto::Object current_obj;
150  carto::Object current_mat;
151  std::string l, element, objname;
152  int timestep = -1;
153 
154  while( ds && !ds.eof() )
155  {
156  if( !soma::StreamUtil::getline( ds, l ) )
157  {
158  if( ds.eof() )
159  break;
160  else
161  {
162  std::cerr << "warning: could read " << mtl_filename << ", line "
163  << line + 1 << std::endl;
164  return mtl_dict;
165  }
166  }
167  ++line;
168 
169  if( l.empty() || l[0] == '#' )
170  continue;
171 
172  std::stringstream s( l );
173  s >> element;
174 
175  if( element == "newmtl" )
176  {
177  objname = l.substr( s.tellg(), l.length() - s.tellg() );
178  size_t i = 0;
179  while( !objname.empty() && objname[i] == ' ' )
180  ++i;
181  objname = objname.substr( i, objname.length() - i );
182  ++timestep;
183  current_obj = carto::Object::value( carto::Dictionary() );
184  mtl_dict->setProperty( objname, current_obj );
185  current_mat = carto::Object::value( carto::Dictionary() );
186  current_obj->setProperty( "material", current_mat );
187  }
188  else if( element == "Ka" )
189  {
190  if( !current_obj )
191  {
192  std::cerr << "MTL error: no current object.\n";
193  continue;
194  }
195  std::vector<float> ambient( 3 );
196  s >> ambient[0] >> ambient[1] >> ambient[2];
197  current_mat->setProperty( "ambient", ambient );
198  }
199  else if( element == "Kd" )
200  {
201  if( !current_obj )
202  {
203  std::cerr << "MTL error: no current object.\n";
204  continue;
205  }
206  std::vector<float> diffuse( 4, 1. );
207  if( current_mat->getProperty( "diffuse", diffuse ) )
208  while( diffuse.size() < 4 )
209  diffuse.push_back( 1. );
210  s >> diffuse[0] >> diffuse[1] >> diffuse[2];
211  current_mat->setProperty( "diffuse", diffuse );
212  }
213  else if( element == "Ks" )
214  {
215  if( !current_obj )
216  {
217  std::cerr << "MTL error: no current object.\n";
218  continue;
219  }
220  std::vector<float> specular( 3 );
221  s >> specular[0] >> specular[1] >> specular[2];
222  current_mat->setProperty( "specular", specular );
223  }
224  else if( element == "Ns" )
225  {
226  if( !current_obj )
227  {
228  std::cerr << "MTL error: no current object.\n";
229  continue;
230  }
231  float ns;
232  s >> ns;
233  current_mat->setProperty( "shininess", ns );
234  }
235  else if( element == "d" )
236  {
237  if( !current_obj )
238  {
239  std::cerr << "MTL error: no current object.\n";
240  continue;
241  }
242  float a;
243  s >> a;
244  std::vector<float> diffuse( 4, 0.8 );
245  if( current_mat->getProperty( "diffuse", diffuse ) )
246  while( diffuse.size() < 4 )
247  diffuse.push_back( 0.8 );
248  diffuse[3] = a;
249  current_mat->setProperty( "diffuse", diffuse );
250  }
251  else if( element == "Tr" )
252  {
253  if( !current_obj )
254  {
255  std::cerr << "MTL error: no current object.\n";
256  continue;
257  }
258  float a;
259  s >> a;
260  std::vector<float> diffuse( 4, 0.8 );
261  if( current_mat->getProperty( "diffuse", diffuse ) )
262  while( diffuse.size() < 4 )
263  diffuse.push_back( 0.8 );
264  diffuse[3] = 1.f - a;
265  current_mat->setProperty( "diffuse", diffuse );
266  }
267  else if( element == "map_Kd" )
268  {
269  if( !current_obj )
270  {
271  std::cerr << "MTL error: no current object.\n";
272  continue;
273  }
274  // texture image and params
275  std::string item;
276  bool image_found = false;
277  while( !image_found && s )
278  {
279  s >> item;
280  if( item == "-o" )
281  {
282  // FIXME might be less than 3 values and up to 4
283  std::vector<float> tex_offset( 3 );
284  s >> tex_offset[0] >> tex_offset[1] >> tex_offset[2];
285  current_obj->setProperty( "texture_offset", tex_offset );
286  }
287  else if( item == "-s" )
288  {
289  // FIXME might be less than 3 values and up to 4
290  std::vector<float> tex_scale( 3 );
291  s >> tex_scale[0] >> tex_scale[1] >> tex_scale[2];
292  current_obj->setProperty( "texture_scale", tex_scale );
293  }
294  else if( item == "-clamp" )
295  {
296  s >> item;
297  }
298  else // TODO: other values
299  {
300  std::string teximage_fname
301  = carto::FileUtil::dirname( mtl_filename )
302  + carto::FileUtil::separator() + item;
303  Reader<carto::Volume<AimsRGBA> > r( teximage_fname );
304  try
305  {
306  carto::rc_ptr<carto::Volume<AimsRGBA> > teximage( r.read() );
307  std::map<int, carto::Object> palettes;
308  mtl_dict->getProperty( "_texture_palettes", palettes );
309  palettes[timestep] = carto::Object::value( teximage );
310  mtl_dict->setProperty( "_texture_palettes", palettes );
311  }
312  catch( ... )
313  {
314  std::cout << "failed to read texture image " << teximage_fname
315  << std::endl;
316  }
317  }
318  }
319  }
320  else if( element == "illum" )
321  {
322  if( !current_obj )
323  {
324  std::cerr << "MTL error: no current object.\n";
325  continue;
326  }
327  float illum;
328  s >> illum;
329  current_obj->setProperty( "illum", illum );
330  // don't know what to do with this...
331  }
332  else
333  {
334  std::cout << "unrecognized MTL tag: " << element << std::endl;
335  }
336  }
337 
338  return mtl_dict;
339  }
340 
341 
342  template <long D, typename T>
343  inline
344  void WavefrontMeshReader<D,T>::setMtl( carto::Object mtl_dict,
345  const std::string & mtl_objname,
346  PythonHeader & hdr )
347  {
348  carto::Object m = mtl_dict->getProperty( mtl_objname );
349  // merge all materials/texture properties
350  carto::Object mat;
351  try
352  {
353  // material can be defined both sides (.mtl, .minf)
354  mat = m->getProperty( "material" );
355  carto::Object minfmat = hdr.getProperty( "material" );
356  mat->copyProperties( minfmat );
357  }
358  catch( ... )
359  {
360  }
361 
362  hdr.copyProperties( m );
363  if( mat.get() )
364  hdr.setProperty( "material", mat );
365 
366  if( mtl_dict->hasProperty( "_texture_palettes" ) )
367  {
368  carto::Object palettes = mtl_dict->getProperty( "_texture_palettes" );
369  hdr.setProperty( "_texture_palettes", palettes );
370  }
371  }
372 
373 
374  template <long D, typename T>
375  inline
376  void WavefrontMeshReader<D,T>::storeMesh(
377  AimsTimeSurface<D,T>& thing,
378  const std::vector<Point3df> & vertices,
379  const std::vector<Point3df> &normals,
380  const std::vector<AimsVector<uint, D> > & polygons,
381  const std::vector<T> & texture,
382  const std::vector<int> & normals_ind,
383  const std::vector<int> & texture_ind, int timestep )
384  {
385  thing[timestep].vertex() = vertices;
386  std::vector<Point3df> o_normals;
387  if( normals_ind.size() != normals.size() )
388  o_normals = normals;
389  else
390  {
391  o_normals.resize( normals_ind.size() );
392  // copy reordered normals
393  for( int i=0, k=normals_ind.size(); i<k; ++i )
394  {
395  if( normals_ind[i] < 0 || normals_ind[i] >= static_cast<int>(vertices.size()) )
396  {
397  std::stringstream s;
398  s << "normal index out of range: " << normals_ind[i] << " / "
399  << vertices.size() << " at index " << i;
400  throw carto::parse_error( s.str() , "", _name, i );
401  }
402  o_normals[i] = normals[normals_ind[i]];
403  }
404  }
405  thing[timestep].normal() = o_normals;
406  thing[timestep].polygon() = polygons;
407 
410  {
411  // copy reordered texture
412  std::vector<T> o_texture;
413  o_texture.resize( texture_ind.size() );
414  for( int i=0, k=texture_ind.size(); i<k; ++i )
415  {
416  if( texture_ind[i] < 0
417  || texture_ind[i] >= static_cast<int>(texture.size()) )
418  {
419  std::stringstream s;
420  if( texture_ind[i] >= 0 )
421  {
422  s << "texture index out of range: " << texture_ind[i] << " / "
423  << texture.size() << " at index " << i;
424  throw carto::parse_error( s.str(), "", _name, i );
425  }
426  // ind < 0, just not used.
427  o_texture[i] = texture[0];
428  }
429  else
430  o_texture[i] = texture[texture_ind[i]];
431  }
432  thing[timestep].texture() = o_texture;
433  }
434  }
435 
436 
437  template <long D, typename T>
438  inline
440  const carto::AllocatorContext & /*context*/,
441  carto::Object /*options*/ )
442  {
443  std::string filename = _name;
444  WavefrontHeader hdr( _name );
445  hdr.read();
446 
447  std::ifstream istr( filename.c_str(), std::ios::in );
448  istr.unsetf( std::ios::skipws );
449  if( !istr )
450  soma::io_error::launchErrnoExcept( filename );
451 
452  istr.setf( std::ios::skipws );
453  soma::IStreamDataSource ds( istr );
454  thing.erase();
455  std::string l;
456  int line = 0;
457 
458  std::vector<Point3df> vertices, normals;
459  std::vector<AimsVector<uint, D> > polygons;
460  std::vector<T> texture;
461  std::vector<int> normals_ind;
462  std::vector<int> texture_ind;
463  int timestep = 0;
464  bool update_normals = false;
465  carto::Object mtl_dict;
466 
467  while( ds && !ds.eof() )
468  {
469  if( !soma::StreamUtil::getline( ds, l ) )
470  {
471  if( ds.eof() )
472  break;
473  else
474  throw soma::wrong_format_error( filename );
475  }
476  ++line;
477 
478  if( l.empty() )
479  continue;
480  if( l[0] == '#' )
481  continue;
482  std::stringstream s(l);
483  std::string element;
484  s >> element;
485  if( element == "o" )
486  {
487  // new object / group
488  if( timestep != 0 || !vertices.empty() )
489  {
490  storeMesh( thing, vertices, normals, polygons, texture, normals_ind,
491  texture_ind, timestep );
492 
493  if( thing[timestep].normal().size() != vertices.size() )
494  update_normals = true;
495  ++timestep;
496  vertices.clear();
497  normals.clear();
498  polygons.clear();
499  texture.clear();
500  normals_ind.clear();
501  texture_ind.clear();
502  }
503  }
504  else if( element == "g" )
505  continue; // TODO: groups
506  else if( element == "s" )
507  continue; // smoothing
508  else if( element == "mtllib" )
509  {
510  std::string mtl_filename;
511  mtl_filename = l.substr( s.tellg(), l.length() - s.tellg() );
512  size_t i = 0;
513  while( !mtl_filename.empty() && mtl_filename[i] == ' ' )
514  ++i;
515  mtl_filename = mtl_filename.substr( i, mtl_filename.length() - i );
516  mtl_filename = carto::FileUtil::dirname( filename )
517  + carto::FileUtil::separator() + mtl_filename;
518  mtl_dict = readMtlFle( mtl_filename );
519  }
520  else if( element == "usemtl" )
521  {
522  std::string mtl_objname;
523  mtl_objname = l.substr( s.tellg(), l.length() - s.tellg() );
524  size_t i = 0;
525  while( !mtl_objname.empty() && mtl_objname[i] == ' ' )
526  ++i;
527  mtl_objname = mtl_objname.substr( i, mtl_objname.length() - i );
528  setMtl( mtl_dict, mtl_objname, hdr );
529  }
530  else if( element == "v" )
531  {
532  // vertex
533  Point3df p;
534  s >> p[0] >> p[1] >> p[2];
535  vertices.push_back(p);
536  }
537  else if( element == "vn" )
538  {
539  // normal
540  Point3df p;
541  s >> p[0] >> p[1] >> p[2];
542  normals.push_back(p);
543  }
544  else if( element == "vt" )
545  {
546  // texture
547  T tex;
548  _readTexture<T>::r( s, tex );
549  texture.push_back(tex);
550  }
551  else if( element == "f" || (D == 2 && element == "l" ) ) // l for line (D=2)
552  {
553  // face
554  std::string item;
555  std::string::size_type pos, pos0;
556  AimsVector<uint, D> poly;
557  int pol;
558  int tex;
559  int norm;
560  for( int p=0; p<D; ++p )
561  {
562  s >> item;
563  pos0 = item.find( '/' );
564  std::stringstream z( item.substr(0, pos0 ) );
565  z >> pol;
566  if( pol < 0 )
567  poly[p] = vertices.size() + pol;
568  else
569  poly[p] = pol - 1; // nums start at 1
570  if( pos0 == std::string::npos )
571  continue;
572 
573  ++pos0;
574  pos = item.find( '/', pos0 );
577  {
578  std::stringstream z( item.substr(pos0, pos - pos0 ) );
579  z >> tex;
580  if( texture_ind.size() <= poly[p] )
581  texture_ind.resize( poly[p] + 1, -1 );
582  if( texture_ind[ poly[p] ] < 0 )
583  texture_ind[ poly[p] ] = tex - 1; // (starts at 1)
584  else if( texture_ind[ poly[p] ] != tex - 1 )
585  {
586  // same vertex is assigned a different texture:
587  // we must duplicate the vertex
588  vertices.push_back( vertices[ poly[p] ] );
589  poly[p] = vertices.size() - 1;
590  texture_ind.resize( poly[p] + 1, -1 );
591  texture_ind[ poly[p] ] = tex - 1;
592  }
593  }
594  else if( pos != pos0 )
595  throw carto::parse_error( "malformed face", "", filename, line );
596  if( pos == std::string::npos )
597  continue;
598  pos0 = pos + 1;
599  std::stringstream n( item.substr( pos0, item.length() - pos0 ) );
600  n >> norm;
601  if( norm < 0 )
602  norm = vertices.size() + norm + 1;
603  if( normals_ind.size() <= poly[p] )
604  normals_ind.resize( poly[p] + 1 );
605  normals_ind[ poly[p] ] = norm - 1; // (starts at 1)
606  }
607  polygons.push_back( poly );
608  }
609  else
610  {
611  std::cerr << "unrecognized element: " << element << ", " << D << ", " << l << std::endl;
612  }
613  }
614 
615  /*
616  std::cout << "wavefront vertices: " << vertices.size() << ", normals: " << normals.size() << ", polygons: " << polygons.size() << ", normals_ind: " << normals_ind.size() << std::endl;
617  std::cout << "timestep: " << timestep << std::endl;
618  */
619 
620  storeMesh( thing, vertices, normals, polygons, texture, normals_ind,
621  texture_ind, timestep );
622 
623  if( update_normals || thing[timestep].normal().size() != vertices.size() )
624  thing.updateNormals();
625 
626  // std::cout << "WAVEFRONT read OK\n";
627 
628  if( hdr.hasProperty( "filenames" ) )
629  hdr.removeProperty( "filenames" );
630  thing.setHeader( hdr );
631  }
632 
633 }
634 
635 #endif
636 
The template class to manage a mesh with time if needed.
Definition: surface.h:317
void erase()
Clear all the meshes.
Definition: surface.h:505
void setHeader(const aims::PythonHeader &hdr)
Set the header.
Definition: surface.h:332
const std::vector< AimsVector< uint, D > > & polygon() const
Get a const reference to the vector of polygons of the 0 surface.
Definition: surface.h:362
const std::vector< T > & texture() const
Get a const reference to the vector of textures of the 0 surface.
Definition: surface.h:356
void updateNormals()
Update/Compute the normals.
Definition: surface.h:514
const std::vector< Point3df > & normal() const
Get a const reference to the vector of normals of the 0 surface.
Definition: surface.h:350
const std::vector< Point3df > & vertex() const
Get a const reference to the vector of verteces of the surface of index 0.
Definition: surface.h:344
int size() const
Attributed python-like header, stores all needed information about an object, currently used for volu...
Definition: pheader.h:52
virtual bool read(size_t *offset=0)
Reads the header, and if offset is not null, sets the file offset to the data field.
void read(AimsTimeSurface< D, T > &thing, const carto::AllocatorContext &context, carto::Object options)
WavefrontMeshReader(const std::string &name)
static char separator()
static std::string dirname(const std::string &)
static Object value()
virtual bool removeProperty(const std::string &)
virtual bool hasProperty(const std::string &) const
GenericObject * get() const
virtual bool eof() const
static bool getline(DataSource &ds, std::string &)
The class for EcatSino data write operation.
Definition: borderfiller.h:13
GenesisReader< T > & operator>>(GenesisReader< T > &reader, AimsData< T > &thing)
Definition: genesisR.h:70
std::map< std::string, Object > Dictionary
AIMSDATA_API float norm(const Tensor &thing)
Definition: tensor.h:141