clray

view src/mesh.cc @ 19:8baea9b66b50

added reflection
author John Tsiombikas <nuclear@member.fsf.org>
date Mon, 09 Aug 2010 06:45:57 +0100
parents 9e4a28063394
children 63a6b46f58a0
line source
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <math.h>
5 #include <errno.h>
6 #include <limits.h>
7 #include <string>
8 #include <vector>
9 #include <map>
10 #include "mesh.h"
12 #ifndef PATH_MAX
13 #define PATH_MAX 512
14 #endif
16 using namespace std;
18 #define COMMANDS \
19 CMD(V), \
20 CMD(VN), \
21 CMD(VT), \
22 CMD(F), \
23 CMD(O), \
24 CMD(G), \
25 CMD(MTLLIB), \
26 CMD(USEMTL), \
27 CMD(NEWMTL), \
28 CMD(KA), \
29 CMD(KD), \
30 CMD(KS), \
31 CMD(KR), \
32 CMD(NS), \
33 CMD(NI), \
34 CMD(D), \
35 CMD(TR), \
36 CMD(MAP_KD), \
37 CMD(MAP_KS), \
38 CMD(MAP_NS), \
39 CMD(MAP_D), \
40 CMD(REFL), \
41 CMD(BUMP)
43 #define CMD(x) CMD_##x
44 enum {
45 COMMANDS,
46 CMD_UNK
47 };
48 #undef CMD
50 #define CMD(x) #x
51 static const char *cmd_names[] = {
52 COMMANDS,
53 0
54 };
55 #undef CMD
57 struct Vector3 {
58 float x, y, z;
60 Vector3() { x = y = z = 0.0; }
61 Vector3(float a, float b, float c) { x = a; y = b; z = c; }
62 };
64 struct Vector2 {
65 float x, y;
67 Vector2() { x = y = 0.0; }
68 Vector2(float a, float b) { x = a; y = b; }
69 };
71 struct obj_face {
72 int elem;
73 int v[4], n[4], t[4];
74 };
76 struct obj_file {
77 string cur_obj, cur_mat;
78 vector<Vector3> v, vn, vt;
79 vector<obj_face> f;
80 };
82 struct obj_mat {
83 string name; // newmtl <name>
84 Vector3 ambient, diffuse, specular; // Ka, Kd, Ks
85 float shininess; // Ns
86 float ior; // Ni
87 float alpha; // d, Tr
88 float refl; // Kr (my extesnsion)
90 string tex_dif, tex_spec, tex_shin, tex_alpha; // map_Kd, map_Ks, map_Ns, map_d
91 string tex_refl; // refl -type sphere|cube file
92 string tex_bump; // bump
94 obj_mat() { reset(); }
96 void reset() {
97 ambient = diffuse = Vector3(0.5, 0.5, 0.5);
98 specular = Vector3(0.0, 0.0, 0.0);
99 name = tex_dif = tex_spec = tex_shin = tex_alpha = tex_refl = tex_bump = "";
100 shininess = 0;
101 ior = alpha = 1;
102 refl = 0.0;
103 }
104 };
106 static bool read_materials(FILE *fp, vector<obj_mat> *vmtl);
107 static Mesh *cons_mesh(obj_file *obj);
109 static int get_cmd(char *str);
110 static bool is_int(const char *str);
111 static bool is_float(const char *str);
112 static bool parse_vec(Vector3 *vec);
113 static bool parse_color(Vector3 *col);
114 static bool parse_face(obj_face *face);
115 static const char *parse_map();
117 static bool find_file(char *res, int sz, const char *fname, const char *path = ".", const char *mode = "rb");
118 static const char *dirname(const char *str);
120 static map<string, int> matnames;
123 #define FEQ(a, b) (fabs((a) - (b)) < 1e-8)
124 bool Face::operator ==(const Face &f) const
125 {
126 for(int i=0; i<3; i++) {
127 for(int j=0; j<3; j++) {
128 if(!FEQ(v[i].pos[j], f.v[i].pos[j])) {
129 return false;
130 }
131 if(!FEQ(v[i].normal[j], f.v[i].normal[j])) {
132 return false;
133 }
134 }
135 if(!FEQ(normal[i], f.normal[i])) {
136 return false;
137 }
138 }
139 return true;
140 }
142 bool Scene::add_mesh(Mesh *m)
143 {
144 // make sure triangles have material ids
145 for(size_t i=0; i<m->faces.size(); i++) {
146 m->faces[i].matid = m->matid;
147 }
148 meshes.push_back(m);
149 return true;
150 }
152 int Scene::get_num_meshes() const
153 {
154 return (int)meshes.size();
155 }
157 int Scene::get_num_faces() const
158 {
159 int num_faces = 0;
160 for(size_t i=0; i<meshes.size(); i++) {
161 num_faces += meshes[i]->faces.size();
162 }
163 printf("get_num_faces() = %d\n", num_faces);
164 return num_faces;
165 }
167 int Scene::get_num_materials() const
168 {
169 return (int)matlib.size();
170 }
172 Material *Scene::get_materials()
173 {
174 if(matlib.empty()) {
175 return 0;
176 }
177 return &matlib[0];
178 }
180 const Material *Scene::get_materials() const
181 {
182 if(matlib.empty()) {
183 return 0;
184 }
185 return &matlib[0];
186 }
189 #define INVALID_IDX INT_MIN
191 #define SEP " \t\n\r\v"
192 #define BUF_SZ 512
194 bool Scene::load(const char *fname)
195 {
196 FILE *fp;
198 if(!(fp = fopen(fname, "rb"))) {
199 fprintf(stderr, "failed to open %s: %s\n", fname, strerror(errno));
200 return false;
201 }
203 bool res = load(fp);
204 fclose(fp);
205 return res;
206 }
208 bool Scene::load(FILE *fp)
209 {
210 static int seq;
211 char cur_name[16];
213 obj_file obj;
215 sprintf(cur_name, "default%02d.obj", seq++);
216 obj.cur_obj = cur_name;
218 int prev_cmd = 0, obj_added = 0;
219 for(;;) {
220 Vector3 vec;
221 obj_face face;
223 char line[BUF_SZ];
224 fgets(line, sizeof line, fp);
225 if(feof(fp)) {
226 break;
227 }
229 char *tok;
230 if(!(tok = strtok(line, SEP))) {
231 continue; // ignore empty lines
232 }
234 int cmd;
235 if((cmd = get_cmd(tok)) == -1) {
236 continue; // ignore unknown commands ...
237 }
239 switch(cmd) {
240 case CMD_V:
241 if(!parse_vec(&vec)) {
242 continue;
243 }
244 obj.v.push_back(vec);
245 break;
247 case CMD_VN:
248 if(!parse_vec(&vec)) {
249 continue;
250 }
251 obj.vn.push_back(vec);
252 break;
254 case CMD_VT:
255 if(!parse_vec(&vec)) {
256 continue;
257 }
258 vec.y = 1.0 - vec.y;
259 obj.vt.push_back(vec);
260 break;
262 case CMD_O:
263 case CMD_G:
264 if(prev_cmd == CMD_O || prev_cmd == CMD_G) {
265 break; // just in case we've got both of them in a row
266 }
267 /* if we have any previous data, group them up, add the object
268 * and continue with the new one...
269 */
270 if(!obj.f.empty()) {
271 Mesh *mesh = cons_mesh(&obj);
272 mesh->matid = matnames[obj.cur_mat];
273 add_mesh(mesh);
274 obj_added++;
276 obj.f.clear(); // clean the face list
277 }
278 if((tok = strtok(0, SEP))) {
279 obj.cur_obj = tok;
280 } else {
281 sprintf(cur_name, "default%02d.obj", seq++);
282 obj.cur_obj = cur_name;
283 }
284 break;
286 case CMD_MTLLIB:
287 if((tok = strtok(0, SEP))) {
288 char path[PATH_MAX];
290 sprintf(path, ".:%s", dirname(tok));
291 if(!find_file(path, PATH_MAX, tok, path)) {
292 fprintf(stderr, "material library not found: %s\n", tok);
293 continue;
294 }
296 FILE *mfile;
297 if(!(mfile = fopen(path, "rb"))) {
298 fprintf(stderr, "failed to open material library: %s\n", path);
299 continue;
300 }
302 // load all materials of the mtl file into a vector
303 vector<obj_mat> vmtl;
304 if(!read_materials(mfile, &vmtl)) {
305 continue;
306 }
307 fclose(mfile);
309 // and add them all to the scene
310 for(size_t i=0; i<vmtl.size(); i++) {
311 Material mat;
313 mat.kd[0] = vmtl[i].diffuse.x;
314 mat.kd[1] = vmtl[i].diffuse.y;
315 mat.kd[2] = vmtl[i].diffuse.z;
317 mat.ks[0] = vmtl[i].specular.x;
318 mat.ks[1] = vmtl[i].specular.y;
319 mat.ks[2] = vmtl[i].specular.z;
321 mat.kt = 1.0 - vmtl[i].alpha;
322 mat.kr = vmtl[i].refl;
323 mat.spow = vmtl[i].shininess;
325 matlib.push_back(mat);
326 matnames[vmtl[i].name] = i;
327 }
328 }
329 break;
331 case CMD_USEMTL:
332 if((tok = strtok(0, SEP))) {
333 obj.cur_mat = tok;
334 } else {
335 obj.cur_mat = "";
336 }
337 break;
339 case CMD_F:
340 if(!parse_face(&face)) {
341 continue;
342 }
344 // convert negative indices to regular indices
345 for(int i=0; i<4; i++) {
346 if(face.v[i] < 0 && face.v[i] != INVALID_IDX) {
347 face.v[i] = obj.v.size() + face.v[i];
348 }
349 if(face.n[i] < 0 && face.n[i] != INVALID_IDX) {
350 face.n[i] = obj.vn.size() + face.n[i];
351 }
352 if(face.t[i] < 0 && face.t[i] != INVALID_IDX) {
353 face.t[i] = obj.vt.size() + face.t[i];
354 }
355 }
357 // break quads into triangles if needed
358 obj.f.push_back(face);
359 if(face.elem == 4) {
360 face.v[1] = face.v[2];
361 face.n[1] = face.n[2];
362 face.t[1] = face.t[2];
364 face.v[2] = face.v[3];
365 face.n[2] = face.n[3];
366 face.t[2] = face.t[3];
368 obj.f.push_back(face);
369 }
370 break;
372 default:
373 break; // ignore unknown commands
374 }
376 prev_cmd = cmd;
377 }
379 // reached end of file...
380 if(!obj.f.empty()) {
381 Mesh *mesh = cons_mesh(&obj);
382 mesh->matid = matnames[obj.cur_mat];
383 add_mesh(mesh);
384 obj_added++;
385 }
387 return obj_added > 0;
388 }
390 static Mesh *cons_mesh(obj_file *obj)
391 {
392 Mesh *mesh;
394 // need at least one of each element
395 bool added_norm = false, added_tc = false;
396 if(obj->vn.empty()) {
397 obj->vn.push_back(Vector3(0, 0, 0));
398 added_norm = true;
399 }
400 if(obj->vt.empty()) {
401 obj->vt.push_back(Vector3(0, 0, 0));
402 added_tc = true;
403 }
405 mesh = new Mesh;
407 for(size_t i=0; i<obj->f.size(); i++) {
408 Face face;
410 for(int j=0; j<3; j++) {
411 obj_face *f = &obj->f[i];
413 face.v[j].pos[0] = obj->v[f->v[j]].x;
414 face.v[j].pos[1] = obj->v[f->v[j]].y;
415 face.v[j].pos[2] = obj->v[f->v[j]].z;
416 face.v[j].pos[3] = 0.0;
418 int nidx = f->n[j] < 0 ? 0 : f->n[j];
419 face.v[j].normal[0] = obj->vn[nidx].x;
420 face.v[j].normal[1] = obj->vn[nidx].y;
421 face.v[j].normal[2] = obj->vn[nidx].z;
422 face.v[j].normal[3] = 0.0;
424 int tidx = f->t[j] < 0 ? 0 : f->t[j];
425 face.v[j].tex[0] = obj->vt[tidx].x;
426 face.v[j].tex[1] = obj->vt[tidx].y;
427 }
429 face.normal[0] = face.v[0].normal[0];
430 face.normal[1] = face.v[1].normal[1];
431 face.normal[2] = face.v[2].normal[2];
432 face.normal[3] = 0.0;
434 mesh->faces.push_back(face);
435 }
437 if(added_norm) {
438 obj->vn.pop_back();
439 }
440 if(added_tc) {
441 obj->vt.pop_back();
442 }
444 return mesh;
445 }
447 static bool read_materials(FILE *fp, vector<obj_mat> *vmtl)
448 {
449 obj_mat mat;
451 for(;;) {
452 char line[BUF_SZ];
453 fgets(line, sizeof line, fp);
454 if(feof(fp)) {
455 break;
456 }
458 char *tok;
459 if(!(tok = strtok(line, SEP))) {
460 continue;
461 }
463 int cmd;
464 if((cmd = get_cmd(tok)) == -1) {
465 continue;
466 }
468 switch(cmd) {
469 case CMD_NEWMTL:
470 // add the previous material, and start a new one
471 if(mat.name.length() > 0) {
472 printf("Adding material: %s\n", mat.name.c_str());
473 vmtl->push_back(mat);
474 mat.reset();
475 }
476 if((tok = strtok(0, SEP))) {
477 mat.name = tok;
478 }
479 break;
481 case CMD_KA:
482 parse_color(&mat.ambient);
483 break;
485 case CMD_KD:
486 parse_color(&mat.diffuse);
487 break;
489 case CMD_KS:
490 parse_color(&mat.specular);
491 break;
493 case CMD_KR:
494 if((tok = strtok(0, SEP)) && is_float(tok)) {
495 mat.refl = atof(tok);
496 }
497 break;
499 case CMD_NS:
500 if((tok = strtok(0, SEP)) && is_float(tok)) {
501 mat.shininess = atof(tok);
502 }
503 break;
505 case CMD_NI:
506 if((tok = strtok(0, SEP)) && is_float(tok)) {
507 mat.ior = atof(tok);
508 }
509 break;
511 case CMD_D:
512 case CMD_TR:
513 {
514 Vector3 c;
515 if(parse_color(&c)) {
516 mat.alpha = cmd == CMD_D ? c.x : 1.0 - c.x;
517 }
518 }
519 break;
521 case CMD_MAP_KD:
522 mat.tex_dif = parse_map();
523 break;
525 default:
526 break;
527 }
528 }
530 if(mat.name.length() > 0) {
531 printf("Adding material: %s\n", mat.name.c_str());
532 vmtl->push_back(mat);
533 }
534 return true;
535 }
537 static int get_cmd(char *str)
538 {
539 char *s = str;
540 while((*s = toupper(*s))) s++;
542 for(int i=0; cmd_names[i]; i++) {
543 if(strcmp(str, cmd_names[i]) == 0) {
544 return i;
545 }
546 }
547 return CMD_UNK;
548 }
550 static bool is_int(const char *str)
551 {
552 char *tmp;
553 strtol(str, &tmp, 10);
554 return tmp != str;
555 }
557 static bool is_float(const char *str)
558 {
559 char *tmp;
560 strtod(str, &tmp);
561 return tmp != str;
562 }
564 static bool parse_vec(Vector3 *vec)
565 {
566 for(int i=0; i<3; i++) {
567 char *tok;
569 if(!(tok = strtok(0, SEP)) || !is_float(tok)) {
570 if(i < 2) {
571 return false;
572 }
573 vec->z = 0.0;
574 } else {
575 float v = atof(tok);
577 switch(i) {
578 case 0:
579 vec->x = v;
580 break;
581 case 1:
582 vec->y = v;
583 break;
584 case 2:
585 vec->z = v;
586 break;
587 }
588 }
589 }
590 return true;
591 }
593 static bool parse_color(Vector3 *col)
594 {
595 for(int i=0; i<3; i++) {
596 char *tok;
598 if(!(tok = strtok(0, SEP)) || !is_float(tok)) {
599 col->y = col->z = col->x;
600 return i > 0 ? true : false;
601 }
603 float v = atof(tok);
604 switch(i) {
605 case 0:
606 col->x = v;
607 break;
608 case 1:
609 col->y = v;
610 break;
611 case 2:
612 col->z = v;
613 break;
614 }
615 }
616 return true;
617 }
619 static bool parse_face(obj_face *face)
620 {
621 char *tok[] = {0, 0, 0, 0};
622 face->elem = 0;
624 for(int i=0; i<4; i++) {
625 if((!(tok[i] = strtok(0, SEP)) || !is_int(tok[i]))) {
626 if(i < 3) return false; // less than 3 verts? not a polygon
627 } else {
628 face->elem++;
629 }
630 }
632 for(int i=0; i<4; i++) {
633 char *subtok = tok[i];
635 if(!subtok || !*subtok || !is_int(subtok)) {
636 if(i < 3) {
637 return false;
638 }
639 face->v[i] = INVALID_IDX;
640 } else {
641 face->v[i] = atoi(subtok);
642 if(face->v[i] > 0) face->v[i]--; /* convert to 0-based */
643 }
645 while(subtok && *subtok && *subtok != '/') {
646 subtok++;
647 }
648 if(subtok && *subtok && *++subtok && is_int(subtok)) {
649 face->t[i] = atoi(subtok);
650 if(face->t[i] > 0) face->t[i]--; /* convert to 0-based */
651 } else {
652 face->t[i] = INVALID_IDX;
653 }
655 while(subtok && *subtok && *subtok != '/') {
656 subtok++;
657 }
658 if(subtok && *subtok && *++subtok && is_int(subtok)) {
659 face->n[i] = atoi(subtok);
660 if(face->n[i] > 0) face->n[i]--; /* convert to 0-based */
661 } else {
662 face->n[i] = INVALID_IDX;
663 }
664 }
666 return true;
667 }
669 static const char *parse_map()
670 {
671 char *tok, *prev = 0;
673 while((tok = strtok(0, SEP))) {
674 prev = tok;
675 }
677 return prev ? prev : "";
678 }
680 static bool find_file(char *res, int sz, const char *fname, const char *path, const char *mode)
681 {
682 FILE *fp;
683 const char *beg, *end;
684 int fnamelen = strlen(fname);
686 beg = path;
687 while(beg && *beg) {
688 end = beg;
689 while(*end && *end != ':') {
690 end++;
691 }
693 int res_len = end - beg;
694 char *pathname = (char*)alloca(res_len + fnamelen + 2);
695 memcpy(pathname, beg, res_len);
696 pathname[res_len] = 0;
697 if(res_len) {
698 strcat(pathname, "/");
699 }
700 strcat(pathname, fname);
702 if((fp = fopen(pathname, mode))) {
703 fclose(fp);
704 strncpy(res, pathname, sz);
705 return true;
706 }
708 beg += res_len;
709 if(*beg == ':') beg++;
710 }
711 return false;
712 }
714 static const char *dirname(const char *str)
715 {
716 static char buf[PATH_MAX];
718 if(!str || !*str) {
719 strcpy(buf, ".");
720 } else {
721 strncpy(buf, str, PATH_MAX);
722 char *ptr = strrchr(buf, '/');
724 if(ptr && *ptr) {
725 *ptr = 0;
726 } else {
727 strcpy(buf, ".");
728 }
729 }
730 return buf;
731 }