clray

view src/mesh.cc @ 16:9e4a28063394

cl compiler segfaults...
author John Tsiombikas <nuclear@member.fsf.org>
date Mon, 09 Aug 2010 04:18:21 +0100
parents 754faf15ba36
children 8baea9b66b50
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 }
103 };
105 static bool read_materials(FILE *fp, vector<obj_mat> *vmtl);
106 static Mesh *cons_mesh(obj_file *obj);
108 static int get_cmd(char *str);
109 static bool is_int(const char *str);
110 static bool is_float(const char *str);
111 static bool parse_vec(Vector3 *vec);
112 static bool parse_color(Vector3 *col);
113 static bool parse_face(obj_face *face);
114 static const char *parse_map();
116 static bool find_file(char *res, int sz, const char *fname, const char *path = ".", const char *mode = "rb");
117 static const char *dirname(const char *str);
119 static map<string, int> matnames;
122 #define FEQ(a, b) (fabs((a) - (b)) < 1e-8)
123 bool Face::operator ==(const Face &f) const
124 {
125 for(int i=0; i<3; i++) {
126 for(int j=0; j<3; j++) {
127 if(!FEQ(v[i].pos[j], f.v[i].pos[j])) {
128 return false;
129 }
130 if(!FEQ(v[i].normal[j], f.v[i].normal[j])) {
131 return false;
132 }
133 }
134 if(!FEQ(normal[i], f.normal[i])) {
135 return false;
136 }
137 }
138 return true;
139 }
141 bool Scene::add_mesh(Mesh *m)
142 {
143 // make sure triangles have material ids
144 for(size_t i=0; i<m->faces.size(); i++) {
145 m->faces[i].matid = m->matid;
146 }
147 meshes.push_back(m);
148 return true;
149 }
151 int Scene::get_num_meshes() const
152 {
153 return (int)meshes.size();
154 }
156 int Scene::get_num_faces() const
157 {
158 int num_faces = 0;
159 for(size_t i=0; i<meshes.size(); i++) {
160 num_faces += meshes[i]->faces.size();
161 }
162 printf("get_num_faces() = %d\n", num_faces);
163 return num_faces;
164 }
166 int Scene::get_num_materials() const
167 {
168 return (int)matlib.size();
169 }
171 Material *Scene::get_materials()
172 {
173 if(matlib.empty()) {
174 return 0;
175 }
176 return &matlib[0];
177 }
179 const Material *Scene::get_materials() const
180 {
181 if(matlib.empty()) {
182 return 0;
183 }
184 return &matlib[0];
185 }
188 #define INVALID_IDX INT_MIN
190 #define SEP " \t\n\r\v"
191 #define BUF_SZ 512
193 bool Scene::load(const char *fname)
194 {
195 FILE *fp;
197 if(!(fp = fopen(fname, "rb"))) {
198 fprintf(stderr, "failed to open %s: %s\n", fname, strerror(errno));
199 return false;
200 }
202 bool res = load(fp);
203 fclose(fp);
204 return res;
205 }
207 bool Scene::load(FILE *fp)
208 {
209 static int seq;
210 char cur_name[16];
212 obj_file obj;
214 sprintf(cur_name, "default%02d.obj", seq++);
215 obj.cur_obj = cur_name;
217 int prev_cmd = 0, obj_added = 0;
218 for(;;) {
219 Vector3 vec;
220 obj_face face;
222 char line[BUF_SZ];
223 fgets(line, sizeof line, fp);
224 if(feof(fp)) {
225 break;
226 }
228 char *tok;
229 if(!(tok = strtok(line, SEP))) {
230 continue; // ignore empty lines
231 }
233 int cmd;
234 if((cmd = get_cmd(tok)) == -1) {
235 continue; // ignore unknown commands ...
236 }
238 switch(cmd) {
239 case CMD_V:
240 if(!parse_vec(&vec)) {
241 continue;
242 }
243 obj.v.push_back(vec);
244 break;
246 case CMD_VN:
247 if(!parse_vec(&vec)) {
248 continue;
249 }
250 obj.vn.push_back(vec);
251 break;
253 case CMD_VT:
254 if(!parse_vec(&vec)) {
255 continue;
256 }
257 vec.y = 1.0 - vec.y;
258 obj.vt.push_back(vec);
259 break;
261 case CMD_O:
262 case CMD_G:
263 if(prev_cmd == CMD_O || prev_cmd == CMD_G) {
264 break; // just in case we've got both of them in a row
265 }
266 /* if we have any previous data, group them up, add the object
267 * and continue with the new one...
268 */
269 if(!obj.f.empty()) {
270 Mesh *mesh = cons_mesh(&obj);
271 mesh->matid = matnames[obj.cur_mat];
272 add_mesh(mesh);
273 obj_added++;
275 obj.f.clear(); // clean the face list
276 }
277 if((tok = strtok(0, SEP))) {
278 obj.cur_obj = tok;
279 } else {
280 sprintf(cur_name, "default%02d.obj", seq++);
281 obj.cur_obj = cur_name;
282 }
283 break;
285 case CMD_MTLLIB:
286 if((tok = strtok(0, SEP))) {
287 char path[PATH_MAX];
289 sprintf(path, ".:%s", dirname(tok));
290 if(!find_file(path, PATH_MAX, tok, path)) {
291 fprintf(stderr, "material library not found: %s\n", tok);
292 continue;
293 }
295 FILE *mfile;
296 if(!(mfile = fopen(path, "rb"))) {
297 fprintf(stderr, "failed to open material library: %s\n", path);
298 continue;
299 }
301 // load all materials of the mtl file into a vector
302 vector<obj_mat> vmtl;
303 if(!read_materials(mfile, &vmtl)) {
304 continue;
305 }
306 fclose(mfile);
308 // and add them all to the scene
309 for(size_t i=0; i<vmtl.size(); i++) {
310 Material mat;
312 mat.kd[0] = vmtl[i].diffuse.x;
313 mat.kd[1] = vmtl[i].diffuse.y;
314 mat.kd[2] = vmtl[i].diffuse.z;
316 mat.ks[0] = vmtl[i].specular.x;
317 mat.ks[1] = vmtl[i].specular.y;
318 mat.ks[2] = vmtl[i].specular.z;
320 mat.kt = 1.0 - vmtl[i].alpha;
321 mat.kr = vmtl[i].refl;
322 mat.spow = vmtl[i].shininess;
324 matlib.push_back(mat);
325 matnames[vmtl[i].name] = i;
326 }
327 }
328 break;
330 case CMD_USEMTL:
331 if((tok = strtok(0, SEP))) {
332 obj.cur_mat = tok;
333 } else {
334 obj.cur_mat = "";
335 }
336 break;
338 case CMD_F:
339 if(!parse_face(&face)) {
340 continue;
341 }
343 // convert negative indices to regular indices
344 for(int i=0; i<4; i++) {
345 if(face.v[i] < 0 && face.v[i] != INVALID_IDX) {
346 face.v[i] = obj.v.size() + face.v[i];
347 }
348 if(face.n[i] < 0 && face.n[i] != INVALID_IDX) {
349 face.n[i] = obj.vn.size() + face.n[i];
350 }
351 if(face.t[i] < 0 && face.t[i] != INVALID_IDX) {
352 face.t[i] = obj.vt.size() + face.t[i];
353 }
354 }
356 // break quads into triangles if needed
357 obj.f.push_back(face);
358 if(face.elem == 4) {
359 face.v[1] = face.v[2];
360 face.n[1] = face.n[2];
361 face.t[1] = face.t[2];
363 face.v[2] = face.v[3];
364 face.n[2] = face.n[3];
365 face.t[2] = face.t[3];
367 obj.f.push_back(face);
368 }
369 break;
371 default:
372 break; // ignore unknown commands
373 }
375 prev_cmd = cmd;
376 }
378 // reached end of file...
379 if(!obj.f.empty()) {
380 Mesh *mesh = cons_mesh(&obj);
381 mesh->matid = matnames[obj.cur_mat];
382 add_mesh(mesh);
383 obj_added++;
384 }
386 return obj_added > 0;
387 }
389 static Mesh *cons_mesh(obj_file *obj)
390 {
391 Mesh *mesh;
393 // need at least one of each element
394 bool added_norm = false, added_tc = false;
395 if(obj->vn.empty()) {
396 obj->vn.push_back(Vector3(0, 0, 0));
397 added_norm = true;
398 }
399 if(obj->vt.empty()) {
400 obj->vt.push_back(Vector3(0, 0, 0));
401 added_tc = true;
402 }
404 mesh = new Mesh;
406 for(size_t i=0; i<obj->f.size(); i++) {
407 Face face;
409 for(int j=0; j<3; j++) {
410 obj_face *f = &obj->f[i];
412 face.v[j].pos[0] = obj->v[f->v[j]].x;
413 face.v[j].pos[1] = obj->v[f->v[j]].y;
414 face.v[j].pos[2] = obj->v[f->v[j]].z;
415 face.v[j].pos[3] = 0.0;
417 int nidx = f->n[j] < 0 ? 0 : f->n[j];
418 face.v[j].normal[0] = obj->vn[nidx].x;
419 face.v[j].normal[1] = obj->vn[nidx].y;
420 face.v[j].normal[2] = obj->vn[nidx].z;
421 face.v[j].normal[3] = 0.0;
423 int tidx = f->t[j] < 0 ? 0 : f->t[j];
424 face.v[j].tex[0] = obj->vt[tidx].x;
425 face.v[j].tex[1] = obj->vt[tidx].y;
426 }
428 face.normal[0] = face.v[0].normal[0];
429 face.normal[1] = face.v[1].normal[1];
430 face.normal[2] = face.v[2].normal[2];
431 face.normal[3] = 0.0;
433 mesh->faces.push_back(face);
434 }
436 if(added_norm) {
437 obj->vn.pop_back();
438 }
439 if(added_tc) {
440 obj->vt.pop_back();
441 }
443 return mesh;
444 }
446 static bool read_materials(FILE *fp, vector<obj_mat> *vmtl)
447 {
448 obj_mat mat;
450 for(;;) {
451 char line[BUF_SZ];
452 fgets(line, sizeof line, fp);
453 if(feof(fp)) {
454 break;
455 }
457 char *tok;
458 if(!(tok = strtok(line, SEP))) {
459 continue;
460 }
462 int cmd;
463 if((cmd = get_cmd(tok)) == -1) {
464 continue;
465 }
467 switch(cmd) {
468 case CMD_NEWMTL:
469 // add the previous material, and start a new one
470 if(mat.name.length() > 0) {
471 printf("Adding material: %s\n", mat.name.c_str());
472 vmtl->push_back(mat);
473 mat.reset();
474 }
475 if((tok = strtok(0, SEP))) {
476 mat.name = tok;
477 }
478 break;
480 case CMD_KA:
481 parse_color(&mat.ambient);
482 break;
484 case CMD_KD:
485 parse_color(&mat.diffuse);
486 break;
488 case CMD_KS:
489 parse_color(&mat.specular);
490 break;
492 case CMD_KR:
493 if((tok = strtok(0, SEP)) && is_float(tok)) {
494 mat.refl = atof(tok);
495 }
496 break;
498 case CMD_NS:
499 if((tok = strtok(0, SEP)) && is_float(tok)) {
500 mat.shininess = atof(tok);
501 }
502 break;
504 case CMD_NI:
505 if((tok = strtok(0, SEP)) && is_float(tok)) {
506 mat.ior = atof(tok);
507 }
508 break;
510 case CMD_D:
511 case CMD_TR:
512 {
513 Vector3 c;
514 if(parse_color(&c)) {
515 mat.alpha = cmd == CMD_D ? c.x : 1.0 - c.x;
516 }
517 }
518 break;
520 case CMD_MAP_KD:
521 mat.tex_dif = parse_map();
522 break;
524 default:
525 break;
526 }
527 }
529 if(mat.name.length() > 0) {
530 printf("Adding material: %s\n", mat.name.c_str());
531 vmtl->push_back(mat);
532 }
533 return true;
534 }
536 static int get_cmd(char *str)
537 {
538 char *s = str;
539 while((*s = toupper(*s))) s++;
541 for(int i=0; cmd_names[i]; i++) {
542 if(strcmp(str, cmd_names[i]) == 0) {
543 return i;
544 }
545 }
546 return CMD_UNK;
547 }
549 static bool is_int(const char *str)
550 {
551 char *tmp;
552 strtol(str, &tmp, 10);
553 return tmp != str;
554 }
556 static bool is_float(const char *str)
557 {
558 char *tmp;
559 strtod(str, &tmp);
560 return tmp != str;
561 }
563 static bool parse_vec(Vector3 *vec)
564 {
565 for(int i=0; i<3; i++) {
566 char *tok;
568 if(!(tok = strtok(0, SEP)) || !is_float(tok)) {
569 if(i < 2) {
570 return false;
571 }
572 vec->z = 0.0;
573 } else {
574 float v = atof(tok);
576 switch(i) {
577 case 0:
578 vec->x = v;
579 break;
580 case 1:
581 vec->y = v;
582 break;
583 case 2:
584 vec->z = v;
585 break;
586 }
587 }
588 }
589 return true;
590 }
592 static bool parse_color(Vector3 *col)
593 {
594 for(int i=0; i<3; i++) {
595 char *tok;
597 if(!(tok = strtok(0, SEP)) || !is_float(tok)) {
598 col->y = col->z = col->x;
599 return i > 0 ? true : false;
600 }
602 float v = atof(tok);
603 switch(i) {
604 case 0:
605 col->x = v;
606 break;
607 case 1:
608 col->y = v;
609 break;
610 case 2:
611 col->z = v;
612 break;
613 }
614 }
615 return true;
616 }
618 static bool parse_face(obj_face *face)
619 {
620 char *tok[] = {0, 0, 0, 0};
621 face->elem = 0;
623 for(int i=0; i<4; i++) {
624 if((!(tok[i] = strtok(0, SEP)) || !is_int(tok[i]))) {
625 if(i < 3) return false; // less than 3 verts? not a polygon
626 } else {
627 face->elem++;
628 }
629 }
631 for(int i=0; i<4; i++) {
632 char *subtok = tok[i];
634 if(!subtok || !*subtok || !is_int(subtok)) {
635 if(i < 3) {
636 return false;
637 }
638 face->v[i] = INVALID_IDX;
639 } else {
640 face->v[i] = atoi(subtok);
641 if(face->v[i] > 0) face->v[i]--; /* convert to 0-based */
642 }
644 while(subtok && *subtok && *subtok != '/') {
645 subtok++;
646 }
647 if(subtok && *subtok && *++subtok && is_int(subtok)) {
648 face->t[i] = atoi(subtok);
649 if(face->t[i] > 0) face->t[i]--; /* convert to 0-based */
650 } else {
651 face->t[i] = INVALID_IDX;
652 }
654 while(subtok && *subtok && *subtok != '/') {
655 subtok++;
656 }
657 if(subtok && *subtok && *++subtok && is_int(subtok)) {
658 face->n[i] = atoi(subtok);
659 if(face->n[i] > 0) face->n[i]--; /* convert to 0-based */
660 } else {
661 face->n[i] = INVALID_IDX;
662 }
663 }
665 return true;
666 }
668 static const char *parse_map()
669 {
670 char *tok, *prev = 0;
672 while((tok = strtok(0, SEP))) {
673 prev = tok;
674 }
676 return prev ? prev : "";
677 }
679 static bool find_file(char *res, int sz, const char *fname, const char *path, const char *mode)
680 {
681 FILE *fp;
682 const char *beg, *end;
683 int fnamelen = strlen(fname);
685 beg = path;
686 while(beg && *beg) {
687 end = beg;
688 while(*end && *end != ':') {
689 end++;
690 }
692 int res_len = end - beg;
693 char *pathname = (char*)alloca(res_len + fnamelen + 2);
694 memcpy(pathname, beg, res_len);
695 pathname[res_len] = 0;
696 if(res_len) {
697 strcat(pathname, "/");
698 }
699 strcat(pathname, fname);
701 if((fp = fopen(pathname, mode))) {
702 fclose(fp);
703 strncpy(res, pathname, sz);
704 return true;
705 }
707 beg += res_len;
708 if(*beg == ':') beg++;
709 }
710 return false;
711 }
713 static const char *dirname(const char *str)
714 {
715 static char buf[PATH_MAX];
717 if(!str || !*str) {
718 strcpy(buf, ".");
719 } else {
720 strncpy(buf, str, PATH_MAX);
721 char *ptr = strrchr(buf, '/');
723 if(ptr && *ptr) {
724 *ptr = 0;
725 } else {
726 strcpy(buf, ".");
727 }
728 }
729 return buf;
730 }