gKit3
Loading...
Searching...
No Matches
mesh_io.cpp
1
2#include <cstdlib>
3#include <cassert>
4#include <map>
5
6#include "mesh_io.h"
7
8#include "image.h"
9#include "image_io.h"
10
11
12bool read_positions( const char *filename, std::vector<Point>& positions )
13{
14 positions.clear();
15
16 FILE *in= fopen(filename, "rt");
17 if(!in)
18 {
19 printf("[error] loading mesh '%s'...\n", filename);
20 return false;
21 }
22
23 printf("loading mesh '%s'...\n", filename);
24
25 std::vector<Point> wpositions;
26 std::vector<int> wp;
27
28 char line_buffer[1024];
29 bool error= true;
30 for(;;)
31 {
32 // charge une ligne du fichier
33 if(!fgets(line_buffer, sizeof(line_buffer), in))
34 {
35 error= false; // fin du fichier, pas d'erreur detectee
36 break;
37 }
38
39 // force la fin de la ligne, au cas ou
40 line_buffer[sizeof(line_buffer) -1]= 0;
41
42 // saute les espaces en debut de ligne
43 char *line= line_buffer;
44 while(*line && isspace(*line))
45 line++;
46
47 if(line[0] == 'v')
48 {
49 float x, y, z;
50 if(line[1] == ' ') // position x y z
51 {
52 if(sscanf(line, "v %f %f %f", &x, &y, &z) != 3)
53 break;
54 wpositions.push_back( Point(x, y, z) );
55 }
56 }
57
58 else if(line[0] == 'f') // triangle a b c, les sommets sont numerotes a partir de 1 ou de la fin du tableau (< 0)
59 {
60 wp.clear();
61
62 int next= 0;
63 for(line= line +1; ; line= line + next)
64 {
65 wp.push_back(0);
66
67 // analyse les attributs du sommet : p/t/n ou p//n ou p/t ou p...
68 next= 0;
69 int wt= 0;
70 int wn= 0;
71 if(sscanf(line, " %d/%d/%d %n", &wp.back(), &wt, &wn, &next) == 3)
72 continue;
73 else if(sscanf(line, " %d/%d %n", &wp.back(), &wt, &next) == 2)
74 continue;
75 else if(sscanf(line, " %d//%d %n", &wp.back(), &wn, &next) == 2)
76 continue;
77 else if(sscanf(line, " %d %n", &wp.back(), &next) == 1)
78 continue;
79 else if(next == 0) // fin de ligne
80 break;
81 }
82
83 // triangule la face
84 for(unsigned v= 2; v +1 < wp.size(); v++)
85 {
86 unsigned idv[3]= { 0, v -1, v };
87 for(unsigned i= 0; i < 3; i++)
88 {
89 unsigned k= idv[i];
90 int p= (wp[k] < 0) ? int(wpositions.size()) + wp[k] : wp[k] -1;
91 if(p < 0) break; // error
92
93 // et duplique les positions...
94 assert(p < int(wpositions.size()));
95 positions.push_back(wpositions[p]);
96 }
97 }
98 }
99 }
100
101 fclose(in);
102
103 if(error)
104 printf("[error] loading mesh '%s'...\n%s\n\n", filename, line_buffer);
105 else
106 printf("mesh '%s': %d positions\n", filename, int(positions.size()));
107
108 return !error;
109}
110
111
112bool read_indexed_positions( const char *filename, std::vector<Point>& positions, std::vector<unsigned>& indices )
113{
114 positions.clear();
115 indices.clear();
116
117 FILE *in= fopen(filename, "rt");
118 if(!in)
119 {
120 printf("[error] loading indexed mesh '%s'...\n", filename);
121 return false;
122 }
123
124 printf("loading indexed mesh '%s'...\n", filename);
125
126 std::vector<int> wp;
127
128 char line_buffer[1024];
129 bool error= true;
130 for(;;)
131 {
132 // charge une ligne du fichier
133 if(!fgets(line_buffer, sizeof(line_buffer), in))
134 {
135 error= false; // fin du fichier, pas d'erreur detectee
136 break;
137 }
138
139 // force la fin de la ligne, au cas ou
140 line_buffer[sizeof(line_buffer) -1]= 0;
141
142 // saute les espaces en debut de ligne
143 char *line= line_buffer;
144 while(*line && isspace(*line))
145 line++;
146
147 if(line[0] == 'v')
148 {
149 float x, y, z;
150 if(line[1] == ' ') // position x y z
151 {
152 if(sscanf(line, "v %f %f %f", &x, &y, &z) != 3)
153 break;
154 positions.push_back( Point(x, y, z) );
155 }
156 }
157
158 else if(line[0] == 'f') // triangle a b c, les sommets sont numerotes a partir de 1 ou de la fin du tableau (< 0)
159 {
160 wp.clear();
161
162 int next= 0;
163 for(line= line +1; ; line= line + next)
164 {
165 wp.push_back(0);
166
167 // analyse les attributs du sommet : p/t/n ou p//n ou p/t ou p...
168 next= 0;
169 int wt= 0;
170 int wn= 0;
171 if(sscanf(line, " %d/%d/%d %n", &wp.back(), &wt, &wn, &next) == 3)
172 continue;
173 else if(sscanf(line, " %d/%d %n", &wp.back(), &wt, &next) == 2)
174 continue;
175 else if(sscanf(line, " %d//%d %n", &wp.back(), &wn, &next) == 2)
176 continue;
177 else if(sscanf(line, " %d %n", &wp.back(), &next) == 1)
178 continue;
179 else if(next == 0) // fin de ligne
180 break;
181 }
182
183 // triangule la face
184 for(unsigned v= 2; v +1 < wp.size(); v++)
185 {
186 unsigned idv[3]= { 0, v -1, v };
187 for(unsigned i= 0; i < 3; i++)
188 {
189 unsigned k= idv[i];
190 int p= (wp[k] < 0) ? int(positions.size()) + wp[k] : wp[k] -1;
191 if(p < 0) break; // error
192
193 assert(p < int(positions.size()));
194 indices.push_back(p);
195 }
196 }
197 }
198 }
199
200 fclose(in);
201
202 if(error)
203 printf("[error] loading indexed mesh '%s'...\n%s\n\n", filename, line_buffer);
204 else
205 printf("indexed mesh '%s': %d positions, %d indices\n", filename, int(positions.size()), int(indices.size()));
206
207 return !error;
208}
209
210
211bool read_materials_mtl( const char *filename, Materials& materials )
212{
213 FILE *in= fopen(filename, "rt");
214 if(!in)
215 {
216 printf("[error] loading materials '%s'...\n", filename);
217 return false;
218 }
219
220 printf("loading materials '%s'...\n", filename);
221
222 Material *material= nullptr;
223 char tmp[1024];
224 char line_buffer[1024];
225 bool error= true;
226 for(;;)
227 {
228 // charge une ligne du fichier
229 if(!fgets(line_buffer, sizeof(line_buffer), in))
230 {
231 error= false; // fin du fichier, pas d'erreur detectee
232 break;
233 }
234
235 // force la fin de la ligne, au cas ou
236 line_buffer[sizeof(line_buffer) -1]= 0;
237
238 // saute les espaces en debut de ligne
239 char *line= line_buffer;
240 while(*line && isspace(*line))
241 line++;
242
243 if(line[0] == 'n')
244 {
245 if(sscanf(line, "newmtl %[^\r\n]", tmp) == 1)
246 {
247 int id= materials.insert(Material(Black()), tmp);
248 material= &materials.material(id);
249 }
250 }
251
252 if(material == nullptr)
253 continue;
254
255 if(line[0] == 'K')
256 {
257 float r, g, b;
258 if(sscanf(line, "Kd %f %f %f", &r, &g, &b) == 3)
259 material->diffuse= Color(r, g, b);
260 else if(sscanf(line, "Ks %f %f %f", &r, &g, &b) == 3)
261 material->specular= Color(r, g, b);
262 else if(sscanf(line, "Ke %f %f %f", &r, &g, &b) == 3)
263 material->emission= Color(r, g, b);
264 }
265
266 else if(line[0] == 'N')
267 {
268 float n;
269 if(sscanf(line, "Ns %f", &n) == 1) // Ns, puissance / concentration du reflet, modele blinn phong
270 material->ns= n;
271 if(sscanf(line, "Ni %f", &n) == 1) // Ni, indice de refraction / fresnel
272 material->ni= n;
273 }
274 else if(line[0] == 'T')
275 {
276 float r, g, b;
277 if(sscanf(line, "Tf %f %f %f", &r, &g, &b) == 3) // Tf, couleur de l'objet transparent
278 material->transmission= Color(r, g, b);
279 }
280
281 else if(line[0] == 'm')
282 {
283 if(sscanf(line, "map_Kd %[^\r\n]", tmp) == 1)
284 material->diffuse_texture= materials.insert_texture( absolute_filename(pathname(filename), tmp).c_str() );
285
286 else if(sscanf(line, "map_Ks %[^\r\n]", tmp) == 1)
287 material->specular_texture= materials.insert_texture( absolute_filename(pathname(filename), tmp).c_str() );
288
289 else if(sscanf(line, "map_Ns %[^\r\n]", tmp) == 1)
290 material->ns_texture= materials.insert_texture( absolute_filename(pathname(filename), tmp).c_str() );
291 }
292 }
293
294 fclose(in);
295
296 if(error)
297 printf("[error] parsing line :\n%s\n", line_buffer);
298
299 return !error;
300}
301
302bool read_materials( const char *filename, Materials& materials, std::vector<int>& indices )
303{
304 indices.clear();
305
306 FILE *in= fopen(filename, "rt");
307 if(!in)
308 {
309 printf("[error] loading materials '%s'...\n", filename);
310 return false;
311 }
312
313 printf("loading materials '%s'...\n", filename);
314
315 std::vector<int> wp;
316 int material_id= -1;
317
318 char tmp[1024];
319 char line_buffer[1024];
320 bool error= true;
321 for(;;)
322 {
323 // charge une ligne du fichier
324 if(!fgets(line_buffer, sizeof(line_buffer), in))
325 {
326 error= false; // fin du fichier, pas d'erreur detectee
327 break;
328 }
329
330 // force la fin de la ligne, au cas ou
331 line_buffer[sizeof(line_buffer) -1]= 0;
332
333 // saute les espaces en debut de ligne
334 char *line= line_buffer;
335 while(*line && isspace(*line))
336 line++;
337
338 if(line[0] == 'f') // triangle a b c
339 {
340 wp.clear();
341
342 int next= 0;
343 for(line= line +1; ; line= line + next)
344 {
345 wp.push_back(0);
346
347 // analyse les attributs du sommet : p/t/n ou p//n ou p/t ou p...
348 next= 0;
349 int wt= 0;
350 int wn= 0;
351 if(sscanf(line, " %d/%d/%d %n", &wp.back(), &wt, &wn, &next) == 3)
352 continue;
353 else if(sscanf(line, " %d/%d %n", &wp.back(), &wt, &next) == 2)
354 continue;
355 else if(sscanf(line, " %d//%d %n", &wp.back(), &wn, &next) == 2)
356 continue;
357 else if(sscanf(line, " %d %n", &wp.back(), &next) == 1)
358 continue;
359 else if(next == 0) // fin de ligne
360 break;
361 }
362
363 // force une matiere par defaut, si necessaire
364 if(material_id == -1)
365 material_id= materials.default_material_index();
366
367 // triangule la face
368 for(unsigned v= 2; v +1 < wp.size(); v++)
369 // indice de la matiere de chaque triangle
370 indices.push_back(material_id);
371 }
372 else if(line[0] == 'm')
373 {
374 if(sscanf(line, "mtllib %[^\r\n]", tmp) == 1)
375 {
376 std::string materials_filename;
377 if(tmp[0] != '/' && tmp[1] != ':') // windows c:\ pour les chemins complets...
378 materials_filename= normalize_filename(pathname(filename) + tmp);
379 else
380 materials_filename= std::string(tmp);
381
382 // charge les matieres
383 if(!read_materials_mtl( materials_filename.c_str(), materials ))
384 break;
385 }
386 }
387
388 else if(line[0] == 'u')
389 {
390 if(sscanf(line, "usemtl %[^\r\n]", tmp) == 1)
391 material_id= materials.find(tmp);
392 }
393 }
394
395 fclose(in);
396
397 if(error)
398 printf("[error] loading materials '%s'...\n%s\n\n", filename, line_buffer);
399
400 return !error;
401}
402
403
404// representation de l'indexation complete d'un sommet .obj / wavefront
405struct vertex
406{
407 int material;
408 int position;
409 int texcoord;
410 int normal;
411
412 vertex( ) : material(-1), position(-1), texcoord(-1), normal(-1) {}
413 vertex( const int m, const int p, const int t, const int n ) : material(m), position(p), texcoord(t), normal(n) {}
414
415 // comparaison lexicographique de 2 sommets / des indices de leurs attributs
416 bool operator< ( const vertex& b ) const
417 {
418 if(material != b.material) return material < b.material;
419 if(position != b.position) return position < b.position;
420 if(texcoord != b.texcoord) return texcoord < b.texcoord;
421 if(normal != b.normal) return normal < b.normal;
422 return false;
423 }
424};
425
426
427int MeshIOData::find_object( const char *name )
428{
429 for(unsigned i= 0; i < object_names.size(); i++)
430 if(object_names[i] == name)
431 return i;
432
433 return -1;
434}
435
436std::vector<MeshIOGroup> MeshIOData::groups( const std::vector<int>& properties )
437{
438 std::vector<int> remap( properties.size() );
439 for(unsigned i= 0; i < remap.size(); i++)
440 remap[i]= i;
441
442 std::stable_sort(remap.begin(), remap.end(),
443 [&]( const int a, const int b)
444 {
445 return properties[a] < properties[b];
446 });
447
448 // re-organise l'index buffer
449 std::vector<unsigned> tmp;
450 tmp.reserve(indices.size());
451 for(unsigned i= 0; i < remap.size(); i++)
452 {
453 int id= remap[i];
454 tmp.push_back( indices[3*id] );
455 tmp.push_back( indices[3*id+1] );
456 tmp.push_back( indices[3*id+2] );
457 }
458
459 std::swap(indices, tmp);
460
461 // construit les groupes de triangles
462 std::vector<MeshIOGroup> groups;
463
464 // first group
465 int id= properties[remap[0]];
466 unsigned first= 0;
467 unsigned count= 1;
468 for(unsigned i= 1; i < remap.size(); i++)
469 {
470 if(properties[remap[i]] != id)
471 {
472 groups.push_back({ id, first, count });
473 // restart
474 id= properties[remap[i]];
475 first= i;
476 count= 0;
477 }
478
479 count++;
480 }
481
482 // last group
483 groups.push_back({ id, first, count });
484
485 {
486 // re-organise aussi les indices de matieres
487 std::vector<int> tmp;
488 tmp.reserve(remap.size());
489 for(unsigned i= 0; i < remap.size(); i++)
490 tmp.push_back( material_indices[remap[i]] );
491
492 std::swap(material_indices, tmp);
493
494 // re-organise aussi les indices d'objets
495 tmp.clear();
496 for(unsigned i= 0; i < remap.size(); i++)
497 tmp.push_back( object_indices[remap[i]] );
498
499 std::swap(object_indices, tmp);
500 }
501
502 return groups;
503}
504
505bool read_meshio_data( const char *filename, MeshIOData& data )
506{
507 FILE *in= fopen(filename, "rt");
508 if(!in)
509 {
510 printf("[error] loading indexed mesh '%s'...\n", filename);
511 return false;
512 }
513
514 printf("loading indexed mesh '%s'...\n", filename);
515
516 std::vector<Point> wpositions;
517 std::vector<Point> wtexcoords;
518 std::vector<Vector> wnormals;
519
520 std::vector<int> wp;
521 std::vector<int> wt;
522 std::vector<int> wn;
523
524 std::map<vertex, unsigned> remap;
525 int material_id= -1;
526 int object_id= -1;
527
528 char tmp[1024];
529 char line_buffer[1024];
530 bool error= true;
531 for(;;)
532 {
533 // charge une ligne du fichier
534 if(!fgets(line_buffer, sizeof(line_buffer), in))
535 {
536 error= false; // fin du fichier, pas d'erreur detectee
537 break;
538 }
539
540 // force la fin de la ligne, au cas ou
541 line_buffer[sizeof(line_buffer) -1]= 0;
542
543 // saute les espaces en debut de ligne
544 char *line= line_buffer;
545 while(*line && isspace(*line))
546 line++;
547
548 if(line[0] == 'v')
549 {
550 float x, y, z;
551 if(line[1] == ' ') // position x y z
552 {
553 if(sscanf(line, "v %f %f %f", &x, &y, &z) != 3)
554 break;
555 wpositions.push_back( Point(x, y, z) );
556 }
557 else if(line[1] == 'n') // normal x y z
558 {
559 if(sscanf(line, "vn %f %f %f", &x, &y, &z) != 3)
560 break;
561 wnormals.push_back( Vector(x, y, z) );
562 }
563 else if(line[1] == 't') // texcoord x y
564 {
565 if(sscanf(line, "vt %f %f", &x, &y) != 2)
566 break;
567 wtexcoords.push_back( Point(x, y, 0) );
568 }
569 }
570 else if(line[0] == 'f') // triangle a b c, les sommets sont numerotes a partir de 1 ou de la fin du tableau (< 0)
571 {
572 wp.clear();
573 wt.clear();
574 wn.clear();
575
576 int next;
577 for(line= line +1; ; line= line + next)
578 {
579 wp.push_back(0);
580 wt.push_back(0);
581 wn.push_back(0); // 0: invalid index
582
583 // analyse les attributs du sommet : p/t/n ou p//n ou p/t ou p...
584 next= 0;
585 if(sscanf(line, " %d/%d/%d %n", &wp.back(), &wt.back(), &wn.back(), &next) == 3)
586 continue;
587 else if(sscanf(line, " %d/%d %n", &wp.back(), &wt.back(), &next) == 2)
588 continue;
589 else if(sscanf(line, " %d//%d %n", &wp.back(), &wn.back(), &next) == 2)
590 continue;
591 else if(sscanf(line, " %d %n", &wp.back(), &next) == 1)
592 continue;
593 else if(next == 0) // fin de ligne
594 break;
595 }
596
597 // force une matiere par defaut, si necessaire
598 if(material_id == -1)
599 material_id= data.materials.default_material_index();
600
601 if(object_id == -1)
602 {
603 object_id= data.find_object("default");
604 if(object_id == -1)
605 {
606 object_id= data.object_names.size();
607 data.object_names.push_back("default");
608 }
609 }
610
611 // triangule la face
612 for(unsigned v= 2; v +1 < wp.size(); v++)
613 {
614 data.material_indices.push_back(material_id);
615 data.object_indices.push_back(object_id);
616
617 unsigned idv[3]= { 0, v -1, v };
618 for(unsigned i= 0; i < 3; i++)
619 {
620 unsigned k= idv[i];
621 // indices des attributs du sommet
622 int p= (wp[k] < 0) ? int(wpositions.size()) + wp[k] : wp[k] -1;
623 int t= (wt[k] < 0) ? int(wtexcoords.size()) + wt[k] : wt[k] -1;
624 int n= (wn[k] < 0) ? int(wnormals.size()) + wn[k] : wn[k] -1;
625
626 if(p < 0) break; // error
627
628 // recherche / insere le sommet
629 auto found= remap.insert( std::make_pair(vertex(material_id, p, t, n), unsigned(remap.size())) );
630 if(found.second)
631 {
632 // pas trouve, copie les nouveaux attributs
633 if(t != -1) data.texcoords.push_back(wtexcoords[t]);
634 if(n != -1) data.normals.push_back(wnormals[n]);
635 data.positions.push_back(wpositions[p]);
636 }
637
638 // construit l'index buffer
639 assert(found.first->second < data.positions.size());
640 data.indices.push_back(found.first->second);
641 }
642 }
643 }
644
645 else if(line[0] == 'm')
646 {
647 if(sscanf(line, "mtllib %[^\r\n]", tmp) == 1)
648 {
649 std::string materials_filename;
650 if(tmp[0] != '/' && tmp[1] != ':') // windows c:\ pour les chemins complets...
651 materials_filename= normalize_filename(pathname(filename) + tmp);
652 else
653 materials_filename= std::string(tmp);
654
655 // charge les matieres, ou pas...
656 read_materials_mtl( materials_filename.c_str(), data.materials );
657 }
658 }
659
660 else if(line[0] == 'u')
661 {
662 if(sscanf(line, "usemtl %[^\r\n]", tmp) == 1)
663 material_id= data.materials.find(tmp);
664 }
665
666 else if(line[0] == 'o')
667 {
668 if(sscanf(line, "o %s", tmp) == 1)
669 {
670 object_id= data.find_object(tmp);
671 if(object_id == -1)
672 {
673 object_id= data.object_names.size();
674 data.object_names.push_back(tmp);
675 }
676
677 //~ printf("object '%s': %d\n", tmp, object_id);
678 }
679 }
680 #if 0
681 else if(line[0] == 'g')
682 {
683 // ne lit que le 1er groupe
684 if(sscanf(line, "g %s", tmp) == 1)
685 {
686 object_id= data.find_object(tmp);
687 if(object_id == -1)
688 {
689 object_id= data.object_names.size();
690 data.object_names.push_back(tmp);
691 }
692
693 printf("object '%s': %d\n", tmp, object_id);
694 }
695 }
696 #endif
697 }
698
699 fclose(in);
700
701 if(error)
702 {
703 printf("[error] loading indexed mesh '%s'...\n%s\n\n", filename, line_buffer);
704 return false;
705 }
706
707 printf(" %d indices, %d positions %d texcoords %d normals\n",
708 int(data.indices.size()), int(data.positions.size()), int(data.texcoords.size()), int(data.normals.size()));
709 printf(" %d materials, %d textures\n", data.materials.count(), data.materials.filename_count());
710 return true;
711}
712
713
714bool read_images( const Materials& materials, std::vector<Image>& images )
715{
716 int n= materials.filename_count();
717 if(n == 0) // pas de textures
718 return true;
719
720 images.resize(n);
721
722#pragma omp parallel for
723 for(int i= 0; i < n; i++)
724 {
725 //~ printf("loading '%s'...\n", materials.filename(i));
726 images[i]= read_image(materials.filename(i));
727 }
728
729 return true;
730}
731
732bool read_images( const MeshIOData& data, std::vector<Image>& images )
733{
734 return read_images(data.materials, images);
735}
Color Black()
utilitaire. renvoie une couleur noire.
Definition color.cpp:18
Image read_image(const char *filename, const bool flipY)
charge une image .bmp .tga .jpeg .png ou .hdr
Definition image_io.cpp:94
bool read_indexed_positions(const char *filename, std::vector< Point > &positions, std::vector< unsigned > &indices)
Definition mesh_io.cpp:112
bool read_positions(const char *filename, std::vector< Point > &positions)
Definition mesh_io.cpp:12
bool read_images(const Materials &materials, std::vector< Image > &images)
Definition mesh_io.cpp:714
bool read_meshio_data(const char *filename, MeshIOData &data)
Definition mesh_io.cpp:505
bool read_materials(const char *filename, Materials &materials, std::vector< int > &indices)
Definition mesh_io.cpp:302
representation d'une couleur (rgba) transparente ou opaque.
Definition color.h:14
int diffuse_texture
indice de la texture de la couleur de base, ou -1.
Definition materials.h:24
float ns
concentration des reflets, exposant pour les reflets blinn-phong.
Definition materials.h:20
int ns_texture
indice de la texture, ou -1.
Definition materials.h:26
Color transmission
transmission, "couleur" des objets transparents.
Definition materials.h:22
Color emission
pour une source de lumiere.
Definition materials.h:19
float ni
indice de refraction, cf coefficients de Fresnel.
Definition materials.h:21
Color diffuse
couleur diffuse / de base.
Definition materials.h:17
Color specular
couleur du reflet.
Definition materials.h:18
int specular_texture
indice de la texture, ou -1.
Definition materials.h:25
int insert(const Material &material, const char *name)
ajoute une matiere.
Definition materials.h:63
int filename_count() const
renvoie le nombre de noms de fichiers de textures.
Definition materials.h:148
int find(const char *name)
recherche une matiere avec son nom. renvoie son indice dans materials, ou -1.
Definition materials.h:89
int insert_texture(const char *filename)
ajoute une texture / nom du fichier.
Definition materials.h:77
const char * filename(const int id) const
renvoie le nom de fichier d'une texture.
Definition materials.h:150
int count() const
nombre de matieres.
Definition materials.h:103
const Material & material(const int id) const
renvoie la ieme matiere.
Definition materials.h:111
int default_material_index()
indice de la matiere par defaut dans le tableau materials.
Definition materials.h:139
representation d'un point 3d.
Definition vec.h:21
representation d'un vecteur 3d.
Definition vec.h:58