gKit2 light
wavefront_fast.cpp
1 
2 #include <cstdio>
3 #include <ctype.h>
4 #include <climits>
5 
6 #include <map>
7 #include <algorithm>
8 
9 #include "files.h"
10 #include "wavefront.h"
11 #include "wavefront_fast.h"
12 
13 // parse_int() + parse_float() + tools from fast_obj parser
14 // https://github.com/thisistherk/fast_obj
15 int is_whitespace( const char c )
16 {
17  return (c == ' ' || c == '\t' || c == '\r');
18 }
19 
20 int is_digit( const char c )
21 {
22  return (c >= '0' && c <= '9');
23 }
24 
25 int is_exponent( const char c )
26 {
27  return (c == 'e' || c == 'E');
28 }
29 
30 const char* skip_whitespace( const char* ptr )
31 {
32  while (is_whitespace(*ptr))
33  ptr++;
34 
35  return ptr;
36 }
37 
38 const char* parse_int( const char* ptr, int* val )
39 {
40  ptr = skip_whitespace(ptr);
41 
42  int sign= 0;
43  if (*ptr == '-')
44  {
45  sign = -1;
46  ptr++;
47  }
48  else
49  {
50  sign = +1;
51  }
52 
53  int num = 0;
54  while (is_digit(*ptr))
55  num = 10 * num + (*ptr++ - '0');
56 
57  *val = sign * num;
58  return ptr;
59 }
60 
61 /* Max supported power when parsing float */
62 #define MAX_POWER 20
63 
64 static const
65 double POWER_10_POS[MAX_POWER] =
66 {
67  1.0e0, 1.0e1, 1.0e2, 1.0e3, 1.0e4, 1.0e5, 1.0e6, 1.0e7, 1.0e8, 1.0e9,
68  1.0e10, 1.0e11, 1.0e12, 1.0e13, 1.0e14, 1.0e15, 1.0e16, 1.0e17, 1.0e18, 1.0e19,
69 };
70 
71 static const
72 double POWER_10_NEG[MAX_POWER] =
73 {
74  1.0e0, 1.0e-1, 1.0e-2, 1.0e-3, 1.0e-4, 1.0e-5, 1.0e-6, 1.0e-7, 1.0e-8, 1.0e-9,
75  1.0e-10, 1.0e-11, 1.0e-12, 1.0e-13, 1.0e-14, 1.0e-15, 1.0e-16, 1.0e-17, 1.0e-18, 1.0e-19,
76 };
77 
78 const char* parse_float(const char* ptr, float* val)
79 {
80  ptr = skip_whitespace(ptr);
81 
82  double sign= 0;
83  switch (*ptr)
84  {
85  case '+':
86  sign = 1.0;
87  ptr++;
88  break;
89 
90  case '-':
91  sign = -1.0;
92  ptr++;
93  break;
94 
95  default:
96  sign = 1.0;
97  break;
98  }
99 
100  double num= 0.0;
101  while (is_digit(*ptr))
102  num = 10.0 * num + (double)(*ptr++ - '0');
103 
104  if (*ptr == '.')
105  ptr++;
106 
107  double fra= 0;
108  double div= 1;
109  while (is_digit(*ptr))
110  {
111  fra = 10.0 * fra + (double)(*ptr++ - '0');
112  div *= 10.0;
113  }
114 
115  num += fra / div;
116 
117  if (is_exponent(*ptr))
118  {
119  ptr++;
120 
121  const double* powers= nullptr;
122  switch (*ptr)
123  {
124  case '+':
125  powers = POWER_10_POS;
126  ptr++;
127  break;
128 
129  case '-':
130  powers = POWER_10_NEG;
131  ptr++;
132  break;
133 
134  default:
135  powers = POWER_10_POS;
136  break;
137  }
138 
139  unsigned int eval= 0;
140  while (is_digit(*ptr))
141  eval = 10 * eval + (*ptr++ - '0');
142 
143  num *= (eval >= MAX_POWER) ? 0.0 : powers[eval];
144  }
145 
146  *val = (float)(sign * num);
147  return ptr;
148 }
149 
150 
151 Mesh read_mesh_fast( const char *filename )
152 {
153  FILE *in= fopen(filename, "rb");
154  if(in == NULL)
155  {
156  printf("[error] loading mesh '%s'...\n", filename);
157  return Mesh::error();
158  }
159 
160  Mesh data(GL_TRIANGLES);
161 
162  printf("loading mesh '%s'...\n", filename);
163 
164  std::vector<vec3> positions;
165  std::vector<vec2> texcoords;
166  std::vector<vec3> normals;
167  int material_id= -1;
168 
169  std::vector<int> idp;
170  std::vector<int> idt;
171  std::vector<int> idn;
172 
173  char tmp[1024*64];
174  char line_buffer[1024*64];
175  bool error= true;
176  for(;;)
177  {
178  // charge une ligne du fichier
179  if(fgets(line_buffer, sizeof(line_buffer), in) == NULL)
180  {
181  error= false; // fin du fichier, pas d'erreur detectee
182  break;
183  }
184 
185  // force la fin de la ligne, au cas ou
186  line_buffer[sizeof(line_buffer) -1]= 0;
187 
188  // saute les espaces en debut de ligne
189  const char *line= skip_whitespace(line_buffer);
190  if(line[0] == 'v')
191  {
192  float x, y, z;
193  if(line[1] == ' ') // position x y z
194  {
195  line+= 2;
196  line= parse_float(line, &x);
197  line= parse_float(line, &y);
198  line= parse_float(line, &z);
199 
200  positions.push_back( vec3(x, y, z) );
201  }
202  else if(line[1] == 'n') // normal x y z
203  {
204  line+= 3;
205  line= parse_float(line, &x);
206  line= parse_float(line, &y);
207  line= parse_float(line, &z);
208 
209  normals.push_back( vec3(x, y, z) );
210  }
211  else if(line[1] == 't') // texcoord x y
212  {
213  line+= 3;
214  line= parse_float(line, &x);
215  line= parse_float(line, &y);
216 
217  texcoords.push_back( vec2(x, y) );
218  }
219  }
220 
221  else if(line[0] == 'f') // triangle a b c, les sommets sont numerotes a partir de 1 ou de la fin du tableau (< 0)
222  {
223  idp.clear();
224  idt.clear();
225  idn.clear();
226 
227  line+= 2;
228  while(*line)
229  {
230  idp.push_back(0);
231  idt.push_back(0);
232  idn.push_back(0); // 0: invalid index
233 
234  line= parse_int(line, &idp.back());
235  if(*line == '/')
236  {
237  line++;
238  if(*line != '/')
239  line= parse_int(line, &idt.back());
240 
241  if(*line == '/')
242  {
243  line++;
244  line= parse_int(line, &idn.back());
245  }
246  }
247 
248  while(isspace(*line))
249  line++;
250  }
251 
252  // verifie qu'une matiere est deja definie pour le triangle
253  if(material_id == -1)
254  // sinon affecte une matiere par defaut
255  material_id= data.materials().default_material_index();
256 
257  data.material(material_id);
258 
259  // triangulation de la face (supposee convexe)
260  for(int v= 2; v < int(idp.size()); v++)
261  {
262  int idv[3]= { 0, v -1, v };
263  for(int i= 0; i < 3; i++)
264  {
265  int k= idv[i];
266  int p= (idp[k] < 0) ? (int) positions.size() + idp[k] : idp[k] -1;
267  int t= (idt[k] < 0) ? (int) texcoords.size() + idt[k] : idt[k] -1;
268  int n= (idn[k] < 0) ? (int) normals.size() + idn[k] : idn[k] -1;
269 
270  if(p < 0) break; // error
271  if(t >= 0) data.texcoord(texcoords[t]);
272  if(n >= 0) data.normal(normals[n]);
273  data.vertex(positions[p]);
274  }
275  }
276  }
277 
278  else if(line[0] == 'm')
279  {
280  if(sscanf(line, "mtllib %[^\r\n]", tmp) == 1)
281  {
282  Materials materials= read_materials( normalize_filename(pathname(filename) + tmp).c_str() );
283  // enregistre les matieres dans le mesh
284  data.materials(materials);
285  }
286  }
287 
288  else if(line[0] == 'u')
289  {
290  if(sscanf(line, "usemtl %[^\r\n]", tmp) == 1)
291  material_id= data.materials().find(tmp);
292  }
293  }
294 
295  fclose(in);
296 
297  if(error)
298  printf("[error] loading mesh '%s'...\n%s\n\n", filename, line_buffer);
299  else
300  printf("mesh '%s': %d positions %s %s\n", filename, int(data.positions().size()), data.has_texcoord() ? "texcoord" : "", data.has_normal() ? "normal" : "");
301 
302  return data;
303 }
304 
305 
307 struct vertex
308 {
309  int material;
310  int position;
311  int texcoord;
312  int normal;
313 
314  vertex( ) : material(-1), position(-1), texcoord(-1), normal(-1) {}
315  vertex( const int m, const int p, const int t, const int n ) : material(m), position(p), texcoord(t), normal(n) {}
316 
317  // comparaison lexicographique de 2 sommets / des indices de leurs attributs
318  bool operator< ( const vertex& b ) const
319  {
320  if(material != b.material) return material < b.material;
321  if(position != b.position) return position < b.position;
322  if(texcoord != b.texcoord) return texcoord < b.texcoord;
323  if(normal != b.normal) return normal < b.normal;
324  return false;
325  }
326 };
327 
328 
329 Mesh read_indexed_mesh_fast( const char *filename )
330 {
331  FILE *in= fopen(filename, "rb");
332  if(in == NULL)
333  {
334  printf("[error] loading indexed mesh '%s'...\n", filename);
335  return Mesh::error();
336  }
337 
338  Mesh data(GL_TRIANGLES);
339 
340  printf("loading indexed mesh '%s'...\n", filename);
341 
342  std::vector<vec3> positions;
343  std::vector<vec2> texcoords;
344  std::vector<vec3> normals;
345  int material_id= -1;
346 
347  std::vector<int> idp;
348  std::vector<int> idt;
349  std::vector<int> idn;
350 
351  std::map<vertex, int> remap;
352 
353  char tmp[1024*64];
354  char line_buffer[1024*64];
355  bool error= true;
356  for(;;)
357  {
358  // charge une ligne du fichier
359  if(fgets(line_buffer, sizeof(line_buffer), in) == NULL)
360  {
361  error= false; // fin du fichier, pas d'erreur detectee
362  break;
363  }
364 
365  // force la fin de la ligne, au cas ou
366  line_buffer[sizeof(line_buffer) -1]= 0;
367 
368  // saute les espaces en debut de ligne
369  const char *line= skip_whitespace(line_buffer);
370  if(line[0] == 'v')
371  {
372  float x, y, z;
373  if(line[1] == ' ') // position x y z
374  {
375  line+= 2;
376  line= parse_float(line, &x);
377  line= parse_float(line, &y);
378  line= parse_float(line, &z);
379 
380  positions.push_back( vec3(x, y, z) );
381  }
382  else if(line[1] == 'n') // normal x y z
383  {
384  line+= 3;
385  line= parse_float(line, &x);
386  line= parse_float(line, &y);
387  line= parse_float(line, &z);
388 
389  normals.push_back( vec3(x, y, z) );
390  }
391  else if(line[1] == 't') // texcoord x y
392  {
393  line+= 3;
394  line= parse_float(line, &x);
395  line= parse_float(line, &y);
396 
397  texcoords.push_back( vec2(x, y) );
398  }
399  }
400 
401  else if(line[0] == 'f') // triangle a b c, les sommets sont numerotes a partir de 1 ou de la fin du tableau (< 0)
402  {
403  idp.clear();
404  idt.clear();
405  idn.clear();
406 
407  line+= 2;
408  while(*line)
409  {
410  idp.push_back(0);
411  idt.push_back(0);
412  idn.push_back(0); // 0: invalid index
413 
414  line= parse_int(line, &idp.back());
415  if(*line == '/')
416  {
417  line++;
418  if(*line != '/')
419  line= parse_int(line, &idt.back());
420 
421  if(*line == '/')
422  {
423  line++;
424  line= parse_int(line, &idn.back());
425  }
426  }
427 
428  while(isspace(*line))
429  line++;
430  }
431 
432  // force une matiere par defaut, si necessaire
433  if(material_id == -1)
434  {
435  material_id= data.materials().default_material_index();
436  printf("usemtl default\n");
437  }
438 
439  data.material(material_id);
440 
441  // triangule la face
442  for(int v= 2; v < int(idp.size()); v++)
443  {
444  int idv[3]= { 0, v -1, v };
445  for(int i= 0; i < 3; i++)
446  {
447  int k= idv[i];
448  // indices des attributs du sommet
449  int p= (idp[k] < 0) ? (int) positions.size() + idp[k] : idp[k] -1;
450  int t= (idt[k] < 0) ? (int) texcoords.size() + idt[k] : idt[k] -1;
451  int n= (idn[k] < 0) ? (int) normals.size() + idn[k] : idn[k] -1;
452 
453  if(p < 0) break; // error
454 
455  // recherche / insere le sommet
456  auto found= remap.insert( std::make_pair(vertex(material_id, p, t, n), int(remap.size())) );
457  if(found.second)
458  {
459  // pas trouve, copie les nouveaux attributs
460  if(t != -1) data.texcoord(texcoords[t]);
461  if(n != -1) data.normal(normals[n]);
462  data.vertex(positions[p]);
463  }
464 
465  // construit l'index buffer
466  data.index(found.first->second);
467  }
468  }
469  }
470 
471  else if(line[0] == 'm')
472  {
473  if(sscanf(line, "mtllib %[^\r\n]", tmp) == 1)
474  {
475  Materials materials= read_materials( normalize_filename(pathname(filename) + tmp).c_str() );
476  // enregistre les matieres dans le mesh
477  data.materials(materials);
478  }
479  }
480 
481  else if(line[0] == 'u')
482  {
483  if(sscanf(line, "usemtl %[^\r\n]", tmp) == 1)
484  material_id= data.materials().find(tmp);
485  }
486  }
487 
488  fclose(in);
489 
490  if(error)
491  printf("[error] loading indexed mesh '%s'...\n%s\n\n", filename, line_buffer);
492  else
493  printf(" %d indices, %d positions %d texcoords %d normals\n",
494  int(data.indices().size()), int(data.positions().size()), int(data.texcoords().size()), int(data.normals().size()));
495 
496  return data;
497 }
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:109
Mesh & texcoord(const vec2 &uv)
definit les coordonnees de texture du prochain sommet.
Definition: mesh.cpp:98
static Mesh & error()
Definition: mesh.h:348
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:275
Mesh & normal(const vec3 &n)
definit la normale du prochain sommet.
Definition: mesh.cpp:88
Mesh & index(const int a)
Definition: mesh.cpp:239
const Materials & materials() const
renvoie la description des matieres.
Definition: mesh.cpp:265
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
Mesh read_mesh_fast(const char *filename)
charge un fichier wavefront .obj et renvoie un mesh compose de triangles non indexes....
Materials read_materials(const char *filename)
charge une description de matieres, utilise par read_mesh.
Definition: wavefront.cpp:454
Mesh read_indexed_mesh_fast(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().
Definition: mesh_data.cpp:307
std::string pathname(const std::string &filename)
Definition: files.cpp:68
int find(const char *name)
recherche une matiere avec son nom. renvoie son indice dans materials, ou -1.
Definition: materials.h:80
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