tinyobjloader.h (43089B)
1 /* 2 The MIT License (MIT) 3 4 Copyright (c) 2016 Syoyo Fujita and many contributors. 5 6 Permission is hereby granted, free of charge, to any person obtaining a copy 7 of this software and associated documentation files (the "Software"), to deal 8 in the Software without restriction, including without limitation the rights 9 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 copies of the Software, and to permit persons to whom the Software is 11 furnished to do so, subject to the following conditions: 12 13 The above copyright notice and this permission notice shall be included in 14 all copies or substantial portions of the Software. 15 16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 THE SOFTWARE. 23 */ 24 #ifndef TINOBJ_LOADER_C_H_ 25 #define TINOBJ_LOADER_C_H_ 26 27 /* @todo { Remoe stdlib dependency. size_t? } */ 28 #include <stdlib.h> 29 30 typedef struct { 31 char *name; 32 33 float ambient[3]; 34 float diffuse[3]; 35 float specular[3]; 36 float transmittance[3]; 37 float emission[3]; 38 float shininess; 39 float ior; /* index of refraction */ 40 float dissolve; /* 1 == opaque; 0 == fully transparent */ 41 /* illumination model (see http://www.fileformat.info/format/material/) */ 42 int illum; 43 44 int pad0; 45 46 char *ambient_texname; /* map_Ka */ 47 char *diffuse_texname; /* map_Kd */ 48 char *specular_texname; /* map_Ks */ 49 char *specular_highlight_texname; /* map_Ns */ 50 char *bump_texname; /* map_bump, bump */ 51 char *displacement_texname; /* disp */ 52 char *alpha_texname; /* map_d */ 53 } tinyobj_material_t; 54 55 typedef struct { 56 char *name; /* group name or object name. */ 57 unsigned int face_offset; 58 unsigned int length; 59 } tinyobj_shape_t; 60 61 typedef struct { int v_idx, vt_idx, vn_idx; } tinyobj_vertex_index_t; 62 63 typedef struct { 64 unsigned int num_vertices; 65 unsigned int num_normals; 66 unsigned int num_texcoords; 67 unsigned int num_faces; 68 unsigned int num_face_num_verts; 69 70 int pad0; 71 72 float *vertices; 73 float *normals; 74 float *texcoords; 75 tinyobj_vertex_index_t *faces; 76 int *face_num_verts; 77 int *material_ids; 78 } tinyobj_attrib_t; 79 80 81 #define TINYOBJ_FLAG_TRIANGULATE (1 << 0) 82 83 #define TINYOBJ_INVALID_INDEX (0x80000000) 84 85 #define TINYOBJ_SUCCESS (0) 86 #define TINYOBJ_ERROR_EMPTY (-1) 87 #define TINYOBJ_ERROR_INVALID_PARAMETER (-2) 88 #define TINYOBJ_ERROR_FILE_OPERATION (-3) 89 90 /* Parse wavefront .obj(.obj string data is expanded to linear char array `buf') 91 * flags are combination of TINYOBJ_FLAG_*** 92 * Retruns TINYOBJ_SUCCESS if things goes well. 93 * Retruns TINYOBJ_ERR_*** when there is an error. 94 */ 95 extern int tinyobj_parse_obj(tinyobj_attrib_t *attrib, tinyobj_shape_t **shapes, 96 size_t *num_shapes, tinyobj_material_t **materials, 97 size_t *num_materials, const char *buf, size_t len, 98 unsigned int flags); 99 extern int tinyobj_parse_mtl_file(tinyobj_material_t **materials_out, 100 size_t *num_materials_out, 101 const char *filename); 102 103 extern void tinyobj_attrib_init(tinyobj_attrib_t *attrib); 104 extern void tinyobj_attrib_free(tinyobj_attrib_t *attrib); 105 extern void tinyobj_shapes_free(tinyobj_shape_t *shapes, size_t num_shapes); 106 extern void tinyobj_materials_free(tinyobj_material_t *materials, 107 size_t num_materials); 108 109 #ifdef TINYOBJ_LOADER_C_IMPLEMENTATION 110 #include <stdio.h> 111 #include <assert.h> 112 #include <string.h> 113 114 115 #define TINYOBJ_MAX_FACES_PER_F_LINE (32) 116 117 #define IS_SPACE(x) (((x) == ' ') || ((x) == '\t')) 118 #define IS_DIGIT(x) ((unsigned int)((x) - '0') < (unsigned int)(10)) 119 #define IS_NEW_LINE(x) (((x) == '\r') || ((x) == '\n') || ((x) == '\0')) 120 121 static void skip_space(const char **token) { 122 while ((*token)[0] == ' ' || (*token)[0] == '\t') { 123 (*token)++; 124 } 125 } 126 127 static void skip_space_and_cr(const char **token) { 128 while ((*token)[0] == ' ' || (*token)[0] == '\t' || (*token)[0] == '\r') { 129 (*token)++; 130 } 131 } 132 133 static int until_space(const char *token) { 134 const char *p = token; 135 while (p[0] != '\0' && p[0] != ' ' && p[0] != '\t' && p[0] != '\r') { 136 p++; 137 } 138 139 return (int)(p - token); 140 } 141 142 static int length_until_newline(const char *token, size_t n) { 143 size_t len = 0; 144 145 /* Assume token[n-1] = '\0' */ 146 for (len = 0; len < n - 1; len++) { 147 if (token[len] == '\n') { 148 break; 149 } 150 if ((token[len] == '\r') && ((len < (n - 2)) && (token[len + 1] != '\n'))) { 151 break; 152 } 153 } 154 155 return (int)len; 156 } 157 158 /* http://stackoverflow.com/questions/5710091/how-does-atoi-function-in-c-work 159 */ 160 static int my_atoi(const char *c) { 161 int value = 0; 162 int sign = 1; 163 if (*c == '+' || *c == '-') { 164 if (*c == '-') sign = -1; 165 c++; 166 } 167 while (((*c) >= '0') && ((*c) <= '9')) { /* isdigit(*c) */ 168 value *= 10; 169 value += (int)(*c - '0'); 170 c++; 171 } 172 return value * sign; 173 } 174 175 /* Make index zero-base, and also support relative index. */ 176 static int fixIndex(int idx, size_t n) { 177 if (idx > 0) return idx - 1; 178 if (idx == 0) return 0; 179 return (int)n + idx; /* negative value = relative */ 180 } 181 182 /* Parse raw triples: i, i/j/k, i//k, i/j */ 183 static tinyobj_vertex_index_t parseRawTriple(const char **token) { 184 tinyobj_vertex_index_t vi; 185 /* 0x80000000 = -2147483648 = invalid */ 186 vi.v_idx = (int)(0x80000000); 187 vi.vn_idx = (int)(0x80000000); 188 vi.vt_idx = (int)(0x80000000); 189 190 vi.v_idx = my_atoi((*token)); 191 while ((*token)[0] != '\0' && (*token)[0] != '/' && (*token)[0] != ' ' && 192 (*token)[0] != '\t' && (*token)[0] != '\r') { 193 (*token)++; 194 } 195 if ((*token)[0] != '/') { 196 return vi; 197 } 198 (*token)++; 199 200 /* i//k */ 201 if ((*token)[0] == '/') { 202 (*token)++; 203 vi.vn_idx = my_atoi((*token)); 204 while ((*token)[0] != '\0' && (*token)[0] != '/' && (*token)[0] != ' ' && 205 (*token)[0] != '\t' && (*token)[0] != '\r') { 206 (*token)++; 207 } 208 return vi; 209 } 210 211 /* i/j/k or i/j */ 212 vi.vt_idx = my_atoi((*token)); 213 while ((*token)[0] != '\0' && (*token)[0] != '/' && (*token)[0] != ' ' && 214 (*token)[0] != '\t' && (*token)[0] != '\r') { 215 (*token)++; 216 } 217 if ((*token)[0] != '/') { 218 return vi; 219 } 220 221 /* i/j/k */ 222 (*token)++; /* skip '/' */ 223 vi.vn_idx = my_atoi((*token)); 224 while ((*token)[0] != '\0' && (*token)[0] != '/' && (*token)[0] != ' ' && 225 (*token)[0] != '\t' && (*token)[0] != '\r') { 226 (*token)++; 227 } 228 return vi; 229 } 230 231 static int parseInt(const char **token) { 232 int i = 0; 233 skip_space(token); 234 i = my_atoi((*token)); 235 (*token) += until_space((*token)); 236 return i; 237 } 238 239 /* 240 * Tries to parse a floating point number located at s. 241 * 242 * s_end should be a location in the string where reading should absolutely 243 * stop. For example at the end of the string, to prevent buffer overflows. 244 * 245 * Parses the following EBNF grammar: 246 * sign = "+" | "-" ; 247 * END = ? anything not in digit ? 248 * digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" ; 249 * integer = [sign] , digit , {digit} ; 250 * decimal = integer , ["." , integer] ; 251 * float = ( decimal , END ) | ( decimal , ("E" | "e") , integer , END ) ; 252 * 253 * Valid strings are for example: 254 * -0 +3.1417e+2 -0.0E-3 1.0324 -1.41 11e2 255 * 256 * If the parsing is a success, result is set to the parsed value and true 257 * is returned. 258 * 259 * The function is greedy and will parse until any of the following happens: 260 * - a non-conforming character is encountered. 261 * - s_end is reached. 262 * 263 * The following situations triggers a failure: 264 * - s >= s_end. 265 * - parse failure. 266 */ 267 static int tryParseDouble(const char *s, const char *s_end, double *result) { 268 double mantissa = 0.0; 269 /* This exponent is base 2 rather than 10. 270 * However the exponent we parse is supposed to be one of ten, 271 * thus we must take care to convert the exponent/and or the 272 * mantissa to a * 2^E, where a is the mantissa and E is the 273 * exponent. 274 * To get the final double we will use ldexp, it requires the 275 * exponent to be in base 2. 276 */ 277 int exponent = 0; 278 279 /* NOTE: THESE MUST BE DECLARED HERE SINCE WE ARE NOT ALLOWED 280 * TO JUMP OVER DEFINITIONS. 281 */ 282 char sign = '+'; 283 char exp_sign = '+'; 284 char const *curr = s; 285 286 /* How many characters were read in a loop. */ 287 int read = 0; 288 /* Tells whether a loop terminated due to reaching s_end. */ 289 int end_not_reached = 0; 290 291 /* 292 BEGIN PARSING. 293 */ 294 295 if (s >= s_end) { 296 return 0; /* fail */ 297 } 298 299 /* Find out what sign we've got. */ 300 if (*curr == '+' || *curr == '-') { 301 sign = *curr; 302 curr++; 303 } else if (IS_DIGIT(*curr)) { /* Pass through. */ 304 } else { 305 goto fail; 306 } 307 308 /* Read the integer part. */ 309 end_not_reached = (curr != s_end); 310 while (end_not_reached && IS_DIGIT(*curr)) { 311 mantissa *= 10; 312 mantissa += (int)(*curr - 0x30); 313 curr++; 314 read++; 315 end_not_reached = (curr != s_end); 316 } 317 318 /* We must make sure we actually got something. */ 319 if (read == 0) goto fail; 320 /* We allow numbers of form "#", "###" etc. */ 321 if (!end_not_reached) goto assemble; 322 323 /* Read the decimal part. */ 324 if (*curr == '.') { 325 curr++; 326 read = 1; 327 end_not_reached = (curr != s_end); 328 while (end_not_reached && IS_DIGIT(*curr)) { 329 /* pow(10.0, -read) */ 330 double frac_value = 1.0; 331 int f; 332 for (f = 0; f < read; f++) { 333 frac_value *= 0.1; 334 } 335 mantissa += (int)(*curr - 0x30) * frac_value; 336 read++; 337 curr++; 338 end_not_reached = (curr != s_end); 339 } 340 } else if (*curr == 'e' || *curr == 'E') { 341 } else { 342 goto assemble; 343 } 344 345 if (!end_not_reached) goto assemble; 346 347 /* Read the exponent part. */ 348 if (*curr == 'e' || *curr == 'E') { 349 curr++; 350 /* Figure out if a sign is present and if it is. */ 351 end_not_reached = (curr != s_end); 352 if (end_not_reached && (*curr == '+' || *curr == '-')) { 353 exp_sign = *curr; 354 curr++; 355 } else if (IS_DIGIT(*curr)) { /* Pass through. */ 356 } else { 357 /* Empty E is not allowed. */ 358 goto fail; 359 } 360 361 read = 0; 362 end_not_reached = (curr != s_end); 363 while (end_not_reached && IS_DIGIT(*curr)) { 364 exponent *= 10; 365 exponent += (int)(*curr - 0x30); 366 curr++; 367 read++; 368 end_not_reached = (curr != s_end); 369 } 370 exponent *= (exp_sign == '+' ? 1 : -1); 371 if (read == 0) goto fail; 372 } 373 374 assemble : 375 376 { 377 /* = pow(5.0, exponent); */ 378 double a = 1.0; 379 int i; 380 for (i = 0; i < exponent; i++) { 381 a = a * 5.0; 382 } 383 *result = 384 /* (sign == '+' ? 1 : -1) * ldexp(mantissa * pow(5.0, exponent), 385 exponent); */ 386 (sign == '+' ? 1 : -1) * (mantissa * a) * 387 (double)(1 << exponent); /* 5.0^exponent * 2^exponent */ 388 } 389 390 return 1; 391 fail: 392 return 0; 393 } 394 395 static float parseFloat(const char **token) { 396 const char *end; 397 double val = 0.0; 398 float f = 0.0f; 399 skip_space(token); 400 end = (*token) + until_space((*token)); 401 val = 0.0; 402 tryParseDouble((*token), end, &val); 403 f = (float)(val); 404 (*token) = end; 405 return f; 406 } 407 408 static void parseFloat2(float *x, float *y, const char **token) { 409 (*x) = parseFloat(token); 410 (*y) = parseFloat(token); 411 } 412 413 static void parseFloat3(float *x, float *y, float *z, const char **token) { 414 (*x) = parseFloat(token); 415 (*y) = parseFloat(token); 416 (*z) = parseFloat(token); 417 } 418 419 static char *my_strdup(const char *s) { 420 char *d; 421 size_t len; 422 423 if (s == NULL) return NULL; 424 425 len = strlen(s); 426 427 d = (char *)malloc(len + 1); /* + '\0' */ 428 memcpy(d, s, (size_t)len); 429 d[len] = '\0'; 430 431 return d; 432 } 433 434 static char *my_strndup(const char *s, size_t len) { 435 char *d; 436 size_t slen; 437 438 if (s == NULL) return NULL; 439 if (len == 0) return NULL; 440 441 d = (char *)malloc(len + 1); /* + '\0' */ 442 slen = strlen(s); 443 if (slen < len) { 444 memcpy(d, s, slen); 445 d[slen] = '\0'; 446 } else { 447 memcpy(d, s, len); 448 d[len] = '\0'; 449 } 450 451 return d; 452 } 453 454 static void initMaterial(tinyobj_material_t *material) { 455 int i; 456 material->name = NULL; 457 material->ambient_texname = NULL; 458 material->diffuse_texname = NULL; 459 material->specular_texname = NULL; 460 material->specular_highlight_texname = NULL; 461 material->bump_texname = NULL; 462 material->displacement_texname = NULL; 463 material->alpha_texname = NULL; 464 for (i = 0; i < 3; i++) { 465 material->ambient[i] = 0.f; 466 material->diffuse[i] = 0.f; 467 material->specular[i] = 0.f; 468 material->transmittance[i] = 0.f; 469 material->emission[i] = 0.f; 470 } 471 material->illum = 0; 472 material->dissolve = 1.f; 473 material->shininess = 1.f; 474 material->ior = 1.f; 475 } 476 477 // Implementation of string to int hashtable 478 479 #define HASH_TABLE_ERROR 1 480 #define HASH_TABLE_SUCCESS 0 481 482 #define HASH_TABLE_DEFAULT_SIZE 10 483 484 typedef struct hash_table_entry_t 485 { 486 unsigned long hash; 487 int filled; 488 long value; 489 490 struct hash_table_entry_t* next; 491 struct hash_table_entry_t* prev; 492 } hash_table_entry_t; 493 494 typedef struct 495 { 496 unsigned long* hashes; 497 hash_table_entry_t* entries; 498 size_t capacity; 499 size_t n; 500 } hash_table_t; 501 502 static unsigned long hash_djb2(const void* p) 503 { 504 const unsigned char* str = (const unsigned char*)p; 505 unsigned long hash = 5381; 506 int c; 507 508 while ((c = *str++)) 509 hash = ((hash << 5) + hash) + c; 510 511 return hash; 512 } 513 514 static void create_hash_table(size_t start_capacity, hash_table_t* hash_table) 515 { 516 if (start_capacity < 1) 517 start_capacity = HASH_TABLE_DEFAULT_SIZE; 518 hash_table->hashes = (unsigned long*) malloc(start_capacity * sizeof(unsigned long)); 519 hash_table->entries = (hash_table_entry_t*) calloc(start_capacity, sizeof(hash_table_entry_t)); 520 hash_table->capacity = start_capacity; 521 hash_table->n = 0; 522 } 523 524 static void destroy_hash_table(hash_table_t* hash_table) 525 { 526 free(hash_table->entries); 527 free(hash_table->hashes); 528 } 529 530 // Insert with quadratic probing 531 static int hash_table_insert_value(unsigned long hash, long value, hash_table_t* hash_table) 532 { 533 // Insert value 534 size_t start_index = hash % hash_table->capacity; 535 size_t index = start_index; 536 hash_table_entry_t* last_entry = NULL; 537 538 // While there are collisions, keep probing and log the last entry touched 539 // If we reach an empty entry, append the entry to the last entry's linked list 540 // We form these lists to avoid doing the probing again, The linear search through 541 // the linked list is the length of numbers initially probed, so it's equivalent to 542 // doing the probing again. 543 for (size_t i = 0; hash_table->entries[index].filled; i++) 544 { 545 if (hash_table->entries[index].hash == hash) 546 break; 547 if (i >= hash_table->capacity) 548 return HASH_TABLE_ERROR; 549 last_entry = hash_table->entries + index; 550 index = (start_index + (i * i)) % hash_table->capacity; 551 } 552 553 hash_table_entry_t* entry = hash_table->entries + index; 554 entry->hash = hash; 555 entry->filled = 1; 556 entry->value = value; 557 entry->next = NULL; 558 entry->prev = last_entry; 559 560 if (last_entry) 561 last_entry->next = entry; 562 563 return HASH_TABLE_SUCCESS; 564 } 565 566 static int hash_table_insert(unsigned long hash, long value, hash_table_t* hash_table) 567 { 568 int ret = hash_table_insert_value(hash, value, hash_table); 569 if (ret == HASH_TABLE_SUCCESS) 570 { 571 hash_table->hashes[hash_table->n] = hash; 572 hash_table->n++; 573 } 574 return ret; 575 } 576 577 static hash_table_entry_t* hash_table_find(unsigned long hash, hash_table_t* hash_table) 578 { 579 hash_table_entry_t* entry = hash_table->entries + (hash % hash_table->capacity); 580 while (entry) 581 { 582 if (entry->hash == hash && entry->filled) 583 { 584 return entry; 585 } 586 entry = entry->next; 587 } 588 return NULL; 589 } 590 591 static void hash_table_maybe_grow(size_t new_n, hash_table_t* hash_table) 592 { 593 if (new_n <= hash_table->capacity) 594 return; 595 size_t new_capacity = 2 * ((2 * hash_table->capacity) > new_n ? hash_table->capacity : new_n); 596 // Create a new hash table. We're not calling create_hash_table because we want to realloc the hash array 597 hash_table_t new_hash_table; 598 new_hash_table.hashes = hash_table->hashes = (unsigned long*) realloc((void*) hash_table->hashes, sizeof(unsigned long) * new_capacity); 599 new_hash_table.entries = (hash_table_entry_t*) calloc(new_capacity, sizeof(hash_table_entry_t)); 600 new_hash_table.capacity = new_capacity; 601 new_hash_table.n = hash_table->n; 602 603 // Rehash 604 for (size_t i = 0; i < hash_table->capacity; i++) 605 { 606 hash_table_entry_t* entry = hash_table_find(hash_table->hashes[i], hash_table); 607 hash_table_insert_value(hash_table->hashes[i], entry->value, &new_hash_table); 608 } 609 610 free(hash_table->entries); 611 (*hash_table) = new_hash_table; 612 } 613 614 static int hash_table_exists(const char* name, hash_table_t* hash_table) 615 { 616 return hash_table_find(hash_djb2(name), hash_table) != NULL; 617 } 618 619 static void hash_table_set(const char* name, size_t val, hash_table_t* hash_table) 620 { 621 // Hash name 622 unsigned long hash = hash_djb2(name); 623 624 hash_table_entry_t* entry = hash_table_find(hash, hash_table); 625 if (entry) 626 { 627 entry->value = val; 628 return; 629 } 630 631 // Expand if necessary 632 // Grow until the element has been added 633 do 634 { 635 hash_table_maybe_grow(hash_table->n + 1, hash_table); 636 } 637 while (hash_table_insert(hash, val, hash_table) != HASH_TABLE_SUCCESS); 638 } 639 640 static long hash_table_get(const char* name, hash_table_t* hash_table) 641 { 642 hash_table_entry_t* ret = hash_table_find(hash_djb2(name), hash_table); 643 return ret->value; 644 } 645 646 static void hash_table_erase(const char* name, hash_table_t* hash_table) 647 { 648 // Find the entry associated with this name 649 hash_table_entry_t* entry = hash_table_find(hash_djb2(name), hash_table); 650 if (!entry) 651 return; 652 653 // Mark the entry as empty 654 entry->filled = 0; 655 656 // Remove it from the linked list if it's on one 657 if (entry->prev) 658 entry->prev->next = entry->next; 659 if (entry->next) 660 entry->next->prev = entry->prev; 661 662 // Delete the associated hash from the array by replacing it with the last hash on the table 663 hash_table->n--; 664 hash_table->hashes[entry - hash_table->entries] = hash_table->hashes[hash_table->n]; 665 } 666 667 static tinyobj_material_t *tinyobj_material_add(tinyobj_material_t *prev, 668 size_t num_materials, 669 tinyobj_material_t *new_mat) { 670 tinyobj_material_t *dst; 671 dst = (tinyobj_material_t *)realloc( 672 prev, sizeof(tinyobj_material_t) * (num_materials + 1)); 673 674 dst[num_materials] = (*new_mat); /* Just copy pointer for char* members */ 675 return dst; 676 } 677 678 static int tinyobj_parse_and_index_mtl_file(tinyobj_material_t **materials_out, 679 size_t *num_materials_out, 680 const char *filename, 681 hash_table_t* material_table) { 682 tinyobj_material_t material; 683 char linebuf[8192]; /* alloc enough size */ 684 FILE *fp; 685 size_t num_materials = 0; 686 tinyobj_material_t *materials = NULL; 687 int has_previous_material = 0; 688 689 if (materials_out == NULL) { 690 return TINYOBJ_ERROR_INVALID_PARAMETER; 691 } 692 693 if (num_materials_out == NULL) { 694 return TINYOBJ_ERROR_INVALID_PARAMETER; 695 } 696 697 (*materials_out) = NULL; 698 (*num_materials_out) = 0; 699 700 fp = fopen(filename, "r"); 701 if (!fp) { 702 return TINYOBJ_ERROR_FILE_OPERATION; 703 } 704 705 /* Create a default material */ 706 initMaterial(&material); 707 708 while (NULL != fgets(linebuf, 8192 - 1, fp)) { 709 const char *token = linebuf; 710 /* Skip leading space. */ 711 token += strspn(token, " \t"); 712 713 assert(token); 714 if (token[0] == '\0') continue; /* empty line */ 715 716 if (token[0] == '#') continue; /* comment line */ 717 718 /* new mtl */ 719 if ((0 == strncmp(token, "newmtl", 6)) && IS_SPACE((token[6]))) { 720 char namebuf[4096]; 721 722 /* flush previous material. */ 723 if (has_previous_material) { 724 materials = tinyobj_material_add(materials, num_materials, &material); 725 num_materials++; 726 } else { 727 has_previous_material = 1; 728 } 729 730 /* initial temporary material */ 731 initMaterial(&material); 732 733 /* set new mtl name */ 734 token += 7; 735 #ifdef _MSC_VER 736 sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf)); 737 #else 738 sscanf(token, "%s", namebuf); 739 #endif 740 material.name = my_strdup(namebuf); 741 742 // Add material to material table 743 if (material_table) 744 hash_table_set(material.name, num_materials, material_table); 745 746 continue; 747 } 748 749 /* ambient */ 750 if (token[0] == 'K' && token[1] == 'a' && IS_SPACE((token[2]))) { 751 float r, g, b; 752 token += 2; 753 parseFloat3(&r, &g, &b, &token); 754 material.ambient[0] = r; 755 material.ambient[1] = g; 756 material.ambient[2] = b; 757 continue; 758 } 759 760 /* diffuse */ 761 if (token[0] == 'K' && token[1] == 'd' && IS_SPACE((token[2]))) { 762 float r, g, b; 763 token += 2; 764 parseFloat3(&r, &g, &b, &token); 765 material.diffuse[0] = r; 766 material.diffuse[1] = g; 767 material.diffuse[2] = b; 768 continue; 769 } 770 771 /* specular */ 772 if (token[0] == 'K' && token[1] == 's' && IS_SPACE((token[2]))) { 773 float r, g, b; 774 token += 2; 775 parseFloat3(&r, &g, &b, &token); 776 material.specular[0] = r; 777 material.specular[1] = g; 778 material.specular[2] = b; 779 continue; 780 } 781 782 /* transmittance */ 783 if (token[0] == 'K' && token[1] == 't' && IS_SPACE((token[2]))) { 784 float r, g, b; 785 token += 2; 786 parseFloat3(&r, &g, &b, &token); 787 material.transmittance[0] = r; 788 material.transmittance[1] = g; 789 material.transmittance[2] = b; 790 continue; 791 } 792 793 /* ior(index of refraction) */ 794 if (token[0] == 'N' && token[1] == 'i' && IS_SPACE((token[2]))) { 795 token += 2; 796 material.ior = parseFloat(&token); 797 continue; 798 } 799 800 /* emission */ 801 if (token[0] == 'K' && token[1] == 'e' && IS_SPACE(token[2])) { 802 float r, g, b; 803 token += 2; 804 parseFloat3(&r, &g, &b, &token); 805 material.emission[0] = r; 806 material.emission[1] = g; 807 material.emission[2] = b; 808 continue; 809 } 810 811 /* shininess */ 812 if (token[0] == 'N' && token[1] == 's' && IS_SPACE(token[2])) { 813 token += 2; 814 material.shininess = parseFloat(&token); 815 continue; 816 } 817 818 /* illum model */ 819 if (0 == strncmp(token, "illum", 5) && IS_SPACE(token[5])) { 820 token += 6; 821 material.illum = parseInt(&token); 822 continue; 823 } 824 825 /* dissolve */ 826 if ((token[0] == 'd' && IS_SPACE(token[1]))) { 827 token += 1; 828 material.dissolve = parseFloat(&token); 829 continue; 830 } 831 if (token[0] == 'T' && token[1] == 'r' && IS_SPACE(token[2])) { 832 token += 2; 833 /* Invert value of Tr(assume Tr is in range [0, 1]) */ 834 material.dissolve = 1.0f - parseFloat(&token); 835 continue; 836 } 837 838 /* ambient texture */ 839 if ((0 == strncmp(token, "map_Ka", 6)) && IS_SPACE(token[6])) { 840 token += 7; 841 material.ambient_texname = my_strdup(token); 842 continue; 843 } 844 845 /* diffuse texture */ 846 if ((0 == strncmp(token, "map_Kd", 6)) && IS_SPACE(token[6])) { 847 token += 7; 848 material.diffuse_texname = my_strdup(token); 849 continue; 850 } 851 852 /* specular texture */ 853 if ((0 == strncmp(token, "map_Ks", 6)) && IS_SPACE(token[6])) { 854 token += 7; 855 material.specular_texname = my_strdup(token); 856 continue; 857 } 858 859 /* specular highlight texture */ 860 if ((0 == strncmp(token, "map_Ns", 6)) && IS_SPACE(token[6])) { 861 token += 7; 862 material.specular_highlight_texname = my_strdup(token); 863 continue; 864 } 865 866 /* bump texture */ 867 if ((0 == strncmp(token, "map_bump", 8)) && IS_SPACE(token[8])) { 868 token += 9; 869 material.bump_texname = my_strdup(token); 870 continue; 871 } 872 873 /* alpha texture */ 874 if ((0 == strncmp(token, "map_d", 5)) && IS_SPACE(token[5])) { 875 token += 6; 876 material.alpha_texname = my_strdup(token); 877 continue; 878 } 879 880 /* bump texture */ 881 if ((0 == strncmp(token, "bump", 4)) && IS_SPACE(token[4])) { 882 token += 5; 883 material.bump_texname = my_strdup(token); 884 continue; 885 } 886 887 /* displacement texture */ 888 if ((0 == strncmp(token, "disp", 4)) && IS_SPACE(token[4])) { 889 token += 5; 890 material.displacement_texname = my_strdup(token); 891 continue; 892 } 893 894 /* @todo { unknown parameter } */ 895 } 896 897 if (material.name) { 898 /* Flush last material element */ 899 materials = tinyobj_material_add(materials, num_materials, &material); 900 num_materials++; 901 } 902 903 (*num_materials_out) = num_materials; 904 (*materials_out) = materials; 905 906 return TINYOBJ_SUCCESS; 907 } 908 909 int tinyobj_parse_mtl_file(tinyobj_material_t **materials_out, 910 size_t *num_materials_out, 911 const char *filename) { 912 return tinyobj_parse_and_index_mtl_file(materials_out, num_materials_out, filename, NULL); 913 } 914 915 916 typedef enum { 917 COMMAND_EMPTY, 918 COMMAND_V, 919 COMMAND_VN, 920 COMMAND_VT, 921 COMMAND_F, 922 COMMAND_G, 923 COMMAND_O, 924 COMMAND_USEMTL, 925 COMMAND_MTLLIB 926 927 } CommandType; 928 929 typedef struct { 930 float vx, vy, vz; 931 float nx, ny, nz; 932 float tx, ty; 933 934 /* @todo { Use dynamic array } */ 935 tinyobj_vertex_index_t f[TINYOBJ_MAX_FACES_PER_F_LINE]; 936 size_t num_f; 937 938 int f_num_verts[TINYOBJ_MAX_FACES_PER_F_LINE]; 939 size_t num_f_num_verts; 940 941 const char *group_name; 942 unsigned int group_name_len; 943 int pad0; 944 945 const char *object_name; 946 unsigned int object_name_len; 947 int pad1; 948 949 const char *material_name; 950 unsigned int material_name_len; 951 int pad2; 952 953 const char *mtllib_name; 954 unsigned int mtllib_name_len; 955 956 CommandType type; 957 } Command; 958 959 static int parseLine(Command *command, const char *p, size_t p_len, 960 int triangulate) { 961 char linebuf[4096]; 962 const char *token; 963 assert(p_len < 4095); 964 965 memcpy(linebuf, p, p_len); 966 linebuf[p_len] = '\0'; 967 968 token = linebuf; 969 970 command->type = COMMAND_EMPTY; 971 972 /* Skip leading space. */ 973 skip_space(&token); 974 975 assert(token); 976 if (token[0] == '\0') { /* empty line */ 977 return 0; 978 } 979 980 if (token[0] == '#') { /* comment line */ 981 return 0; 982 } 983 984 /* vertex */ 985 if (token[0] == 'v' && IS_SPACE((token[1]))) { 986 float x, y, z; 987 token += 2; 988 parseFloat3(&x, &y, &z, &token); 989 command->vx = x; 990 command->vy = y; 991 command->vz = z; 992 command->type = COMMAND_V; 993 return 1; 994 } 995 996 /* normal */ 997 if (token[0] == 'v' && token[1] == 'n' && IS_SPACE((token[2]))) { 998 float x, y, z; 999 token += 3; 1000 parseFloat3(&x, &y, &z, &token); 1001 command->nx = x; 1002 command->ny = y; 1003 command->nz = z; 1004 command->type = COMMAND_VN; 1005 return 1; 1006 } 1007 1008 /* texcoord */ 1009 if (token[0] == 'v' && token[1] == 't' && IS_SPACE((token[2]))) { 1010 float x, y; 1011 token += 3; 1012 parseFloat2(&x, &y, &token); 1013 command->tx = x; 1014 command->ty = y; 1015 command->type = COMMAND_VT; 1016 return 1; 1017 } 1018 1019 /* face */ 1020 if (token[0] == 'f' && IS_SPACE((token[1]))) { 1021 size_t num_f = 0; 1022 1023 tinyobj_vertex_index_t f[TINYOBJ_MAX_FACES_PER_F_LINE]; 1024 token += 2; 1025 skip_space(&token); 1026 1027 while (!IS_NEW_LINE(token[0])) { 1028 tinyobj_vertex_index_t vi = parseRawTriple(&token); 1029 skip_space_and_cr(&token); 1030 1031 f[num_f] = vi; 1032 num_f++; 1033 } 1034 1035 command->type = COMMAND_F; 1036 1037 if (triangulate) { 1038 size_t k; 1039 size_t n = 0; 1040 1041 tinyobj_vertex_index_t i0 = f[0]; 1042 tinyobj_vertex_index_t i1; 1043 tinyobj_vertex_index_t i2 = f[1]; 1044 1045 assert(3 * num_f < TINYOBJ_MAX_FACES_PER_F_LINE); 1046 1047 for (k = 2; k < num_f; k++) { 1048 i1 = i2; 1049 i2 = f[k]; 1050 command->f[3 * n + 0] = i0; 1051 command->f[3 * n + 1] = i1; 1052 command->f[3 * n + 2] = i2; 1053 1054 command->f_num_verts[n] = 3; 1055 n++; 1056 } 1057 command->num_f = 3 * n; 1058 command->num_f_num_verts = n; 1059 1060 } else { 1061 size_t k = 0; 1062 assert(num_f < TINYOBJ_MAX_FACES_PER_F_LINE); 1063 for (k = 0; k < num_f; k++) { 1064 command->f[k] = f[k]; 1065 } 1066 1067 command->num_f = num_f; 1068 command->f_num_verts[0] = (int)num_f; 1069 command->num_f_num_verts = 1; 1070 } 1071 1072 return 1; 1073 } 1074 1075 /* use mtl */ 1076 if ((0 == strncmp(token, "usemtl", 6)) && IS_SPACE((token[6]))) { 1077 token += 7; 1078 1079 skip_space(&token); 1080 command->material_name = p + (token - linebuf); 1081 command->material_name_len = (unsigned int)length_until_newline( 1082 token, (p_len - (size_t)(token - linebuf)) + 1); 1083 command->type = COMMAND_USEMTL; 1084 1085 return 1; 1086 } 1087 1088 /* load mtl */ 1089 if ((0 == strncmp(token, "mtllib", 6)) && IS_SPACE((token[6]))) { 1090 /* By specification, `mtllib` should be appear only once in .obj */ 1091 token += 7; 1092 1093 skip_space(&token); 1094 command->mtllib_name = p + (token - linebuf); 1095 command->mtllib_name_len = (unsigned int)length_until_newline( 1096 token, p_len - (size_t)(token - linebuf)) + 1097 1; 1098 command->type = COMMAND_MTLLIB; 1099 1100 return 1; 1101 } 1102 1103 /* group name */ 1104 if (token[0] == 'g' && IS_SPACE((token[1]))) { 1105 /* @todo { multiple group name. } */ 1106 token += 2; 1107 1108 command->group_name = p + (token - linebuf); 1109 command->group_name_len = (unsigned int)length_until_newline( 1110 token, p_len - (size_t)(token - linebuf)) + 1111 1; 1112 command->type = COMMAND_G; 1113 1114 return 1; 1115 } 1116 1117 /* object name */ 1118 if (token[0] == 'o' && IS_SPACE((token[1]))) { 1119 /* @todo { multiple object name? } */ 1120 token += 2; 1121 1122 command->object_name = p + (token - linebuf); 1123 command->object_name_len = (unsigned int)length_until_newline( 1124 token, p_len - (size_t)(token - linebuf)) + 1125 1; 1126 command->type = COMMAND_O; 1127 1128 return 1; 1129 } 1130 1131 return 0; 1132 } 1133 1134 typedef struct { 1135 size_t pos; 1136 size_t len; 1137 } LineInfo; 1138 1139 static int is_line_ending(const char *p, size_t i, size_t end_i) { 1140 if (p[i] == '\0') return 1; 1141 if (p[i] == '\n') return 1; /* this includes \r\n */ 1142 if (p[i] == '\r') { 1143 if (((i + 1) < end_i) && (p[i + 1] != '\n')) { /* detect only \r case */ 1144 return 1; 1145 } 1146 } 1147 return 0; 1148 } 1149 1150 int tinyobj_parse_obj(tinyobj_attrib_t *attrib, tinyobj_shape_t **shapes, 1151 size_t *num_shapes, tinyobj_material_t **materials_out, 1152 size_t *num_materials_out, const char *buf, size_t len, 1153 unsigned int flags) { 1154 LineInfo *line_infos = NULL; 1155 Command *commands = NULL; 1156 size_t num_lines = 0; 1157 1158 size_t num_v = 0; 1159 size_t num_vn = 0; 1160 size_t num_vt = 0; 1161 size_t num_f = 0; 1162 size_t num_faces = 0; 1163 1164 int mtllib_line_index = -1; 1165 1166 tinyobj_material_t *materials = NULL; 1167 size_t num_materials = 0; 1168 1169 if (len < 1) return TINYOBJ_ERROR_INVALID_PARAMETER; 1170 if (attrib == NULL) return TINYOBJ_ERROR_INVALID_PARAMETER; 1171 if (shapes == NULL) return TINYOBJ_ERROR_INVALID_PARAMETER; 1172 if (num_shapes == NULL) return TINYOBJ_ERROR_INVALID_PARAMETER; 1173 if (buf == NULL) return TINYOBJ_ERROR_INVALID_PARAMETER; 1174 if (materials_out == NULL) return TINYOBJ_ERROR_INVALID_PARAMETER; 1175 if (num_materials_out == NULL) return TINYOBJ_ERROR_INVALID_PARAMETER; 1176 1177 tinyobj_attrib_init(attrib); 1178 /* 1. Find '\n' and create line data. */ 1179 { 1180 size_t i; 1181 size_t end_idx = len - 1; 1182 size_t prev_pos = 0; 1183 size_t line_no = 0; 1184 1185 /* Count # of lines. */ 1186 for (i = 0; i < end_idx; i++) { /* Assume last char is '\0' */ 1187 if (is_line_ending(buf, i, end_idx)) { 1188 num_lines++; 1189 } 1190 } 1191 1192 if (num_lines == 0) return TINYOBJ_ERROR_EMPTY; 1193 1194 line_infos = (LineInfo *)malloc(sizeof(LineInfo) * num_lines); 1195 1196 /* Fill line infoss. */ 1197 for (i = 0; i < end_idx; i++) { 1198 if (is_line_ending(buf, i, end_idx)) { 1199 line_infos[line_no].pos = prev_pos; 1200 line_infos[line_no].len = i - prev_pos; 1201 prev_pos = i + 1; 1202 line_no++; 1203 } 1204 } 1205 } 1206 1207 commands = (Command *)malloc(sizeof(Command) * num_lines); 1208 1209 hash_table_t material_table; 1210 create_hash_table(HASH_TABLE_DEFAULT_SIZE, &material_table); 1211 1212 /* 2. parse each line */ 1213 { 1214 size_t i = 0; 1215 for (i = 0; i < num_lines; i++) { 1216 int ret = parseLine(&commands[i], &buf[line_infos[i].pos], 1217 line_infos[i].len, flags & TINYOBJ_FLAG_TRIANGULATE); 1218 if (ret) { 1219 if (commands[i].type == COMMAND_V) { 1220 num_v++; 1221 } else if (commands[i].type == COMMAND_VN) { 1222 num_vn++; 1223 } else if (commands[i].type == COMMAND_VT) { 1224 num_vt++; 1225 } else if (commands[i].type == COMMAND_F) { 1226 num_f += commands[i].num_f; 1227 num_faces += commands[i].num_f_num_verts; 1228 } 1229 1230 if (commands[i].type == COMMAND_MTLLIB) { 1231 mtllib_line_index = (int)i; 1232 } 1233 } 1234 } 1235 } 1236 1237 /* line_infos are not used anymore. Release memory. */ 1238 if (line_infos) { 1239 free(line_infos); 1240 } 1241 1242 /* Load material(if exits) */ 1243 if (mtllib_line_index >= 0 && commands[mtllib_line_index].mtllib_name && 1244 commands[mtllib_line_index].mtllib_name_len > 0) { 1245 char *filename = my_strndup(commands[mtllib_line_index].mtllib_name, 1246 commands[mtllib_line_index].mtllib_name_len); 1247 1248 int ret = tinyobj_parse_and_index_mtl_file(&materials, &num_materials, filename, &material_table); 1249 1250 if (ret != TINYOBJ_SUCCESS) { 1251 /* warning. */ 1252 fprintf(stderr, "TINYOBJ: Failed to parse .mtl file: %s\n", filename); 1253 } 1254 1255 free(filename); 1256 1257 } 1258 1259 /* Construct attributes */ 1260 1261 { 1262 size_t v_count = 0; 1263 size_t n_count = 0; 1264 size_t t_count = 0; 1265 size_t f_count = 0; 1266 size_t face_count = 0; 1267 int material_id = -1; /* -1 = default unknown material. */ 1268 size_t i = 0; 1269 1270 attrib->vertices = (float *)malloc(sizeof(float) * num_v * 3); 1271 attrib->num_vertices = (unsigned int)num_v; 1272 attrib->normals = (float *)malloc(sizeof(float) * num_vn * 3); 1273 attrib->num_normals = (unsigned int)num_vn; 1274 attrib->texcoords = (float *)malloc(sizeof(float) * num_vt * 2); 1275 attrib->num_texcoords = (unsigned int)num_vt; 1276 attrib->faces = (tinyobj_vertex_index_t *)malloc( 1277 sizeof(tinyobj_vertex_index_t) * num_f); 1278 attrib->num_faces = (unsigned int)num_f; 1279 attrib->face_num_verts = (int *)malloc(sizeof(int) * num_faces); 1280 attrib->material_ids = (int *)malloc(sizeof(int) * num_faces); 1281 attrib->num_face_num_verts = (unsigned int)num_faces; 1282 1283 for (i = 0; i < num_lines; i++) { 1284 if (commands[i].type == COMMAND_EMPTY) { 1285 continue; 1286 } else if (commands[i].type == COMMAND_USEMTL) { 1287 /* @todo 1288 if (commands[t][i].material_name && 1289 commands[t][i].material_name_len > 0) { 1290 std::string material_name(commands[t][i].material_name, 1291 commands[t][i].material_name_len); 1292 1293 if (material_map.find(material_name) != material_map.end()) { 1294 material_id = material_map[material_name]; 1295 } else { 1296 // Assign invalid material ID 1297 material_id = -1; 1298 } 1299 } 1300 */ 1301 if (commands[i].material_name && 1302 commands[i].material_name_len >0) 1303 { 1304 // Create a null terminated string 1305 char* material_name_null_term = (char*) malloc(commands[i].material_name_len + 1); 1306 memcpy((void*) material_name_null_term, (void*) commands[i].material_name, commands[i].material_name_len); 1307 material_name_null_term[commands[i].material_name_len - 1] = 0; 1308 1309 if (hash_table_exists(material_name_null_term, &material_table)) 1310 material_id = hash_table_get(material_name_null_term, &material_table); 1311 else 1312 material_id = -1; 1313 1314 free(material_name_null_term); 1315 } 1316 } else if (commands[i].type == COMMAND_V) { 1317 attrib->vertices[3 * v_count + 0] = commands[i].vx; 1318 attrib->vertices[3 * v_count + 1] = commands[i].vy; 1319 attrib->vertices[3 * v_count + 2] = commands[i].vz; 1320 v_count++; 1321 } else if (commands[i].type == COMMAND_VN) { 1322 attrib->normals[3 * n_count + 0] = commands[i].nx; 1323 attrib->normals[3 * n_count + 1] = commands[i].ny; 1324 attrib->normals[3 * n_count + 2] = commands[i].nz; 1325 n_count++; 1326 } else if (commands[i].type == COMMAND_VT) { 1327 attrib->texcoords[2 * t_count + 0] = commands[i].tx; 1328 attrib->texcoords[2 * t_count + 1] = commands[i].ty; 1329 t_count++; 1330 } else if (commands[i].type == COMMAND_F) { 1331 size_t k = 0; 1332 for (k = 0; k < commands[i].num_f; k++) { 1333 tinyobj_vertex_index_t vi = commands[i].f[k]; 1334 int v_idx = fixIndex(vi.v_idx, v_count); 1335 int vn_idx = fixIndex(vi.vn_idx, n_count); 1336 int vt_idx = fixIndex(vi.vt_idx, t_count); 1337 attrib->faces[f_count + k].v_idx = v_idx; 1338 attrib->faces[f_count + k].vn_idx = vn_idx; 1339 attrib->faces[f_count + k].vt_idx = vt_idx; 1340 } 1341 1342 for (k = 0; k < commands[i].num_f_num_verts; k++) { 1343 attrib->material_ids[face_count + k] = material_id; 1344 attrib->face_num_verts[face_count + k] = commands[i].f_num_verts[k]; 1345 } 1346 1347 f_count += commands[i].num_f; 1348 face_count += commands[i].num_f_num_verts; 1349 } 1350 } 1351 } 1352 1353 /* 5. Construct shape information. */ 1354 { 1355 unsigned int face_count = 0; 1356 size_t i = 0; 1357 size_t n = 0; 1358 size_t shape_idx = 0; 1359 1360 const char *shape_name = NULL; 1361 unsigned int shape_name_len = 0; 1362 const char *prev_shape_name = NULL; 1363 unsigned int prev_shape_name_len = 0; 1364 unsigned int prev_shape_face_offset = 0; 1365 // unsigned int prev_shape_length = 0; 1366 unsigned int prev_face_offset = 0; 1367 tinyobj_shape_t prev_shape = {NULL, 0, 0}; 1368 1369 /* Find the number of shapes in .obj */ 1370 for (i = 0; i < num_lines; i++) { 1371 if (commands[i].type == COMMAND_O || commands[i].type == COMMAND_G) { 1372 n++; 1373 } 1374 } 1375 1376 /* Allocate array of shapes with maximum possible size(+1 for unnamed 1377 * group/object). 1378 * Actual # of shapes found in .obj is determined in the later */ 1379 (*shapes) = (tinyobj_shape_t*)malloc(sizeof(tinyobj_shape_t) * (n + 1)); 1380 1381 for (i = 0; i < num_lines; i++) { 1382 if (commands[i].type == COMMAND_O || commands[i].type == COMMAND_G) { 1383 if (commands[i].type == COMMAND_O) { 1384 shape_name = commands[i].object_name; 1385 shape_name_len = commands[i].object_name_len; 1386 } else { 1387 shape_name = commands[i].group_name; 1388 shape_name_len = commands[i].group_name_len; 1389 } 1390 1391 if (face_count == 0) { 1392 /* 'o' or 'g' appears before any 'f' */ 1393 prev_shape_name = shape_name; 1394 prev_shape_name_len = shape_name_len; 1395 prev_shape_face_offset = face_count; 1396 prev_face_offset = face_count; 1397 } else { 1398 if (shape_idx == 0) { 1399 /* 'o' or 'g' after some 'v' lines. */ 1400 (*shapes)[shape_idx].name = my_strndup( 1401 prev_shape_name, prev_shape_name_len); /* may be NULL */ 1402 (*shapes)[shape_idx].face_offset = prev_shape.face_offset; 1403 (*shapes)[shape_idx].length = face_count - prev_face_offset; 1404 shape_idx++; 1405 1406 // prev_shape_length = face_count - prev_face_offset; 1407 prev_face_offset = face_count; 1408 1409 } else { 1410 if ((face_count - prev_face_offset) > 0) { 1411 (*shapes)[shape_idx].name = 1412 my_strndup(prev_shape_name, prev_shape_name_len); 1413 (*shapes)[shape_idx].face_offset = prev_face_offset; 1414 (*shapes)[shape_idx].length = face_count - prev_face_offset; 1415 shape_idx++; 1416 // prev_shape_length = face_count - prev_face_offset; 1417 prev_face_offset = face_count; 1418 } 1419 } 1420 1421 /* Record shape info for succeeding 'o' or 'g' command. */ 1422 prev_shape_name = shape_name; 1423 prev_shape_name_len = shape_name_len; 1424 prev_shape_face_offset = face_count; 1425 // prev_shape_length = 0; 1426 } 1427 } 1428 if (commands[i].type == COMMAND_F) { 1429 face_count++; 1430 } 1431 } 1432 1433 if ((face_count - prev_face_offset) > 0) { 1434 size_t length = face_count - prev_shape_face_offset; 1435 if (length > 0) { 1436 (*shapes)[shape_idx].name = 1437 my_strndup(prev_shape_name, prev_shape_name_len); 1438 (*shapes)[shape_idx].face_offset = prev_face_offset; 1439 (*shapes)[shape_idx].length = face_count - prev_face_offset; 1440 shape_idx++; 1441 } 1442 } else { 1443 /* Guess no 'v' line occurrence after 'o' or 'g', so discards current 1444 * shape information. */ 1445 } 1446 1447 (*num_shapes) = shape_idx; 1448 } 1449 1450 if (commands) { 1451 free(commands); 1452 } 1453 1454 destroy_hash_table(&material_table); 1455 1456 (*materials_out) = materials; 1457 (*num_materials_out) = num_materials; 1458 1459 return TINYOBJ_SUCCESS; 1460 } 1461 1462 void tinyobj_attrib_init(tinyobj_attrib_t *attrib) { 1463 attrib->vertices = NULL; 1464 attrib->num_vertices = 0; 1465 attrib->normals = NULL; 1466 attrib->num_normals = 0; 1467 attrib->texcoords = NULL; 1468 attrib->num_texcoords = 0; 1469 attrib->faces = NULL; 1470 attrib->num_faces = 0; 1471 attrib->face_num_verts = NULL; 1472 attrib->num_face_num_verts = 0; 1473 attrib->material_ids = NULL; 1474 } 1475 1476 void tinyobj_attrib_free(tinyobj_attrib_t *attrib) { 1477 if (attrib->vertices) free(attrib->vertices); 1478 if (attrib->normals) free(attrib->normals); 1479 if (attrib->texcoords) free(attrib->texcoords); 1480 if (attrib->faces) free(attrib->faces); 1481 if (attrib->face_num_verts) free(attrib->face_num_verts); 1482 if (attrib->material_ids) free(attrib->material_ids); 1483 } 1484 1485 void tinyobj_shapes_free(tinyobj_shape_t *shapes, size_t num_shapes) { 1486 size_t i; 1487 if (shapes == NULL) return; 1488 1489 for (i = 0; i < num_shapes; i++) { 1490 if (shapes[i].name) free(shapes[i].name); 1491 } 1492 1493 free(shapes); 1494 } 1495 1496 void tinyobj_materials_free(tinyobj_material_t *materials, 1497 size_t num_materials) { 1498 size_t i; 1499 if (materials == NULL) return; 1500 1501 for (i = 0; i < num_materials; i++) { 1502 if (materials[i].name) free(materials[i].name); 1503 if (materials[i].ambient_texname) free(materials[i].ambient_texname); 1504 if (materials[i].diffuse_texname) free(materials[i].diffuse_texname); 1505 if (materials[i].specular_texname) free(materials[i].specular_texname); 1506 if (materials[i].specular_highlight_texname) 1507 free(materials[i].specular_highlight_texname); 1508 if (materials[i].bump_texname) free(materials[i].bump_texname); 1509 if (materials[i].displacement_texname) 1510 free(materials[i].displacement_texname); 1511 if (materials[i].alpha_texname) free(materials[i].alpha_texname); 1512 } 1513 1514 free(materials); 1515 } 1516 #endif /* TINYOBJ_LOADER_C_IMPLEMENTATION */ 1517 1518 #endif /* TINOBJ_LOADER_C_H_ */