eqemu

annotate src/objfile.cc @ 12:2656099aff12

added copyright notices and license
author John Tsiombikas <nuclear@member.fsf.org>
date Fri, 18 Jul 2014 07:04:21 +0300
parents 977bc1cb055b
children
rev   line source
nuclear@12 1 /*
nuclear@12 2 eqemu - electronic queue system emulator
nuclear@12 3 Copyright (C) 2014 John Tsiombikas <nuclear@member.fsf.org>,
nuclear@12 4 Eleni-Maria Stea <eleni@mutantstargoat.com>
nuclear@12 5
nuclear@12 6 This program is free software: you can redistribute it and/or modify
nuclear@12 7 it under the terms of the GNU General Public License as published by
nuclear@12 8 the Free Software Foundation, either version 3 of the License, or
nuclear@12 9 (at your option) any later version.
nuclear@12 10
nuclear@12 11 This program is distributed in the hope that it will be useful,
nuclear@12 12 but WITHOUT ANY WARRANTY; without even the implied warranty of
nuclear@12 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
nuclear@12 14 GNU General Public License for more details.
nuclear@12 15
nuclear@12 16 You should have received a copy of the GNU General Public License
nuclear@12 17 along with this program. If not, see <http://www.gnu.org/licenses/>.
nuclear@12 18 */
nuclear@3 19 #include <stdlib.h>
nuclear@3 20 #include <string.h>
nuclear@3 21 #include <ctype.h>
nuclear@3 22 #include <limits.h>
nuclear@4 23 #include <assert.h>
nuclear@3 24 #include <vector>
nuclear@3 25 #include <map>
nuclear@3 26 #include <string>
nuclear@3 27 #include <sstream>
nuclear@3 28 #include <iomanip>
nuclear@3 29 #include "scene.h"
nuclear@3 30
nuclear@3 31 using namespace std;
nuclear@3 32
nuclear@3 33 #define COMMANDS \
nuclear@3 34 CMD(V), \
nuclear@3 35 CMD(VN), \
nuclear@3 36 CMD(VT), \
nuclear@3 37 CMD(F), \
nuclear@3 38 CMD(O), \
nuclear@3 39 CMD(G), \
nuclear@3 40 CMD(MTLLIB), \
nuclear@3 41 CMD(USEMTL), \
nuclear@3 42 CMD(NEWMTL), \
nuclear@6 43 CMD(KE), \
nuclear@3 44 CMD(KA), \
nuclear@3 45 CMD(KD), \
nuclear@3 46 CMD(KS), \
nuclear@3 47 CMD(NS), \
nuclear@3 48 CMD(NI), \
nuclear@3 49 CMD(D), \
nuclear@3 50 CMD(TR), \
nuclear@3 51 CMD(MAP_KD), \
nuclear@6 52 CMD(MAP_REFL), \
nuclear@6 53 CMD(MAP_BUMP), \
nuclear@3 54 CMD(MAP_KS), \
nuclear@3 55 CMD(MAP_NS), \
nuclear@3 56 CMD(MAP_D), \
nuclear@3 57 CMD(REFL), \
nuclear@3 58 CMD(BUMP)
nuclear@3 59
nuclear@3 60 #define CMD(x) CMD_##x
nuclear@3 61 enum {
nuclear@3 62 COMMANDS,
nuclear@3 63 CMD_UNK
nuclear@3 64 };
nuclear@3 65 #undef CMD
nuclear@3 66
nuclear@3 67 #define CMD(x) #x
nuclear@3 68 static const char *cmd_names[] = {
nuclear@3 69 COMMANDS,
nuclear@3 70 0
nuclear@3 71 };
nuclear@3 72 #undef CMD
nuclear@3 73
nuclear@3 74
nuclear@3 75 struct ObjFace {
nuclear@3 76 int elem;
nuclear@3 77 int v[4], n[4], t[4];
nuclear@3 78 };
nuclear@3 79
nuclear@3 80 struct ObjFile {
nuclear@3 81 string cur_obj, cur_mat;
nuclear@3 82 vector<Vector3> v, vn;
nuclear@3 83 vector<Vector2> vt;
nuclear@3 84 vector<ObjFace> f;
nuclear@3 85 };
nuclear@3 86
nuclear@3 87 typedef Vector3 Color;
nuclear@3 88
nuclear@3 89 struct ObjMat {
nuclear@3 90 string name; // newmtl <name>
nuclear@3 91 Color ambient, diffuse, specular; // Ka, Kd, Ks
nuclear@6 92 Color emissive; // Ke
nuclear@3 93 float shininess; // Ns
nuclear@3 94 float ior; // Ni
nuclear@3 95 float alpha; // d, Tr
nuclear@3 96
nuclear@3 97 string tex_dif, tex_spec, tex_shin, tex_alpha; // map_Kd, map_Ks, map_Ns, map_d
nuclear@3 98 string tex_refl; // refl -type sphere|cube file
nuclear@3 99 string tex_bump; // bump
nuclear@3 100
nuclear@3 101 ObjMat() { reset(); }
nuclear@3 102
nuclear@3 103 void reset() {
nuclear@3 104 ambient = diffuse = Color(0.5, 0.5, 0.5);
nuclear@3 105 specular = Color(0.0, 0.0, 0.0);
nuclear@3 106 name = tex_dif = tex_spec = tex_shin = tex_alpha = tex_refl = tex_bump = "";
nuclear@3 107 shininess = 0;
nuclear@3 108 ior = alpha = 1;
nuclear@3 109 }
nuclear@3 110 };
nuclear@3 111
nuclear@3 112 static bool read_materials(FILE *fp, vector<ObjMat> *vmtl);
nuclear@3 113 static Object *cons_object(ObjFile *obj);
nuclear@3 114
nuclear@3 115 static int get_cmd(char *str);
nuclear@3 116 static bool is_int(const char *str);
nuclear@3 117 static bool is_float(const char *str);
nuclear@3 118 static bool parse_vec(Vector3 *vec);
nuclear@3 119 static bool parse_color(Color *col);
nuclear@3 120 static bool parse_face(ObjFace *face);
nuclear@3 121 static const char *parse_map();
nuclear@3 122
nuclear@3 123
nuclear@3 124 static map<string, Material> matlib;
nuclear@3 125
nuclear@3 126
nuclear@3 127 #define INVALID_IDX INT_MIN
nuclear@3 128
nuclear@3 129 #define SEP " \t\n\r\v"
nuclear@3 130 #define BUF_SZ 512
nuclear@3 131 bool Scene::load_obj(FILE *fp)
nuclear@3 132 {
nuclear@3 133 static int seq;
nuclear@3 134 char cur_name[16];
nuclear@3 135 stringstream sstr;
nuclear@3 136
nuclear@3 137 ObjFile obj;
nuclear@3 138
nuclear@3 139 sprintf(cur_name, "default%02d.obj", seq++);
nuclear@3 140 obj.cur_obj = cur_name;
nuclear@3 141
nuclear@3 142 int prev_cmd = 0, obj_added = 0;
nuclear@3 143 for(;;) {
nuclear@3 144 Vector3 vec;
nuclear@3 145 ObjFace face;
nuclear@3 146
nuclear@3 147 char line[BUF_SZ];
nuclear@3 148 fgets(line, sizeof line, fp);
nuclear@3 149 if(feof(fp)) {
nuclear@3 150 break;
nuclear@3 151 }
nuclear@3 152
nuclear@3 153 char *tok;
nuclear@3 154 if(!(tok = strtok(line, SEP))) {
nuclear@3 155 continue; // ignore empty lines
nuclear@3 156 }
nuclear@3 157
nuclear@3 158 int cmd;
nuclear@3 159 if((cmd = get_cmd(tok)) == -1) {
nuclear@3 160 continue; // ignore unknown commands ...
nuclear@3 161 }
nuclear@3 162
nuclear@3 163 switch(cmd) {
nuclear@3 164 case CMD_V:
nuclear@3 165 if(!parse_vec(&vec)) {
nuclear@3 166 continue;
nuclear@3 167 }
nuclear@3 168 obj.v.push_back(vec);
nuclear@3 169 break;
nuclear@3 170
nuclear@3 171 case CMD_VN:
nuclear@3 172 if(!parse_vec(&vec)) {
nuclear@3 173 continue;
nuclear@3 174 }
nuclear@3 175 obj.vn.push_back(vec);
nuclear@3 176 break;
nuclear@3 177
nuclear@3 178 case CMD_VT:
nuclear@3 179 if(!parse_vec(&vec)) {
nuclear@3 180 continue;
nuclear@3 181 }
nuclear@3 182 vec.y = 1.0 - vec.y;
nuclear@3 183 obj.vt.push_back(Vector2(vec.x, vec.y));
nuclear@3 184 break;
nuclear@3 185
nuclear@3 186 case CMD_O:
nuclear@3 187 case CMD_G:
nuclear@3 188 if(prev_cmd == CMD_O || prev_cmd == CMD_G) {
nuclear@3 189 break; // just in case we've got both of them in a row
nuclear@3 190 }
nuclear@3 191 /* if we have any previous data, group them up, add the object
nuclear@3 192 * and continue with the new one...
nuclear@3 193 */
nuclear@3 194 if(!obj.f.empty()) {
nuclear@3 195 Object *robj = cons_object(&obj);
nuclear@3 196 robj->mtl = matlib[obj.cur_mat];
nuclear@3 197 add_object(robj);
nuclear@3 198 obj_added++;
nuclear@3 199
nuclear@3 200 obj.f.clear(); // clean the face list
nuclear@3 201 }
nuclear@3 202 if((tok = strtok(0, SEP))) {
nuclear@3 203 obj.cur_obj = tok;
nuclear@3 204 } else {
nuclear@3 205 sprintf(cur_name, "default%02d.obj", seq++);
nuclear@3 206 obj.cur_obj = cur_name;
nuclear@3 207 }
nuclear@3 208 break;
nuclear@3 209
nuclear@3 210 case CMD_MTLLIB:
nuclear@3 211 if((tok = strtok(0, SEP))) {
nuclear@3 212 FILE *mfile;
nuclear@3 213 if(!(mfile = fopen(tok, "rb"))) {
nuclear@3 214 fprintf(stderr, "failed to open material library: %s\n", tok);
nuclear@3 215 continue;
nuclear@3 216 }
nuclear@3 217
nuclear@3 218 // load all materials of the mtl file into a vector
nuclear@3 219 vector<ObjMat> vmtl;
nuclear@3 220 if(!read_materials(mfile, &vmtl)) {
nuclear@3 221 continue;
nuclear@3 222 }
nuclear@3 223 fclose(mfile);
nuclear@3 224
nuclear@3 225 // and add them all to the scene
nuclear@3 226 for(size_t i=0; i<vmtl.size(); i++) {
nuclear@3 227 Material mat;
nuclear@3 228 mat.ambient = vmtl[i].ambient;
nuclear@3 229 mat.diffuse = vmtl[i].diffuse;
nuclear@3 230 mat.specular = vmtl[i].specular;
nuclear@3 231 mat.shininess = vmtl[i].shininess;
nuclear@6 232 mat.emissive = vmtl[i].emissive;
nuclear@3 233 mat.alpha = vmtl[i].alpha;
nuclear@3 234
nuclear@5 235 if(vmtl[i].tex_dif.length()) {
nuclear@5 236 mat.tex[TEX_DIFFUSE] = load_texture(vmtl[i].tex_dif.c_str());
nuclear@5 237 }
nuclear@5 238 if(vmtl[i].tex_refl.length()) {
nuclear@5 239 mat.tex[TEX_ENVMAP] = load_texture(vmtl[i].tex_refl.c_str());
nuclear@5 240 }
nuclear@3 241
nuclear@3 242 matlib[vmtl[i].name] = mat;
nuclear@3 243 }
nuclear@3 244 }
nuclear@3 245 break;
nuclear@3 246
nuclear@3 247 case CMD_USEMTL:
nuclear@3 248 if((tok = strtok(0, SEP))) {
nuclear@3 249 obj.cur_mat = tok;
nuclear@3 250 } else {
nuclear@3 251 obj.cur_mat = "";
nuclear@3 252 }
nuclear@3 253 break;
nuclear@3 254
nuclear@3 255 case CMD_F:
nuclear@3 256 if(!parse_face(&face)) {
nuclear@3 257 continue;
nuclear@3 258 }
nuclear@3 259
nuclear@3 260 // convert negative indices to regular indices
nuclear@3 261 for(int i=0; i<4; i++) {
nuclear@3 262 if(face.v[i] < 0 && face.v[i] != INVALID_IDX) {
nuclear@3 263 face.v[i] = obj.v.size() + face.v[i];
nuclear@3 264 }
nuclear@3 265 if(face.n[i] < 0 && face.n[i] != INVALID_IDX) {
nuclear@3 266 face.n[i] = obj.vn.size() + face.n[i];
nuclear@3 267 }
nuclear@3 268 if(face.t[i] < 0 && face.t[i] != INVALID_IDX) {
nuclear@3 269 face.t[i] = obj.vt.size() + face.t[i];
nuclear@3 270 }
nuclear@3 271 }
nuclear@3 272
nuclear@3 273 // break quads into triangles if needed
nuclear@3 274 obj.f.push_back(face);
nuclear@3 275 if(face.elem == 4) {
nuclear@3 276 face.v[1] = face.v[2];
nuclear@3 277 face.n[1] = face.n[2];
nuclear@3 278 face.t[1] = face.t[2];
nuclear@3 279
nuclear@3 280 face.v[2] = face.v[3];
nuclear@3 281 face.n[2] = face.n[3];
nuclear@3 282 face.t[2] = face.t[3];
nuclear@3 283
nuclear@3 284 obj.f.push_back(face);
nuclear@3 285 }
nuclear@3 286 break;
nuclear@3 287
nuclear@3 288 default:
nuclear@3 289 break; // ignore unknown commands
nuclear@3 290 }
nuclear@3 291
nuclear@3 292 prev_cmd = cmd;
nuclear@3 293 }
nuclear@3 294
nuclear@3 295 // reached end of file...
nuclear@3 296 if(!obj.f.empty()) {
nuclear@3 297 Object *robj = cons_object(&obj);
nuclear@3 298 robj->mtl = matlib[obj.cur_mat];
nuclear@3 299 add_object(robj);
nuclear@3 300 obj_added++;
nuclear@3 301 }
nuclear@3 302
nuclear@3 303 return obj_added > 0;
nuclear@3 304 }
nuclear@3 305
nuclear@3 306 static Object *cons_object(ObjFile *obj)
nuclear@3 307 {
nuclear@3 308 Object *robj;
nuclear@3 309 Vector3 *varr, *narr;
nuclear@3 310 Vector2 *tarr;
nuclear@3 311
nuclear@3 312 int nelem = obj->f.size() * 3;
nuclear@3 313
nuclear@4 314 assert(sizeof(Vector3) == 3 * sizeof(float));
nuclear@4 315 assert(sizeof(Vector2) == 2 * sizeof(float));
nuclear@4 316
nuclear@3 317 try {
nuclear@3 318 robj = new Object;
nuclear@3 319 varr = new Vector3[nelem];
nuclear@3 320 narr = new Vector3[nelem];
nuclear@3 321 tarr = new Vector2[nelem];
nuclear@3 322 }
nuclear@3 323 catch(...) {
nuclear@3 324 return 0;
nuclear@3 325 }
nuclear@4 326 if(obj->cur_obj.length() > 0) {
nuclear@3 327 robj->set_name(obj->cur_obj.c_str());
nuclear@4 328 }
nuclear@3 329
nuclear@3 330 // need at least one of each element
nuclear@3 331 bool added_norm = false, added_tc = false;
nuclear@3 332 if(obj->vn.empty()) {
nuclear@3 333 obj->vn.push_back(Vector3(0, 0, 0));
nuclear@4 334 added_norm = true;
nuclear@3 335 }
nuclear@3 336 if(obj->vt.empty()) {
nuclear@3 337 obj->vt.push_back(Vector2(0, 0));
nuclear@3 338 added_tc = true;
nuclear@3 339 }
nuclear@3 340
nuclear@3 341 for(size_t i=0; i<obj->f.size(); i++) {
nuclear@3 342 for(int j=0; j<3; j++) {
nuclear@3 343 int idx = i * 3 + j;
nuclear@3 344 ObjFace *f = &obj->f[i];
nuclear@3 345
nuclear@3 346 varr[idx] = obj->v[f->v[j]];
nuclear@3 347 narr[idx] = obj->vn[f->n[j] < 0 ? 0 : f->n[j]];
nuclear@3 348
nuclear@3 349 float t = obj->vt[f->t[j] < 0 ? 0 : f->t[j]].x;
nuclear@3 350 float s = obj->vt[f->t[j] < 0 ? 0 : f->t[j]].y;
nuclear@3 351 tarr[idx] = Vector2(t, s);
nuclear@3 352 }
nuclear@3 353 }
nuclear@3 354
nuclear@3 355 if(added_norm) {
nuclear@3 356 obj->vn.pop_back();
nuclear@3 357 }
nuclear@3 358 if(added_tc) {
nuclear@3 359 obj->vt.pop_back();
nuclear@3 360 }
nuclear@3 361
nuclear@4 362 Mesh *mesh = new Mesh;
nuclear@3 363 mesh->set_attrib(MESH_ATTR_VERTEX, nelem, 3, &varr->x);
nuclear@3 364 mesh->set_attrib(MESH_ATTR_NORMAL, nelem, 3, &narr->x);
nuclear@3 365 mesh->set_attrib(MESH_ATTR_TEXCOORD, nelem, 2, &tarr->x);
nuclear@4 366 robj->set_mesh(mesh);
nuclear@4 367
nuclear@4 368 printf("loaded object %s: %d faces\n", obj->cur_obj.c_str(), nelem / 3);
nuclear@3 369
nuclear@3 370 delete [] varr;
nuclear@3 371 delete [] narr;
nuclear@3 372 delete [] tarr;
nuclear@3 373 return robj;
nuclear@3 374 }
nuclear@3 375
nuclear@3 376 static bool read_materials(FILE *fp, vector<ObjMat> *vmtl)
nuclear@3 377 {
nuclear@3 378 ObjMat mat;
nuclear@3 379
nuclear@3 380 for(;;) {
nuclear@3 381 char line[BUF_SZ];
nuclear@3 382 fgets(line, sizeof line, fp);
nuclear@3 383 if(feof(fp)) {
nuclear@3 384 break;
nuclear@3 385 }
nuclear@3 386
nuclear@3 387 char *tok;
nuclear@3 388 if(!(tok = strtok(line, SEP))) {
nuclear@3 389 continue;
nuclear@3 390 }
nuclear@3 391
nuclear@3 392 int cmd;
nuclear@3 393 if((cmd = get_cmd(tok)) == -1) {
nuclear@3 394 continue;
nuclear@3 395 }
nuclear@3 396
nuclear@3 397 switch(cmd) {
nuclear@3 398 case CMD_NEWMTL:
nuclear@3 399 // add the previous material, and start a new one
nuclear@3 400 if(mat.name.length() > 0) {
nuclear@3 401 vmtl->push_back(mat);
nuclear@3 402 mat.reset();
nuclear@3 403 }
nuclear@3 404 if((tok = strtok(0, SEP))) {
nuclear@3 405 mat.name = tok;
nuclear@3 406 }
nuclear@3 407 break;
nuclear@3 408
nuclear@6 409 case CMD_KE:
nuclear@6 410 parse_color(&mat.emissive);
nuclear@6 411 break;
nuclear@6 412
nuclear@3 413 case CMD_KA:
nuclear@3 414 parse_color(&mat.ambient);
nuclear@3 415 break;
nuclear@3 416
nuclear@3 417 case CMD_KD:
nuclear@3 418 parse_color(&mat.diffuse);
nuclear@3 419 break;
nuclear@3 420
nuclear@3 421 case CMD_KS:
nuclear@3 422 parse_color(&mat.specular);
nuclear@3 423 break;
nuclear@3 424
nuclear@3 425 case CMD_NS:
nuclear@3 426 if((tok = strtok(0, SEP)) && is_float(tok)) {
nuclear@3 427 mat.shininess = atof(tok);
nuclear@3 428 }
nuclear@3 429 break;
nuclear@3 430
nuclear@3 431 case CMD_NI:
nuclear@3 432 if((tok = strtok(0, SEP)) && is_float(tok)) {
nuclear@3 433 mat.ior = atof(tok);
nuclear@3 434 }
nuclear@3 435 break;
nuclear@3 436
nuclear@3 437 case CMD_D:
nuclear@3 438 case CMD_TR:
nuclear@3 439 {
nuclear@3 440 Color c;
nuclear@3 441 if(parse_color(&c)) {
nuclear@3 442 mat.alpha = cmd == CMD_D ? c.x : 1.0 - c.x;
nuclear@3 443 }
nuclear@3 444 }
nuclear@3 445 break;
nuclear@3 446
nuclear@3 447 case CMD_MAP_KD:
nuclear@3 448 mat.tex_dif = parse_map();
nuclear@3 449 break;
nuclear@3 450
nuclear@6 451 case CMD_MAP_REFL:
nuclear@6 452 mat.tex_refl = parse_map();
nuclear@6 453 break;
nuclear@6 454
nuclear@3 455 default:
nuclear@3 456 break;
nuclear@3 457 }
nuclear@3 458 }
nuclear@3 459
nuclear@3 460 if(mat.name.length() > 0) {
nuclear@3 461 vmtl->push_back(mat);
nuclear@3 462 }
nuclear@3 463 return true;
nuclear@3 464 }
nuclear@3 465
nuclear@3 466 static int get_cmd(char *str)
nuclear@3 467 {
nuclear@3 468 char *s = str;
nuclear@3 469 while((*s = toupper(*s))) s++;
nuclear@3 470
nuclear@3 471 for(int i=0; cmd_names[i]; i++) {
nuclear@3 472 if(strcmp(str, cmd_names[i]) == 0) {
nuclear@3 473 return i;
nuclear@3 474 }
nuclear@3 475 }
nuclear@3 476 return CMD_UNK;
nuclear@3 477 }
nuclear@3 478
nuclear@3 479 static bool is_int(const char *str)
nuclear@3 480 {
nuclear@3 481 char *tmp;
nuclear@3 482 strtol(str, &tmp, 10);
nuclear@3 483 return tmp != str;
nuclear@3 484 }
nuclear@3 485
nuclear@3 486 static bool is_float(const char *str)
nuclear@3 487 {
nuclear@3 488 char *tmp;
nuclear@3 489 strtod(str, &tmp);
nuclear@3 490 return tmp != str;
nuclear@3 491 }
nuclear@3 492
nuclear@3 493 static bool parse_vec(Vector3 *vec)
nuclear@3 494 {
nuclear@3 495 for(int i=0; i<3; i++) {
nuclear@3 496 char *tok;
nuclear@3 497
nuclear@3 498 if(!(tok = strtok(0, SEP)) || !is_float(tok)) {
nuclear@3 499 if(i < 2) {
nuclear@3 500 return false;
nuclear@3 501 }
nuclear@3 502 vec->z = 0.0;
nuclear@3 503 } else {
nuclear@3 504 (*vec)[i] = atof(tok);
nuclear@3 505 }
nuclear@3 506 }
nuclear@3 507 return true;
nuclear@3 508 }
nuclear@3 509
nuclear@3 510 static bool parse_color(Color *col)
nuclear@3 511 {
nuclear@3 512 for(int i=0; i<3; i++) {
nuclear@3 513 char *tok;
nuclear@3 514
nuclear@3 515 if(!(tok = strtok(0, SEP)) || !is_float(tok)) {
nuclear@3 516 col->y = col->z = col->x;
nuclear@3 517 return i > 0 ? true : false;
nuclear@3 518 }
nuclear@3 519 (*col)[i] = atof(tok);
nuclear@3 520 }
nuclear@3 521 return true;
nuclear@3 522 }
nuclear@3 523
nuclear@3 524 static bool parse_face(ObjFace *face)
nuclear@3 525 {
nuclear@3 526 char *tok[] = {0, 0, 0, 0};
nuclear@3 527 face->elem = 0;
nuclear@3 528
nuclear@3 529 for(int i=0; i<4; i++) {
nuclear@3 530 if((!(tok[i] = strtok(0, SEP)) || !is_int(tok[i]))) {
nuclear@3 531 if(i < 3) return false; // less than 3 verts? not a polygon
nuclear@3 532 } else {
nuclear@3 533 face->elem++;
nuclear@3 534 }
nuclear@3 535 }
nuclear@3 536
nuclear@3 537 for(int i=0; i<4; i++) {
nuclear@3 538 char *subtok = tok[i];
nuclear@3 539
nuclear@3 540 if(!subtok || !*subtok || !is_int(subtok)) {
nuclear@3 541 if(i < 3) {
nuclear@3 542 return false;
nuclear@3 543 }
nuclear@3 544 face->v[i] = INVALID_IDX;
nuclear@3 545 } else {
nuclear@3 546 face->v[i] = atoi(subtok);
nuclear@3 547 if(face->v[i] > 0) face->v[i]--; /* convert to 0-based */
nuclear@3 548 }
nuclear@3 549
nuclear@3 550 while(subtok && *subtok && *subtok != '/') {
nuclear@3 551 subtok++;
nuclear@3 552 }
nuclear@3 553 if(subtok && *subtok && *++subtok && is_int(subtok)) {
nuclear@3 554 face->t[i] = atoi(subtok);
nuclear@3 555 if(face->t[i] > 0) face->t[i]--; /* convert to 0-based */
nuclear@3 556 } else {
nuclear@3 557 face->t[i] = INVALID_IDX;
nuclear@3 558 }
nuclear@3 559
nuclear@3 560 while(subtok && *subtok && *subtok != '/') {
nuclear@3 561 subtok++;
nuclear@3 562 }
nuclear@3 563 if(subtok && *subtok && *++subtok && is_int(subtok)) {
nuclear@3 564 face->n[i] = atoi(subtok);
nuclear@3 565 if(face->n[i] > 0) face->n[i]--; /* convert to 0-based */
nuclear@3 566 } else {
nuclear@3 567 face->n[i] = INVALID_IDX;
nuclear@3 568 }
nuclear@3 569 }
nuclear@3 570
nuclear@3 571 return true;
nuclear@3 572 }
nuclear@3 573
nuclear@3 574 static const char *parse_map()
nuclear@3 575 {
nuclear@3 576 char *tok, *prev = 0;
nuclear@3 577
nuclear@3 578 while((tok = strtok(0, SEP))) {
nuclear@3 579 prev = tok;
nuclear@3 580 }
nuclear@3 581
nuclear@3 582 return prev ? prev : "";
nuclear@3 583 }