clray

view src/mesh.cc @ 14:29f9330cfa4b

trying to debug the bloody thing
author John Tsiombikas
date Sat, 07 Aug 2010 03:36:36 +0100
parents 407935b73af3
children 754faf15ba36
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;
118 bool Scene::add_mesh(Mesh *m)
119 {
120 // make sure triangles have material ids
121 for(size_t i=0; i<m->faces.size(); i++) {
122 m->faces[i].matid = m->matid;
123 }
124 meshes.push_back(m);
125 return true;
126 }
128 int Scene::get_num_meshes() const
129 {
130 return (int)meshes.size();
131 }
133 int Scene::get_num_faces() const
134 {
135 int num_faces = 0;
136 for(size_t i=0; i<meshes.size(); i++) {
137 num_faces += meshes[i]->faces.size();
138 }
139 printf("get_num_faces() = %d\n", num_faces);
140 return num_faces;
141 }
143 int Scene::get_num_materials() const
144 {
145 return (int)matlib.size();
146 }
148 Material *Scene::get_materials()
149 {
150 if(matlib.empty()) {
151 return 0;
152 }
153 return &matlib[0];
154 }
156 const Material *Scene::get_materials() const
157 {
158 if(matlib.empty()) {
159 return 0;
160 }
161 return &matlib[0];
162 }
165 #define INVALID_IDX INT_MIN
167 #define SEP " \t\n\r\v"
168 #define BUF_SZ 512
170 bool Scene::load(const char *fname)
171 {
172 FILE *fp;
174 if(!(fp = fopen(fname, "rb"))) {
175 fprintf(stderr, "failed to open %s: %s\n", fname, strerror(errno));
176 return false;
177 }
179 bool res = load(fp);
180 fclose(fp);
181 return res;
182 }
184 bool Scene::load(FILE *fp)
185 {
186 static int seq;
187 char cur_name[16];
189 obj_file obj;
191 sprintf(cur_name, "default%02d.obj", seq++);
192 obj.cur_obj = cur_name;
194 int prev_cmd = 0, obj_added = 0;
195 for(;;) {
196 Vector3 vec;
197 obj_face face;
199 char line[BUF_SZ];
200 fgets(line, sizeof line, fp);
201 if(feof(fp)) {
202 break;
203 }
205 char *tok;
206 if(!(tok = strtok(line, SEP))) {
207 continue; // ignore empty lines
208 }
210 int cmd;
211 if((cmd = get_cmd(tok)) == -1) {
212 continue; // ignore unknown commands ...
213 }
215 switch(cmd) {
216 case CMD_V:
217 if(!parse_vec(&vec)) {
218 continue;
219 }
220 obj.v.push_back(vec);
221 break;
223 case CMD_VN:
224 if(!parse_vec(&vec)) {
225 continue;
226 }
227 obj.vn.push_back(vec);
228 break;
230 case CMD_VT:
231 if(!parse_vec(&vec)) {
232 continue;
233 }
234 vec.y = 1.0 - vec.y;
235 obj.vt.push_back(vec);
236 break;
238 case CMD_O:
239 case CMD_G:
240 if(prev_cmd == CMD_O || prev_cmd == CMD_G) {
241 break; // just in case we've got both of them in a row
242 }
243 /* if we have any previous data, group them up, add the object
244 * and continue with the new one...
245 */
246 if(!obj.f.empty()) {
247 Mesh *mesh = cons_mesh(&obj);
248 mesh->matid = matnames[obj.cur_mat];
249 add_mesh(mesh);
250 obj_added++;
252 obj.f.clear(); // clean the face list
253 }
254 if((tok = strtok(0, SEP))) {
255 obj.cur_obj = tok;
256 } else {
257 sprintf(cur_name, "default%02d.obj", seq++);
258 obj.cur_obj = cur_name;
259 }
260 break;
262 case CMD_MTLLIB:
263 if((tok = strtok(0, SEP))) {
264 char path[PATH_MAX];
266 sprintf(path, ".:%s", dirname(tok));
267 if(!find_file(path, PATH_MAX, tok, path)) {
268 fprintf(stderr, "material library not found: %s\n", tok);
269 continue;
270 }
272 FILE *mfile;
273 if(!(mfile = fopen(path, "rb"))) {
274 fprintf(stderr, "failed to open material library: %s\n", path);
275 continue;
276 }
278 // load all materials of the mtl file into a vector
279 vector<obj_mat> vmtl;
280 if(!read_materials(mfile, &vmtl)) {
281 continue;
282 }
283 fclose(mfile);
285 // and add them all to the scene
286 for(size_t i=0; i<vmtl.size(); i++) {
287 Material mat;
289 mat.kd[0] = vmtl[i].diffuse.x;
290 mat.kd[1] = vmtl[i].diffuse.y;
291 mat.kd[2] = vmtl[i].diffuse.z;
293 mat.ks[0] = vmtl[i].specular.x;
294 mat.ks[1] = vmtl[i].specular.y;
295 mat.ks[2] = vmtl[i].specular.z;
297 mat.kt = 1.0 - vmtl[i].alpha;
298 mat.kr = 0.0; // TODO
299 mat.spow = vmtl[i].shininess;
301 matlib.push_back(mat);
302 matnames[vmtl[i].name] = i;
303 }
304 }
305 break;
307 case CMD_USEMTL:
308 if((tok = strtok(0, SEP))) {
309 obj.cur_mat = tok;
310 } else {
311 obj.cur_mat = "";
312 }
313 break;
315 case CMD_F:
316 if(!parse_face(&face)) {
317 continue;
318 }
320 // convert negative indices to regular indices
321 for(int i=0; i<4; i++) {
322 if(face.v[i] < 0 && face.v[i] != INVALID_IDX) {
323 face.v[i] = obj.v.size() + face.v[i];
324 }
325 if(face.n[i] < 0 && face.n[i] != INVALID_IDX) {
326 face.n[i] = obj.vn.size() + face.n[i];
327 }
328 if(face.t[i] < 0 && face.t[i] != INVALID_IDX) {
329 face.t[i] = obj.vt.size() + face.t[i];
330 }
331 }
333 // break quads into triangles if needed
334 obj.f.push_back(face);
335 if(face.elem == 4) {
336 face.v[1] = face.v[2];
337 face.n[1] = face.n[2];
338 face.t[1] = face.t[2];
340 face.v[2] = face.v[3];
341 face.n[2] = face.n[3];
342 face.t[2] = face.t[3];
344 obj.f.push_back(face);
345 }
346 break;
348 default:
349 break; // ignore unknown commands
350 }
352 prev_cmd = cmd;
353 }
355 // reached end of file...
356 if(!obj.f.empty()) {
357 Mesh *mesh = cons_mesh(&obj);
358 mesh->matid = matnames[obj.cur_mat];
359 add_mesh(mesh);
360 obj_added++;
361 }
363 return obj_added > 0;
364 }
366 static Mesh *cons_mesh(obj_file *obj)
367 {
368 Mesh *mesh;
370 // need at least one of each element
371 bool added_norm = false, added_tc = false;
372 if(obj->vn.empty()) {
373 obj->vn.push_back(Vector3(0, 0, 0));
374 added_norm = true;
375 }
376 if(obj->vt.empty()) {
377 obj->vt.push_back(Vector3(0, 0, 0));
378 added_tc = true;
379 }
381 mesh = new Mesh;
383 for(size_t i=0; i<obj->f.size(); i++) {
384 Face face;
386 for(int j=0; j<3; j++) {
387 obj_face *f = &obj->f[i];
389 face.v[j].pos[0] = obj->v[f->v[j]].x;
390 face.v[j].pos[1] = obj->v[f->v[j]].y;
391 face.v[j].pos[2] = obj->v[f->v[j]].z;
392 face.v[j].pos[3] = 0.0;
394 int nidx = f->n[j] < 0 ? 0 : f->n[j];
395 face.v[j].normal[0] = obj->vn[nidx].x;
396 face.v[j].normal[1] = obj->vn[nidx].y;
397 face.v[j].normal[2] = obj->vn[nidx].z;
398 face.v[j].normal[3] = 0.0;
400 int tidx = f->t[j] < 0 ? 0 : f->t[j];
401 face.v[j].tex[0] = obj->vt[tidx].x;
402 face.v[j].tex[1] = obj->vt[tidx].y;
403 }
405 face.normal[0] = face.v[0].normal[0];
406 face.normal[1] = face.v[1].normal[1];
407 face.normal[2] = face.v[2].normal[2];
408 face.normal[3] = 0.0;
410 mesh->faces.push_back(face);
411 }
413 if(added_norm) {
414 obj->vn.pop_back();
415 }
416 if(added_tc) {
417 obj->vt.pop_back();
418 }
420 return mesh;
421 }
423 static bool read_materials(FILE *fp, vector<obj_mat> *vmtl)
424 {
425 obj_mat mat;
427 for(;;) {
428 char line[BUF_SZ];
429 fgets(line, sizeof line, fp);
430 if(feof(fp)) {
431 break;
432 }
434 char *tok;
435 if(!(tok = strtok(line, SEP))) {
436 continue;
437 }
439 int cmd;
440 if((cmd = get_cmd(tok)) == -1) {
441 continue;
442 }
444 switch(cmd) {
445 case CMD_NEWMTL:
446 // add the previous material, and start a new one
447 if(mat.name.length() > 0) {
448 printf("Adding material: %s\n", mat.name.c_str());
449 vmtl->push_back(mat);
450 mat.reset();
451 }
452 if((tok = strtok(0, SEP))) {
453 mat.name = tok;
454 }
455 break;
457 case CMD_KA:
458 parse_color(&mat.ambient);
459 break;
461 case CMD_KD:
462 parse_color(&mat.diffuse);
463 break;
465 case CMD_KS:
466 parse_color(&mat.specular);
467 break;
469 case CMD_NS:
470 if((tok = strtok(0, SEP)) && is_float(tok)) {
471 mat.shininess = atof(tok);
472 }
473 break;
475 case CMD_NI:
476 if((tok = strtok(0, SEP)) && is_float(tok)) {
477 mat.ior = atof(tok);
478 }
479 break;
481 case CMD_D:
482 case CMD_TR:
483 {
484 Vector3 c;
485 if(parse_color(&c)) {
486 mat.alpha = cmd == CMD_D ? c.x : 1.0 - c.x;
487 }
488 }
489 break;
491 case CMD_MAP_KD:
492 mat.tex_dif = parse_map();
493 break;
495 default:
496 break;
497 }
498 }
500 if(mat.name.length() > 0) {
501 printf("Adding material: %s\n", mat.name.c_str());
502 vmtl->push_back(mat);
503 }
504 return true;
505 }
507 static int get_cmd(char *str)
508 {
509 char *s = str;
510 while((*s = toupper(*s))) s++;
512 for(int i=0; cmd_names[i]; i++) {
513 if(strcmp(str, cmd_names[i]) == 0) {
514 return i;
515 }
516 }
517 return CMD_UNK;
518 }
520 static bool is_int(const char *str)
521 {
522 char *tmp;
523 strtol(str, &tmp, 10);
524 return tmp != str;
525 }
527 static bool is_float(const char *str)
528 {
529 char *tmp;
530 strtod(str, &tmp);
531 return tmp != str;
532 }
534 static bool parse_vec(Vector3 *vec)
535 {
536 for(int i=0; i<3; i++) {
537 char *tok;
539 if(!(tok = strtok(0, SEP)) || !is_float(tok)) {
540 if(i < 2) {
541 return false;
542 }
543 vec->z = 0.0;
544 } else {
545 float v = atof(tok);
547 switch(i) {
548 case 0:
549 vec->x = v;
550 break;
551 case 1:
552 vec->y = v;
553 break;
554 case 2:
555 vec->z = v;
556 break;
557 }
558 }
559 }
560 return true;
561 }
563 static bool parse_color(Vector3 *col)
564 {
565 for(int i=0; i<3; i++) {
566 char *tok;
568 if(!(tok = strtok(0, SEP)) || !is_float(tok)) {
569 col->y = col->z = col->x;
570 return i > 0 ? true : false;
571 }
573 float v = atof(tok);
574 switch(i) {
575 case 0:
576 col->x = v;
577 break;
578 case 1:
579 col->y = v;
580 break;
581 case 2:
582 col->z = v;
583 break;
584 }
585 }
586 return true;
587 }
589 static bool parse_face(obj_face *face)
590 {
591 char *tok[] = {0, 0, 0, 0};
592 face->elem = 0;
594 for(int i=0; i<4; i++) {
595 if((!(tok[i] = strtok(0, SEP)) || !is_int(tok[i]))) {
596 if(i < 3) return false; // less than 3 verts? not a polygon
597 } else {
598 face->elem++;
599 }
600 }
602 for(int i=0; i<4; i++) {
603 char *subtok = tok[i];
605 if(!subtok || !*subtok || !is_int(subtok)) {
606 if(i < 3) {
607 return false;
608 }
609 face->v[i] = INVALID_IDX;
610 } else {
611 face->v[i] = atoi(subtok);
612 if(face->v[i] > 0) face->v[i]--; /* convert to 0-based */
613 }
615 while(subtok && *subtok && *subtok != '/') {
616 subtok++;
617 }
618 if(subtok && *subtok && *++subtok && is_int(subtok)) {
619 face->t[i] = atoi(subtok);
620 if(face->t[i] > 0) face->t[i]--; /* convert to 0-based */
621 } else {
622 face->t[i] = INVALID_IDX;
623 }
625 while(subtok && *subtok && *subtok != '/') {
626 subtok++;
627 }
628 if(subtok && *subtok && *++subtok && is_int(subtok)) {
629 face->n[i] = atoi(subtok);
630 if(face->n[i] > 0) face->n[i]--; /* convert to 0-based */
631 } else {
632 face->n[i] = INVALID_IDX;
633 }
634 }
636 return true;
637 }
639 static const char *parse_map()
640 {
641 char *tok, *prev = 0;
643 while((tok = strtok(0, SEP))) {
644 prev = tok;
645 }
647 return prev ? prev : "";
648 }
650 static bool find_file(char *res, int sz, const char *fname, const char *path, const char *mode)
651 {
652 FILE *fp;
653 const char *beg, *end;
654 int fnamelen = strlen(fname);
656 beg = path;
657 while(beg && *beg) {
658 end = beg;
659 while(*end && *end != ':') {
660 end++;
661 }
663 int res_len = end - beg;
664 char *pathname = (char*)alloca(res_len + fnamelen + 2);
665 memcpy(pathname, beg, res_len);
666 pathname[res_len] = 0;
667 if(res_len) {
668 strcat(pathname, "/");
669 }
670 strcat(pathname, fname);
672 if((fp = fopen(pathname, mode))) {
673 fclose(fp);
674 strncpy(res, pathname, sz);
675 return true;
676 }
678 beg += res_len;
679 if(*beg == ':') beg++;
680 }
681 return false;
682 }
684 static const char *dirname(const char *str)
685 {
686 static char buf[PATH_MAX];
688 if(!str || !*str) {
689 strcpy(buf, ".");
690 } else {
691 strncpy(buf, str, PATH_MAX);
692 char *ptr = strrchr(buf, '/');
694 if(ptr && *ptr) {
695 *ptr = 0;
696 } else {
697 strcpy(buf, ".");
698 }
699 }
700 return buf;
701 }