eqemu
diff src/objfile.cc @ 3:f9274bebe55e
adding 3d graphics stuff
author | John Tsiombikas <nuclear@member.fsf.org> |
---|---|
date | Thu, 17 Jul 2014 02:35:19 +0300 |
parents | |
children | 3d3656360a82 |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/objfile.cc Thu Jul 17 02:35:19 2014 +0300 1.3 @@ -0,0 +1,542 @@ 1.4 +#include <stdlib.h> 1.5 +#include <string.h> 1.6 +#include <ctype.h> 1.7 +#include <limits.h> 1.8 +#include <vector> 1.9 +#include <map> 1.10 +#include <string> 1.11 +#include <sstream> 1.12 +#include <iomanip> 1.13 +#include "scene.h" 1.14 + 1.15 +using namespace std; 1.16 + 1.17 +#define COMMANDS \ 1.18 + CMD(V), \ 1.19 + CMD(VN), \ 1.20 + CMD(VT), \ 1.21 + CMD(F), \ 1.22 + CMD(O), \ 1.23 + CMD(G), \ 1.24 + CMD(MTLLIB), \ 1.25 + CMD(USEMTL), \ 1.26 + CMD(NEWMTL), \ 1.27 + CMD(KA), \ 1.28 + CMD(KD), \ 1.29 + CMD(KS), \ 1.30 + CMD(NS), \ 1.31 + CMD(NI), \ 1.32 + CMD(D), \ 1.33 + CMD(TR), \ 1.34 + CMD(MAP_KD), \ 1.35 + CMD(MAP_KS), \ 1.36 + CMD(MAP_NS), \ 1.37 + CMD(MAP_D), \ 1.38 + CMD(REFL), \ 1.39 + CMD(BUMP) 1.40 + 1.41 +#define CMD(x) CMD_##x 1.42 +enum { 1.43 + COMMANDS, 1.44 + CMD_UNK 1.45 +}; 1.46 +#undef CMD 1.47 + 1.48 +#define CMD(x) #x 1.49 +static const char *cmd_names[] = { 1.50 + COMMANDS, 1.51 + 0 1.52 +}; 1.53 +#undef CMD 1.54 + 1.55 + 1.56 +struct ObjFace { 1.57 + int elem; 1.58 + int v[4], n[4], t[4]; 1.59 +}; 1.60 + 1.61 +struct ObjFile { 1.62 + string cur_obj, cur_mat; 1.63 + vector<Vector3> v, vn; 1.64 + vector<Vector2> vt; 1.65 + vector<ObjFace> f; 1.66 +}; 1.67 + 1.68 +typedef Vector3 Color; 1.69 + 1.70 +struct ObjMat { 1.71 + string name; // newmtl <name> 1.72 + Color ambient, diffuse, specular; // Ka, Kd, Ks 1.73 + float shininess; // Ns 1.74 + float ior; // Ni 1.75 + float alpha; // d, Tr 1.76 + 1.77 + string tex_dif, tex_spec, tex_shin, tex_alpha; // map_Kd, map_Ks, map_Ns, map_d 1.78 + string tex_refl; // refl -type sphere|cube file 1.79 + string tex_bump; // bump 1.80 + 1.81 + ObjMat() { reset(); } 1.82 + 1.83 + void reset() { 1.84 + ambient = diffuse = Color(0.5, 0.5, 0.5); 1.85 + specular = Color(0.0, 0.0, 0.0); 1.86 + name = tex_dif = tex_spec = tex_shin = tex_alpha = tex_refl = tex_bump = ""; 1.87 + shininess = 0; 1.88 + ior = alpha = 1; 1.89 + } 1.90 +}; 1.91 + 1.92 +static bool read_materials(FILE *fp, vector<ObjMat> *vmtl); 1.93 +static Object *cons_object(ObjFile *obj); 1.94 + 1.95 +static int get_cmd(char *str); 1.96 +static bool is_int(const char *str); 1.97 +static bool is_float(const char *str); 1.98 +static bool parse_vec(Vector3 *vec); 1.99 +static bool parse_color(Color *col); 1.100 +static bool parse_face(ObjFace *face); 1.101 +static const char *parse_map(); 1.102 + 1.103 + 1.104 +static map<string, Material> matlib; 1.105 + 1.106 + 1.107 +#define INVALID_IDX INT_MIN 1.108 + 1.109 +#define SEP " \t\n\r\v" 1.110 +#define BUF_SZ 512 1.111 +bool Scene::load_obj(FILE *fp) 1.112 +{ 1.113 + static int seq; 1.114 + char cur_name[16]; 1.115 + stringstream sstr; 1.116 + 1.117 + ObjFile obj; 1.118 + 1.119 + sprintf(cur_name, "default%02d.obj", seq++); 1.120 + obj.cur_obj = cur_name; 1.121 + 1.122 + int prev_cmd = 0, obj_added = 0; 1.123 + for(;;) { 1.124 + Vector3 vec; 1.125 + ObjFace face; 1.126 + 1.127 + char line[BUF_SZ]; 1.128 + fgets(line, sizeof line, fp); 1.129 + if(feof(fp)) { 1.130 + break; 1.131 + } 1.132 + 1.133 + char *tok; 1.134 + if(!(tok = strtok(line, SEP))) { 1.135 + continue; // ignore empty lines 1.136 + } 1.137 + 1.138 + int cmd; 1.139 + if((cmd = get_cmd(tok)) == -1) { 1.140 + continue; // ignore unknown commands ... 1.141 + } 1.142 + 1.143 + switch(cmd) { 1.144 + case CMD_V: 1.145 + if(!parse_vec(&vec)) { 1.146 + continue; 1.147 + } 1.148 + obj.v.push_back(vec); 1.149 + break; 1.150 + 1.151 + case CMD_VN: 1.152 + if(!parse_vec(&vec)) { 1.153 + continue; 1.154 + } 1.155 + obj.vn.push_back(vec); 1.156 + break; 1.157 + 1.158 + case CMD_VT: 1.159 + if(!parse_vec(&vec)) { 1.160 + continue; 1.161 + } 1.162 + vec.y = 1.0 - vec.y; 1.163 + obj.vt.push_back(Vector2(vec.x, vec.y)); 1.164 + break; 1.165 + 1.166 + case CMD_O: 1.167 + case CMD_G: 1.168 + if(prev_cmd == CMD_O || prev_cmd == CMD_G) { 1.169 + break; // just in case we've got both of them in a row 1.170 + } 1.171 + /* if we have any previous data, group them up, add the object 1.172 + * and continue with the new one... 1.173 + */ 1.174 + if(!obj.f.empty()) { 1.175 + Object *robj = cons_object(&obj); 1.176 + robj->mtl = matlib[obj.cur_mat]; 1.177 + add_object(robj); 1.178 + obj_added++; 1.179 + 1.180 + obj.f.clear(); // clean the face list 1.181 + } 1.182 + if((tok = strtok(0, SEP))) { 1.183 + obj.cur_obj = tok; 1.184 + } else { 1.185 + sprintf(cur_name, "default%02d.obj", seq++); 1.186 + obj.cur_obj = cur_name; 1.187 + } 1.188 + break; 1.189 + 1.190 + case CMD_MTLLIB: 1.191 + if((tok = strtok(0, SEP))) { 1.192 + FILE *mfile; 1.193 + if(!(mfile = fopen(tok, "rb"))) { 1.194 + fprintf(stderr, "failed to open material library: %s\n", tok); 1.195 + continue; 1.196 + } 1.197 + 1.198 + // load all materials of the mtl file into a vector 1.199 + vector<ObjMat> vmtl; 1.200 + if(!read_materials(mfile, &vmtl)) { 1.201 + continue; 1.202 + } 1.203 + fclose(mfile); 1.204 + 1.205 + // and add them all to the scene 1.206 + for(size_t i=0; i<vmtl.size(); i++) { 1.207 + Material mat; 1.208 + mat.ambient = vmtl[i].ambient; 1.209 + mat.diffuse = vmtl[i].diffuse; 1.210 + mat.specular = vmtl[i].specular; 1.211 + mat.shininess = vmtl[i].shininess; 1.212 + mat.alpha = vmtl[i].alpha; 1.213 + 1.214 + /*if(vmtl[i].tex_dif.length()) { 1.215 + mat.set_texture(get_texture(vmtl[i].tex_dif.c_str())); 1.216 + }*/ 1.217 + 1.218 + matlib[vmtl[i].name] = mat; 1.219 + } 1.220 + } 1.221 + break; 1.222 + 1.223 + case CMD_USEMTL: 1.224 + if((tok = strtok(0, SEP))) { 1.225 + obj.cur_mat = tok; 1.226 + } else { 1.227 + obj.cur_mat = ""; 1.228 + } 1.229 + break; 1.230 + 1.231 + case CMD_F: 1.232 + if(!parse_face(&face)) { 1.233 + continue; 1.234 + } 1.235 + 1.236 + // convert negative indices to regular indices 1.237 + for(int i=0; i<4; i++) { 1.238 + if(face.v[i] < 0 && face.v[i] != INVALID_IDX) { 1.239 + face.v[i] = obj.v.size() + face.v[i]; 1.240 + } 1.241 + if(face.n[i] < 0 && face.n[i] != INVALID_IDX) { 1.242 + face.n[i] = obj.vn.size() + face.n[i]; 1.243 + } 1.244 + if(face.t[i] < 0 && face.t[i] != INVALID_IDX) { 1.245 + face.t[i] = obj.vt.size() + face.t[i]; 1.246 + } 1.247 + } 1.248 + 1.249 + // break quads into triangles if needed 1.250 + obj.f.push_back(face); 1.251 + if(face.elem == 4) { 1.252 + face.v[1] = face.v[2]; 1.253 + face.n[1] = face.n[2]; 1.254 + face.t[1] = face.t[2]; 1.255 + 1.256 + face.v[2] = face.v[3]; 1.257 + face.n[2] = face.n[3]; 1.258 + face.t[2] = face.t[3]; 1.259 + 1.260 + obj.f.push_back(face); 1.261 + } 1.262 + break; 1.263 + 1.264 + default: 1.265 + break; // ignore unknown commands 1.266 + } 1.267 + 1.268 + prev_cmd = cmd; 1.269 + } 1.270 + 1.271 + // reached end of file... 1.272 + if(!obj.f.empty()) { 1.273 + Object *robj = cons_object(&obj); 1.274 + robj->mtl = matlib[obj.cur_mat]; 1.275 + add_object(robj); 1.276 + obj_added++; 1.277 + } 1.278 + 1.279 + return obj_added > 0; 1.280 +} 1.281 + 1.282 +static Object *cons_object(ObjFile *obj) 1.283 +{ 1.284 + Object *robj; 1.285 + Vector3 *varr, *narr; 1.286 + Vector2 *tarr; 1.287 + 1.288 + int nelem = obj->f.size() * 3; 1.289 + 1.290 + try { 1.291 + robj = new Object; 1.292 + varr = new Vector3[nelem]; 1.293 + narr = new Vector3[nelem]; 1.294 + tarr = new Vector2[nelem]; 1.295 + } 1.296 + catch(...) { 1.297 + return 0; 1.298 + } 1.299 + /*if(obj->cur_obj.length() > 0) { 1.300 + robj->set_name(obj->cur_obj.c_str()); 1.301 + }*/ 1.302 + 1.303 + // need at least one of each element 1.304 + bool added_norm = false, added_tc = false; 1.305 + if(obj->vn.empty()) { 1.306 + obj->vn.push_back(Vector3(0, 0, 0)); 1.307 + added_norm = true;; 1.308 + } 1.309 + if(obj->vt.empty()) { 1.310 + obj->vt.push_back(Vector2(0, 0)); 1.311 + added_tc = true; 1.312 + } 1.313 + 1.314 + for(size_t i=0; i<obj->f.size(); i++) { 1.315 + for(int j=0; j<3; j++) { 1.316 + int idx = i * 3 + j; 1.317 + ObjFace *f = &obj->f[i]; 1.318 + 1.319 + varr[idx] = obj->v[f->v[j]]; 1.320 + narr[idx] = obj->vn[f->n[j] < 0 ? 0 : f->n[j]]; 1.321 + 1.322 + float t = obj->vt[f->t[j] < 0 ? 0 : f->t[j]].x; 1.323 + float s = obj->vt[f->t[j] < 0 ? 0 : f->t[j]].y; 1.324 + tarr[idx] = Vector2(t, s); 1.325 + } 1.326 + } 1.327 + 1.328 + if(added_norm) { 1.329 + obj->vn.pop_back(); 1.330 + } 1.331 + if(added_tc) { 1.332 + obj->vt.pop_back(); 1.333 + } 1.334 + 1.335 + Mesh *mesh = robj->get_mesh(); 1.336 + mesh->set_attrib(MESH_ATTR_VERTEX, nelem, 3, &varr->x); 1.337 + mesh->set_attrib(MESH_ATTR_NORMAL, nelem, 3, &narr->x); 1.338 + mesh->set_attrib(MESH_ATTR_TEXCOORD, nelem, 2, &tarr->x); 1.339 + 1.340 + delete [] varr; 1.341 + delete [] narr; 1.342 + delete [] tarr; 1.343 + return robj; 1.344 +} 1.345 + 1.346 +static bool read_materials(FILE *fp, vector<ObjMat> *vmtl) 1.347 +{ 1.348 + ObjMat mat; 1.349 + 1.350 + for(;;) { 1.351 + char line[BUF_SZ]; 1.352 + fgets(line, sizeof line, fp); 1.353 + if(feof(fp)) { 1.354 + break; 1.355 + } 1.356 + 1.357 + char *tok; 1.358 + if(!(tok = strtok(line, SEP))) { 1.359 + continue; 1.360 + } 1.361 + 1.362 + int cmd; 1.363 + if((cmd = get_cmd(tok)) == -1) { 1.364 + continue; 1.365 + } 1.366 + 1.367 + switch(cmd) { 1.368 + case CMD_NEWMTL: 1.369 + // add the previous material, and start a new one 1.370 + if(mat.name.length() > 0) { 1.371 + vmtl->push_back(mat); 1.372 + mat.reset(); 1.373 + } 1.374 + if((tok = strtok(0, SEP))) { 1.375 + mat.name = tok; 1.376 + } 1.377 + break; 1.378 + 1.379 + case CMD_KA: 1.380 + parse_color(&mat.ambient); 1.381 + break; 1.382 + 1.383 + case CMD_KD: 1.384 + parse_color(&mat.diffuse); 1.385 + break; 1.386 + 1.387 + case CMD_KS: 1.388 + parse_color(&mat.specular); 1.389 + break; 1.390 + 1.391 + case CMD_NS: 1.392 + if((tok = strtok(0, SEP)) && is_float(tok)) { 1.393 + mat.shininess = atof(tok); 1.394 + } 1.395 + break; 1.396 + 1.397 + case CMD_NI: 1.398 + if((tok = strtok(0, SEP)) && is_float(tok)) { 1.399 + mat.ior = atof(tok); 1.400 + } 1.401 + break; 1.402 + 1.403 + case CMD_D: 1.404 + case CMD_TR: 1.405 + { 1.406 + Color c; 1.407 + if(parse_color(&c)) { 1.408 + mat.alpha = cmd == CMD_D ? c.x : 1.0 - c.x; 1.409 + } 1.410 + } 1.411 + break; 1.412 + 1.413 + case CMD_MAP_KD: 1.414 + mat.tex_dif = parse_map(); 1.415 + break; 1.416 + 1.417 + default: 1.418 + break; 1.419 + } 1.420 + } 1.421 + 1.422 + if(mat.name.length() > 0) { 1.423 + vmtl->push_back(mat); 1.424 + } 1.425 + return true; 1.426 +} 1.427 + 1.428 +static int get_cmd(char *str) 1.429 +{ 1.430 + char *s = str; 1.431 + while((*s = toupper(*s))) s++; 1.432 + 1.433 + for(int i=0; cmd_names[i]; i++) { 1.434 + if(strcmp(str, cmd_names[i]) == 0) { 1.435 + return i; 1.436 + } 1.437 + } 1.438 + return CMD_UNK; 1.439 +} 1.440 + 1.441 +static bool is_int(const char *str) 1.442 +{ 1.443 + char *tmp; 1.444 + strtol(str, &tmp, 10); 1.445 + return tmp != str; 1.446 +} 1.447 + 1.448 +static bool is_float(const char *str) 1.449 +{ 1.450 + char *tmp; 1.451 + strtod(str, &tmp); 1.452 + return tmp != str; 1.453 +} 1.454 + 1.455 +static bool parse_vec(Vector3 *vec) 1.456 +{ 1.457 + for(int i=0; i<3; i++) { 1.458 + char *tok; 1.459 + 1.460 + if(!(tok = strtok(0, SEP)) || !is_float(tok)) { 1.461 + if(i < 2) { 1.462 + return false; 1.463 + } 1.464 + vec->z = 0.0; 1.465 + } else { 1.466 + (*vec)[i] = atof(tok); 1.467 + } 1.468 + } 1.469 + return true; 1.470 +} 1.471 + 1.472 +static bool parse_color(Color *col) 1.473 +{ 1.474 + for(int i=0; i<3; i++) { 1.475 + char *tok; 1.476 + 1.477 + if(!(tok = strtok(0, SEP)) || !is_float(tok)) { 1.478 + col->y = col->z = col->x; 1.479 + return i > 0 ? true : false; 1.480 + } 1.481 + (*col)[i] = atof(tok); 1.482 + } 1.483 + return true; 1.484 +} 1.485 + 1.486 +static bool parse_face(ObjFace *face) 1.487 +{ 1.488 + char *tok[] = {0, 0, 0, 0}; 1.489 + face->elem = 0; 1.490 + 1.491 + for(int i=0; i<4; i++) { 1.492 + if((!(tok[i] = strtok(0, SEP)) || !is_int(tok[i]))) { 1.493 + if(i < 3) return false; // less than 3 verts? not a polygon 1.494 + } else { 1.495 + face->elem++; 1.496 + } 1.497 + } 1.498 + 1.499 + for(int i=0; i<4; i++) { 1.500 + char *subtok = tok[i]; 1.501 + 1.502 + if(!subtok || !*subtok || !is_int(subtok)) { 1.503 + if(i < 3) { 1.504 + return false; 1.505 + } 1.506 + face->v[i] = INVALID_IDX; 1.507 + } else { 1.508 + face->v[i] = atoi(subtok); 1.509 + if(face->v[i] > 0) face->v[i]--; /* convert to 0-based */ 1.510 + } 1.511 + 1.512 + while(subtok && *subtok && *subtok != '/') { 1.513 + subtok++; 1.514 + } 1.515 + if(subtok && *subtok && *++subtok && is_int(subtok)) { 1.516 + face->t[i] = atoi(subtok); 1.517 + if(face->t[i] > 0) face->t[i]--; /* convert to 0-based */ 1.518 + } else { 1.519 + face->t[i] = INVALID_IDX; 1.520 + } 1.521 + 1.522 + while(subtok && *subtok && *subtok != '/') { 1.523 + subtok++; 1.524 + } 1.525 + if(subtok && *subtok && *++subtok && is_int(subtok)) { 1.526 + face->n[i] = atoi(subtok); 1.527 + if(face->n[i] > 0) face->n[i]--; /* convert to 0-based */ 1.528 + } else { 1.529 + face->n[i] = INVALID_IDX; 1.530 + } 1.531 + } 1.532 + 1.533 + return true; 1.534 +} 1.535 + 1.536 +static const char *parse_map() 1.537 +{ 1.538 + char *tok, *prev = 0; 1.539 + 1.540 + while((tok = strtok(0, SEP))) { 1.541 + prev = tok; 1.542 + } 1.543 + 1.544 + return prev ? prev : ""; 1.545 +}