aimsdata 6.0.0
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>
40#include <aims/io/reader.h>
41#include <aims/rgb/rgb.h>
42#include <cartodata/volume/volume.h>
43#include <cartobase/exception/file.h>
44#include <cartobase/exception/format.h>
45#include <cartobase/stream/fileutil.h>
46
47#include <aims/mesh/texture.h>
48#include <aims/io/writer.h>
49
53#include <cartobase/exception/ioexcept.h>
54
55
56namespace
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
89namespace 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 )
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;
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:519
void setHeader(const aims::PythonHeader &hdr)
Set the header.
Definition surface.h:336
const std::vector< Point3df > & normal() const
Get a const reference to the vector of normals of the 0 surface.
Definition surface.h:354
const std::vector< AimsVector< uint, D > > & polygon() const
Get a const reference to the vector of polygons of the 0 surface.
Definition surface.h:366
const std::vector< T > & texture() const
Get a const reference to the vector of textures of the 0 surface.
Definition surface.h:360
void updateNormals()
Update/Compute the normals.
Definition surface.h:528
const std::vector< Point3df > & vertex() const
Get a const reference to the vector of verteces of the surface of index 0.
Definition surface.h:348
Attributed python-like header, stores all needed information about an object, currently used for volu...
Definition pheader.h:52
Generic reader for every format of Aims object.
Definition reader.h:70
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)
std::string name()
static char separator()
static std::string dirname(const std::string &)
static Object value()
Object value(const Object &value)
virtual bool removeProperty(const std::string &)
virtual bool hasProperty(const std::string &) const
T * get() const
virtual bool eof() const
static bool getline(DataSource &ds, std::string &)
The class for EcatSino data write operation.
GenesisReader< T > & operator>>(GenesisReader< T > &reader, AimsData< T > &thing)
Definition genesisR.h:70
std::map< std::string, Object > Dictionary
AimsVector< float, 3 > Point3df
float norm(const AimsVector< T, D > &v1)