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