clray
view src/mesh.cc @ 13:407935b73af3
bollocks
author | John Tsiombikas <nuclear@member.fsf.org> |
---|---|
date | Wed, 04 Aug 2010 04:51:06 +0100 |
parents | d9a1bab1c3f5 |
children | 29f9330cfa4b |
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_faces() const
129 {
130 int num_faces = 0;
131 for(size_t i=0; i<meshes.size(); i++) {
132 num_faces += meshes[i]->faces.size();
133 }
134 printf("get_num_faces() = %d\n", num_faces);
135 return num_faces;
136 }
139 #define INVALID_IDX INT_MIN
141 #define SEP " \t\n\r\v"
142 #define BUF_SZ 512
144 bool Scene::load(const char *fname)
145 {
146 FILE *fp;
148 if(!(fp = fopen(fname, "rb"))) {
149 fprintf(stderr, "failed to open %s: %s\n", fname, strerror(errno));
150 return false;
151 }
153 bool res = load(fp);
154 fclose(fp);
155 return res;
156 }
158 bool Scene::load(FILE *fp)
159 {
160 static int seq;
161 char cur_name[16];
163 obj_file obj;
165 sprintf(cur_name, "default%02d.obj", seq++);
166 obj.cur_obj = cur_name;
168 int prev_cmd = 0, obj_added = 0;
169 for(;;) {
170 Vector3 vec;
171 obj_face face;
173 char line[BUF_SZ];
174 fgets(line, sizeof line, fp);
175 if(feof(fp)) {
176 break;
177 }
179 char *tok;
180 if(!(tok = strtok(line, SEP))) {
181 continue; // ignore empty lines
182 }
184 int cmd;
185 if((cmd = get_cmd(tok)) == -1) {
186 continue; // ignore unknown commands ...
187 }
189 switch(cmd) {
190 case CMD_V:
191 if(!parse_vec(&vec)) {
192 continue;
193 }
194 obj.v.push_back(vec);
195 break;
197 case CMD_VN:
198 if(!parse_vec(&vec)) {
199 continue;
200 }
201 obj.vn.push_back(vec);
202 break;
204 case CMD_VT:
205 if(!parse_vec(&vec)) {
206 continue;
207 }
208 vec.y = 1.0 - vec.y;
209 obj.vt.push_back(vec);
210 break;
212 case CMD_O:
213 case CMD_G:
214 if(prev_cmd == CMD_O || prev_cmd == CMD_G) {
215 break; // just in case we've got both of them in a row
216 }
217 /* if we have any previous data, group them up, add the object
218 * and continue with the new one...
219 */
220 if(!obj.f.empty()) {
221 Mesh *mesh = cons_mesh(&obj);
222 mesh->matid = matnames[obj.cur_mat];
223 add_mesh(mesh);
224 obj_added++;
226 obj.f.clear(); // clean the face list
227 }
228 if((tok = strtok(0, SEP))) {
229 obj.cur_obj = tok;
230 } else {
231 sprintf(cur_name, "default%02d.obj", seq++);
232 obj.cur_obj = cur_name;
233 }
234 break;
236 case CMD_MTLLIB:
237 if((tok = strtok(0, SEP))) {
238 char path[PATH_MAX];
240 sprintf(path, ".:%s", dirname(tok));
241 if(!find_file(path, PATH_MAX, tok, path)) {
242 fprintf(stderr, "material library not found: %s\n", tok);
243 continue;
244 }
246 FILE *mfile;
247 if(!(mfile = fopen(path, "rb"))) {
248 fprintf(stderr, "failed to open material library: %s\n", path);
249 continue;
250 }
252 // load all materials of the mtl file into a vector
253 vector<obj_mat> vmtl;
254 if(!read_materials(mfile, &vmtl)) {
255 continue;
256 }
257 fclose(mfile);
259 // and add them all to the scene
260 for(size_t i=0; i<vmtl.size(); i++) {
261 Material mat;
263 mat.kd[0] = vmtl[i].diffuse.x;
264 mat.kd[1] = vmtl[i].diffuse.y;
265 mat.kd[2] = vmtl[i].diffuse.z;
267 mat.ks[0] = vmtl[i].specular.x;
268 mat.ks[1] = vmtl[i].specular.y;
269 mat.ks[2] = vmtl[i].specular.z;
271 mat.kt = 1.0 - vmtl[i].alpha;
272 mat.kr = 0.0; // TODO
273 mat.spow = vmtl[i].shininess;
275 matlib.push_back(mat);
276 matnames[vmtl[i].name] = i;
277 }
278 }
279 break;
281 case CMD_USEMTL:
282 if((tok = strtok(0, SEP))) {
283 obj.cur_mat = tok;
284 } else {
285 obj.cur_mat = "";
286 }
287 break;
289 case CMD_F:
290 if(!parse_face(&face)) {
291 continue;
292 }
294 // convert negative indices to regular indices
295 for(int i=0; i<4; i++) {
296 if(face.v[i] < 0 && face.v[i] != INVALID_IDX) {
297 face.v[i] = obj.v.size() + face.v[i];
298 }
299 if(face.n[i] < 0 && face.n[i] != INVALID_IDX) {
300 face.n[i] = obj.vn.size() + face.n[i];
301 }
302 if(face.t[i] < 0 && face.t[i] != INVALID_IDX) {
303 face.t[i] = obj.vt.size() + face.t[i];
304 }
305 }
307 // break quads into triangles if needed
308 obj.f.push_back(face);
309 if(face.elem == 4) {
310 face.v[1] = face.v[2];
311 face.n[1] = face.n[2];
312 face.t[1] = face.t[2];
314 face.v[2] = face.v[3];
315 face.n[2] = face.n[3];
316 face.t[2] = face.t[3];
318 obj.f.push_back(face);
319 }
320 break;
322 default:
323 break; // ignore unknown commands
324 }
326 prev_cmd = cmd;
327 }
329 // reached end of file...
330 if(!obj.f.empty()) {
331 Mesh *mesh = cons_mesh(&obj);
332 mesh->matid = matnames[obj.cur_mat];
333 add_mesh(mesh);
334 obj_added++;
335 }
337 return obj_added > 0;
338 }
340 static Mesh *cons_mesh(obj_file *obj)
341 {
342 Mesh *mesh;
344 // need at least one of each element
345 bool added_norm = false, added_tc = false;
346 if(obj->vn.empty()) {
347 obj->vn.push_back(Vector3(0, 0, 0));
348 added_norm = true;
349 }
350 if(obj->vt.empty()) {
351 obj->vt.push_back(Vector3(0, 0, 0));
352 added_tc = true;
353 }
355 mesh = new Mesh;
357 for(size_t i=0; i<obj->f.size(); i++) {
358 Face face;
360 for(int j=0; j<3; j++) {
361 obj_face *f = &obj->f[i];
363 face.v[j].pos[0] = obj->v[f->v[j]].x;
364 face.v[j].pos[1] = obj->v[f->v[j]].y;
365 face.v[j].pos[2] = obj->v[f->v[j]].z;
366 face.v[j].pos[3] = 0.0;
368 int nidx = f->n[j] < 0 ? 0 : f->n[j];
369 face.v[j].normal[0] = obj->vn[nidx].x;
370 face.v[j].normal[1] = obj->vn[nidx].y;
371 face.v[j].normal[2] = obj->vn[nidx].z;
372 face.v[j].normal[3] = 0.0;
374 int tidx = f->t[j] < 0 ? 0 : f->t[j];
375 face.v[j].tex[0] = obj->vt[tidx].x;
376 face.v[j].tex[1] = obj->vt[tidx].y;
377 }
379 face.normal[0] = face.v[0].normal[0];
380 face.normal[1] = face.v[1].normal[1];
381 face.normal[2] = face.v[2].normal[2];
382 face.normal[3] = 0.0;
384 mesh->faces.push_back(face);
385 }
387 if(added_norm) {
388 obj->vn.pop_back();
389 }
390 if(added_tc) {
391 obj->vt.pop_back();
392 }
394 return mesh;
395 }
397 static bool read_materials(FILE *fp, vector<obj_mat> *vmtl)
398 {
399 obj_mat mat;
401 for(;;) {
402 char line[BUF_SZ];
403 fgets(line, sizeof line, fp);
404 if(feof(fp)) {
405 break;
406 }
408 char *tok;
409 if(!(tok = strtok(line, SEP))) {
410 continue;
411 }
413 int cmd;
414 if((cmd = get_cmd(tok)) == -1) {
415 continue;
416 }
418 switch(cmd) {
419 case CMD_NEWMTL:
420 // add the previous material, and start a new one
421 if(mat.name.length() > 0) {
422 printf("Adding material: %s\n", mat.name.c_str());
423 vmtl->push_back(mat);
424 mat.reset();
425 }
426 if((tok = strtok(0, SEP))) {
427 mat.name = tok;
428 }
429 break;
431 case CMD_KA:
432 parse_color(&mat.ambient);
433 break;
435 case CMD_KD:
436 parse_color(&mat.diffuse);
437 break;
439 case CMD_KS:
440 parse_color(&mat.specular);
441 break;
443 case CMD_NS:
444 if((tok = strtok(0, SEP)) && is_float(tok)) {
445 mat.shininess = atof(tok);
446 }
447 break;
449 case CMD_NI:
450 if((tok = strtok(0, SEP)) && is_float(tok)) {
451 mat.ior = atof(tok);
452 }
453 break;
455 case CMD_D:
456 case CMD_TR:
457 {
458 Vector3 c;
459 if(parse_color(&c)) {
460 mat.alpha = cmd == CMD_D ? c.x : 1.0 - c.x;
461 }
462 }
463 break;
465 case CMD_MAP_KD:
466 mat.tex_dif = parse_map();
467 break;
469 default:
470 break;
471 }
472 }
474 if(mat.name.length() > 0) {
475 printf("Adding material: %s\n", mat.name.c_str());
476 vmtl->push_back(mat);
477 }
478 return true;
479 }
481 static int get_cmd(char *str)
482 {
483 char *s = str;
484 while((*s = toupper(*s))) s++;
486 for(int i=0; cmd_names[i]; i++) {
487 if(strcmp(str, cmd_names[i]) == 0) {
488 return i;
489 }
490 }
491 return CMD_UNK;
492 }
494 static bool is_int(const char *str)
495 {
496 char *tmp;
497 strtol(str, &tmp, 10);
498 return tmp != str;
499 }
501 static bool is_float(const char *str)
502 {
503 char *tmp;
504 strtod(str, &tmp);
505 return tmp != str;
506 }
508 static bool parse_vec(Vector3 *vec)
509 {
510 for(int i=0; i<3; i++) {
511 char *tok;
513 if(!(tok = strtok(0, SEP)) || !is_float(tok)) {
514 if(i < 2) {
515 return false;
516 }
517 vec->z = 0.0;
518 } else {
519 float v = atof(tok);
521 switch(i) {
522 case 0:
523 vec->x = v;
524 break;
525 case 1:
526 vec->y = v;
527 break;
528 case 2:
529 vec->z = v;
530 break;
531 }
532 }
533 }
534 return true;
535 }
537 static bool parse_color(Vector3 *col)
538 {
539 for(int i=0; i<3; i++) {
540 char *tok;
542 if(!(tok = strtok(0, SEP)) || !is_float(tok)) {
543 col->y = col->z = col->x;
544 return i > 0 ? true : false;
545 }
547 float v = atof(tok);
548 switch(i) {
549 case 0:
550 col->x = v;
551 break;
552 case 1:
553 col->y = v;
554 break;
555 case 2:
556 col->z = v;
557 break;
558 }
559 }
560 return true;
561 }
563 static bool parse_face(obj_face *face)
564 {
565 char *tok[] = {0, 0, 0, 0};
566 face->elem = 0;
568 for(int i=0; i<4; i++) {
569 if((!(tok[i] = strtok(0, SEP)) || !is_int(tok[i]))) {
570 if(i < 3) return false; // less than 3 verts? not a polygon
571 } else {
572 face->elem++;
573 }
574 }
576 for(int i=0; i<4; i++) {
577 char *subtok = tok[i];
579 if(!subtok || !*subtok || !is_int(subtok)) {
580 if(i < 3) {
581 return false;
582 }
583 face->v[i] = INVALID_IDX;
584 } else {
585 face->v[i] = atoi(subtok);
586 if(face->v[i] > 0) face->v[i]--; /* convert to 0-based */
587 }
589 while(subtok && *subtok && *subtok != '/') {
590 subtok++;
591 }
592 if(subtok && *subtok && *++subtok && is_int(subtok)) {
593 face->t[i] = atoi(subtok);
594 if(face->t[i] > 0) face->t[i]--; /* convert to 0-based */
595 } else {
596 face->t[i] = INVALID_IDX;
597 }
599 while(subtok && *subtok && *subtok != '/') {
600 subtok++;
601 }
602 if(subtok && *subtok && *++subtok && is_int(subtok)) {
603 face->n[i] = atoi(subtok);
604 if(face->n[i] > 0) face->n[i]--; /* convert to 0-based */
605 } else {
606 face->n[i] = INVALID_IDX;
607 }
608 }
610 return true;
611 }
613 static const char *parse_map()
614 {
615 char *tok, *prev = 0;
617 while((tok = strtok(0, SEP))) {
618 prev = tok;
619 }
621 return prev ? prev : "";
622 }
624 static bool find_file(char *res, int sz, const char *fname, const char *path, const char *mode)
625 {
626 FILE *fp;
627 const char *beg, *end;
628 int fnamelen = strlen(fname);
630 beg = path;
631 while(beg && *beg) {
632 end = beg;
633 while(*end && *end != ':') {
634 end++;
635 }
637 int res_len = end - beg;
638 char *pathname = (char*)alloca(res_len + fnamelen + 2);
639 memcpy(pathname, beg, res_len);
640 pathname[res_len] = 0;
641 strcat(pathname, "/");
642 strcat(pathname, fname);
644 if((fp = fopen(pathname, mode))) {
645 fclose(fp);
646 strncpy(res, pathname, sz);
647 return true;
648 }
650 beg += res_len;
651 }
652 return false;
653 }
655 static const char *dirname(const char *str)
656 {
657 static char buf[PATH_MAX];
659 if(!str || !*str) {
660 strcpy(buf, ".");
661 } else {
662 strncpy(buf, str, PATH_MAX);
663 char *ptr = strrchr(buf, '/');
665 if(ptr && *ptr) *ptr = 0;
666 }
667 return buf;
668 }