clray
view src/mesh.cc @ 15:754faf15ba36
burp
author | John Tsiombikas |
---|---|
date | Sun, 08 Aug 2010 09:51:45 +0100 |
parents | 29f9330cfa4b |
children | 9e4a28063394 |
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(NS), \
32 CMD(NI), \
33 CMD(D), \
34 CMD(TR), \
35 CMD(MAP_KD), \
36 CMD(MAP_KS), \
37 CMD(MAP_NS), \
38 CMD(MAP_D), \
39 CMD(REFL), \
40 CMD(BUMP)
42 #define CMD(x) CMD_##x
43 enum {
44 COMMANDS,
45 CMD_UNK
46 };
47 #undef CMD
49 #define CMD(x) #x
50 static const char *cmd_names[] = {
51 COMMANDS,
52 0
53 };
54 #undef CMD
56 struct Vector3 {
57 float x, y, z;
59 Vector3() { x = y = z = 0.0; }
60 Vector3(float a, float b, float c) { x = a; y = b; z = c; }
61 };
63 struct Vector2 {
64 float x, y;
66 Vector2() { x = y = 0.0; }
67 Vector2(float a, float b) { x = a; y = b; }
68 };
70 struct obj_face {
71 int elem;
72 int v[4], n[4], t[4];
73 };
75 struct obj_file {
76 string cur_obj, cur_mat;
77 vector<Vector3> v, vn, vt;
78 vector<obj_face> f;
79 };
81 struct obj_mat {
82 string name; // newmtl <name>
83 Vector3 ambient, diffuse, specular; // Ka, Kd, Ks
84 float shininess; // Ns
85 float ior; // Ni
86 float alpha; // d, Tr
88 string tex_dif, tex_spec, tex_shin, tex_alpha; // map_Kd, map_Ks, map_Ns, map_d
89 string tex_refl; // refl -type sphere|cube file
90 string tex_bump; // bump
92 obj_mat() { reset(); }
94 void reset() {
95 ambient = diffuse = Vector3(0.5, 0.5, 0.5);
96 specular = Vector3(0.0, 0.0, 0.0);
97 name = tex_dif = tex_spec = tex_shin = tex_alpha = tex_refl = tex_bump = "";
98 shininess = 0;
99 ior = alpha = 1;
100 }
101 };
103 static bool read_materials(FILE *fp, vector<obj_mat> *vmtl);
104 static Mesh *cons_mesh(obj_file *obj);
106 static int get_cmd(char *str);
107 static bool is_int(const char *str);
108 static bool is_float(const char *str);
109 static bool parse_vec(Vector3 *vec);
110 static bool parse_color(Vector3 *col);
111 static bool parse_face(obj_face *face);
112 static const char *parse_map();
114 static bool find_file(char *res, int sz, const char *fname, const char *path = ".", const char *mode = "rb");
115 static const char *dirname(const char *str);
117 static map<string, int> matnames;
120 #define FEQ(a, b) (fabs((a) - (b)) < 1e-8)
121 bool Face::operator ==(const Face &f) const
122 {
123 for(int i=0; i<3; i++) {
124 for(int j=0; j<3; j++) {
125 if(!FEQ(v[i].pos[j], f.v[i].pos[j])) {
126 return false;
127 }
128 if(!FEQ(v[i].normal[j], f.v[i].normal[j])) {
129 return false;
130 }
131 }
132 if(!FEQ(normal[i], f.normal[i])) {
133 return false;
134 }
135 }
136 return true;
137 }
139 bool Scene::add_mesh(Mesh *m)
140 {
141 // make sure triangles have material ids
142 for(size_t i=0; i<m->faces.size(); i++) {
143 m->faces[i].matid = m->matid;
144 }
145 meshes.push_back(m);
146 return true;
147 }
149 int Scene::get_num_meshes() const
150 {
151 return (int)meshes.size();
152 }
154 int Scene::get_num_faces() const
155 {
156 int num_faces = 0;
157 for(size_t i=0; i<meshes.size(); i++) {
158 num_faces += meshes[i]->faces.size();
159 }
160 printf("get_num_faces() = %d\n", num_faces);
161 return num_faces;
162 }
164 int Scene::get_num_materials() const
165 {
166 return (int)matlib.size();
167 }
169 Material *Scene::get_materials()
170 {
171 if(matlib.empty()) {
172 return 0;
173 }
174 return &matlib[0];
175 }
177 const Material *Scene::get_materials() const
178 {
179 if(matlib.empty()) {
180 return 0;
181 }
182 return &matlib[0];
183 }
186 #define INVALID_IDX INT_MIN
188 #define SEP " \t\n\r\v"
189 #define BUF_SZ 512
191 bool Scene::load(const char *fname)
192 {
193 FILE *fp;
195 if(!(fp = fopen(fname, "rb"))) {
196 fprintf(stderr, "failed to open %s: %s\n", fname, strerror(errno));
197 return false;
198 }
200 bool res = load(fp);
201 fclose(fp);
202 return res;
203 }
205 bool Scene::load(FILE *fp)
206 {
207 static int seq;
208 char cur_name[16];
210 obj_file obj;
212 sprintf(cur_name, "default%02d.obj", seq++);
213 obj.cur_obj = cur_name;
215 int prev_cmd = 0, obj_added = 0;
216 for(;;) {
217 Vector3 vec;
218 obj_face face;
220 char line[BUF_SZ];
221 fgets(line, sizeof line, fp);
222 if(feof(fp)) {
223 break;
224 }
226 char *tok;
227 if(!(tok = strtok(line, SEP))) {
228 continue; // ignore empty lines
229 }
231 int cmd;
232 if((cmd = get_cmd(tok)) == -1) {
233 continue; // ignore unknown commands ...
234 }
236 switch(cmd) {
237 case CMD_V:
238 if(!parse_vec(&vec)) {
239 continue;
240 }
241 obj.v.push_back(vec);
242 break;
244 case CMD_VN:
245 if(!parse_vec(&vec)) {
246 continue;
247 }
248 obj.vn.push_back(vec);
249 break;
251 case CMD_VT:
252 if(!parse_vec(&vec)) {
253 continue;
254 }
255 vec.y = 1.0 - vec.y;
256 obj.vt.push_back(vec);
257 break;
259 case CMD_O:
260 case CMD_G:
261 if(prev_cmd == CMD_O || prev_cmd == CMD_G) {
262 break; // just in case we've got both of them in a row
263 }
264 /* if we have any previous data, group them up, add the object
265 * and continue with the new one...
266 */
267 if(!obj.f.empty()) {
268 Mesh *mesh = cons_mesh(&obj);
269 mesh->matid = matnames[obj.cur_mat];
270 add_mesh(mesh);
271 obj_added++;
273 obj.f.clear(); // clean the face list
274 }
275 if((tok = strtok(0, SEP))) {
276 obj.cur_obj = tok;
277 } else {
278 sprintf(cur_name, "default%02d.obj", seq++);
279 obj.cur_obj = cur_name;
280 }
281 break;
283 case CMD_MTLLIB:
284 if((tok = strtok(0, SEP))) {
285 char path[PATH_MAX];
287 sprintf(path, ".:%s", dirname(tok));
288 if(!find_file(path, PATH_MAX, tok, path)) {
289 fprintf(stderr, "material library not found: %s\n", tok);
290 continue;
291 }
293 FILE *mfile;
294 if(!(mfile = fopen(path, "rb"))) {
295 fprintf(stderr, "failed to open material library: %s\n", path);
296 continue;
297 }
299 // load all materials of the mtl file into a vector
300 vector<obj_mat> vmtl;
301 if(!read_materials(mfile, &vmtl)) {
302 continue;
303 }
304 fclose(mfile);
306 // and add them all to the scene
307 for(size_t i=0; i<vmtl.size(); i++) {
308 Material mat;
310 mat.kd[0] = vmtl[i].diffuse.x;
311 mat.kd[1] = vmtl[i].diffuse.y;
312 mat.kd[2] = vmtl[i].diffuse.z;
314 mat.ks[0] = vmtl[i].specular.x;
315 mat.ks[1] = vmtl[i].specular.y;
316 mat.ks[2] = vmtl[i].specular.z;
318 mat.kt = 1.0 - vmtl[i].alpha;
319 mat.kr = 0.0; // TODO
320 mat.spow = vmtl[i].shininess;
322 matlib.push_back(mat);
323 matnames[vmtl[i].name] = i;
324 }
325 }
326 break;
328 case CMD_USEMTL:
329 if((tok = strtok(0, SEP))) {
330 obj.cur_mat = tok;
331 } else {
332 obj.cur_mat = "";
333 }
334 break;
336 case CMD_F:
337 if(!parse_face(&face)) {
338 continue;
339 }
341 // convert negative indices to regular indices
342 for(int i=0; i<4; i++) {
343 if(face.v[i] < 0 && face.v[i] != INVALID_IDX) {
344 face.v[i] = obj.v.size() + face.v[i];
345 }
346 if(face.n[i] < 0 && face.n[i] != INVALID_IDX) {
347 face.n[i] = obj.vn.size() + face.n[i];
348 }
349 if(face.t[i] < 0 && face.t[i] != INVALID_IDX) {
350 face.t[i] = obj.vt.size() + face.t[i];
351 }
352 }
354 // break quads into triangles if needed
355 obj.f.push_back(face);
356 if(face.elem == 4) {
357 face.v[1] = face.v[2];
358 face.n[1] = face.n[2];
359 face.t[1] = face.t[2];
361 face.v[2] = face.v[3];
362 face.n[2] = face.n[3];
363 face.t[2] = face.t[3];
365 obj.f.push_back(face);
366 }
367 break;
369 default:
370 break; // ignore unknown commands
371 }
373 prev_cmd = cmd;
374 }
376 // reached end of file...
377 if(!obj.f.empty()) {
378 Mesh *mesh = cons_mesh(&obj);
379 mesh->matid = matnames[obj.cur_mat];
380 add_mesh(mesh);
381 obj_added++;
382 }
384 return obj_added > 0;
385 }
387 static Mesh *cons_mesh(obj_file *obj)
388 {
389 Mesh *mesh;
391 // need at least one of each element
392 bool added_norm = false, added_tc = false;
393 if(obj->vn.empty()) {
394 obj->vn.push_back(Vector3(0, 0, 0));
395 added_norm = true;
396 }
397 if(obj->vt.empty()) {
398 obj->vt.push_back(Vector3(0, 0, 0));
399 added_tc = true;
400 }
402 mesh = new Mesh;
404 for(size_t i=0; i<obj->f.size(); i++) {
405 Face face;
407 for(int j=0; j<3; j++) {
408 obj_face *f = &obj->f[i];
410 face.v[j].pos[0] = obj->v[f->v[j]].x;
411 face.v[j].pos[1] = obj->v[f->v[j]].y;
412 face.v[j].pos[2] = obj->v[f->v[j]].z;
413 face.v[j].pos[3] = 0.0;
415 int nidx = f->n[j] < 0 ? 0 : f->n[j];
416 face.v[j].normal[0] = obj->vn[nidx].x;
417 face.v[j].normal[1] = obj->vn[nidx].y;
418 face.v[j].normal[2] = obj->vn[nidx].z;
419 face.v[j].normal[3] = 0.0;
421 int tidx = f->t[j] < 0 ? 0 : f->t[j];
422 face.v[j].tex[0] = obj->vt[tidx].x;
423 face.v[j].tex[1] = obj->vt[tidx].y;
424 }
426 face.normal[0] = face.v[0].normal[0];
427 face.normal[1] = face.v[1].normal[1];
428 face.normal[2] = face.v[2].normal[2];
429 face.normal[3] = 0.0;
431 mesh->faces.push_back(face);
432 }
434 if(added_norm) {
435 obj->vn.pop_back();
436 }
437 if(added_tc) {
438 obj->vt.pop_back();
439 }
441 return mesh;
442 }
444 static bool read_materials(FILE *fp, vector<obj_mat> *vmtl)
445 {
446 obj_mat mat;
448 for(;;) {
449 char line[BUF_SZ];
450 fgets(line, sizeof line, fp);
451 if(feof(fp)) {
452 break;
453 }
455 char *tok;
456 if(!(tok = strtok(line, SEP))) {
457 continue;
458 }
460 int cmd;
461 if((cmd = get_cmd(tok)) == -1) {
462 continue;
463 }
465 switch(cmd) {
466 case CMD_NEWMTL:
467 // add the previous material, and start a new one
468 if(mat.name.length() > 0) {
469 printf("Adding material: %s\n", mat.name.c_str());
470 vmtl->push_back(mat);
471 mat.reset();
472 }
473 if((tok = strtok(0, SEP))) {
474 mat.name = tok;
475 }
476 break;
478 case CMD_KA:
479 parse_color(&mat.ambient);
480 break;
482 case CMD_KD:
483 parse_color(&mat.diffuse);
484 break;
486 case CMD_KS:
487 parse_color(&mat.specular);
488 break;
490 case CMD_NS:
491 if((tok = strtok(0, SEP)) && is_float(tok)) {
492 mat.shininess = atof(tok);
493 }
494 break;
496 case CMD_NI:
497 if((tok = strtok(0, SEP)) && is_float(tok)) {
498 mat.ior = atof(tok);
499 }
500 break;
502 case CMD_D:
503 case CMD_TR:
504 {
505 Vector3 c;
506 if(parse_color(&c)) {
507 mat.alpha = cmd == CMD_D ? c.x : 1.0 - c.x;
508 }
509 }
510 break;
512 case CMD_MAP_KD:
513 mat.tex_dif = parse_map();
514 break;
516 default:
517 break;
518 }
519 }
521 if(mat.name.length() > 0) {
522 printf("Adding material: %s\n", mat.name.c_str());
523 vmtl->push_back(mat);
524 }
525 return true;
526 }
528 static int get_cmd(char *str)
529 {
530 char *s = str;
531 while((*s = toupper(*s))) s++;
533 for(int i=0; cmd_names[i]; i++) {
534 if(strcmp(str, cmd_names[i]) == 0) {
535 return i;
536 }
537 }
538 return CMD_UNK;
539 }
541 static bool is_int(const char *str)
542 {
543 char *tmp;
544 strtol(str, &tmp, 10);
545 return tmp != str;
546 }
548 static bool is_float(const char *str)
549 {
550 char *tmp;
551 strtod(str, &tmp);
552 return tmp != str;
553 }
555 static bool parse_vec(Vector3 *vec)
556 {
557 for(int i=0; i<3; i++) {
558 char *tok;
560 if(!(tok = strtok(0, SEP)) || !is_float(tok)) {
561 if(i < 2) {
562 return false;
563 }
564 vec->z = 0.0;
565 } else {
566 float v = atof(tok);
568 switch(i) {
569 case 0:
570 vec->x = v;
571 break;
572 case 1:
573 vec->y = v;
574 break;
575 case 2:
576 vec->z = v;
577 break;
578 }
579 }
580 }
581 return true;
582 }
584 static bool parse_color(Vector3 *col)
585 {
586 for(int i=0; i<3; i++) {
587 char *tok;
589 if(!(tok = strtok(0, SEP)) || !is_float(tok)) {
590 col->y = col->z = col->x;
591 return i > 0 ? true : false;
592 }
594 float v = atof(tok);
595 switch(i) {
596 case 0:
597 col->x = v;
598 break;
599 case 1:
600 col->y = v;
601 break;
602 case 2:
603 col->z = v;
604 break;
605 }
606 }
607 return true;
608 }
610 static bool parse_face(obj_face *face)
611 {
612 char *tok[] = {0, 0, 0, 0};
613 face->elem = 0;
615 for(int i=0; i<4; i++) {
616 if((!(tok[i] = strtok(0, SEP)) || !is_int(tok[i]))) {
617 if(i < 3) return false; // less than 3 verts? not a polygon
618 } else {
619 face->elem++;
620 }
621 }
623 for(int i=0; i<4; i++) {
624 char *subtok = tok[i];
626 if(!subtok || !*subtok || !is_int(subtok)) {
627 if(i < 3) {
628 return false;
629 }
630 face->v[i] = INVALID_IDX;
631 } else {
632 face->v[i] = atoi(subtok);
633 if(face->v[i] > 0) face->v[i]--; /* convert to 0-based */
634 }
636 while(subtok && *subtok && *subtok != '/') {
637 subtok++;
638 }
639 if(subtok && *subtok && *++subtok && is_int(subtok)) {
640 face->t[i] = atoi(subtok);
641 if(face->t[i] > 0) face->t[i]--; /* convert to 0-based */
642 } else {
643 face->t[i] = INVALID_IDX;
644 }
646 while(subtok && *subtok && *subtok != '/') {
647 subtok++;
648 }
649 if(subtok && *subtok && *++subtok && is_int(subtok)) {
650 face->n[i] = atoi(subtok);
651 if(face->n[i] > 0) face->n[i]--; /* convert to 0-based */
652 } else {
653 face->n[i] = INVALID_IDX;
654 }
655 }
657 return true;
658 }
660 static const char *parse_map()
661 {
662 char *tok, *prev = 0;
664 while((tok = strtok(0, SEP))) {
665 prev = tok;
666 }
668 return prev ? prev : "";
669 }
671 static bool find_file(char *res, int sz, const char *fname, const char *path, const char *mode)
672 {
673 FILE *fp;
674 const char *beg, *end;
675 int fnamelen = strlen(fname);
677 beg = path;
678 while(beg && *beg) {
679 end = beg;
680 while(*end && *end != ':') {
681 end++;
682 }
684 int res_len = end - beg;
685 char *pathname = (char*)alloca(res_len + fnamelen + 2);
686 memcpy(pathname, beg, res_len);
687 pathname[res_len] = 0;
688 if(res_len) {
689 strcat(pathname, "/");
690 }
691 strcat(pathname, fname);
693 if((fp = fopen(pathname, mode))) {
694 fclose(fp);
695 strncpy(res, pathname, sz);
696 return true;
697 }
699 beg += res_len;
700 if(*beg == ':') beg++;
701 }
702 return false;
703 }
705 static const char *dirname(const char *str)
706 {
707 static char buf[PATH_MAX];
709 if(!str || !*str) {
710 strcpy(buf, ".");
711 } else {
712 strncpy(buf, str, PATH_MAX);
713 char *ptr = strrchr(buf, '/');
715 if(ptr && *ptr) {
716 *ptr = 0;
717 } else {
718 strcpy(buf, ".");
719 }
720 }
721 return buf;
722 }