clray

view src/mesh.cc @ 8:deaf85acf6af

interactive spheres
author John Tsiombikas <nuclear@member.fsf.org>
date Fri, 23 Jul 2010 19:48:43 +0100
parents b06518bb16e9
children d9a1bab1c3f5
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 using namespace std;
13 #define COMMANDS \
14 CMD(V), \
15 CMD(VN), \
16 CMD(VT), \
17 CMD(F), \
18 CMD(O), \
19 CMD(G), \
20 CMD(MTLLIB), \
21 CMD(USEMTL), \
22 CMD(NEWMTL), \
23 CMD(KA), \
24 CMD(KD), \
25 CMD(KS), \
26 CMD(NS), \
27 CMD(NI), \
28 CMD(D), \
29 CMD(TR), \
30 CMD(MAP_KD), \
31 CMD(MAP_KS), \
32 CMD(MAP_NS), \
33 CMD(MAP_D), \
34 CMD(REFL), \
35 CMD(BUMP)
37 #define CMD(x) CMD_##x
38 enum {
39 COMMANDS,
40 CMD_UNK
41 };
42 #undef CMD
44 #define CMD(x) #x
45 static const char *cmd_names[] = {
46 COMMANDS,
47 0
48 };
49 #undef CMD
51 struct Vector3 {
52 float x, y, z;
54 Vector3() { x = y = z = 0.0; }
55 Vector3(float a, float b, float c) { x = a; y = b; z = c; }
56 };
58 struct Vector2 {
59 float x, y;
61 Vector2() { x = y = 0.0; }
62 Vector2(float a, float b) { x = a; y = b; }
63 };
65 struct obj_face {
66 int elem;
67 int v[4], n[4], t[4];
68 };
70 struct obj_file {
71 string cur_obj, cur_mat;
72 vector<Vector3> v, vn, vt;
73 vector<obj_face> f;
74 };
76 struct obj_mat {
77 string name; // newmtl <name>
78 Vector3 ambient, diffuse, specular; // Ka, Kd, Ks
79 float shininess; // Ns
80 float ior; // Ni
81 float alpha; // d, Tr
83 string tex_dif, tex_spec, tex_shin, tex_alpha; // map_Kd, map_Ks, map_Ns, map_d
84 string tex_refl; // refl -type sphere|cube file
85 string tex_bump; // bump
87 obj_mat() { reset(); }
89 void reset() {
90 ambient = diffuse = Vector3(0.5, 0.5, 0.5);
91 specular = Vector3(0.0, 0.0, 0.0);
92 name = tex_dif = tex_spec = tex_shin = tex_alpha = tex_refl = tex_bump = "";
93 shininess = 0;
94 ior = alpha = 1;
95 }
96 };
98 static bool read_materials(FILE *fp, vector<obj_mat> *vmtl);
99 static Mesh *cons_mesh(obj_file *obj);
101 static int get_cmd(char *str);
102 static bool is_int(const char *str);
103 static bool is_float(const char *str);
104 static bool parse_vec(Vector3 *vec);
105 static bool parse_color(Vector3 *col);
106 static bool parse_face(obj_face *face);
107 static const char *parse_map();
109 static bool find_file(char *res, int sz, const char *fname, const char *path = ".", const char *mode = "rb");
110 static const char *dirname(const char *str);
112 static map<string, int> matnames;
115 #define INVALID_IDX INT_MIN
117 #define SEP " \t\n\r\v"
118 #define BUF_SZ 512
120 bool Scene::load(const char *fname)
121 {
122 FILE *fp;
124 if(!(fp = fopen(fname, "rb"))) {
125 fprintf(stderr, "failed to open %s: %s\n", fname, strerror(errno));
126 return false;
127 }
129 bool res = load(fp);
130 fclose(fp);
131 return res;
132 }
134 bool Scene::load(FILE *fp)
135 {
136 static int seq;
137 char cur_name[16];
139 obj_file obj;
141 sprintf(cur_name, "default%02d.obj", seq++);
142 obj.cur_obj = cur_name;
144 int prev_cmd = 0, obj_added = 0;
145 for(;;) {
146 Vector3 vec;
147 obj_face face;
149 char line[BUF_SZ];
150 fgets(line, sizeof line, fp);
151 if(feof(fp)) {
152 break;
153 }
155 char *tok;
156 if(!(tok = strtok(line, SEP))) {
157 continue; // ignore empty lines
158 }
160 int cmd;
161 if((cmd = get_cmd(tok)) == -1) {
162 continue; // ignore unknown commands ...
163 }
165 switch(cmd) {
166 case CMD_V:
167 if(!parse_vec(&vec)) {
168 continue;
169 }
170 obj.v.push_back(vec);
171 break;
173 case CMD_VN:
174 if(!parse_vec(&vec)) {
175 continue;
176 }
177 obj.vn.push_back(vec);
178 break;
180 case CMD_VT:
181 if(!parse_vec(&vec)) {
182 continue;
183 }
184 vec.y = 1.0 - vec.y;
185 obj.vt.push_back(vec);
186 break;
188 case CMD_O:
189 case CMD_G:
190 if(prev_cmd == CMD_O || prev_cmd == CMD_G) {
191 break; // just in case we've got both of them in a row
192 }
193 /* if we have any previous data, group them up, add the object
194 * and continue with the new one...
195 */
196 if(!obj.f.empty()) {
197 Mesh *mesh = cons_mesh(&obj);
198 mesh->matid = matnames[obj.cur_mat];
199 meshes.push_back(mesh);
200 obj_added++;
202 obj.f.clear(); // clean the face list
203 }
204 if((tok = strtok(0, SEP))) {
205 obj.cur_obj = tok;
206 } else {
207 sprintf(cur_name, "default%02d.obj", seq++);
208 obj.cur_obj = cur_name;
209 }
210 break;
212 case CMD_MTLLIB:
213 if((tok = strtok(0, SEP))) {
214 char path[PATH_MAX];
216 sprintf(path, ".:%s", dirname(tok));
217 if(!find_file(path, PATH_MAX, tok, path)) {
218 fprintf(stderr, "material library not found: %s\n", tok);
219 continue;
220 }
222 FILE *mfile;
223 if(!(mfile = fopen(path, "rb"))) {
224 fprintf(stderr, "failed to open material library: %s\n", path);
225 continue;
226 }
228 // load all materials of the mtl file into a vector
229 vector<obj_mat> vmtl;
230 if(!read_materials(mfile, &vmtl)) {
231 continue;
232 }
233 fclose(mfile);
235 // and add them all to the scene
236 for(size_t i=0; i<vmtl.size(); i++) {
237 Material mat;
239 mat.kd[0] = vmtl[i].diffuse.x;
240 mat.kd[1] = vmtl[i].diffuse.y;
241 mat.kd[2] = vmtl[i].diffuse.z;
243 mat.ks[0] = vmtl[i].specular.x;
244 mat.ks[1] = vmtl[i].specular.y;
245 mat.ks[2] = vmtl[i].specular.z;
247 mat.kt = 1.0 - vmtl[i].alpha;
248 mat.kr = 0.0; // TODO
249 mat.spow = vmtl[i].shininess;
251 matnames[vmtl[i].name] = i;
252 }
253 }
254 break;
256 case CMD_USEMTL:
257 if((tok = strtok(0, SEP))) {
258 obj.cur_mat = tok;
259 } else {
260 obj.cur_mat = "";
261 }
262 break;
264 case CMD_F:
265 if(!parse_face(&face)) {
266 continue;
267 }
269 // convert negative indices to regular indices
270 for(int i=0; i<4; i++) {
271 if(face.v[i] < 0 && face.v[i] != INVALID_IDX) {
272 face.v[i] = obj.v.size() + face.v[i];
273 }
274 if(face.n[i] < 0 && face.n[i] != INVALID_IDX) {
275 face.n[i] = obj.vn.size() + face.n[i];
276 }
277 if(face.t[i] < 0 && face.t[i] != INVALID_IDX) {
278 face.t[i] = obj.vt.size() + face.t[i];
279 }
280 }
282 // break quads into triangles if needed
283 obj.f.push_back(face);
284 if(face.elem == 4) {
285 face.v[1] = face.v[2];
286 face.n[1] = face.n[2];
287 face.t[1] = face.t[2];
289 face.v[2] = face.v[3];
290 face.n[2] = face.n[3];
291 face.t[2] = face.t[3];
293 obj.f.push_back(face);
294 }
295 break;
297 default:
298 break; // ignore unknown commands
299 }
301 prev_cmd = cmd;
302 }
304 // reached end of file...
305 if(!obj.f.empty()) {
306 Mesh *mesh = cons_mesh(&obj);
307 mesh->matid = matnames[obj.cur_mat];
308 meshes.push_back(mesh);
309 obj_added++;
310 }
312 return obj_added > 0;
313 }
315 static Mesh *cons_mesh(obj_file *obj)
316 {
317 Mesh *mesh;
319 // need at least one of each element
320 bool added_norm = false, added_tc = false;
321 if(obj->vn.empty()) {
322 obj->vn.push_back(Vector3(0, 0, 0));
323 added_norm = true;
324 }
325 if(obj->vt.empty()) {
326 obj->vt.push_back(Vector3(0, 0, 0));
327 added_tc = true;
328 }
330 mesh = new Mesh;
332 for(size_t i=0; i<obj->f.size(); i++) {
333 Face face;
335 for(int j=0; j<3; j++) {
336 obj_face *f = &obj->f[i];
338 face.v[j].pos[0] = obj->v[f->v[j]].x;
339 face.v[j].pos[1] = obj->v[f->v[j]].y;
340 face.v[j].pos[2] = obj->v[f->v[j]].z;
342 int nidx = f->n[j] < 0 ? 0 : f->n[j];
343 face.v[j].normal[0] = obj->vn[nidx].x;
344 face.v[j].normal[1] = obj->vn[nidx].y;
345 face.v[j].normal[2] = obj->vn[nidx].z;
347 int tidx = f->t[j] < 0 ? 0 : f->t[j];
348 face.v[j].tex[0] = obj->vt[tidx].x;
349 face.v[j].tex[1] = obj->vt[tidx].y;
350 }
351 mesh->faces.push_back(face);
352 }
354 if(added_norm) {
355 obj->vn.pop_back();
356 }
357 if(added_tc) {
358 obj->vt.pop_back();
359 }
361 return mesh;
362 }
364 static bool read_materials(FILE *fp, vector<obj_mat> *vmtl)
365 {
366 obj_mat mat;
368 for(;;) {
369 char line[BUF_SZ];
370 fgets(line, sizeof line, fp);
371 if(feof(fp)) {
372 break;
373 }
375 char *tok;
376 if(!(tok = strtok(line, SEP))) {
377 continue;
378 }
380 int cmd;
381 if((cmd = get_cmd(tok)) == -1) {
382 continue;
383 }
385 switch(cmd) {
386 case CMD_NEWMTL:
387 // add the previous material, and start a new one
388 if(mat.name.length() > 0) {
389 vmtl->push_back(mat);
390 mat.reset();
391 }
392 if((tok = strtok(0, SEP))) {
393 mat.name = tok;
394 }
395 break;
397 case CMD_KA:
398 parse_color(&mat.ambient);
399 break;
401 case CMD_KD:
402 parse_color(&mat.diffuse);
403 break;
405 case CMD_KS:
406 parse_color(&mat.specular);
407 break;
409 case CMD_NS:
410 if((tok = strtok(0, SEP)) && is_float(tok)) {
411 mat.shininess = atof(tok);
412 }
413 break;
415 case CMD_NI:
416 if((tok = strtok(0, SEP)) && is_float(tok)) {
417 mat.ior = atof(tok);
418 }
419 break;
421 case CMD_D:
422 case CMD_TR:
423 {
424 Vector3 c;
425 if(parse_color(&c)) {
426 mat.alpha = cmd == CMD_D ? c.x : 1.0 - c.x;
427 }
428 }
429 break;
431 case CMD_MAP_KD:
432 mat.tex_dif = parse_map();
433 break;
435 default:
436 break;
437 }
438 }
440 if(mat.name.length() > 0) {
441 vmtl->push_back(mat);
442 }
443 return true;
444 }
446 static int get_cmd(char *str)
447 {
448 char *s = str;
449 while((*s = toupper(*s))) s++;
451 for(int i=0; cmd_names[i]; i++) {
452 if(strcmp(str, cmd_names[i]) == 0) {
453 return i;
454 }
455 }
456 return CMD_UNK;
457 }
459 static bool is_int(const char *str)
460 {
461 char *tmp;
462 strtol(str, &tmp, 10);
463 return tmp != str;
464 }
466 static bool is_float(const char *str)
467 {
468 char *tmp;
469 strtod(str, &tmp);
470 return tmp != str;
471 }
473 static bool parse_vec(Vector3 *vec)
474 {
475 for(int i=0; i<3; i++) {
476 char *tok;
478 if(!(tok = strtok(0, SEP)) || !is_float(tok)) {
479 if(i < 2) {
480 return false;
481 }
482 vec->z = 0.0;
483 } else {
484 float v = atof(tok);
486 switch(i) {
487 case 0:
488 vec->x = v;
489 break;
490 case 1:
491 vec->y = v;
492 break;
493 case 2:
494 vec->z = v;
495 break;
496 }
497 }
498 }
499 return true;
500 }
502 static bool parse_color(Vector3 *col)
503 {
504 for(int i=0; i<3; i++) {
505 char *tok;
507 if(!(tok = strtok(0, SEP)) || !is_float(tok)) {
508 col->y = col->z = col->x;
509 return i > 0 ? true : false;
510 }
512 float v = atof(tok);
513 switch(i) {
514 case 0:
515 col->x = v;
516 break;
517 case 1:
518 col->y = v;
519 break;
520 case 2:
521 col->z = v;
522 break;
523 }
524 }
525 return true;
526 }
528 static bool parse_face(obj_face *face)
529 {
530 char *tok[] = {0, 0, 0, 0};
531 face->elem = 0;
533 for(int i=0; i<4; i++) {
534 if((!(tok[i] = strtok(0, SEP)) || !is_int(tok[i]))) {
535 if(i < 3) return false; // less than 3 verts? not a polygon
536 } else {
537 face->elem++;
538 }
539 }
541 for(int i=0; i<4; i++) {
542 char *subtok = tok[i];
544 if(!subtok || !*subtok || !is_int(subtok)) {
545 if(i < 3) {
546 return false;
547 }
548 face->v[i] = INVALID_IDX;
549 } else {
550 face->v[i] = atoi(subtok);
551 if(face->v[i] > 0) face->v[i]--; /* convert to 0-based */
552 }
554 while(subtok && *subtok && *subtok != '/') {
555 subtok++;
556 }
557 if(subtok && *subtok && *++subtok && is_int(subtok)) {
558 face->t[i] = atoi(subtok);
559 if(face->t[i] > 0) face->t[i]--; /* convert to 0-based */
560 } else {
561 face->t[i] = INVALID_IDX;
562 }
564 while(subtok && *subtok && *subtok != '/') {
565 subtok++;
566 }
567 if(subtok && *subtok && *++subtok && is_int(subtok)) {
568 face->n[i] = atoi(subtok);
569 if(face->n[i] > 0) face->n[i]--; /* convert to 0-based */
570 } else {
571 face->n[i] = INVALID_IDX;
572 }
573 }
575 return true;
576 }
578 static const char *parse_map()
579 {
580 char *tok, *prev = 0;
582 while((tok = strtok(0, SEP))) {
583 prev = tok;
584 }
586 return prev ? prev : "";
587 }
589 static bool find_file(char *res, int sz, const char *fname, const char *path, const char *mode)
590 {
591 FILE *fp;
592 const char *beg, *end;
593 int fnamelen = strlen(fname);
595 beg = path;
596 while(beg && *beg) {
597 end = beg;
598 while(*end && *end != ':') {
599 end++;
600 }
602 int sz = end - beg + 1;
603 char *pathname = (char*)alloca(sz + fnamelen + 2);
604 memcpy(pathname, beg, sz);
605 strcat(pathname, "/");
606 strcat(pathname, fname);
608 if((fp = fopen(pathname, mode))) {
609 fclose(fp);
610 strncpy(res, pathname, sz);
611 return true;
612 }
614 beg += sz;
615 }
616 return false;
617 }
619 static const char *dirname(const char *str)
620 {
621 static char buf[PATH_MAX];
623 if(!str || !*str) {
624 strcpy(buf, ".");
625 } else {
626 strncpy(buf, str, PATH_MAX);
627 char *ptr = strrchr(buf, '/');
629 if(*ptr) *ptr = 0;
630 }
631 return buf;
632 }