gKit2 light
Loading...
Searching...
No Matches
wavefront.cpp
1
2#include <cstdio>
3#include <cstring>
4#include <ctype.h>
5#include <climits>
6
7#include <map>
8#include <algorithm>
9
10#include "files.h"
11#include "wavefront.h"
12
13
14Mesh read_mesh( const char *filename )
15{
16 FILE *in= fopen(filename, "rb");
17 if(in == NULL)
18 {
19 printf("[error] loading mesh '%s'...\n", filename);
20 return {};
21 }
22
23 Mesh data(GL_TRIANGLES);
24
25 printf("loading mesh '%s'...\n", filename);
26
27 std::vector<vec3> positions;
28 std::vector<vec2> texcoords;
29 std::vector<vec3> normals;
30 int material_id= -1;
31
32 std::vector<int> idp;
33 std::vector<int> idt;
34 std::vector<int> idn;
35
36 char tmp[1024];
37 char line_buffer[1024];
38 bool error= true;
39 for(;;)
40 {
41 // charge une ligne du fichier
42 if(fgets(line_buffer, sizeof(line_buffer), in) == NULL)
43 {
44 error= false; // fin du fichier, pas d'erreur detectee
45 break;
46 }
47
48 // force la fin de la ligne, au cas ou
49 line_buffer[sizeof(line_buffer) -1]= 0;
50
51 // saute les espaces en debut de ligne
52 char *line= line_buffer;
53 while(*line && isspace(*line))
54 line++;
55
56 if(line[0] == 'v')
57 {
58 float x, y, z;
59 if(line[1] == ' ') // position x y z
60 {
61 if(sscanf(line, "v %f %f %f", &x, &y, &z) != 3)
62 break;
63 positions.push_back( vec3(x, y, z) );
64 }
65 else if(line[1] == 'n') // normal x y z
66 {
67 if(sscanf(line, "vn %f %f %f", &x, &y, &z) != 3)
68 break;
69 normals.push_back( vec3(x, y, z) );
70 }
71 else if(line[1] == 't') // texcoord x y
72 {
73 if(sscanf(line, "vt %f %f", &x, &y) != 2)
74 break;
75 texcoords.push_back( vec2(x, y) );
76 }
77 }
78
79 else if(line[0] == 'f') // triangle a b c, les sommets sont numerotes a partir de 1 ou de la fin du tableau (< 0)
80 {
81 idp.clear();
82 idt.clear();
83 idn.clear();
84
85 int next;
86 for(line= line +1; ; line= line + next)
87 {
88 idp.push_back(0);
89 idt.push_back(0);
90 idn.push_back(0); // 0: invalid index
91
92 // analyse les attributs du sommet : p/t/n ou p//n ou p/t ou p...
93 next= 0;
94 if(sscanf(line, " %d/%d/%d %n", &idp.back(), &idt.back(), &idn.back(), &next) == 3)
95 continue;
96 else if(sscanf(line, " %d/%d %n", &idp.back(), &idt.back(), &next) == 2)
97 continue;
98 else if(sscanf(line, " %d//%d %n", &idp.back(), &idn.back(), &next) == 2)
99 continue;
100 else if(sscanf(line, " %d %n", &idp.back(), &next) == 1)
101 continue;
102 else if(next == 0) // fin de ligne
103 break;
104 }
105 assert(idt.size() == idp.size());
106 assert(idn.size() == idp.size());
107
108 // force une matiere par defaut, si necessaire
109 if(material_id == -1)
110 {
111 material_id= data.materials().default_material_index();
112 printf("usemtl default\n");
113 }
114
115 data.material(material_id);
116
117 // triangule la face
118 for(int v= 2; v +1 < int(idp.size()); v++)
119 {
120 int idv[3]= { 0, v -1, v };
121 for(int i= 0; i < 3; i++)
122 {
123 int k= idv[i];
124 int p= (idp[k] < 0) ? int(positions.size()) + idp[k] : idp[k] -1;
125 int t= (idt[k] < 0) ? int(texcoords.size()) + idt[k] : idt[k] -1;
126 int n= (idn[k] < 0) ? int(normals.size()) + idn[k] : idn[k] -1;
127
128 if(p < 0) break; // error
129
130 // attribut du ieme sommet
131 if(t >= 0) data.texcoord(texcoords[t]);
132 if(n >= 0) data.normal(normals[n]);
133 data.vertex(positions[p]);
134 }
135 }
136 }
137
138 else if(line[0] == 'm')
139 {
140 if(sscanf(line, "mtllib %[^\r\n]", tmp) == 1)
141 {
142 std::string materials_filename;
143 if(tmp[0] != '/' && tmp[1] != ':') // windows c:\ pour les chemins complets...
144 materials_filename= normalize_filename(pathname(filename) + tmp);
145 else
146 materials_filename= std::string(tmp);
147
148 // charge les matieres
149 Materials materials= read_materials( materials_filename.c_str() );
150
151 // enregistre les matieres dans le mesh
152 data.materials(materials);
153 }
154 }
155
156 else if(line[0] == 'u')
157 {
158 if(sscanf(line, "usemtl %[^\r\n]", tmp) == 1)
159 material_id= data.materials().find(tmp);
160 }
161 }
162
163 fclose(in);
164
165 if(error)
166 printf("[error] loading mesh '%s'...\n%s\n\n", filename, line_buffer);
167 else
168 printf("mesh '%s': %d positions %s %s\n", filename, int(data.positions().size()), data.has_texcoord() ? "texcoord" : "", data.has_normal() ? "normal" : "");
169
170 return data;
171}
172
173
175struct vertex
176{
177 int material;
178 int position;
179 int texcoord;
180 int normal;
181
182 vertex( ) : material(-1), position(-1), texcoord(-1), normal(-1) {}
183 vertex( const int m, const int p, const int t, const int n ) : material(m), position(p), texcoord(t), normal(n) {}
184
185 // comparaison lexicographique de 2 sommets / des indices de leurs attributs
186 bool operator< ( const vertex& b ) const
187 {
188 if(material != b.material) return material < b.material;
189 if(position != b.position) return position < b.position;
190 if(texcoord != b.texcoord) return texcoord < b.texcoord;
191 if(normal != b.normal) return normal < b.normal;
192 return false;
193 }
194};
195
196
197Mesh read_indexed_mesh( const char *filename )
198{
199 FILE *in= fopen(filename, "rb");
200 if(in == NULL)
201 {
202 printf("[error] loading indexed mesh '%s'...\n", filename);
203 return {};
204 }
205
206 Mesh data(GL_TRIANGLES);
207
208 printf("loading indexed mesh '%s'...\n", filename);
209
210 std::vector<vec3> positions;
211 std::vector<vec2> texcoords;
212 std::vector<vec3> normals;
213 int material_id= -1;
214
215 std::vector<int> idp;
216 std::vector<int> idt;
217 std::vector<int> idn;
218
219 std::map<vertex, int> remap;
220
221 char tmp[1024];
222 char line_buffer[1024];
223 bool error= true;
224 for(;;)
225 {
226 // charge une ligne du fichier
227 if(fgets(line_buffer, sizeof(line_buffer), in) == NULL)
228 {
229 error= false; // fin du fichier, pas d'erreur detectee
230 break;
231 }
232
233 // force la fin de la ligne, au cas ou
234 line_buffer[sizeof(line_buffer) -1]= 0;
235
236 // saute les espaces en debut de ligne
237 char *line= line_buffer;
238 while(*line && isspace(*line))
239 line++;
240
241 if(line[0] == 'v')
242 {
243 float x, y, z;
244 if(line[1] == ' ') // position x y z
245 {
246 if(sscanf(line, "v %f %f %f", &x, &y, &z) != 3)
247 break;
248 positions.push_back( vec3(x, y, z) );
249 }
250 else if(line[1] == 'n') // normal x y z
251 {
252 if(sscanf(line, "vn %f %f %f", &x, &y, &z) != 3)
253 break;
254 normals.push_back( vec3(x, y, z) );
255 }
256 else if(line[1] == 't') // texcoord x y
257 {
258 if(sscanf(line, "vt %f %f", &x, &y) != 2)
259 break;
260 texcoords.push_back( vec2(x, y) );
261 }
262 }
263
264 else if(line[0] == 'f') // triangle a b c, les sommets sont numerotes a partir de 1 ou de la fin du tableau (< 0)
265 {
266 idp.clear();
267 idt.clear();
268 idn.clear();
269
270 int next;
271 for(line= line +1; ; line= line + next)
272 {
273 idp.push_back(0);
274 idt.push_back(0);
275 idn.push_back(0); // 0: invalid index
276
277 // analyse les attributs du sommet : p/t/n ou p//n ou p/t ou p...
278 next= 0;
279 if(sscanf(line, " %d/%d/%d %n", &idp.back(), &idt.back(), &idn.back(), &next) == 3)
280 continue;
281 else if(sscanf(line, " %d/%d %n", &idp.back(), &idt.back(), &next) == 2)
282 continue;
283 else if(sscanf(line, " %d//%d %n", &idp.back(), &idn.back(), &next) == 2)
284 continue;
285 else if(sscanf(line, " %d %n", &idp.back(), &next) == 1)
286 continue;
287 else if(next == 0) // fin de ligne
288 break;
289 }
290
291 // force une matiere par defaut, si necessaire
292 if(material_id == -1 && data.materials().count() > 0)
293 {
294 material_id= data.materials().default_material_index();
295 printf("usemtl default\n");
296 }
297
298 data.material(material_id);
299
300 // triangule la face
301 for(int v= 2; v +1 < (int) idp.size(); v++)
302 {
303 int idv[3]= { 0, v -1, v };
304 for(int i= 0; i < 3; i++)
305 {
306 int k= idv[i];
307 // indices des attributs du sommet
308 int p= (idp[k] < 0) ? (int) positions.size() + idp[k] : idp[k] -1;
309 int t= (idt[k] < 0) ? (int) texcoords.size() + idt[k] : idt[k] -1;
310 int n= (idn[k] < 0) ? (int) normals.size() + idn[k] : idn[k] -1;
311
312 if(p < 0) break; // error
313
314 // recherche / insere le sommet
315 auto found= remap.insert( std::make_pair(vertex(material_id, p, t, n), int(remap.size())) );
316 if(found.second)
317 {
318 // pas trouve, copie les nouveaux attributs
319 if(t != -1) data.texcoord(texcoords[t]);
320 if(n != -1) data.normal(normals[n]);
321 data.vertex(positions[p]);
322 assert(data.texcoords().size() > 0 && data.positions().size() == data.texcoords().size());
323 assert(data.normals().size() > 0 && data.positions().size() == data.normals().size());
324 }
325
326 // construit l'index buffer
327 assert(found.first->second < data.positions().size());
328 assert(remap.size() == data.positions().size());
329 data.index(found.first->second);
330 }
331 }
332 }
333
334 else if(line[0] == 'm')
335 {
336 if(sscanf(line, "mtllib %[^\r\n]", tmp) == 1)
337 {
338 Materials materials= read_materials( normalize_filename(pathname(filename) + tmp).c_str() );
339 // enregistre les matieres dans le mesh
340 data.materials(materials);
341 }
342 }
343
344 else if(line[0] == 'u')
345 {
346 if(sscanf(line, "usemtl %[^\r\n]", tmp) == 1)
347 material_id= data.materials().find(tmp);
348 }
349 }
350
351 fclose(in);
352
353 if(error)
354 printf("[error] loading indexed mesh '%s'...\n%s\n\n", filename, line_buffer);
355 else
356 printf(" %d indices, %d positions %d texcoords %d normals\n",
357 int(data.indices().size()), int(data.positions().size()), int(data.texcoords().size()), int(data.normals().size()));
358
359 return data;
360}
361
362
363int write_mesh( const Mesh& mesh, const char *filename, const char *materials_filename )
364{
365 if(mesh.primitives() != GL_TRIANGLES)
366 return -1;
367 if(mesh.positions().size() == 0)
368 return -1;
369 if(filename == nullptr)
370 return -1;
371
372 FILE *out= fopen(filename, "wt");
373 if(out == nullptr)
374 return -1;
375
376 printf("writing mesh '%s'...\n", filename);
377 if(materials_filename && materials_filename[0] && strcmp(filename, materials_filename))
378 {
379 printf(" materials '%s'...\n", materials_filename);
380 fprintf(out, "mtllib %s\n", materials_filename);
381 }
382 {
383 printf(" %d positions, %d texcoords, %d normals\n", int(mesh.positions().size()), int(mesh.texcoords().size()), int(mesh.normals().size()));
384 }
385
386 const std::vector<vec3>& positions= mesh.positions();
387 for(unsigned i= 0; i < positions.size(); i++)
388 fprintf(out, "v %f %f %f\n", positions[i].x, positions[i].y, positions[i].z);
389 fprintf(out, "\n");
390
391 //~ bool has_texcoords= false;
392 const std::vector<vec2>& texcoords= mesh.texcoords();
393 bool has_texcoords= (texcoords.size() == positions.size());
394 for(unsigned i= 0; i < texcoords.size(); i++)
395 fprintf(out, "vt %f %f\n", texcoords[i].x, texcoords[i].y);
396 fprintf(out, "\n");
397
398 //~ bool has_normals= false;
399 const std::vector<vec3>& normals= mesh.normals();
400 bool has_normals= (normals.size() == positions.size());
401 for(unsigned i= 0; i < normals.size(); i++)
402 fprintf(out, "vn %f %f %f\n", normals[i].x, normals[i].y, normals[i].z);
403 fprintf(out, "\n");
404
405 int material_id= -1;
406 const std::vector<unsigned>& materials= mesh.material_indices();
407 bool has_materials= (materials.size() > 0);
408
409 const std::vector<unsigned>& indices= mesh.indices();
410 bool has_indices= (indices.size() > 0);
411
412 unsigned n= has_indices ? indices.size() : positions.size();
413 for(unsigned i= 0; i +2 < n; i+= 3)
414 {
415 if(has_materials && material_id != int(materials[i/3]))
416 {
417 material_id= int(materials[i/3]);
418 if(material_id != -1)
419 {
420 fprintf(out, "o %s\n", mesh.materials().name(material_id));
421 fprintf(out, "usemtl %s\n", mesh.materials().name(material_id));
422 }
423 }
424
425 fprintf(out, "f");
426 for(unsigned k= 0; k < 3; k++)
427 {
428 unsigned id= has_indices ? indices[i+k] +1 : i+k +1;
429 fprintf(out, " %u", id);
431 fprintf(out, "/%u/%u", id, id);
432 else if(has_texcoords)
433 fprintf(out, "/%u", id);
434 else if(has_normals)
435 fprintf(out, "//%u", id);
436 }
437 fprintf(out, "\n");
438 }
439
440 fclose(out);
441 return 0;
442}
443
444
445//
446std::string texture_filename( const std::string& filename, const std::string& path )
447{
448 if(filename[0] == '.' || filename[0] == '/')
449 return normalize_filename(filename);
450 else
451 return normalize_filename(path + filename);
452}
453
454
455Materials read_materials( const char *filename )
456{
457 Materials materials;
458
459 FILE *in= fopen(filename, "rt");
460 if(in == NULL)
461 {
462 printf("[error] loading materials '%s'...\n", filename);
463 return materials;
464 }
465
466 printf("loading materials '%s'...\n", filename);
467
468 Material *material= NULL;
469 char tmp[1024];
470 char line_buffer[1024];
471 bool error= true;
472 for(;;)
473 {
474 // charge une ligne du fichier
475 if(fgets(line_buffer, sizeof(line_buffer), in) == NULL)
476 {
477 error= false; // fin du fichier, pas d'erreur detectee
478 break;
479 }
480
481 // force la fin de la ligne, au cas ou
482 line_buffer[sizeof(line_buffer) -1]= 0;
483
484 // saute les espaces en debut de ligne
485 char *line= line_buffer;
486 while(*line && isspace(*line))
487 line++;
488
489 if(line[0] == 'n')
490 {
491 if(sscanf(line, "newmtl %[^\r\n]", tmp) == 1)
492 {
493 int id= materials.insert(Material(Black()), tmp);
494 material= &materials.material(id);
495 }
496 }
497
498 if(material == nullptr)
499 continue;
500
501 if(line[0] == 'K')
502 {
503 float r, g, b;
504 if(sscanf(line, "Kd %f %f %f", &r, &g, &b) == 3)
505 material->diffuse= Color(r, g, b);
506 else if(sscanf(line, "Ks %f %f %f", &r, &g, &b) == 3)
507 material->specular= Color(r, g, b);
508 else if(sscanf(line, "Ke %f %f %f", &r, &g, &b) == 3)
509 material->emission= Color(r, g, b);
510 }
511
512 else if(line[0] == 'N')
513 {
514 float n;
515 if(sscanf(line, "Ns %f", &n) == 1) // Ns, puissance / concentration du reflet, modele blinn phong
516 material->ns= n;
517 }
518
519 else if(line[0] == 'm')
520 {
521 if(sscanf(line, "map_Kd %[^\r\n]", tmp) == 1)
522 material->diffuse_texture= materials.insert_texture( texture_filename(tmp, pathname(filename)).c_str() );
523
524 else if(sscanf(line, "map_Ks %[^\r\n]", tmp) == 1)
525 material->specular_texture= materials.insert_texture( texture_filename(tmp, pathname(filename)).c_str() );
526
527 else if(sscanf(line, "map_Ke %[^\r\n]", tmp) == 1)
528 material->emission_texture= materials.insert_texture( texture_filename(tmp, pathname(filename)).c_str() );
529 }
530
531 }
532
533 fclose(in);
534 if(error)
535 printf("[error] parsing line :\n%s\n", line_buffer);
536
537 return materials;
538}
539
540
541int write_materials( const Materials& materials, const char *filename, const char *path )
542{
543 FILE *out= fopen(filename, "wt");
544 if(out == NULL)
545 return -1;
546
547 printf("writing materials '%s'...\n", filename);
548
549 for(int i= 0; i < materials.count(); i++)
550 {
551 const Material& m= materials.material(i);
552
553 fprintf(out, "newmtl %s\n", materials.name(i));
554
555 if(m.diffuse.r + m.diffuse.g + m.diffuse.b)
556 fprintf(out, " Kd %f %f %f\n", m.diffuse.r, m.diffuse.g, m.diffuse.b);
557 if(m.diffuse_texture != -1)
558 fprintf(out, " map_Kd %s\n", relative_filename(materials.filename(m.diffuse_texture), path).c_str());
559
560 if(m.specular.r + m.specular.g + m.specular.b)
561 fprintf(out, " Ks %f %f %f\n", m.specular.r, m.specular.g, m.specular.b);
562 if(m.specular_texture != -1)
563 fprintf(out, " map_Ks %s\n", relative_filename(materials.filename(m.specular_texture), path).c_str());
564
565 if(m.specular.power() > 0)
566 {
567 fprintf(out, " Ns %f\n", m.ns);
568 if(m.ns_texture != -1)
569 fprintf(out, " map_Ns %s\n", relative_filename(materials.filename(m.ns_texture), path).c_str());
570 }
571
572 if(m.emission.power() > 0)
573 {
574 fprintf(out, " Ke %f %f %f\n", m.emission.r, m.emission.g, m.emission.b);
575 if(m.emission_texture != -1)
576 fprintf(out, " map_Ke %s\n", relative_filename(materials.filename(m.emission_texture), path).c_str());
577 }
578
579 fprintf(out, "\n");
580 }
581
582 fclose(out);
583 return 0;
584}
585
representation d'un objet / maillage.
Definition mesh.h:121
void printf(Text &text, const int px, const int py, const char *format,...)
affiche un texte a la position x, y. meme utilisation que printf().
Definition text.cpp:140
Color Black()
utilitaire. renvoie une couleur noire.
Definition color.cpp:18
int write_materials(const Materials &materials, const char *filename, const char *path)
enregistre une description de matieres.
Materials read_materials(const char *filename)
charge une description de matieres, utilise par read_mesh.
Mesh read_mesh(const char *filename)
charge un fichier wavefront .obj et renvoie un mesh compose de triangles non indexes....
Definition wavefront.cpp:14
int write_mesh(const Mesh &mesh, const char *filename, const char *materials_filename)
enregistre un mesh dans un fichier .obj.
Mesh read_indexed_mesh(const char *filename)
charge un fichier wavefront .obj et renvoie un mesh compose de triangles indexes. utiliser glDrawElem...
void normals(MeshData &data)
(re-) calcule les normales des sommets. utiliser avant les reindexations, cf indices() et vertices().
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:21
float ns
concentration des reflets, exposant pour les reflets blinn-phong.
Definition materials.h:20
int ns_texture
indice de la texture de reflet, ou -1.
Definition materials.h:24
Color emission
pour une source de lumiere.
Definition materials.h:19
Color diffuse
couleur diffuse / de base.
Definition materials.h:17
int emission_texture
indice de la texture, ou -1.
Definition materials.h:23
Color specular
couleur du reflet.
Definition materials.h:18
int specular_texture
indice de la texture, ou -1.
Definition materials.h:22
int insert(const Material &material, const char *name)
ajoute une matiere.
Definition materials.h:62
int insert_texture(const char *filename)
ajoute une texture / nom du fichier.
Definition materials.h:76
const char * filename(const int id) const
renvoie le nom de fichier d'une texture.
Definition materials.h:149
int count() const
nombre de matieres.
Definition materials.h:102
const Material & material(const int id) const
renvoie la ieme matiere.
Definition materials.h:110
const char * name(const int id) const
renvoie le nom de la ieme matiere.
Definition materials.h:105
vecteur generique, utilitaire.
Definition vec.h:152
vecteur generique, utilitaire.
Definition vec.h:169
representation de l'indexation complete d'un sommet
bool has_normals(const Hit &hit, const GLTFScene &scene)
verifie la presence des normales par sommet.
bool has_texcoords(const Hit &hit, const GLTFScene &scene)
verifie la presence des coordonnees de texture...