clray

view src/mesh.cc @ 21:bd6c2b25f6e7

fixed, now we need to start with optimizations
author John Tsiombikas <nuclear@member.fsf.org>
date Tue, 10 Aug 2010 07:24:18 +0100
parents 63a6b46f58a0
children
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; }
63 void normalize() { float len = sqrt(x * x + y * y + z * z); x /= len; y /= len; z /= len; }
64 };
66 struct Vector2 {
67 float x, y;
69 Vector2() { x = y = 0.0; }
70 Vector2(float a, float b) { x = a; y = b; }
71 };
73 struct obj_face {
74 int elem;
75 int v[4], n[4], t[4];
76 };
78 struct obj_file {
79 string cur_obj, cur_mat;
80 vector<Vector3> v, vn, vt;
81 vector<obj_face> f;
82 };
84 struct obj_mat {
85 string name; // newmtl <name>
86 Vector3 ambient, diffuse, specular; // Ka, Kd, Ks
87 float shininess; // Ns
88 float ior; // Ni
89 float alpha; // d, Tr
90 float refl; // Kr (my extesnsion)
92 string tex_dif, tex_spec, tex_shin, tex_alpha; // map_Kd, map_Ks, map_Ns, map_d
93 string tex_refl; // refl -type sphere|cube file
94 string tex_bump; // bump
96 obj_mat() { reset(); }
98 void reset() {
99 ambient = diffuse = Vector3(0.5, 0.5, 0.5);
100 specular = Vector3(0.0, 0.0, 0.0);
101 name = tex_dif = tex_spec = tex_shin = tex_alpha = tex_refl = tex_bump = "";
102 shininess = 0;
103 ior = alpha = 1;
104 refl = 0.0;
105 }
106 };
108 static bool read_materials(FILE *fp, vector<obj_mat> *vmtl);
109 static Mesh *cons_mesh(obj_file *obj);
111 static int get_cmd(char *str);
112 static bool is_int(const char *str);
113 static bool is_float(const char *str);
114 static bool parse_vec(Vector3 *vec);
115 static bool parse_color(Vector3 *col);
116 static bool parse_face(obj_face *face);
117 static const char *parse_map();
119 static bool find_file(char *res, int sz, const char *fname, const char *path = ".", const char *mode = "rb");
120 static const char *dirname(const char *str);
122 static Vector3 operator -(const Vector3 &a, const Vector3 &b);
123 static Vector3 cross(const Vector3 &a, const Vector3 &b);
125 static map<string, int> matnames;
128 #define FEQ(a, b) (fabs((a) - (b)) < 1e-8)
129 bool Face::operator ==(const Face &f) const
130 {
131 for(int i=0; i<3; i++) {
132 for(int j=0; j<3; j++) {
133 if(!FEQ(v[i].pos[j], f.v[i].pos[j])) {
134 return false;
135 }
136 if(!FEQ(v[i].normal[j], f.v[i].normal[j])) {
137 return false;
138 }
139 }
140 if(!FEQ(normal[i], f.normal[i])) {
141 return false;
142 }
143 }
144 return true;
145 }
147 bool Scene::add_mesh(Mesh *m)
148 {
149 // make sure triangles have material ids
150 for(size_t i=0; i<m->faces.size(); i++) {
151 m->faces[i].matid = m->matid;
152 }
153 meshes.push_back(m);
154 return true;
155 }
157 int Scene::get_num_meshes() const
158 {
159 return (int)meshes.size();
160 }
162 int Scene::get_num_faces() const
163 {
164 int num_faces = 0;
165 for(size_t i=0; i<meshes.size(); i++) {
166 num_faces += meshes[i]->faces.size();
167 }
168 return num_faces;
169 }
171 int Scene::get_num_materials() const
172 {
173 return (int)matlib.size();
174 }
176 Material *Scene::get_materials()
177 {
178 if(matlib.empty()) {
179 return 0;
180 }
181 return &matlib[0];
182 }
184 const Material *Scene::get_materials() const
185 {
186 if(matlib.empty()) {
187 return 0;
188 }
189 return &matlib[0];
190 }
193 #define INVALID_IDX INT_MIN
195 #define SEP " \t\n\r\v"
196 #define BUF_SZ 512
198 bool Scene::load(const char *fname)
199 {
200 FILE *fp;
202 if(!(fp = fopen(fname, "rb"))) {
203 fprintf(stderr, "failed to open %s: %s\n", fname, strerror(errno));
204 return false;
205 }
207 bool res = load(fp);
208 fclose(fp);
209 return res;
210 }
212 bool Scene::load(FILE *fp)
213 {
214 static int seq;
215 char cur_name[16];
217 obj_file obj;
219 sprintf(cur_name, "default%02d.obj", seq++);
220 obj.cur_obj = cur_name;
222 int prev_cmd = 0, obj_added = 0;
223 for(;;) {
224 Vector3 vec;
225 obj_face face;
227 char line[BUF_SZ];
228 fgets(line, sizeof line, fp);
229 if(feof(fp)) {
230 break;
231 }
233 char *tok;
234 if(!(tok = strtok(line, SEP))) {
235 continue; // ignore empty lines
236 }
238 int cmd;
239 if((cmd = get_cmd(tok)) == -1) {
240 continue; // ignore unknown commands ...
241 }
243 switch(cmd) {
244 case CMD_V:
245 if(!parse_vec(&vec)) {
246 continue;
247 }
248 obj.v.push_back(vec);
249 break;
251 case CMD_VN:
252 if(!parse_vec(&vec)) {
253 continue;
254 }
255 obj.vn.push_back(vec);
256 break;
258 case CMD_VT:
259 if(!parse_vec(&vec)) {
260 continue;
261 }
262 vec.y = 1.0 - vec.y;
263 obj.vt.push_back(vec);
264 break;
266 case CMD_O:
267 case CMD_G:
268 if(prev_cmd == CMD_O || prev_cmd == CMD_G) {
269 break; // just in case we've got both of them in a row
270 }
271 /* if we have any previous data, group them up, add the object
272 * and continue with the new one...
273 */
274 if(!obj.f.empty()) {
275 Mesh *mesh = cons_mesh(&obj);
276 mesh->matid = matnames[obj.cur_mat];
277 add_mesh(mesh);
278 obj_added++;
280 obj.f.clear(); // clean the face list
281 }
282 if((tok = strtok(0, SEP))) {
283 obj.cur_obj = tok;
284 } else {
285 sprintf(cur_name, "default%02d.obj", seq++);
286 obj.cur_obj = cur_name;
287 }
288 break;
290 case CMD_MTLLIB:
291 if((tok = strtok(0, SEP))) {
292 char path[PATH_MAX];
294 sprintf(path, ".:%s", dirname(tok));
295 if(!find_file(path, PATH_MAX, tok, path)) {
296 fprintf(stderr, "material library not found: %s\n", tok);
297 continue;
298 }
300 FILE *mfile;
301 if(!(mfile = fopen(path, "rb"))) {
302 fprintf(stderr, "failed to open material library: %s\n", path);
303 continue;
304 }
306 // load all materials of the mtl file into a vector
307 vector<obj_mat> vmtl;
308 if(!read_materials(mfile, &vmtl)) {
309 continue;
310 }
311 fclose(mfile);
313 // and add them all to the scene
314 for(size_t i=0; i<vmtl.size(); i++) {
315 Material mat;
317 mat.kd[0] = vmtl[i].diffuse.x;
318 mat.kd[1] = vmtl[i].diffuse.y;
319 mat.kd[2] = vmtl[i].diffuse.z;
321 mat.ks[0] = vmtl[i].specular.x;
322 mat.ks[1] = vmtl[i].specular.y;
323 mat.ks[2] = vmtl[i].specular.z;
325 mat.kt = 1.0 - vmtl[i].alpha;
326 mat.kr = vmtl[i].refl;
327 mat.spow = vmtl[i].shininess;
329 matlib.push_back(mat);
330 matnames[vmtl[i].name] = i;
331 }
332 }
333 break;
335 case CMD_USEMTL:
336 if((tok = strtok(0, SEP))) {
337 obj.cur_mat = tok;
338 } else {
339 obj.cur_mat = "";
340 }
341 break;
343 case CMD_F:
344 if(!parse_face(&face)) {
345 continue;
346 }
348 // convert negative indices to regular indices
349 for(int i=0; i<4; i++) {
350 if(face.v[i] < 0 && face.v[i] != INVALID_IDX) {
351 face.v[i] = obj.v.size() + face.v[i];
352 }
353 if(face.n[i] < 0 && face.n[i] != INVALID_IDX) {
354 face.n[i] = obj.vn.size() + face.n[i];
355 }
356 if(face.t[i] < 0 && face.t[i] != INVALID_IDX) {
357 face.t[i] = obj.vt.size() + face.t[i];
358 }
359 }
361 // break quads into triangles if needed
362 obj.f.push_back(face);
363 if(face.elem == 4) {
364 face.v[1] = face.v[2];
365 face.n[1] = face.n[2];
366 face.t[1] = face.t[2];
368 face.v[2] = face.v[3];
369 face.n[2] = face.n[3];
370 face.t[2] = face.t[3];
372 obj.f.push_back(face);
373 }
374 break;
376 default:
377 break; // ignore unknown commands
378 }
380 prev_cmd = cmd;
381 }
383 // reached end of file...
384 if(!obj.f.empty()) {
385 Mesh *mesh = cons_mesh(&obj);
386 mesh->matid = matnames[obj.cur_mat];
387 add_mesh(mesh);
388 obj_added++;
389 }
391 return obj_added > 0;
392 }
394 static Mesh *cons_mesh(obj_file *obj)
395 {
396 Mesh *mesh;
398 // need at least one of each element
399 bool added_norm = false, added_tc = false;
400 if(obj->vn.empty()) {
401 obj->vn.push_back(Vector3(0, 0, 0));
402 added_norm = true;
403 }
404 if(obj->vt.empty()) {
405 obj->vt.push_back(Vector3(0, 0, 0));
406 added_tc = true;
407 }
409 mesh = new Mesh;
411 for(size_t i=0; i<obj->f.size(); i++) {
412 Face face;
413 Vector3 v[3];
415 for(int j=0; j<3; j++) {
416 obj_face *f = &obj->f[i];
418 face.v[j].pos[0] = v[j].x = obj->v[f->v[j]].x;
419 face.v[j].pos[1] = v[j].y = obj->v[f->v[j]].y;
420 face.v[j].pos[2] = v[j].z = obj->v[f->v[j]].z;
421 face.v[j].pos[3] = 0.0;
423 int nidx = f->n[j] < 0 ? 0 : f->n[j];
424 face.v[j].normal[0] = obj->vn[nidx].x;
425 face.v[j].normal[1] = obj->vn[nidx].y;
426 face.v[j].normal[2] = obj->vn[nidx].z;
427 face.v[j].normal[3] = 0.0;
429 int tidx = f->t[j] < 0 ? 0 : f->t[j];
430 face.v[j].tex[0] = obj->vt[tidx].x;
431 face.v[j].tex[1] = obj->vt[tidx].y;
432 }
434 Vector3 a = v[1] - v[0];
435 Vector3 b = v[2] - v[0];
436 Vector3 n = cross(a, b);
437 n.normalize();
439 face.normal[0] = n.x;
440 face.normal[1] = n.y;
441 face.normal[2] = n.z;
442 face.normal[3] = 0.0;
444 mesh->faces.push_back(face);
445 }
447 if(added_norm) {
448 obj->vn.pop_back();
449 }
450 if(added_tc) {
451 obj->vt.pop_back();
452 }
454 return mesh;
455 }
457 static bool read_materials(FILE *fp, vector<obj_mat> *vmtl)
458 {
459 obj_mat mat;
461 for(;;) {
462 char line[BUF_SZ];
463 fgets(line, sizeof line, fp);
464 if(feof(fp)) {
465 break;
466 }
468 char *tok;
469 if(!(tok = strtok(line, SEP))) {
470 continue;
471 }
473 int cmd;
474 if((cmd = get_cmd(tok)) == -1) {
475 continue;
476 }
478 switch(cmd) {
479 case CMD_NEWMTL:
480 // add the previous material, and start a new one
481 if(mat.name.length() > 0) {
482 printf("Adding material: %s\n", mat.name.c_str());
483 vmtl->push_back(mat);
484 mat.reset();
485 }
486 if((tok = strtok(0, SEP))) {
487 mat.name = tok;
488 }
489 break;
491 case CMD_KA:
492 parse_color(&mat.ambient);
493 break;
495 case CMD_KD:
496 parse_color(&mat.diffuse);
497 break;
499 case CMD_KS:
500 parse_color(&mat.specular);
501 break;
503 case CMD_KR:
504 if((tok = strtok(0, SEP)) && is_float(tok)) {
505 mat.refl = atof(tok);
506 }
507 break;
509 case CMD_NS:
510 if((tok = strtok(0, SEP)) && is_float(tok)) {
511 mat.shininess = atof(tok);
512 }
513 break;
515 case CMD_NI:
516 if((tok = strtok(0, SEP)) && is_float(tok)) {
517 mat.ior = atof(tok);
518 }
519 break;
521 case CMD_D:
522 case CMD_TR:
523 {
524 Vector3 c;
525 if(parse_color(&c)) {
526 mat.alpha = cmd == CMD_D ? c.x : 1.0 - c.x;
527 }
528 }
529 break;
531 case CMD_MAP_KD:
532 mat.tex_dif = parse_map();
533 break;
535 default:
536 break;
537 }
538 }
540 if(mat.name.length() > 0) {
541 printf("Adding material: %s\n", mat.name.c_str());
542 vmtl->push_back(mat);
543 }
544 return true;
545 }
547 static int get_cmd(char *str)
548 {
549 char *s = str;
550 while((*s = toupper(*s))) s++;
552 for(int i=0; cmd_names[i]; i++) {
553 if(strcmp(str, cmd_names[i]) == 0) {
554 return i;
555 }
556 }
557 return CMD_UNK;
558 }
560 static bool is_int(const char *str)
561 {
562 char *tmp;
563 strtol(str, &tmp, 10);
564 return tmp != str;
565 }
567 static bool is_float(const char *str)
568 {
569 char *tmp;
570 strtod(str, &tmp);
571 return tmp != str;
572 }
574 static bool parse_vec(Vector3 *vec)
575 {
576 for(int i=0; i<3; i++) {
577 char *tok;
579 if(!(tok = strtok(0, SEP)) || !is_float(tok)) {
580 if(i < 2) {
581 return false;
582 }
583 vec->z = 0.0;
584 } else {
585 float v = atof(tok);
587 switch(i) {
588 case 0:
589 vec->x = v;
590 break;
591 case 1:
592 vec->y = v;
593 break;
594 case 2:
595 vec->z = v;
596 break;
597 }
598 }
599 }
600 return true;
601 }
603 static bool parse_color(Vector3 *col)
604 {
605 for(int i=0; i<3; i++) {
606 char *tok;
608 if(!(tok = strtok(0, SEP)) || !is_float(tok)) {
609 col->y = col->z = col->x;
610 return i > 0 ? true : false;
611 }
613 float v = atof(tok);
614 switch(i) {
615 case 0:
616 col->x = v;
617 break;
618 case 1:
619 col->y = v;
620 break;
621 case 2:
622 col->z = v;
623 break;
624 }
625 }
626 return true;
627 }
629 static bool parse_face(obj_face *face)
630 {
631 char *tok[] = {0, 0, 0, 0};
632 face->elem = 0;
634 for(int i=0; i<4; i++) {
635 if((!(tok[i] = strtok(0, SEP)) || !is_int(tok[i]))) {
636 if(i < 3) return false; // less than 3 verts? not a polygon
637 } else {
638 face->elem++;
639 }
640 }
642 for(int i=0; i<4; i++) {
643 char *subtok = tok[i];
645 if(!subtok || !*subtok || !is_int(subtok)) {
646 if(i < 3) {
647 return false;
648 }
649 face->v[i] = INVALID_IDX;
650 } else {
651 face->v[i] = atoi(subtok);
652 if(face->v[i] > 0) face->v[i]--; /* convert to 0-based */
653 }
655 while(subtok && *subtok && *subtok != '/') {
656 subtok++;
657 }
658 if(subtok && *subtok && *++subtok && is_int(subtok)) {
659 face->t[i] = atoi(subtok);
660 if(face->t[i] > 0) face->t[i]--; /* convert to 0-based */
661 } else {
662 face->t[i] = INVALID_IDX;
663 }
665 while(subtok && *subtok && *subtok != '/') {
666 subtok++;
667 }
668 if(subtok && *subtok && *++subtok && is_int(subtok)) {
669 face->n[i] = atoi(subtok);
670 if(face->n[i] > 0) face->n[i]--; /* convert to 0-based */
671 } else {
672 face->n[i] = INVALID_IDX;
673 }
674 }
676 return true;
677 }
679 static const char *parse_map()
680 {
681 char *tok, *prev = 0;
683 while((tok = strtok(0, SEP))) {
684 prev = tok;
685 }
687 return prev ? prev : "";
688 }
690 static bool find_file(char *res, int sz, const char *fname, const char *path, const char *mode)
691 {
692 FILE *fp;
693 const char *beg, *end;
694 int fnamelen = strlen(fname);
696 beg = path;
697 while(beg && *beg) {
698 end = beg;
699 while(*end && *end != ':') {
700 end++;
701 }
703 int res_len = end - beg;
704 char *pathname = (char*)alloca(res_len + fnamelen + 2);
705 memcpy(pathname, beg, res_len);
706 pathname[res_len] = 0;
707 if(res_len) {
708 strcat(pathname, "/");
709 }
710 strcat(pathname, fname);
712 if((fp = fopen(pathname, mode))) {
713 fclose(fp);
714 strncpy(res, pathname, sz);
715 return true;
716 }
718 beg += res_len;
719 if(*beg == ':') beg++;
720 }
721 return false;
722 }
724 static const char *dirname(const char *str)
725 {
726 static char buf[PATH_MAX];
728 if(!str || !*str) {
729 strcpy(buf, ".");
730 } else {
731 strncpy(buf, str, PATH_MAX);
732 char *ptr = strrchr(buf, '/');
734 if(ptr && *ptr) {
735 *ptr = 0;
736 } else {
737 strcpy(buf, ".");
738 }
739 }
740 return buf;
741 }
743 static Vector3 operator -(const Vector3 &a, const Vector3 &b)
744 {
745 return Vector3(a.x - b.x, a.y - b.y, a.z - b.z);
746 }
748 static Vector3 cross(const Vector3 &a, const Vector3 &b)
749 {
750 Vector3 res;
751 res.x = a.y * b.z - a.z * b.y;
752 res.y = a.z * b.x - a.x * b.z;
753 res.z = a.x * b.y - a.y * b.x;
754 return res;
755 }