aimsdata 6.0.13
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 == "Ke" )
200 {
201 if( !current_obj )
202 {
203 std::cerr << "MTL error: no current object.\n";
204 continue;
205 }
206 std::vector<float> emission( 3 );
207 s >> emission[0] >> emission[1] >> emission[2];
208 current_mat->setProperty( "emission", emission );
209 }
210 else if( element == "Kd" )
211 {
212 if( !current_obj )
213 {
214 std::cerr << "MTL error: no current object.\n";
215 continue;
216 }
217 std::vector<float> diffuse( 4, 1. );
218 if( current_mat->getProperty( "diffuse", diffuse ) )
219 while( diffuse.size() < 4 )
220 diffuse.push_back( 1. );
221 s >> diffuse[0] >> diffuse[1] >> diffuse[2];
222 current_mat->setProperty( "diffuse", diffuse );
223 }
224 else if( element == "Ks" )
225 {
226 if( !current_obj )
227 {
228 std::cerr << "MTL error: no current object.\n";
229 continue;
230 }
231 std::vector<float> specular( 3 );
232 s >> specular[0] >> specular[1] >> specular[2];
233 current_mat->setProperty( "specular", specular );
234 }
235 else if( element == "Ns" )
236 {
237 if( !current_obj )
238 {
239 std::cerr << "MTL error: no current object.\n";
240 continue;
241 }
242 float ns;
243 s >> ns;
244 current_mat->setProperty( "shininess", ns );
245 }
246 else if( element == "d" )
247 {
248 if( !current_obj )
249 {
250 std::cerr << "MTL error: no current object.\n";
251 continue;
252 }
253 float a;
254 s >> a;
255 std::vector<float> diffuse( 4, 0.8 );
256 if( current_mat->getProperty( "diffuse", diffuse ) )
257 while( diffuse.size() < 4 )
258 diffuse.push_back( 0.8 );
259 diffuse[3] = a;
260 current_mat->setProperty( "diffuse", diffuse );
261 }
262 else if( element == "Tr" )
263 {
264 if( !current_obj )
265 {
266 std::cerr << "MTL error: no current object.\n";
267 continue;
268 }
269 float a;
270 s >> a;
271 std::vector<float> diffuse( 4, 0.8 );
272 if( current_mat->getProperty( "diffuse", diffuse ) )
273 while( diffuse.size() < 4 )
274 diffuse.push_back( 0.8 );
275 diffuse[3] = 1.f - a;
276 current_mat->setProperty( "diffuse", diffuse );
277 }
278 else if( element == "map_Kd" )
279 {
280 if( !current_obj )
281 {
282 std::cerr << "MTL error: no current object.\n";
283 continue;
284 }
285 // texture image and params
286 std::string item;
287 bool image_found = false;
288 while( !image_found && s )
289 {
290 s >> item;
291 if( item == "-o" )
292 {
293 // FIXME might be less than 3 values and up to 4
294 std::vector<float> tex_offset( 3 );
295 s >> tex_offset[0] >> tex_offset[1] >> tex_offset[2];
296 current_obj->setProperty( "texture_offset", tex_offset );
297 }
298 else if( item == "-s" )
299 {
300 // FIXME might be less than 3 values and up to 4
301 std::vector<float> tex_scale( 3 );
302 s >> tex_scale[0] >> tex_scale[1] >> tex_scale[2];
303 current_obj->setProperty( "texture_scale", tex_scale );
304 }
305 else if( item == "-clamp" )
306 {
307 s >> item;
308 }
309 else // TODO: other values
310 {
311 std::string teximage_fname
312 = carto::FileUtil::dirname( mtl_filename )
314 Reader<carto::Volume<AimsRGBA> > r( teximage_fname );
315 try
316 {
317 carto::rc_ptr<carto::Volume<AimsRGBA> > teximage( r.read() );
318 std::map<int, carto::Object> palettes;
319 mtl_dict->getProperty( "_texture_palettes", palettes );
320 palettes[timestep] = carto::Object::value( teximage );
321 mtl_dict->setProperty( "_texture_palettes", palettes );
322 }
323 catch( ... )
324 {
325 std::cout << "failed to read texture image " << teximage_fname
326 << std::endl;
327 }
328 }
329 }
330 }
331 else if( element == "illum" )
332 {
333 if( !current_obj )
334 {
335 std::cerr << "MTL error: no current object.\n";
336 continue;
337 }
338 float illum;
339 s >> illum;
340 current_obj->setProperty( "illum", illum );
341 // don't know what to do with this...
342 }
343 else if( element == "Ni" )
344 {
345 if( !current_obj )
346 {
347 std::cerr << "MTL error: no current object.\n";
348 continue;
349 }
350 float ni;
351 s >> ni;
352 current_obj->setProperty( "optical_density", ni );
353 // don't know what to do with this... (refraction index)
354 }
355 else
356 {
357 std::cout << "unrecognized MTL tag: " << element << std::endl;
358 }
359 }
360
361 return mtl_dict;
362 }
363
364
365 template <long D, typename T>
366 inline
367 void WavefrontMeshReader<D,T>::setMtl( carto::Object mtl_dict,
368 const std::string & mtl_objname,
369 PythonHeader & hdr )
370 {
371 carto::Object m = mtl_dict->getProperty( mtl_objname );
372 // merge all materials/texture properties
373 carto::Object mat;
374 try
375 {
376 // material can be defined both sides (.mtl, .minf)
377 mat = m->getProperty( "material" );
378 carto::Object minfmat = hdr.getProperty( "material" );
379 mat->copyProperties( minfmat );
380 }
381 catch( ... )
382 {
383 }
384
385 hdr.copyProperties( m );
386 if( mat.get() )
387 hdr.setProperty( "material", mat );
388
389 if( mtl_dict->hasProperty( "_texture_palettes" ) )
390 {
391 carto::Object palettes = mtl_dict->getProperty( "_texture_palettes" );
392 hdr.setProperty( "_texture_palettes", palettes );
393 }
394 }
395
396
397 template <long D, typename T>
398 inline
399 void WavefrontMeshReader<D,T>::storeMesh(
400 AimsTimeSurface<D,T>& thing,
401 const std::vector<Point3df> & vertices,
402 const std::vector<Point3df> &normals,
403 const std::vector<AimsVector<uint, D> > & polygons,
404 const std::vector<T> & texture,
405 const std::vector<int> & normals_ind,
406 const std::vector<int> & texture_ind, int timestep )
407 {
408 thing[timestep].vertex() = vertices;
409 std::vector<Point3df> o_normals;
410 if( normals_ind.size() != normals.size() )
411 o_normals = normals;
412 else
413 {
414 o_normals.resize( normals_ind.size() );
415 // copy reordered normals
416 for( int i=0, k=normals_ind.size(); i<k; ++i )
417 {
418 if( normals_ind[i] < 0 || normals_ind[i] >= static_cast<int>(vertices.size()) )
419 {
420 std::stringstream s;
421 s << "normal index out of range: " << normals_ind[i] << " / "
422 << vertices.size() << " at index " << i;
423 throw carto::parse_error( s.str() , "", _name, i );
424 }
425 o_normals[i] = normals[normals_ind[i]];
426 }
427 }
428 thing[timestep].normal() = o_normals;
429 thing[timestep].polygon() = polygons;
430
433 {
434 // copy reordered texture
435 std::vector<T> o_texture;
436 o_texture.resize( texture_ind.size() );
437 for( int i=0, k=texture_ind.size(); i<k; ++i )
438 {
439 if( texture_ind[i] < 0
440 || texture_ind[i] >= static_cast<int>(texture.size()) )
441 {
442 std::stringstream s;
443 if( texture_ind[i] >= 0 )
444 {
445 s << "texture index out of range: " << texture_ind[i] << " / "
446 << texture.size() << " at index " << i;
447 throw carto::parse_error( s.str(), "", _name, i );
448 }
449 // ind < 0, just not used.
450 o_texture[i] = texture[0];
451 }
452 else
453 o_texture[i] = texture[texture_ind[i]];
454 }
455 thing[timestep].texture() = o_texture;
456 }
457 }
458
459
460 template <long D, typename T>
461 inline
463 const carto::AllocatorContext & /*context*/,
464 carto::Object /*options*/ )
465 {
466 std::string filename = _name;
467 WavefrontHeader hdr( _name );
468 hdr.read();
469
470 std::ifstream istr( filename.c_str(), std::ios::in );
471 istr.unsetf( std::ios::skipws );
472 if( !istr )
473 soma::io_error::launchErrnoExcept( filename );
474
475 istr.setf( std::ios::skipws );
476 soma::IStreamDataSource ds( istr );
477 thing.erase();
478 std::string l;
479 int line = 0;
480
481 std::vector<Point3df> vertices, normals;
482 std::vector<AimsVector<uint, D> > polygons;
483 std::vector<T> texture;
484 std::vector<int> normals_ind;
485 std::vector<int> texture_ind;
486 int timestep = 0;
487 bool update_normals = false;
488 carto::Object mtl_dict;
489
490 while( ds && !ds.eof() )
491 {
492 if( !soma::StreamUtil::getline( ds, l ) )
493 {
494 if( ds.eof() )
495 break;
496 else
497 throw soma::wrong_format_error( filename );
498 }
499 ++line;
500
501 if( l.empty() )
502 continue;
503 if( l[0] == '#' )
504 continue;
505 std::stringstream s(l);
506 std::string element;
507 s >> element;
508 if( element == "o" )
509 {
510 // new object / group
511 if( timestep != 0 || !vertices.empty() )
512 {
513 storeMesh( thing, vertices, normals, polygons, texture, normals_ind,
514 texture_ind, timestep );
515
516 if( thing[timestep].normal().size() != vertices.size() )
517 update_normals = true;
518 ++timestep;
519 vertices.clear();
520 normals.clear();
521 polygons.clear();
522 texture.clear();
523 normals_ind.clear();
524 texture_ind.clear();
525 }
526 }
527 else if( element == "g" )
528 continue; // TODO: groups
529 else if( element == "s" )
530 continue; // smoothing
531 else if( element == "mtllib" )
532 {
533 std::string mtl_filename;
534 mtl_filename = l.substr( s.tellg(), l.length() - s.tellg() );
535 size_t i = 0;
536 while( !mtl_filename.empty() && mtl_filename[i] == ' ' )
537 ++i;
538 mtl_filename = mtl_filename.substr( i, mtl_filename.length() - i );
539 mtl_filename = carto::FileUtil::dirname( filename )
540 + carto::FileUtil::separator() + mtl_filename;
541 mtl_dict = readMtlFle( mtl_filename );
542 }
543 else if( element == "usemtl" )
544 {
545 std::string mtl_objname;
546 mtl_objname = l.substr( s.tellg(), l.length() - s.tellg() );
547 size_t i = 0;
548 while( !mtl_objname.empty() && mtl_objname[i] == ' ' )
549 ++i;
550 mtl_objname = mtl_objname.substr( i, mtl_objname.length() - i );
551 setMtl( mtl_dict, mtl_objname, hdr );
552 }
553 else if( element == "v" )
554 {
555 // vertex
556 Point3df p;
557 s >> p[0] >> p[1] >> p[2];
558 vertices.push_back(p);
559 }
560 else if( element == "vn" )
561 {
562 // normal
563 Point3df p;
564 s >> p[0] >> p[1] >> p[2];
565 normals.push_back(p);
566 }
567 else if( element == "vt" )
568 {
569 // texture
570 T tex;
571 _readTexture<T>::r( s, tex );
572 texture.push_back(tex);
573 }
574 else if( element == "f" || (D == 2 && element == "l" ) ) // l for line (D=2)
575 {
576 // face
577 std::string item;
578 std::string::size_type pos, pos0;
580 int pol;
581 int tex;
582 int norm;
583 for( int p=0; p<D; ++p )
584 {
585 s >> item;
586 pos0 = item.find( '/' );
587 std::stringstream z( item.substr(0, pos0 ) );
588 z >> pol;
589 if( pol < 0 )
590 poly[p] = vertices.size() + pol;
591 else
592 poly[p] = pol - 1; // nums start at 1
593 if( pos0 == std::string::npos )
594 continue;
595
596 ++pos0;
597 pos = item.find( '/', pos0 );
600 {
601 std::stringstream z( item.substr(pos0, pos - pos0 ) );
602 z >> tex;
603 if( texture_ind.size() <= poly[p] )
604 texture_ind.resize( poly[p] + 1, -1 );
605 if( texture_ind[ poly[p] ] < 0 )
606 texture_ind[ poly[p] ] = tex - 1; // (starts at 1)
607 else if( texture_ind[ poly[p] ] != tex - 1 )
608 {
609 // same vertex is assigned a different texture:
610 // we must duplicate the vertex
611 vertices.push_back( vertices[ poly[p] ] );
612 poly[p] = vertices.size() - 1;
613 texture_ind.resize( poly[p] + 1, -1 );
614 texture_ind[ poly[p] ] = tex - 1;
615 }
616 }
617 else if( pos != pos0 )
618 throw carto::parse_error( "malformed face", "", filename, line );
619 if( pos == std::string::npos )
620 continue;
621 pos0 = pos + 1;
622 std::stringstream n( item.substr( pos0, item.length() - pos0 ) );
623 n >> norm;
624 if( norm < 0 )
625 norm = vertices.size() + norm + 1;
626 if( normals_ind.size() <= poly[p] )
627 normals_ind.resize( poly[p] + 1 );
628 normals_ind[ poly[p] ] = norm - 1; // (starts at 1)
629 }
630 polygons.push_back( poly );
631 }
632 else
633 {
634 std::cerr << "unrecognized element: " << element << ", " << D << ", " << l << std::endl;
635 }
636 }
637
638 /*
639 std::cout << "wavefront vertices: " << vertices.size() << ", normals: " << normals.size() << ", polygons: " << polygons.size() << ", normals_ind: " << normals_ind.size() << std::endl;
640 std::cout << "timestep: " << timestep << std::endl;
641 */
642
643 storeMesh( thing, vertices, normals, polygons, texture, normals_ind,
644 texture_ind, timestep );
645
646 if( update_normals || thing[timestep].normal().size() != vertices.size() )
647 thing.updateNormals();
648
649 // std::cout << "WAVEFRONT read OK\n";
650
651 if( hdr.hasProperty( "filenames" ) )
652 hdr.removeProperty( "filenames" );
653 thing.setHeader( hdr );
654 }
655
656}
657
658#endif
659
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)