clray

view src/mesh.cc @ 11:d9a1bab1c3f5

ported to windows
author John Tsiombikas
date Sat, 31 Jul 2010 22:23:57 +0100
parents deaf85acf6af
children 407935b73af3
line source
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <errno.h>
5 #include <limits.h>
6 #include <string>
7 #include <vector>
8 #include <map>
9 #include "mesh.h"
11 #ifndef PATH_MAX
12 #define PATH_MAX 512
13 #endif
15 using namespace std;
17 #define COMMANDS \
18 CMD(V), \
19 CMD(VN), \
20 CMD(VT), \
21 CMD(F), \
22 CMD(O), \
23 CMD(G), \
24 CMD(MTLLIB), \
25 CMD(USEMTL), \
26 CMD(NEWMTL), \
27 CMD(KA), \
28 CMD(KD), \
29 CMD(KS), \
30 CMD(NS), \
31 CMD(NI), \
32 CMD(D), \
33 CMD(TR), \
34 CMD(MAP_KD), \
35 CMD(MAP_KS), \
36 CMD(MAP_NS), \
37 CMD(MAP_D), \
38 CMD(REFL), \
39 CMD(BUMP)
41 #define CMD(x) CMD_##x
42 enum {
43 COMMANDS,
44 CMD_UNK
45 };
46 #undef CMD
48 #define CMD(x) #x
49 static const char *cmd_names[] = {
50 COMMANDS,
51 0
52 };
53 #undef CMD
55 struct Vector3 {
56 float x, y, z;
58 Vector3() { x = y = z = 0.0; }
59 Vector3(float a, float b, float c) { x = a; y = b; z = c; }
60 };
62 struct Vector2 {
63 float x, y;
65 Vector2() { x = y = 0.0; }
66 Vector2(float a, float b) { x = a; y = b; }
67 };
69 struct obj_face {
70 int elem;
71 int v[4], n[4], t[4];
72 };
74 struct obj_file {
75 string cur_obj, cur_mat;
76 vector<Vector3> v, vn, vt;
77 vector<obj_face> f;
78 };
80 struct obj_mat {
81 string name; // newmtl <name>
82 Vector3 ambient, diffuse, specular; // Ka, Kd, Ks
83 float shininess; // Ns
84 float ior; // Ni
85 float alpha; // d, Tr
87 string tex_dif, tex_spec, tex_shin, tex_alpha; // map_Kd, map_Ks, map_Ns, map_d
88 string tex_refl; // refl -type sphere|cube file
89 string tex_bump; // bump
91 obj_mat() { reset(); }
93 void reset() {
94 ambient = diffuse = Vector3(0.5, 0.5, 0.5);
95 specular = Vector3(0.0, 0.0, 0.0);
96 name = tex_dif = tex_spec = tex_shin = tex_alpha = tex_refl = tex_bump = "";
97 shininess = 0;
98 ior = alpha = 1;
99 }
100 };
102 static bool read_materials(FILE *fp, vector<obj_mat> *vmtl);
103 static Mesh *cons_mesh(obj_file *obj);
105 static int get_cmd(char *str);
106 static bool is_int(const char *str);
107 static bool is_float(const char *str);
108 static bool parse_vec(Vector3 *vec);
109 static bool parse_color(Vector3 *col);
110 static bool parse_face(obj_face *face);
111 static const char *parse_map();
113 static bool find_file(char *res, int sz, const char *fname, const char *path = ".", const char *mode = "rb");
114 static const char *dirname(const char *str);
116 static map<string, int> matnames;
119 #define INVALID_IDX INT_MIN
121 #define SEP " \t\n\r\v"
122 #define BUF_SZ 512
124 bool Scene::load(const char *fname)
125 {
126 FILE *fp;
128 if(!(fp = fopen(fname, "rb"))) {
129 fprintf(stderr, "failed to open %s: %s\n", fname, strerror(errno));
130 return false;
131 }
133 bool res = load(fp);
134 fclose(fp);
135 return res;
136 }
138 bool Scene::load(FILE *fp)
139 {
140 static int seq;
141 char cur_name[16];
143 obj_file obj;
145 sprintf(cur_name, "default%02d.obj", seq++);
146 obj.cur_obj = cur_name;
148 int prev_cmd = 0, obj_added = 0;
149 for(;;) {
150 Vector3 vec;
151 obj_face face;
153 char line[BUF_SZ];
154 fgets(line, sizeof line, fp);
155 if(feof(fp)) {
156 break;
157 }
159 char *tok;
160 if(!(tok = strtok(line, SEP))) {
161 continue; // ignore empty lines
162 }
164 int cmd;
165 if((cmd = get_cmd(tok)) == -1) {
166 continue; // ignore unknown commands ...
167 }
169 switch(cmd) {
170 case CMD_V:
171 if(!parse_vec(&vec)) {
172 continue;
173 }
174 obj.v.push_back(vec);
175 break;
177 case CMD_VN:
178 if(!parse_vec(&vec)) {
179 continue;
180 }
181 obj.vn.push_back(vec);
182 break;
184 case CMD_VT:
185 if(!parse_vec(&vec)) {
186 continue;
187 }
188 vec.y = 1.0 - vec.y;
189 obj.vt.push_back(vec);
190 break;
192 case CMD_O:
193 case CMD_G:
194 if(prev_cmd == CMD_O || prev_cmd == CMD_G) {
195 break; // just in case we've got both of them in a row
196 }
197 /* if we have any previous data, group them up, add the object
198 * and continue with the new one...
199 */
200 if(!obj.f.empty()) {
201 Mesh *mesh = cons_mesh(&obj);
202 mesh->matid = matnames[obj.cur_mat];
203 meshes.push_back(mesh);
204 obj_added++;
206 obj.f.clear(); // clean the face list
207 }
208 if((tok = strtok(0, SEP))) {
209 obj.cur_obj = tok;
210 } else {
211 sprintf(cur_name, "default%02d.obj", seq++);
212 obj.cur_obj = cur_name;
213 }
214 break;
216 case CMD_MTLLIB:
217 if((tok = strtok(0, SEP))) {
218 char path[PATH_MAX];
220 sprintf(path, ".:%s", dirname(tok));
221 if(!find_file(path, PATH_MAX, tok, path)) {
222 fprintf(stderr, "material library not found: %s\n", tok);
223 continue;
224 }
226 FILE *mfile;
227 if(!(mfile = fopen(path, "rb"))) {
228 fprintf(stderr, "failed to open material library: %s\n", path);
229 continue;
230 }
232 // load all materials of the mtl file into a vector
233 vector<obj_mat> vmtl;
234 if(!read_materials(mfile, &vmtl)) {
235 continue;
236 }
237 fclose(mfile);
239 // and add them all to the scene
240 for(size_t i=0; i<vmtl.size(); i++) {
241 Material mat;
243 mat.kd[0] = vmtl[i].diffuse.x;
244 mat.kd[1] = vmtl[i].diffuse.y;
245 mat.kd[2] = vmtl[i].diffuse.z;
247 mat.ks[0] = vmtl[i].specular.x;
248 mat.ks[1] = vmtl[i].specular.y;
249 mat.ks[2] = vmtl[i].specular.z;
251 mat.kt = 1.0 - vmtl[i].alpha;
252 mat.kr = 0.0; // TODO
253 mat.spow = vmtl[i].shininess;
255 matnames[vmtl[i].name] = i;
256 }
257 }
258 break;
260 case CMD_USEMTL:
261 if((tok = strtok(0, SEP))) {
262 obj.cur_mat = tok;
263 } else {
264 obj.cur_mat = "";
265 }
266 break;
268 case CMD_F:
269 if(!parse_face(&face)) {
270 continue;
271 }
273 // convert negative indices to regular indices
274 for(int i=0; i<4; i++) {
275 if(face.v[i] < 0 && face.v[i] != INVALID_IDX) {
276 face.v[i] = obj.v.size() + face.v[i];
277 }
278 if(face.n[i] < 0 && face.n[i] != INVALID_IDX) {
279 face.n[i] = obj.vn.size() + face.n[i];
280 }
281 if(face.t[i] < 0 && face.t[i] != INVALID_IDX) {
282 face.t[i] = obj.vt.size() + face.t[i];
283 }
284 }
286 // break quads into triangles if needed
287 obj.f.push_back(face);
288 if(face.elem == 4) {
289 face.v[1] = face.v[2];
290 face.n[1] = face.n[2];
291 face.t[1] = face.t[2];
293 face.v[2] = face.v[3];
294 face.n[2] = face.n[3];
295 face.t[2] = face.t[3];
297 obj.f.push_back(face);
298 }
299 break;
301 default:
302 break; // ignore unknown commands
303 }
305 prev_cmd = cmd;
306 }
308 // reached end of file...
309 if(!obj.f.empty()) {
310 Mesh *mesh = cons_mesh(&obj);
311 mesh->matid = matnames[obj.cur_mat];
312 meshes.push_back(mesh);
313 obj_added++;
314 }
316 return obj_added > 0;
317 }
319 static Mesh *cons_mesh(obj_file *obj)
320 {
321 Mesh *mesh;
323 // need at least one of each element
324 bool added_norm = false, added_tc = false;
325 if(obj->vn.empty()) {
326 obj->vn.push_back(Vector3(0, 0, 0));
327 added_norm = true;
328 }
329 if(obj->vt.empty()) {
330 obj->vt.push_back(Vector3(0, 0, 0));
331 added_tc = true;
332 }
334 mesh = new Mesh;
336 for(size_t i=0; i<obj->f.size(); i++) {
337 Face face;
339 for(int j=0; j<3; j++) {
340 obj_face *f = &obj->f[i];
342 face.v[j].pos[0] = obj->v[f->v[j]].x;
343 face.v[j].pos[1] = obj->v[f->v[j]].y;
344 face.v[j].pos[2] = obj->v[f->v[j]].z;
346 int nidx = f->n[j] < 0 ? 0 : f->n[j];
347 face.v[j].normal[0] = obj->vn[nidx].x;
348 face.v[j].normal[1] = obj->vn[nidx].y;
349 face.v[j].normal[2] = obj->vn[nidx].z;
351 int tidx = f->t[j] < 0 ? 0 : f->t[j];
352 face.v[j].tex[0] = obj->vt[tidx].x;
353 face.v[j].tex[1] = obj->vt[tidx].y;
354 }
355 mesh->faces.push_back(face);
356 }
358 if(added_norm) {
359 obj->vn.pop_back();
360 }
361 if(added_tc) {
362 obj->vt.pop_back();
363 }
365 return mesh;
366 }
368 static bool read_materials(FILE *fp, vector<obj_mat> *vmtl)
369 {
370 obj_mat mat;
372 for(;;) {
373 char line[BUF_SZ];
374 fgets(line, sizeof line, fp);
375 if(feof(fp)) {
376 break;
377 }
379 char *tok;
380 if(!(tok = strtok(line, SEP))) {
381 continue;
382 }
384 int cmd;
385 if((cmd = get_cmd(tok)) == -1) {
386 continue;
387 }
389 switch(cmd) {
390 case CMD_NEWMTL:
391 // add the previous material, and start a new one
392 if(mat.name.length() > 0) {
393 vmtl->push_back(mat);
394 mat.reset();
395 }
396 if((tok = strtok(0, SEP))) {
397 mat.name = tok;
398 }
399 break;
401 case CMD_KA:
402 parse_color(&mat.ambient);
403 break;
405 case CMD_KD:
406 parse_color(&mat.diffuse);
407 break;
409 case CMD_KS:
410 parse_color(&mat.specular);
411 break;
413 case CMD_NS:
414 if((tok = strtok(0, SEP)) && is_float(tok)) {
415 mat.shininess = atof(tok);
416 }
417 break;
419 case CMD_NI:
420 if((tok = strtok(0, SEP)) && is_float(tok)) {
421 mat.ior = atof(tok);
422 }
423 break;
425 case CMD_D:
426 case CMD_TR:
427 {
428 Vector3 c;
429 if(parse_color(&c)) {
430 mat.alpha = cmd == CMD_D ? c.x : 1.0 - c.x;
431 }
432 }
433 break;
435 case CMD_MAP_KD:
436 mat.tex_dif = parse_map();
437 break;
439 default:
440 break;
441 }
442 }
444 if(mat.name.length() > 0) {
445 vmtl->push_back(mat);
446 }
447 return true;
448 }
450 static int get_cmd(char *str)
451 {
452 char *s = str;
453 while((*s = toupper(*s))) s++;
455 for(int i=0; cmd_names[i]; i++) {
456 if(strcmp(str, cmd_names[i]) == 0) {
457 return i;
458 }
459 }
460 return CMD_UNK;
461 }
463 static bool is_int(const char *str)
464 {
465 char *tmp;
466 strtol(str, &tmp, 10);
467 return tmp != str;
468 }
470 static bool is_float(const char *str)
471 {
472 char *tmp;
473 strtod(str, &tmp);
474 return tmp != str;
475 }
477 static bool parse_vec(Vector3 *vec)
478 {
479 for(int i=0; i<3; i++) {
480 char *tok;
482 if(!(tok = strtok(0, SEP)) || !is_float(tok)) {
483 if(i < 2) {
484 return false;
485 }
486 vec->z = 0.0;
487 } else {
488 float v = atof(tok);
490 switch(i) {
491 case 0:
492 vec->x = v;
493 break;
494 case 1:
495 vec->y = v;
496 break;
497 case 2:
498 vec->z = v;
499 break;
500 }
501 }
502 }
503 return true;
504 }
506 static bool parse_color(Vector3 *col)
507 {
508 for(int i=0; i<3; i++) {
509 char *tok;
511 if(!(tok = strtok(0, SEP)) || !is_float(tok)) {
512 col->y = col->z = col->x;
513 return i > 0 ? true : false;
514 }
516 float v = atof(tok);
517 switch(i) {
518 case 0:
519 col->x = v;
520 break;
521 case 1:
522 col->y = v;
523 break;
524 case 2:
525 col->z = v;
526 break;
527 }
528 }
529 return true;
530 }
532 static bool parse_face(obj_face *face)
533 {
534 char *tok[] = {0, 0, 0, 0};
535 face->elem = 0;
537 for(int i=0; i<4; i++) {
538 if((!(tok[i] = strtok(0, SEP)) || !is_int(tok[i]))) {
539 if(i < 3) return false; // less than 3 verts? not a polygon
540 } else {
541 face->elem++;
542 }
543 }
545 for(int i=0; i<4; i++) {
546 char *subtok = tok[i];
548 if(!subtok || !*subtok || !is_int(subtok)) {
549 if(i < 3) {
550 return false;
551 }
552 face->v[i] = INVALID_IDX;
553 } else {
554 face->v[i] = atoi(subtok);
555 if(face->v[i] > 0) face->v[i]--; /* convert to 0-based */
556 }
558 while(subtok && *subtok && *subtok != '/') {
559 subtok++;
560 }
561 if(subtok && *subtok && *++subtok && is_int(subtok)) {
562 face->t[i] = atoi(subtok);
563 if(face->t[i] > 0) face->t[i]--; /* convert to 0-based */
564 } else {
565 face->t[i] = INVALID_IDX;
566 }
568 while(subtok && *subtok && *subtok != '/') {
569 subtok++;
570 }
571 if(subtok && *subtok && *++subtok && is_int(subtok)) {
572 face->n[i] = atoi(subtok);
573 if(face->n[i] > 0) face->n[i]--; /* convert to 0-based */
574 } else {
575 face->n[i] = INVALID_IDX;
576 }
577 }
579 return true;
580 }
582 static const char *parse_map()
583 {
584 char *tok, *prev = 0;
586 while((tok = strtok(0, SEP))) {
587 prev = tok;
588 }
590 return prev ? prev : "";
591 }
593 static bool find_file(char *res, int sz, const char *fname, const char *path, const char *mode)
594 {
595 FILE *fp;
596 const char *beg, *end;
597 int fnamelen = strlen(fname);
599 beg = path;
600 while(beg && *beg) {
601 end = beg;
602 while(*end && *end != ':') {
603 end++;
604 }
606 int sz = end - beg + 1;
607 char *pathname = (char*)alloca(sz + fnamelen + 2);
608 memcpy(pathname, beg, sz);
609 strcat(pathname, "/");
610 strcat(pathname, fname);
612 if((fp = fopen(pathname, mode))) {
613 fclose(fp);
614 strncpy(res, pathname, sz);
615 return true;
616 }
618 beg += sz;
619 }
620 return false;
621 }
623 static const char *dirname(const char *str)
624 {
625 static char buf[PATH_MAX];
627 if(!str || !*str) {
628 strcpy(buf, ".");
629 } else {
630 strncpy(buf, str, PATH_MAX);
631 char *ptr = strrchr(buf, '/');
633 if(*ptr) *ptr = 0;
634 }
635 return buf;
636 }