clray

annotate src/mesh.cc @ 11:d9a1bab1c3f5

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