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