gKit2 light
Loading...
Searching...
No Matches
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
15int is_whitespace( const char c )
16{
17 return (c == ' ' || c == '\t' || c == '\r');
18}
19
20int is_digit( const char c )
21{
22 return (c >= '0' && c <= '9');
23}
24
25int is_exponent( const char c )
26{
27 return (c == 'e' || c == 'E');
28}
29
30const char* skip_whitespace( const char* ptr )
31{
32 while (is_whitespace(*ptr))
33 ptr++;
34
35 return ptr;
36}
37
38const 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
64static const
65double 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
71static const
72double 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
78const 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
151Mesh 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 {};
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
307struct 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
329Mesh 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 {};
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: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
Mesh read_mesh_fast(const char *filename)
charge un fichier wavefront .obj et renvoie un mesh compose de triangles non indexes....
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().
vecteur generique, utilitaire.
Definition vec.h:152
vecteur generique, utilitaire.
Definition vec.h:169
representation de l'indexation complete d'un sommet