gKit2 light
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 
14 Mesh 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 Mesh::error();
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 
175 struct 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 
197 Mesh 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 Mesh::error();
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  }
323 
324  // construit l'index buffer
325  data.index(found.first->second);
326  }
327  }
328  }
329 
330  else if(line[0] == 'm')
331  {
332  if(sscanf(line, "mtllib %[^\r\n]", tmp) == 1)
333  {
334  Materials materials= read_materials( normalize_filename(pathname(filename) + tmp).c_str() );
335  // enregistre les matieres dans le mesh
336  data.materials(materials);
337  }
338  }
339 
340  else if(line[0] == 'u')
341  {
342  if(sscanf(line, "usemtl %[^\r\n]", tmp) == 1)
343  material_id= data.materials().find(tmp);
344  }
345  }
346 
347  fclose(in);
348 
349  if(error)
350  printf("[error] loading indexed mesh '%s'...\n%s\n\n", filename, line_buffer);
351  else
352  printf(" %d indices, %d positions %d texcoords %d normals\n",
353  int(data.indices().size()), int(data.positions().size()), int(data.texcoords().size()), int(data.normals().size()));
354 
355  return data;
356 }
357 
358 
359 int write_mesh( const Mesh& mesh, const char *filename, const char *materials_filename )
360 {
361  if(mesh == Mesh::error())
362  return -1;
363 
364  if(mesh.primitives() != GL_TRIANGLES)
365  return -1;
366  if(mesh.positions().size() == 0)
367  return -1;
368  if(filename == nullptr)
369  return -1;
370 
371  FILE *out= fopen(filename, "wt");
372  if(out == nullptr)
373  return -1;
374 
375  printf("writing mesh '%s'...\n", filename);
376  if(materials_filename && materials_filename[0] && strcmp(filename, materials_filename))
377  {
378  printf(" materials '%s'...\n", materials_filename);
379  fprintf(out, "mtllib %s\n", materials_filename);
380  }
381  {
382  printf(" %d positions, %d texcoords, %d normals\n", int(mesh.positions().size()), int(mesh.texcoords().size()), int(mesh.normals().size()));
383  }
384 
385  const std::vector<vec3>& positions= mesh.positions();
386  for(unsigned i= 0; i < positions.size(); i++)
387  fprintf(out, "v %f %f %f\n", positions[i].x, positions[i].y, positions[i].z);
388  fprintf(out, "\n");
389 
390  //~ bool has_texcoords= false;
391  const std::vector<vec2>& texcoords= mesh.texcoords();
392  bool has_texcoords= (texcoords.size() == positions.size());
393  for(unsigned i= 0; i < texcoords.size(); i++)
394  fprintf(out, "vt %f %f\n", texcoords[i].x, texcoords[i].y);
395  fprintf(out, "\n");
396 
397  //~ bool has_normals= false;
398  const std::vector<vec3>& normals= mesh.normals();
399  bool has_normals= (normals.size() == positions.size());
400  for(unsigned i= 0; i < normals.size(); i++)
401  fprintf(out, "vn %f %f %f\n", normals[i].x, normals[i].y, normals[i].z);
402  fprintf(out, "\n");
403 
404  int material_id= -1;
405  const std::vector<unsigned>& materials= mesh.material_indices();
406  bool has_materials= (materials.size() > 0);
407 
408  const std::vector<unsigned>& indices= mesh.indices();
409  bool has_indices= (indices.size() > 0);
410 
411  unsigned n= has_indices ? indices.size() : positions.size();
412  for(unsigned i= 0; i +2 < n; i+= 3)
413  {
414  if(has_materials && material_id != int(materials[i/3]))
415  {
416  material_id= int(materials[i/3]);
417  if(material_id != -1)
418  {
419  fprintf(out, "o %s\n", mesh.materials().name(material_id));
420  fprintf(out, "usemtl %s\n", mesh.materials().name(material_id));
421  }
422  }
423 
424  fprintf(out, "f");
425  for(unsigned k= 0; k < 3; k++)
426  {
427  unsigned id= has_indices ? indices[i+k] +1 : i+k +1;
428  fprintf(out, " %u", id);
430  fprintf(out, "/%u/%u", id, id);
431  else if(has_texcoords)
432  fprintf(out, "/%u", id);
433  else if(has_normals)
434  fprintf(out, "//%u", id);
435  }
436  fprintf(out, "\n");
437  }
438 
439  fclose(out);
440  return 0;
441 }
442 
443 
444 //
445 std::string texture_filename( const std::string& filename, const std::string& path )
446 {
447  if(filename[0] == '.' || filename[0] == '/')
448  return normalize_filename(filename);
449  else
450  return normalize_filename(path + filename);
451 }
452 
453 
454 Materials read_materials( const char *filename )
455 {
456  Materials materials;
457 
458  FILE *in= fopen(filename, "rt");
459  if(in == NULL)
460  {
461  printf("[error] loading materials '%s'...\n", filename);
462  return materials;
463  }
464 
465  printf("loading materials '%s'...\n", filename);
466 
467  Material *material= NULL;
468  char tmp[1024];
469  char line_buffer[1024];
470  bool error= true;
471  for(;;)
472  {
473  // charge une ligne du fichier
474  if(fgets(line_buffer, sizeof(line_buffer), in) == NULL)
475  {
476  error= false; // fin du fichier, pas d'erreur detectee
477  break;
478  }
479 
480  // force la fin de la ligne, au cas ou
481  line_buffer[sizeof(line_buffer) -1]= 0;
482 
483  // saute les espaces en debut de ligne
484  char *line= line_buffer;
485  while(*line && isspace(*line))
486  line++;
487 
488  if(line[0] == 'n')
489  {
490  if(sscanf(line, "newmtl %[^\r\n]", tmp) == 1)
491  {
492  int id= materials.insert(Material(Black()), tmp);
493  material= &materials.material(id);
494  }
495  }
496 
497  if(material == nullptr)
498  continue;
499 
500  if(line[0] == 'K')
501  {
502  float r, g, b;
503  if(sscanf(line, "Kd %f %f %f", &r, &g, &b) == 3)
504  material->diffuse= Color(r, g, b);
505  else if(sscanf(line, "Ks %f %f %f", &r, &g, &b) == 3)
506  material->specular= Color(r, g, b);
507  else if(sscanf(line, "Ke %f %f %f", &r, &g, &b) == 3)
508  material->emission= Color(r, g, b);
509  }
510 
511  else if(line[0] == 'N')
512  {
513  float n;
514  if(sscanf(line, "Ns %f", &n) == 1) // Ns, puissance / concentration du reflet, modele blinn phong
515  material->ns= n;
516  }
517 
518  else if(line[0] == 'm')
519  {
520  if(sscanf(line, "map_Kd %[^\r\n]", tmp) == 1)
521  material->diffuse_texture= materials.insert_texture( texture_filename(tmp, pathname(filename)).c_str() );
522 
523  else if(sscanf(line, "map_Ks %[^\r\n]", tmp) == 1)
524  material->specular_texture= materials.insert_texture( texture_filename(tmp, pathname(filename)).c_str() );
525 
526  else if(sscanf(line, "map_Ke %[^\r\n]", tmp) == 1)
527  material->emission_texture= materials.insert_texture( texture_filename(tmp, pathname(filename)).c_str() );
528  }
529 
530  }
531 
532  fclose(in);
533  if(error)
534  printf("[error] parsing line :\n%s\n", line_buffer);
535 
536  return materials;
537 }
538 
539 
540 int write_materials( const Materials& materials, const char *filename, const char *path )
541 {
542  FILE *out= fopen(filename, "wt");
543  if(out == NULL)
544  return -1;
545 
546  printf("writing materials '%s'...\n", filename);
547 
548  for(int i= 0; i < materials.count(); i++)
549  {
550  const Material& m= materials.material(i);
551 
552  fprintf(out, "newmtl %s\n", materials.name(i));
553 
554  if(m.diffuse.r + m.diffuse.g + m.diffuse.b)
555  fprintf(out, " Kd %f %f %f\n", m.diffuse.r, m.diffuse.g, m.diffuse.b);
556  if(m.diffuse_texture != -1)
557  fprintf(out, " map_Kd %s\n", relative_filename(materials.filename(m.diffuse_texture), path).c_str());
558 
559  if(m.specular.r + m.specular.g + m.specular.b)
560  fprintf(out, " Ks %f %f %f\n", m.specular.r, m.specular.g, m.specular.b);
561  if(m.specular_texture != -1)
562  fprintf(out, " map_Ks %s\n", relative_filename(materials.filename(m.specular_texture), path).c_str());
563 
564  if(m.specular.power() > 0)
565  {
566  fprintf(out, " Ns %f\n", m.ns);
567  if(m.ns_texture != -1)
568  fprintf(out, " map_Ns %s\n", relative_filename(materials.filename(m.ns_texture), path).c_str());
569  }
570 
571  if(m.emission.power() > 0)
572  {
573  fprintf(out, " Ke %f %f %f\n", m.emission.r, m.emission.g, m.emission.b);
574  if(m.emission_texture != -1)
575  fprintf(out, " map_Ke %s\n", relative_filename(materials.filename(m.emission_texture), path).c_str());
576  }
577 
578  fprintf(out, "\n");
579  }
580 
581  fclose(out);
582  return 0;
583 }
584 
representation d'un objet / maillage.
Definition: mesh.h:112
unsigned int vertex(const vec3 &p)
insere un sommet de position p, et ses attributs (s'ils sont definis par color(), texcoord(),...
Definition: mesh.cpp:111
Mesh & texcoord(const vec2 &uv)
definit les coordonnees de texture du prochain sommet.
Definition: mesh.cpp:100
static Mesh & error()
Definition: mesh.h:348
const std::vector< unsigned int > & material_indices() const
renvoie les indices des matieres des triangles.
Definition: mesh.cpp:287
Mesh & material(const unsigned int id)
definit la matiere du prochain triangle. id est l'indice d'une matiere ajoutee dans materials(),...
Definition: mesh.cpp:277
Mesh & normal(const vec3 &n)
definit la normale du prochain sommet.
Definition: mesh.cpp:90
Mesh & index(const int a)
Definition: mesh.cpp:241
GLenum primitives() const
renvoie le type de primitives.
Definition: mesh.h:336
const Materials & materials() const
renvoie la description des matieres.
Definition: mesh.cpp:267
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:47
int write_materials(const Materials &materials, const char *filename, const char *path)
enregistre une description de matieres.
Definition: wavefront.cpp:540
Materials read_materials(const char *filename)
charge une description de matieres, utilise par read_mesh.
Definition: wavefront.cpp:454
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.
Definition: wavefront.cpp:359
Mesh read_indexed_mesh(const char *filename)
charge un fichier wavefront .obj et renvoie un mesh compose de triangles indexes. utiliser glDrawElem...
Definition: wavefront.cpp:197
void normals(MeshData &data)
(re-) calcule les normales des sommets. utiliser avant les reindexations, cf indices() et vertices().
Definition: mesh_data.cpp:307
std::string pathname(const std::string &filename)
Definition: files.cpp:68
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:54
const Material & material(const int id) const
renvoie la ieme matiere.
Definition: materials.h:102
int find(const char *name)
recherche une matiere avec son nom. renvoie son indice dans materials, ou -1.
Definition: materials.h:80
int insert_texture(const char *filename)
ajoute une texture / nom du fichier.
Definition: materials.h:68
int count() const
nombre de matieres.
Definition: materials.h:94
const char * filename(const int id) const
renvoie le nombre de noms de fichiers de textures.
Definition: materials.h:141
const char * name(const int id) const
renvoie le nom de la ieme matiere.
Definition: materials.h:97
int default_material_index()
indice de la matiere par defaut dans le tableau materials.
Definition: materials.h:130
vecteur generique, utilitaire.
Definition: vec.h:131
vecteur generique, utilitaire.
Definition: vec.h:146
representation de l'indexation complete d'un sommet
Definition: wavefront.cpp:176
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...