clray
changeset 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 | 9f0ddb701882 |
children | 575383f3a239 |
files | src/mesh.cc src/mesh.h |
diffstat | 2 files changed, 669 insertions(+), 0 deletions(-) [+] |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/mesh.cc Tue Jul 20 20:02:41 2010 +0100 1.3 @@ -0,0 +1,631 @@ 1.4 +#include <stdio.h> 1.5 +#include <stdlib.h> 1.6 +#include <string.h> 1.7 +#include <errno.h> 1.8 +#include <string> 1.9 +#include <vector> 1.10 +#include <map> 1.11 +#include "mesh.h" 1.12 + 1.13 +using namespace std; 1.14 + 1.15 +#define COMMANDS \ 1.16 + CMD(V), \ 1.17 + CMD(VN), \ 1.18 + CMD(VT), \ 1.19 + CMD(F), \ 1.20 + CMD(O), \ 1.21 + CMD(G), \ 1.22 + CMD(MTLLIB), \ 1.23 + CMD(USEMTL), \ 1.24 + CMD(NEWMTL), \ 1.25 + CMD(KA), \ 1.26 + CMD(KD), \ 1.27 + CMD(KS), \ 1.28 + CMD(NS), \ 1.29 + CMD(NI), \ 1.30 + CMD(D), \ 1.31 + CMD(TR), \ 1.32 + CMD(MAP_KD), \ 1.33 + CMD(MAP_KS), \ 1.34 + CMD(MAP_NS), \ 1.35 + CMD(MAP_D), \ 1.36 + CMD(REFL), \ 1.37 + CMD(BUMP) 1.38 + 1.39 +#define CMD(x) CMD_##x 1.40 +enum { 1.41 + COMMANDS, 1.42 + CMD_UNK 1.43 +}; 1.44 +#undef CMD 1.45 + 1.46 +#define CMD(x) #x 1.47 +static const char *cmd_names[] = { 1.48 + COMMANDS, 1.49 + 0 1.50 +}; 1.51 +#undef CMD 1.52 + 1.53 +struct Vector3 { 1.54 + float x, y, z; 1.55 + 1.56 + Vector3() { x = y = z = 0.0; } 1.57 + Vector3(float a, float b, float c) { x = a; y = b; z = c; } 1.58 +}; 1.59 + 1.60 +struct Vector2 { 1.61 + float x, y; 1.62 + 1.63 + Vector2() { x = y = 0.0; } 1.64 + Vector2(float a, float b) { x = a; y = b; } 1.65 +}; 1.66 + 1.67 +struct obj_face { 1.68 + int elem; 1.69 + int v[4], n[4], t[4]; 1.70 +}; 1.71 + 1.72 +struct obj_file { 1.73 + string cur_obj, cur_mat; 1.74 + vector<Vector3> v, vn, vt; 1.75 + vector<obj_face> f; 1.76 +}; 1.77 + 1.78 +struct obj_mat { 1.79 + string name; // newmtl <name> 1.80 + Vector3 ambient, diffuse, specular; // Ka, Kd, Ks 1.81 + float shininess; // Ns 1.82 + float ior; // Ni 1.83 + float alpha; // d, Tr 1.84 + 1.85 + string tex_dif, tex_spec, tex_shin, tex_alpha; // map_Kd, map_Ks, map_Ns, map_d 1.86 + string tex_refl; // refl -type sphere|cube file 1.87 + string tex_bump; // bump 1.88 + 1.89 + obj_mat() { reset(); } 1.90 + 1.91 + void reset() { 1.92 + ambient = diffuse = Vector3(0.5, 0.5, 0.5); 1.93 + specular = Vector3(0.0, 0.0, 0.0); 1.94 + name = tex_dif = tex_spec = tex_shin = tex_alpha = tex_refl = tex_bump = ""; 1.95 + shininess = 0; 1.96 + ior = alpha = 1; 1.97 + } 1.98 +}; 1.99 + 1.100 +static bool read_materials(FILE *fp, vector<obj_mat> *vmtl); 1.101 +static Mesh *cons_mesh(obj_file *obj); 1.102 + 1.103 +static int get_cmd(char *str); 1.104 +static bool is_int(const char *str); 1.105 +static bool is_float(const char *str); 1.106 +static bool parse_vec(Vector3 *vec); 1.107 +static bool parse_color(Vector3 *col); 1.108 +static bool parse_face(obj_face *face); 1.109 +static const char *parse_map(); 1.110 + 1.111 +static bool find_file(char *res, int sz, const char *fname, const char *path = ".", const char *mode = "rb"); 1.112 +static const char *dirname(const char *str); 1.113 + 1.114 +static map<string, int> matnames; 1.115 + 1.116 + 1.117 +#define INVALID_IDX INT_MIN 1.118 + 1.119 +#define SEP " \t\n\r\v" 1.120 +#define BUF_SZ 512 1.121 + 1.122 +bool Scene::load(const char *fname) 1.123 +{ 1.124 + FILE *fp; 1.125 + 1.126 + if(!(fp = fopen(fname, "rb"))) { 1.127 + fprintf(stderr, "failed to open %s: %s\n", fname, strerror(errno)); 1.128 + return false; 1.129 + } 1.130 + 1.131 + bool res = load(fp); 1.132 + fclose(fp); 1.133 + return res; 1.134 +} 1.135 + 1.136 +bool Scene::load(FILE *fp) 1.137 +{ 1.138 + static int seq; 1.139 + char cur_name[16]; 1.140 + 1.141 + obj_file obj; 1.142 + 1.143 + sprintf(cur_name, "default%02d.obj", seq++); 1.144 + obj.cur_obj = cur_name; 1.145 + 1.146 + int prev_cmd = 0, obj_added = 0; 1.147 + for(;;) { 1.148 + Vector3 vec; 1.149 + obj_face face; 1.150 + 1.151 + char line[BUF_SZ]; 1.152 + fgets(line, sizeof line, fp); 1.153 + if(feof(fp)) { 1.154 + break; 1.155 + } 1.156 + 1.157 + char *tok; 1.158 + if(!(tok = strtok(line, SEP))) { 1.159 + continue; // ignore empty lines 1.160 + } 1.161 + 1.162 + int cmd; 1.163 + if((cmd = get_cmd(tok)) == -1) { 1.164 + continue; // ignore unknown commands ... 1.165 + } 1.166 + 1.167 + switch(cmd) { 1.168 + case CMD_V: 1.169 + if(!parse_vec(&vec)) { 1.170 + continue; 1.171 + } 1.172 + obj.v.push_back(vec); 1.173 + break; 1.174 + 1.175 + case CMD_VN: 1.176 + if(!parse_vec(&vec)) { 1.177 + continue; 1.178 + } 1.179 + obj.vn.push_back(vec); 1.180 + break; 1.181 + 1.182 + case CMD_VT: 1.183 + if(!parse_vec(&vec)) { 1.184 + continue; 1.185 + } 1.186 + vec.y = 1.0 - vec.y; 1.187 + obj.vt.push_back(vec); 1.188 + break; 1.189 + 1.190 + case CMD_O: 1.191 + case CMD_G: 1.192 + if(prev_cmd == CMD_O || prev_cmd == CMD_G) { 1.193 + break; // just in case we've got both of them in a row 1.194 + } 1.195 + /* if we have any previous data, group them up, add the object 1.196 + * and continue with the new one... 1.197 + */ 1.198 + if(!obj.f.empty()) { 1.199 + Mesh *mesh = cons_mesh(&obj); 1.200 + mesh->matid = matnames[obj.cur_mat]; 1.201 + meshes.push_back(mesh); 1.202 + obj_added++; 1.203 + 1.204 + obj.f.clear(); // clean the face list 1.205 + } 1.206 + if((tok = strtok(0, SEP))) { 1.207 + obj.cur_obj = tok; 1.208 + } else { 1.209 + sprintf(cur_name, "default%02d.obj", seq++); 1.210 + obj.cur_obj = cur_name; 1.211 + } 1.212 + break; 1.213 + 1.214 + case CMD_MTLLIB: 1.215 + if((tok = strtok(0, SEP))) { 1.216 + char path[PATH_MAX]; 1.217 + 1.218 + sprintf(path, ".:%s", dirname(tok)); 1.219 + if(!find_file(path, PATH_MAX, tok, path)) { 1.220 + fprintf(stderr, "material library not found: %s\n", tok); 1.221 + continue; 1.222 + } 1.223 + 1.224 + FILE *mfile; 1.225 + if(!(mfile = fopen(path, "rb"))) { 1.226 + fprintf(stderr, "failed to open material library: %s\n", path); 1.227 + continue; 1.228 + } 1.229 + 1.230 + // load all materials of the mtl file into a vector 1.231 + vector<obj_mat> vmtl; 1.232 + if(!read_materials(mfile, &vmtl)) { 1.233 + continue; 1.234 + } 1.235 + fclose(mfile); 1.236 + 1.237 + // and add them all to the scene 1.238 + for(size_t i=0; i<vmtl.size(); i++) { 1.239 + Material mat; 1.240 + 1.241 + mat.kd[0] = vmtl[i].diffuse.x; 1.242 + mat.kd[1] = vmtl[i].diffuse.y; 1.243 + mat.kd[2] = vmtl[i].diffuse.z; 1.244 + 1.245 + mat.ks[0] = vmtl[i].specular.x; 1.246 + mat.ks[1] = vmtl[i].specular.y; 1.247 + mat.ks[2] = vmtl[i].specular.z; 1.248 + 1.249 + mat.kt = 1.0 - vmtl[i].alpha; 1.250 + mat.kr = 0.0; // TODO 1.251 + mat.spow = vmtl[i].shininess; 1.252 + 1.253 + matnames[vmtl[i].name] = i; 1.254 + } 1.255 + } 1.256 + break; 1.257 + 1.258 + case CMD_USEMTL: 1.259 + if((tok = strtok(0, SEP))) { 1.260 + obj.cur_mat = tok; 1.261 + } else { 1.262 + obj.cur_mat = ""; 1.263 + } 1.264 + break; 1.265 + 1.266 + case CMD_F: 1.267 + if(!parse_face(&face)) { 1.268 + continue; 1.269 + } 1.270 + 1.271 + // convert negative indices to regular indices 1.272 + for(int i=0; i<4; i++) { 1.273 + if(face.v[i] < 0 && face.v[i] != INVALID_IDX) { 1.274 + face.v[i] = obj.v.size() + face.v[i]; 1.275 + } 1.276 + if(face.n[i] < 0 && face.n[i] != INVALID_IDX) { 1.277 + face.n[i] = obj.vn.size() + face.n[i]; 1.278 + } 1.279 + if(face.t[i] < 0 && face.t[i] != INVALID_IDX) { 1.280 + face.t[i] = obj.vt.size() + face.t[i]; 1.281 + } 1.282 + } 1.283 + 1.284 + // break quads into triangles if needed 1.285 + obj.f.push_back(face); 1.286 + if(face.elem == 4) { 1.287 + face.v[1] = face.v[2]; 1.288 + face.n[1] = face.n[2]; 1.289 + face.t[1] = face.t[2]; 1.290 + 1.291 + face.v[2] = face.v[3]; 1.292 + face.n[2] = face.n[3]; 1.293 + face.t[2] = face.t[3]; 1.294 + 1.295 + obj.f.push_back(face); 1.296 + } 1.297 + break; 1.298 + 1.299 + default: 1.300 + break; // ignore unknown commands 1.301 + } 1.302 + 1.303 + prev_cmd = cmd; 1.304 + } 1.305 + 1.306 + // reached end of file... 1.307 + if(!obj.f.empty()) { 1.308 + Mesh *mesh = cons_mesh(&obj); 1.309 + mesh->matid = matnames[obj.cur_mat]; 1.310 + meshes.push_back(mesh); 1.311 + obj_added++; 1.312 + } 1.313 + 1.314 + return obj_added > 0; 1.315 +} 1.316 + 1.317 +static Mesh *cons_mesh(obj_file *obj) 1.318 +{ 1.319 + Mesh *mesh; 1.320 + 1.321 + // need at least one of each element 1.322 + bool added_norm = false, added_tc = false; 1.323 + if(obj->vn.empty()) { 1.324 + obj->vn.push_back(Vector3(0, 0, 0)); 1.325 + added_norm = true; 1.326 + } 1.327 + if(obj->vt.empty()) { 1.328 + obj->vt.push_back(Vector3(0, 0, 0)); 1.329 + added_tc = true; 1.330 + } 1.331 + 1.332 + mesh = new Mesh; 1.333 + 1.334 + for(size_t i=0; i<obj->f.size(); i++) { 1.335 + Face face; 1.336 + 1.337 + for(int j=0; j<3; j++) { 1.338 + obj_face *f = &obj->f[i]; 1.339 + 1.340 + face.v[j].pos[0] = obj->v[f->v[j]].x; 1.341 + face.v[j].pos[1] = obj->v[f->v[j]].y; 1.342 + face.v[j].pos[2] = obj->v[f->v[j]].z; 1.343 + 1.344 + int nidx = f->n[j] < 0 ? 0 : f->n[j]; 1.345 + face.v[j].normal[0] = obj->vn[nidx].x; 1.346 + face.v[j].normal[1] = obj->vn[nidx].y; 1.347 + face.v[j].normal[2] = obj->vn[nidx].z; 1.348 + 1.349 + int tidx = f->t[j] < 0 ? 0 : f->t[j]; 1.350 + face.v[j].tex[0] = obj->vt[tidx].x; 1.351 + face.v[j].tex[1] = obj->vt[tidx].y; 1.352 + } 1.353 + mesh->faces.push_back(face); 1.354 + } 1.355 + 1.356 + if(added_norm) { 1.357 + obj->vn.pop_back(); 1.358 + } 1.359 + if(added_tc) { 1.360 + obj->vt.pop_back(); 1.361 + } 1.362 + 1.363 + return mesh; 1.364 +} 1.365 + 1.366 +static bool read_materials(FILE *fp, vector<obj_mat> *vmtl) 1.367 +{ 1.368 + obj_mat mat; 1.369 + 1.370 + for(;;) { 1.371 + char line[BUF_SZ]; 1.372 + fgets(line, sizeof line, fp); 1.373 + if(feof(fp)) { 1.374 + break; 1.375 + } 1.376 + 1.377 + char *tok; 1.378 + if(!(tok = strtok(line, SEP))) { 1.379 + continue; 1.380 + } 1.381 + 1.382 + int cmd; 1.383 + if((cmd = get_cmd(tok)) == -1) { 1.384 + continue; 1.385 + } 1.386 + 1.387 + switch(cmd) { 1.388 + case CMD_NEWMTL: 1.389 + // add the previous material, and start a new one 1.390 + if(mat.name.length() > 0) { 1.391 + vmtl->push_back(mat); 1.392 + mat.reset(); 1.393 + } 1.394 + if((tok = strtok(0, SEP))) { 1.395 + mat.name = tok; 1.396 + } 1.397 + break; 1.398 + 1.399 + case CMD_KA: 1.400 + parse_color(&mat.ambient); 1.401 + break; 1.402 + 1.403 + case CMD_KD: 1.404 + parse_color(&mat.diffuse); 1.405 + break; 1.406 + 1.407 + case CMD_KS: 1.408 + parse_color(&mat.specular); 1.409 + break; 1.410 + 1.411 + case CMD_NS: 1.412 + if((tok = strtok(0, SEP)) && is_float(tok)) { 1.413 + mat.shininess = atof(tok); 1.414 + } 1.415 + break; 1.416 + 1.417 + case CMD_NI: 1.418 + if((tok = strtok(0, SEP)) && is_float(tok)) { 1.419 + mat.ior = atof(tok); 1.420 + } 1.421 + break; 1.422 + 1.423 + case CMD_D: 1.424 + case CMD_TR: 1.425 + { 1.426 + Vector3 c; 1.427 + if(parse_color(&c)) { 1.428 + mat.alpha = cmd == CMD_D ? c.x : 1.0 - c.x; 1.429 + } 1.430 + } 1.431 + break; 1.432 + 1.433 + case CMD_MAP_KD: 1.434 + mat.tex_dif = parse_map(); 1.435 + break; 1.436 + 1.437 + default: 1.438 + break; 1.439 + } 1.440 + } 1.441 + 1.442 + if(mat.name.length() > 0) { 1.443 + vmtl->push_back(mat); 1.444 + } 1.445 + return true; 1.446 +} 1.447 + 1.448 +static int get_cmd(char *str) 1.449 +{ 1.450 + char *s = str; 1.451 + while((*s = toupper(*s))) s++; 1.452 + 1.453 + for(int i=0; cmd_names[i]; i++) { 1.454 + if(strcmp(str, cmd_names[i]) == 0) { 1.455 + return i; 1.456 + } 1.457 + } 1.458 + return CMD_UNK; 1.459 +} 1.460 + 1.461 +static bool is_int(const char *str) 1.462 +{ 1.463 + char *tmp; 1.464 + strtol(str, &tmp, 10); 1.465 + return tmp != str; 1.466 +} 1.467 + 1.468 +static bool is_float(const char *str) 1.469 +{ 1.470 + char *tmp; 1.471 + strtod(str, &tmp); 1.472 + return tmp != str; 1.473 +} 1.474 + 1.475 +static bool parse_vec(Vector3 *vec) 1.476 +{ 1.477 + for(int i=0; i<3; i++) { 1.478 + char *tok; 1.479 + 1.480 + if(!(tok = strtok(0, SEP)) || !is_float(tok)) { 1.481 + if(i < 2) { 1.482 + return false; 1.483 + } 1.484 + vec->z = 0.0; 1.485 + } else { 1.486 + float v = atof(tok); 1.487 + 1.488 + switch(i) { 1.489 + case 0: 1.490 + vec->x = v; 1.491 + break; 1.492 + case 1: 1.493 + vec->y = v; 1.494 + break; 1.495 + case 2: 1.496 + vec->z = v; 1.497 + break; 1.498 + } 1.499 + } 1.500 + } 1.501 + return true; 1.502 +} 1.503 + 1.504 +static bool parse_color(Vector3 *col) 1.505 +{ 1.506 + for(int i=0; i<3; i++) { 1.507 + char *tok; 1.508 + 1.509 + if(!(tok = strtok(0, SEP)) || !is_float(tok)) { 1.510 + col->y = col->z = col->x; 1.511 + return i > 0 ? true : false; 1.512 + } 1.513 + 1.514 + float v = atof(tok); 1.515 + switch(i) { 1.516 + case 0: 1.517 + col->x = v; 1.518 + break; 1.519 + case 1: 1.520 + col->y = v; 1.521 + break; 1.522 + case 2: 1.523 + col->z = v; 1.524 + break; 1.525 + } 1.526 + } 1.527 + return true; 1.528 +} 1.529 + 1.530 +static bool parse_face(obj_face *face) 1.531 +{ 1.532 + char *tok[] = {0, 0, 0, 0}; 1.533 + face->elem = 0; 1.534 + 1.535 + for(int i=0; i<4; i++) { 1.536 + if((!(tok[i] = strtok(0, SEP)) || !is_int(tok[i]))) { 1.537 + if(i < 3) return false; // less than 3 verts? not a polygon 1.538 + } else { 1.539 + face->elem++; 1.540 + } 1.541 + } 1.542 + 1.543 + for(int i=0; i<4; i++) { 1.544 + char *subtok = tok[i]; 1.545 + 1.546 + if(!subtok || !*subtok || !is_int(subtok)) { 1.547 + if(i < 3) { 1.548 + return false; 1.549 + } 1.550 + face->v[i] = INVALID_IDX; 1.551 + } else { 1.552 + face->v[i] = atoi(subtok); 1.553 + if(face->v[i] > 0) face->v[i]--; /* convert to 0-based */ 1.554 + } 1.555 + 1.556 + while(subtok && *subtok && *subtok != '/') { 1.557 + subtok++; 1.558 + } 1.559 + if(subtok && *subtok && *++subtok && is_int(subtok)) { 1.560 + face->t[i] = atoi(subtok); 1.561 + if(face->t[i] > 0) face->t[i]--; /* convert to 0-based */ 1.562 + } else { 1.563 + face->t[i] = INVALID_IDX; 1.564 + } 1.565 + 1.566 + while(subtok && *subtok && *subtok != '/') { 1.567 + subtok++; 1.568 + } 1.569 + if(subtok && *subtok && *++subtok && is_int(subtok)) { 1.570 + face->n[i] = atoi(subtok); 1.571 + if(face->n[i] > 0) face->n[i]--; /* convert to 0-based */ 1.572 + } else { 1.573 + face->n[i] = INVALID_IDX; 1.574 + } 1.575 + } 1.576 + 1.577 + return true; 1.578 +} 1.579 + 1.580 +static const char *parse_map() 1.581 +{ 1.582 + char *tok, *prev = 0; 1.583 + 1.584 + while((tok = strtok(0, SEP))) { 1.585 + prev = tok; 1.586 + } 1.587 + 1.588 + return prev ? prev : ""; 1.589 +} 1.590 + 1.591 +static bool find_file(char *res, int sz, const char *fname, const char *path, const char *mode) 1.592 +{ 1.593 + FILE *fp; 1.594 + const char *beg, *end; 1.595 + int fnamelen = strlen(fname); 1.596 + 1.597 + beg = path; 1.598 + while(beg && *beg) { 1.599 + end = beg; 1.600 + while(*end && *end != ':') { 1.601 + end++; 1.602 + } 1.603 + 1.604 + int sz = end - beg + 1; 1.605 + char *pathname = (char*)alloca(sz + fnamelen + 2); 1.606 + memcpy(pathname, beg, sz); 1.607 + strcat(pathname, "/"); 1.608 + strcat(pathname, fname); 1.609 + 1.610 + if((fp = fopen(pathname, mode))) { 1.611 + fclose(fp); 1.612 + strncpy(res, pathname, sz); 1.613 + return true; 1.614 + } 1.615 + 1.616 + beg += sz; 1.617 + } 1.618 + return false; 1.619 +} 1.620 + 1.621 +static const char *dirname(const char *str) 1.622 +{ 1.623 + static char buf[PATH_MAX]; 1.624 + 1.625 + if(!str || !*str) { 1.626 + strcpy(buf, "."); 1.627 + } else { 1.628 + strncpy(buf, str, PATH_MAX); 1.629 + char *ptr = strrchr(buf, '/'); 1.630 + 1.631 + if(*ptr) *ptr = 0; 1.632 + } 1.633 + return buf; 1.634 +}
2.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 2.2 +++ b/src/mesh.h Tue Jul 20 20:02:41 2010 +0100 2.3 @@ -0,0 +1,38 @@ 2.4 +#ifndef MESH_H_ 2.5 +#define MESH_H_ 2.6 + 2.7 +#include <vector> 2.8 + 2.9 +struct Vertex { 2.10 + float pos[3]; 2.11 + float normal[3]; 2.12 + float tex[2]; 2.13 +}; 2.14 + 2.15 +struct Face { 2.16 + Vertex v[3]; 2.17 + float normal[3]; 2.18 + int matid; 2.19 +}; 2.20 + 2.21 +struct Material { 2.22 + float kd[3], ks[3]; 2.23 + float kr, kt; 2.24 + float spow; 2.25 +}; 2.26 + 2.27 +struct Mesh { 2.28 + std::vector<Face> faces; 2.29 + int matid; 2.30 +}; 2.31 + 2.32 +class Scene { 2.33 +public: 2.34 + std::vector<Mesh*> meshes; 2.35 + std::vector<Material> matlib; 2.36 + 2.37 + bool load(const char *fname); 2.38 + bool load(FILE *fp); 2.39 +}; 2.40 + 2.41 +#endif /* MESH_H_ */