clray
view src/mesh.cc @ 6:b06518bb16e9
adding polygon meshes and obj loading
author | John Tsiombikas <nuclear@member.fsf.org> |
---|---|
date | Tue, 20 Jul 2010 20:02:41 +0100 |
parents | |
children | deaf85acf6af |
line source
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <errno.h>
5 #include <string>
6 #include <vector>
7 #include <map>
8 #include "mesh.h"
10 using namespace std;
12 #define COMMANDS \
13 CMD(V), \
14 CMD(VN), \
15 CMD(VT), \
16 CMD(F), \
17 CMD(O), \
18 CMD(G), \
19 CMD(MTLLIB), \
20 CMD(USEMTL), \
21 CMD(NEWMTL), \
22 CMD(KA), \
23 CMD(KD), \
24 CMD(KS), \
25 CMD(NS), \
26 CMD(NI), \
27 CMD(D), \
28 CMD(TR), \
29 CMD(MAP_KD), \
30 CMD(MAP_KS), \
31 CMD(MAP_NS), \
32 CMD(MAP_D), \
33 CMD(REFL), \
34 CMD(BUMP)
36 #define CMD(x) CMD_##x
37 enum {
38 COMMANDS,
39 CMD_UNK
40 };
41 #undef CMD
43 #define CMD(x) #x
44 static const char *cmd_names[] = {
45 COMMANDS,
46 0
47 };
48 #undef CMD
50 struct Vector3 {
51 float x, y, z;
53 Vector3() { x = y = z = 0.0; }
54 Vector3(float a, float b, float c) { x = a; y = b; z = c; }
55 };
57 struct Vector2 {
58 float x, y;
60 Vector2() { x = y = 0.0; }
61 Vector2(float a, float b) { x = a; y = b; }
62 };
64 struct obj_face {
65 int elem;
66 int v[4], n[4], t[4];
67 };
69 struct obj_file {
70 string cur_obj, cur_mat;
71 vector<Vector3> v, vn, vt;
72 vector<obj_face> f;
73 };
75 struct obj_mat {
76 string name; // newmtl <name>
77 Vector3 ambient, diffuse, specular; // Ka, Kd, Ks
78 float shininess; // Ns
79 float ior; // Ni
80 float alpha; // d, Tr
82 string tex_dif, tex_spec, tex_shin, tex_alpha; // map_Kd, map_Ks, map_Ns, map_d
83 string tex_refl; // refl -type sphere|cube file
84 string tex_bump; // bump
86 obj_mat() { reset(); }
88 void reset() {
89 ambient = diffuse = Vector3(0.5, 0.5, 0.5);
90 specular = Vector3(0.0, 0.0, 0.0);
91 name = tex_dif = tex_spec = tex_shin = tex_alpha = tex_refl = tex_bump = "";
92 shininess = 0;
93 ior = alpha = 1;
94 }
95 };
97 static bool read_materials(FILE *fp, vector<obj_mat> *vmtl);
98 static Mesh *cons_mesh(obj_file *obj);
100 static int get_cmd(char *str);
101 static bool is_int(const char *str);
102 static bool is_float(const char *str);
103 static bool parse_vec(Vector3 *vec);
104 static bool parse_color(Vector3 *col);
105 static bool parse_face(obj_face *face);
106 static const char *parse_map();
108 static bool find_file(char *res, int sz, const char *fname, const char *path = ".", const char *mode = "rb");
109 static const char *dirname(const char *str);
111 static map<string, int> matnames;
114 #define INVALID_IDX INT_MIN
116 #define SEP " \t\n\r\v"
117 #define BUF_SZ 512
119 bool Scene::load(const char *fname)
120 {
121 FILE *fp;
123 if(!(fp = fopen(fname, "rb"))) {
124 fprintf(stderr, "failed to open %s: %s\n", fname, strerror(errno));
125 return false;
126 }
128 bool res = load(fp);
129 fclose(fp);
130 return res;
131 }
133 bool Scene::load(FILE *fp)
134 {
135 static int seq;
136 char cur_name[16];
138 obj_file obj;
140 sprintf(cur_name, "default%02d.obj", seq++);
141 obj.cur_obj = cur_name;
143 int prev_cmd = 0, obj_added = 0;
144 for(;;) {
145 Vector3 vec;
146 obj_face face;
148 char line[BUF_SZ];
149 fgets(line, sizeof line, fp);
150 if(feof(fp)) {
151 break;
152 }
154 char *tok;
155 if(!(tok = strtok(line, SEP))) {
156 continue; // ignore empty lines
157 }
159 int cmd;
160 if((cmd = get_cmd(tok)) == -1) {
161 continue; // ignore unknown commands ...
162 }
164 switch(cmd) {
165 case CMD_V:
166 if(!parse_vec(&vec)) {
167 continue;
168 }
169 obj.v.push_back(vec);
170 break;
172 case CMD_VN:
173 if(!parse_vec(&vec)) {
174 continue;
175 }
176 obj.vn.push_back(vec);
177 break;
179 case CMD_VT:
180 if(!parse_vec(&vec)) {
181 continue;
182 }
183 vec.y = 1.0 - vec.y;
184 obj.vt.push_back(vec);
185 break;
187 case CMD_O:
188 case CMD_G:
189 if(prev_cmd == CMD_O || prev_cmd == CMD_G) {
190 break; // just in case we've got both of them in a row
191 }
192 /* if we have any previous data, group them up, add the object
193 * and continue with the new one...
194 */
195 if(!obj.f.empty()) {
196 Mesh *mesh = cons_mesh(&obj);
197 mesh->matid = matnames[obj.cur_mat];
198 meshes.push_back(mesh);
199 obj_added++;
201 obj.f.clear(); // clean the face list
202 }
203 if((tok = strtok(0, SEP))) {
204 obj.cur_obj = tok;
205 } else {
206 sprintf(cur_name, "default%02d.obj", seq++);
207 obj.cur_obj = cur_name;
208 }
209 break;
211 case CMD_MTLLIB:
212 if((tok = strtok(0, SEP))) {
213 char path[PATH_MAX];
215 sprintf(path, ".:%s", dirname(tok));
216 if(!find_file(path, PATH_MAX, tok, path)) {
217 fprintf(stderr, "material library not found: %s\n", tok);
218 continue;
219 }
221 FILE *mfile;
222 if(!(mfile = fopen(path, "rb"))) {
223 fprintf(stderr, "failed to open material library: %s\n", path);
224 continue;
225 }
227 // load all materials of the mtl file into a vector
228 vector<obj_mat> vmtl;
229 if(!read_materials(mfile, &vmtl)) {
230 continue;
231 }
232 fclose(mfile);
234 // and add them all to the scene
235 for(size_t i=0; i<vmtl.size(); i++) {
236 Material mat;
238 mat.kd[0] = vmtl[i].diffuse.x;
239 mat.kd[1] = vmtl[i].diffuse.y;
240 mat.kd[2] = vmtl[i].diffuse.z;
242 mat.ks[0] = vmtl[i].specular.x;
243 mat.ks[1] = vmtl[i].specular.y;
244 mat.ks[2] = vmtl[i].specular.z;
246 mat.kt = 1.0 - vmtl[i].alpha;
247 mat.kr = 0.0; // TODO
248 mat.spow = vmtl[i].shininess;
250 matnames[vmtl[i].name] = i;
251 }
252 }
253 break;
255 case CMD_USEMTL:
256 if((tok = strtok(0, SEP))) {
257 obj.cur_mat = tok;
258 } else {
259 obj.cur_mat = "";
260 }
261 break;
263 case CMD_F:
264 if(!parse_face(&face)) {
265 continue;
266 }
268 // convert negative indices to regular indices
269 for(int i=0; i<4; i++) {
270 if(face.v[i] < 0 && face.v[i] != INVALID_IDX) {
271 face.v[i] = obj.v.size() + face.v[i];
272 }
273 if(face.n[i] < 0 && face.n[i] != INVALID_IDX) {
274 face.n[i] = obj.vn.size() + face.n[i];
275 }
276 if(face.t[i] < 0 && face.t[i] != INVALID_IDX) {
277 face.t[i] = obj.vt.size() + face.t[i];
278 }
279 }
281 // break quads into triangles if needed
282 obj.f.push_back(face);
283 if(face.elem == 4) {
284 face.v[1] = face.v[2];
285 face.n[1] = face.n[2];
286 face.t[1] = face.t[2];
288 face.v[2] = face.v[3];
289 face.n[2] = face.n[3];
290 face.t[2] = face.t[3];
292 obj.f.push_back(face);
293 }
294 break;
296 default:
297 break; // ignore unknown commands
298 }
300 prev_cmd = cmd;
301 }
303 // reached end of file...
304 if(!obj.f.empty()) {
305 Mesh *mesh = cons_mesh(&obj);
306 mesh->matid = matnames[obj.cur_mat];
307 meshes.push_back(mesh);
308 obj_added++;
309 }
311 return obj_added > 0;
312 }
314 static Mesh *cons_mesh(obj_file *obj)
315 {
316 Mesh *mesh;
318 // need at least one of each element
319 bool added_norm = false, added_tc = false;
320 if(obj->vn.empty()) {
321 obj->vn.push_back(Vector3(0, 0, 0));
322 added_norm = true;
323 }
324 if(obj->vt.empty()) {
325 obj->vt.push_back(Vector3(0, 0, 0));
326 added_tc = true;
327 }
329 mesh = new Mesh;
331 for(size_t i=0; i<obj->f.size(); i++) {
332 Face face;
334 for(int j=0; j<3; j++) {
335 obj_face *f = &obj->f[i];
337 face.v[j].pos[0] = obj->v[f->v[j]].x;
338 face.v[j].pos[1] = obj->v[f->v[j]].y;
339 face.v[j].pos[2] = obj->v[f->v[j]].z;
341 int nidx = f->n[j] < 0 ? 0 : f->n[j];
342 face.v[j].normal[0] = obj->vn[nidx].x;
343 face.v[j].normal[1] = obj->vn[nidx].y;
344 face.v[j].normal[2] = obj->vn[nidx].z;
346 int tidx = f->t[j] < 0 ? 0 : f->t[j];
347 face.v[j].tex[0] = obj->vt[tidx].x;
348 face.v[j].tex[1] = obj->vt[tidx].y;
349 }
350 mesh->faces.push_back(face);
351 }
353 if(added_norm) {
354 obj->vn.pop_back();
355 }
356 if(added_tc) {
357 obj->vt.pop_back();
358 }
360 return mesh;
361 }
363 static bool read_materials(FILE *fp, vector<obj_mat> *vmtl)
364 {
365 obj_mat mat;
367 for(;;) {
368 char line[BUF_SZ];
369 fgets(line, sizeof line, fp);
370 if(feof(fp)) {
371 break;
372 }
374 char *tok;
375 if(!(tok = strtok(line, SEP))) {
376 continue;
377 }
379 int cmd;
380 if((cmd = get_cmd(tok)) == -1) {
381 continue;
382 }
384 switch(cmd) {
385 case CMD_NEWMTL:
386 // add the previous material, and start a new one
387 if(mat.name.length() > 0) {
388 vmtl->push_back(mat);
389 mat.reset();
390 }
391 if((tok = strtok(0, SEP))) {
392 mat.name = tok;
393 }
394 break;
396 case CMD_KA:
397 parse_color(&mat.ambient);
398 break;
400 case CMD_KD:
401 parse_color(&mat.diffuse);
402 break;
404 case CMD_KS:
405 parse_color(&mat.specular);
406 break;
408 case CMD_NS:
409 if((tok = strtok(0, SEP)) && is_float(tok)) {
410 mat.shininess = atof(tok);
411 }
412 break;
414 case CMD_NI:
415 if((tok = strtok(0, SEP)) && is_float(tok)) {
416 mat.ior = atof(tok);
417 }
418 break;
420 case CMD_D:
421 case CMD_TR:
422 {
423 Vector3 c;
424 if(parse_color(&c)) {
425 mat.alpha = cmd == CMD_D ? c.x : 1.0 - c.x;
426 }
427 }
428 break;
430 case CMD_MAP_KD:
431 mat.tex_dif = parse_map();
432 break;
434 default:
435 break;
436 }
437 }
439 if(mat.name.length() > 0) {
440 vmtl->push_back(mat);
441 }
442 return true;
443 }
445 static int get_cmd(char *str)
446 {
447 char *s = str;
448 while((*s = toupper(*s))) s++;
450 for(int i=0; cmd_names[i]; i++) {
451 if(strcmp(str, cmd_names[i]) == 0) {
452 return i;
453 }
454 }
455 return CMD_UNK;
456 }
458 static bool is_int(const char *str)
459 {
460 char *tmp;
461 strtol(str, &tmp, 10);
462 return tmp != str;
463 }
465 static bool is_float(const char *str)
466 {
467 char *tmp;
468 strtod(str, &tmp);
469 return tmp != str;
470 }
472 static bool parse_vec(Vector3 *vec)
473 {
474 for(int i=0; i<3; i++) {
475 char *tok;
477 if(!(tok = strtok(0, SEP)) || !is_float(tok)) {
478 if(i < 2) {
479 return false;
480 }
481 vec->z = 0.0;
482 } else {
483 float v = atof(tok);
485 switch(i) {
486 case 0:
487 vec->x = v;
488 break;
489 case 1:
490 vec->y = v;
491 break;
492 case 2:
493 vec->z = v;
494 break;
495 }
496 }
497 }
498 return true;
499 }
501 static bool parse_color(Vector3 *col)
502 {
503 for(int i=0; i<3; i++) {
504 char *tok;
506 if(!(tok = strtok(0, SEP)) || !is_float(tok)) {
507 col->y = col->z = col->x;
508 return i > 0 ? true : false;
509 }
511 float v = atof(tok);
512 switch(i) {
513 case 0:
514 col->x = v;
515 break;
516 case 1:
517 col->y = v;
518 break;
519 case 2:
520 col->z = v;
521 break;
522 }
523 }
524 return true;
525 }
527 static bool parse_face(obj_face *face)
528 {
529 char *tok[] = {0, 0, 0, 0};
530 face->elem = 0;
532 for(int i=0; i<4; i++) {
533 if((!(tok[i] = strtok(0, SEP)) || !is_int(tok[i]))) {
534 if(i < 3) return false; // less than 3 verts? not a polygon
535 } else {
536 face->elem++;
537 }
538 }
540 for(int i=0; i<4; i++) {
541 char *subtok = tok[i];
543 if(!subtok || !*subtok || !is_int(subtok)) {
544 if(i < 3) {
545 return false;
546 }
547 face->v[i] = INVALID_IDX;
548 } else {
549 face->v[i] = atoi(subtok);
550 if(face->v[i] > 0) face->v[i]--; /* convert to 0-based */
551 }
553 while(subtok && *subtok && *subtok != '/') {
554 subtok++;
555 }
556 if(subtok && *subtok && *++subtok && is_int(subtok)) {
557 face->t[i] = atoi(subtok);
558 if(face->t[i] > 0) face->t[i]--; /* convert to 0-based */
559 } else {
560 face->t[i] = INVALID_IDX;
561 }
563 while(subtok && *subtok && *subtok != '/') {
564 subtok++;
565 }
566 if(subtok && *subtok && *++subtok && is_int(subtok)) {
567 face->n[i] = atoi(subtok);
568 if(face->n[i] > 0) face->n[i]--; /* convert to 0-based */
569 } else {
570 face->n[i] = INVALID_IDX;
571 }
572 }
574 return true;
575 }
577 static const char *parse_map()
578 {
579 char *tok, *prev = 0;
581 while((tok = strtok(0, SEP))) {
582 prev = tok;
583 }
585 return prev ? prev : "";
586 }
588 static bool find_file(char *res, int sz, const char *fname, const char *path, const char *mode)
589 {
590 FILE *fp;
591 const char *beg, *end;
592 int fnamelen = strlen(fname);
594 beg = path;
595 while(beg && *beg) {
596 end = beg;
597 while(*end && *end != ':') {
598 end++;
599 }
601 int sz = end - beg + 1;
602 char *pathname = (char*)alloca(sz + fnamelen + 2);
603 memcpy(pathname, beg, sz);
604 strcat(pathname, "/");
605 strcat(pathname, fname);
607 if((fp = fopen(pathname, mode))) {
608 fclose(fp);
609 strncpy(res, pathname, sz);
610 return true;
611 }
613 beg += sz;
614 }
615 return false;
616 }
618 static const char *dirname(const char *str)
619 {
620 static char buf[PATH_MAX];
622 if(!str || !*str) {
623 strcpy(buf, ".");
624 } else {
625 strncpy(buf, str, PATH_MAX);
626 char *ptr = strrchr(buf, '/');
628 if(*ptr) *ptr = 0;
629 }
630 return buf;
631 }