4 Copyright (c) 2016 - 2019 Syoyo Fujita and many contributors.
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:
13 The above copyright notice and this permission notice shall be included in
14 all copies or substantial portions of the Software.
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
24 #ifndef TINOBJ_LOADER_C_H_
25 #define TINOBJ_LOADER_C_H_
27 /* @todo { Remove stddef dependency. unsigned int? } ---> RAY: DONE. */
36 float transmittance[3];
39 float ior; /* index of refraction */
40 float dissolve; /* 1 == opaque; 0 == fully transparent */
41 /* illumination model (see http://www.fileformat.info/format/material/) */
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 */
56 char *name; /* group name or object name. */
57 unsigned int face_offset;
61 typedef struct { int v_idx, vt_idx, vn_idx; } tinyobj_vertex_index_t;
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;
75 tinyobj_vertex_index_t *faces;
81 #define TINYOBJ_FLAG_TRIANGULATE (1 << 0)
83 #define TINYOBJ_INVALID_INDEX (0x80000000)
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)
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.
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,
99 extern int tinyobj_parse_mtl_file(tinyobj_material_t **materials_out,
100 unsigned int *num_materials_out,
101 const char *filename);
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);
109 #ifdef TINYOBJ_LOADER_C_IMPLEMENTATION
115 #if defined(TINYOBJ_MALLOC) && defined(TINYOBJ_REALLOC) && defined(TINYOBJ_CALLOC) && defined(TINYOBJ_FREE)
117 #elif !defined(TINYOBJ_MALLOC) && !defined(TINYOBJ_REALLOC) && !defined(TINYOBJ_CALLOC) && !defined(TINYOBJ_FREE)
120 #error "Must define all or none of TINYOBJ_MALLOC, TINYOBJ_REALLOC, TINYOBJ_CALLOC and TINYOBJ_FREE."
123 #ifndef TINYOBJ_MALLOC
125 #define TINYOBJ_MALLOC malloc
126 #define TINYOBJ_REALLOC realloc
127 #define TINYOBJ_CALLOC calloc
128 #define TINYOBJ_FREE free
131 #define TINYOBJ_MAX_FACES_PER_F_LINE (16)
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'))
137 static void skip_space(const char **token) {
138 while ((*token)[0] == ' ' || (*token)[0] == '\t') {
143 static void skip_space_and_cr(const char **token) {
144 while ((*token)[0] == ' ' || (*token)[0] == '\t' || (*token)[0] == '\r') {
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') {
155 return (int)(p - token);
158 static unsigned int length_until_newline(const char *token, unsigned int n) {
159 unsigned int len = 0;
161 /* Assume token[n-1] = '\0' */
162 for (len = 0; len < n - 1; len++) {
163 if (token[len] == '\n') {
166 if ((token[len] == '\r') && ((len < (n - 2)) && (token[len + 1] != '\n'))) {
174 static unsigned int length_until_line_feed(const char *token, unsigned int n) {
175 unsigned int len = 0;
177 /* Assume token[n-1] = '\0' */
178 for (len = 0; len < n; len++) {
179 if ((token[len] == '\n') || (token[len] == '\r')) {
187 /* http://stackoverflow.com/questions/5710091/how-does-atoi-function-in-c-work
189 static int my_atoi(const char *c) {
192 if (*c == '+' || *c == '-') {
193 if (*c == '-') sign = -1;
196 while (((*c) >= '0') && ((*c) <= '9')) { /* isdigit(*c) */
198 value += (int)(*c - '0');
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 */
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);
219 vi.v_idx = my_atoi((*token));
220 while ((*token)[0] != '\0' && (*token)[0] != '/' && (*token)[0] != ' ' &&
221 (*token)[0] != '\t' && (*token)[0] != '\r') {
224 if ((*token)[0] != '/') {
230 if ((*token)[0] == '/') {
232 vi.vn_idx = my_atoi((*token));
233 while ((*token)[0] != '\0' && (*token)[0] != '/' && (*token)[0] != ' ' &&
234 (*token)[0] != '\t' && (*token)[0] != '\r') {
241 vi.vt_idx = my_atoi((*token));
242 while ((*token)[0] != '\0' && (*token)[0] != '/' && (*token)[0] != ' ' &&
243 (*token)[0] != '\t' && (*token)[0] != '\r') {
246 if ((*token)[0] != '/') {
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') {
260 static int parseInt(const char **token) {
263 i = my_atoi((*token));
264 (*token) += until_space((*token));
269 * Tries to parse a floating point number located at s.
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.
274 * Parses the following EBNF grammar:
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 ) ;
282 * Valid strings are for example:
283 * -0 +3.1417e+2 -0.0E-3 1.0324 -1.41 11e2
285 * If the parsing is a success, result is set to the parsed value and true
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.
292 * The following situations triggers a failure:
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
303 * To get the final double we will use ldexp, it requires the
304 * exponent to be in base 2.
308 /* NOTE: THESE MUST BE DECLARED HERE SINCE WE ARE NOT ALLOWED
309 * TO JUMP OVER DEFINITIONS.
313 char const *curr = s;
315 /* How many characters were read in a loop. */
317 /* Tells whether a loop terminated due to reaching s_end. */
318 int end_not_reached = 0;
328 /* Find out what sign we've got. */
329 if (*curr == '+' || *curr == '-') {
332 } else if (IS_DIGIT(*curr)) { /* Pass through. */
337 /* Read the integer part. */
338 end_not_reached = (curr != s_end);
339 while (end_not_reached && IS_DIGIT(*curr)) {
341 mantissa += (int)(*curr - 0x30);
344 end_not_reached = (curr != s_end);
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;
352 /* Read the decimal part. */
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;
361 for (f = 0; f < read; f++) {
364 mantissa += (int)(*curr - 0x30) * frac_value;
367 end_not_reached = (curr != s_end);
369 } else if (*curr == 'e' || *curr == 'E') {
374 if (!end_not_reached) goto assemble;
376 /* Read the exponent part. */
377 if (*curr == 'e' || *curr == 'E') {
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 == '-')) {
384 } else if (IS_DIGIT(*curr)) { /* Pass through. */
386 /* Empty E is not allowed. */
391 end_not_reached = (curr != s_end);
392 while (end_not_reached && IS_DIGIT(*curr)) {
394 exponent += (int)(*curr - 0x30);
397 end_not_reached = (curr != s_end);
399 if (read == 0) goto fail;
405 double a = 1.0; /* = pow(5.0, exponent); */
406 double b = 1.0; /* = 2.0^exponent */
408 for (i = 0; i < exponent; i++) {
412 for (i = 0; i < exponent; i++) {
416 if (exp_sign == '-') {
422 /* (sign == '+' ? 1 : -1) * ldexp(mantissa * pow(5.0, exponent),
424 (sign == '+' ? 1 : -1) * (mantissa * a * b);
432 static float parseFloat(const char **token) {
437 end = (*token) + until_space((*token));
439 tryParseDouble((*token), end, &val);
445 static void parseFloat2(float *x, float *y, const char **token) {
446 (*x) = parseFloat(token);
447 (*y) = parseFloat(token);
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);
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;
461 static char *my_strdup(const char *s, unsigned int max_length) {
465 if (s == NULL) return NULL;
467 /* Do not consider CRLF line ending(#19) */
468 len = length_until_line_feed(s, max_length);
469 /* len = strlen(s); */
471 /* trim line ending and append '\0' */
472 d = (char *)TINYOBJ_MALLOC(len + 1); /* + '\0' */
473 memcpy(d, s, (unsigned int)(len));
479 static char *my_strndup(const char *s, unsigned int len) {
483 if (s == NULL) return NULL;
484 if (len == 0) return NULL;
486 slen = my_strnlen(s, len);
487 d = (char *)TINYOBJ_MALLOC(slen + 1); /* + '\0' */
497 char *dynamic_fgets(char **buf, unsigned int *size, FILE *file) {
500 unsigned int old_size;
502 if (!(ret = fgets(*buf, (int)*size, file))) {
506 if (NULL != strchr(*buf, '\n')) {
513 *buf = (char*)TINYOBJ_REALLOC(*buf, *size);
514 offset = &((*buf)[old_size - 1]);
516 ret = fgets(offset, (int)(old_size + 1), file);
517 } while(ret && (NULL == strchr(*buf, '\n')));
522 static void initMaterial(tinyobj_material_t *material) {
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;
540 material->dissolve = 1.f;
541 material->shininess = 1.f;
545 /* Implementation of string to int hashtable */
547 #define HASH_TABLE_ERROR 1
548 #define HASH_TABLE_SUCCESS 0
550 #define HASH_TABLE_DEFAULT_SIZE 10
552 typedef struct hash_table_entry_t
559 struct hash_table_entry_t* next;
560 } hash_table_entry_t;
564 unsigned long* hashes;
565 hash_table_entry_t* entries;
566 unsigned int capacity;
570 static unsigned long hash_djb2(const unsigned char* str)
572 unsigned long hash = 5381;
575 while ((c = *str++)) {
576 hash = ((hash << 5) + hash) + (unsigned long)(c);
582 static void create_hash_table(unsigned int start_capacity, hash_table_t* hash_table)
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;
592 static void destroy_hash_table(hash_table_t* hash_table)
594 TINYOBJ_FREE(hash_table->entries);
595 TINYOBJ_FREE(hash_table->hashes);
598 /* Insert with quadratic probing */
599 static int hash_table_insert_value(unsigned long hash, long value, hash_table_t* hash_table)
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;
606 hash_table_entry_t* entry;
608 for (i = 1; hash_table->entries[index].filled; i++)
610 if (i >= hash_table->capacity)
611 return HASH_TABLE_ERROR;
612 index = (start_index + (i * i)) % hash_table->capacity;
615 entry = hash_table->entries + index;
618 entry->value = value;
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;
626 return HASH_TABLE_SUCCESS;
629 static int hash_table_insert(unsigned long hash, long value, hash_table_t* hash_table)
631 int ret = hash_table_insert_value(hash, value, hash_table);
632 if (ret == HASH_TABLE_SUCCESS)
634 hash_table->hashes[hash_table->n] = hash;
640 static hash_table_entry_t* hash_table_find(unsigned long hash, hash_table_t* hash_table)
642 hash_table_entry_t* entry = hash_table->entries + (hash % hash_table->capacity);
645 if (entry->hash == hash && entry->filled)
654 static void hash_table_maybe_grow(unsigned int new_n, hash_table_t* hash_table)
656 unsigned int new_capacity;
657 hash_table_t new_hash_table;
660 if (new_n <= hash_table->capacity) {
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;
671 for (i = 0; i < hash_table->capacity; i++)
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);
677 TINYOBJ_FREE(hash_table->entries);
678 (*hash_table) = new_hash_table;
681 static int hash_table_exists(const char* name, hash_table_t* hash_table)
683 return hash_table_find(hash_djb2((const unsigned char*)name), hash_table) != NULL;
686 static void hash_table_set(const char* name, unsigned int val, hash_table_t* hash_table)
689 unsigned long hash = hash_djb2((const unsigned char *)name);
691 hash_table_entry_t* entry = hash_table_find(hash, hash_table);
694 entry->value = (long)val;
698 /* Expand if necessary
699 * Grow until the element has been added
703 hash_table_maybe_grow(hash_table->n + 1, hash_table);
705 while (hash_table_insert(hash, (long)val, hash_table) != HASH_TABLE_SUCCESS);
708 static long hash_table_get(const char* name, hash_table_t* hash_table)
710 hash_table_entry_t* ret = hash_table_find(hash_djb2((const unsigned char*)(name)), hash_table);
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));
721 dst[num_materials] = (*new_mat); /* Just copy pointer for char* members */
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;
733 unsigned int num_materials = 0;
734 tinyobj_material_t *materials = NULL;
735 int has_previous_material = 0;
736 const char *line_end = NULL;
738 if (materials_out == NULL) {
739 return TINYOBJ_ERROR_INVALID_PARAMETER;
742 if (num_materials_out == NULL) {
743 return TINYOBJ_ERROR_INVALID_PARAMETER;
746 (*materials_out) = NULL;
747 (*num_materials_out) = 0;
749 fp = fopen(filename, "r");
751 fprintf(stderr, "TINYOBJ: Error reading file '%s': %s (%d)\n", filename, strerror(errno), errno);
752 return TINYOBJ_ERROR_FILE_OPERATION;
755 /* Create a default material */
756 initMaterial(&material);
758 linebuf = (char*)TINYOBJ_MALLOC(buffer_size);
759 while (NULL != dynamic_fgets(&linebuf, &buffer_size, fp)) {
760 const char *token = linebuf;
762 line_end = token + strlen(token);
764 /* Skip leading space. */
765 token += strspn(token, " \t");
768 if (token[0] == '\0') continue; /* empty line */
770 if (token[0] == '#') continue; /* comment line */
773 if ((0 == strncmp(token, "newmtl", 6)) && IS_SPACE((token[6]))) {
776 /* flush previous material. */
777 if (has_previous_material) {
778 materials = tinyobj_material_add(materials, num_materials, &material);
781 has_previous_material = 1;
784 /* initial temporary material */
785 initMaterial(&material);
787 /* set new mtl name */
790 sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf));
792 sscanf(token, "%s", namebuf);
794 material.name = my_strdup(namebuf, (unsigned int) (line_end - token));
796 /* Add material to material table */
798 hash_table_set(material.name, num_materials, material_table);
804 if (token[0] == 'K' && token[1] == 'a' && IS_SPACE((token[2]))) {
807 parseFloat3(&r, &g, &b, &token);
808 material.ambient[0] = r;
809 material.ambient[1] = g;
810 material.ambient[2] = b;
815 if (token[0] == 'K' && token[1] == 'd' && IS_SPACE((token[2]))) {
818 parseFloat3(&r, &g, &b, &token);
819 material.diffuse[0] = r;
820 material.diffuse[1] = g;
821 material.diffuse[2] = b;
826 if (token[0] == 'K' && token[1] == 's' && IS_SPACE((token[2]))) {
829 parseFloat3(&r, &g, &b, &token);
830 material.specular[0] = r;
831 material.specular[1] = g;
832 material.specular[2] = b;
837 if (token[0] == 'K' && token[1] == 't' && IS_SPACE((token[2]))) {
840 parseFloat3(&r, &g, &b, &token);
841 material.transmittance[0] = r;
842 material.transmittance[1] = g;
843 material.transmittance[2] = b;
847 /* ior(index of refraction) */
848 if (token[0] == 'N' && token[1] == 'i' && IS_SPACE((token[2]))) {
850 material.ior = parseFloat(&token);
855 if (token[0] == 'K' && token[1] == 'e' && IS_SPACE(token[2])) {
858 parseFloat3(&r, &g, &b, &token);
859 material.emission[0] = r;
860 material.emission[1] = g;
861 material.emission[2] = b;
866 if (token[0] == 'N' && token[1] == 's' && IS_SPACE(token[2])) {
868 material.shininess = parseFloat(&token);
873 if (0 == strncmp(token, "illum", 5) && IS_SPACE(token[5])) {
875 material.illum = parseInt(&token);
880 if ((token[0] == 'd' && IS_SPACE(token[1]))) {
882 material.dissolve = parseFloat(&token);
885 if (token[0] == 'T' && token[1] == 'r' && IS_SPACE(token[2])) {
887 /* Invert value of Tr(assume Tr is in range [0, 1]) */
888 material.dissolve = 1.0f - parseFloat(&token);
892 /* ambient texture */
893 if ((0 == strncmp(token, "map_Ka", 6)) && IS_SPACE(token[6])) {
895 material.ambient_texname = my_strdup(token, (unsigned int) (line_end - token));
899 /* diffuse texture */
900 if ((0 == strncmp(token, "map_Kd", 6)) && IS_SPACE(token[6])) {
902 material.diffuse_texname = my_strdup(token, (unsigned int) (line_end - token));
906 /* specular texture */
907 if ((0 == strncmp(token, "map_Ks", 6)) && IS_SPACE(token[6])) {
909 material.specular_texname = my_strdup(token, (unsigned int) (line_end - token));
913 /* specular highlight texture */
914 if ((0 == strncmp(token, "map_Ns", 6)) && IS_SPACE(token[6])) {
916 material.specular_highlight_texname = my_strdup(token, (unsigned int) (line_end - token));
921 if ((0 == strncmp(token, "map_bump", 8)) && IS_SPACE(token[8])) {
923 material.bump_texname = my_strdup(token, (unsigned int) (line_end - token));
928 if ((0 == strncmp(token, "map_d", 5)) && IS_SPACE(token[5])) {
930 material.alpha_texname = my_strdup(token, (unsigned int) (line_end - token));
935 if ((0 == strncmp(token, "bump", 4)) && IS_SPACE(token[4])) {
937 material.bump_texname = my_strdup(token, (unsigned int) (line_end - token));
941 /* displacement texture */
942 if ((0 == strncmp(token, "disp", 4)) && IS_SPACE(token[4])) {
944 material.displacement_texname = my_strdup(token, (unsigned int) (line_end - token));
948 /* @todo { unknown parameter } */
952 /* Flush last material element */
953 materials = tinyobj_material_add(materials, num_materials, &material);
957 (*num_materials_out) = num_materials;
958 (*materials_out) = materials;
961 TINYOBJ_FREE(linebuf);
964 return TINYOBJ_SUCCESS;
967 int tinyobj_parse_mtl_file(tinyobj_material_t **materials_out,
968 unsigned int *num_materials_out,
969 const char *filename) {
970 return tinyobj_parse_and_index_mtl_file(materials_out, num_materials_out, filename, NULL);
992 /* @todo { Use dynamic array } */
993 tinyobj_vertex_index_t f[TINYOBJ_MAX_FACES_PER_F_LINE];
996 int f_num_verts[TINYOBJ_MAX_FACES_PER_F_LINE];
997 unsigned int num_f_num_verts;
999 const char *group_name;
1000 unsigned int group_name_len;
1003 const char *object_name;
1004 unsigned int object_name_len;
1007 const char *material_name;
1008 unsigned int material_name_len;
1011 const char *mtllib_name;
1012 unsigned int mtllib_name_len;
1017 static int parseLine(Command *command, const char *p, unsigned int p_len,
1021 assert(p_len < 4095);
1023 memcpy(linebuf, p, p_len);
1024 linebuf[p_len] = '\0';
1028 command->type = COMMAND_EMPTY;
1030 /* Skip leading space. */
1034 if (token[0] == '\0') { /* empty line */
1038 if (token[0] == '#') { /* comment line */
1043 if (token[0] == 'v' && IS_SPACE((token[1]))) {
1046 parseFloat3(&x, &y, &z, &token);
1050 command->type = COMMAND_V;
1055 if (token[0] == 'v' && token[1] == 'n' && IS_SPACE((token[2]))) {
1058 parseFloat3(&x, &y, &z, &token);
1062 command->type = COMMAND_VN;
1067 if (token[0] == 'v' && token[1] == 't' && IS_SPACE((token[2]))) {
1070 parseFloat2(&x, &y, &token);
1073 command->type = COMMAND_VT;
1078 if (token[0] == 'f' && IS_SPACE((token[1]))) {
1079 unsigned int num_f = 0;
1081 tinyobj_vertex_index_t f[TINYOBJ_MAX_FACES_PER_F_LINE];
1085 while (!IS_NEW_LINE(token[0])) {
1086 tinyobj_vertex_index_t vi = parseRawTriple(&token);
1087 skip_space_and_cr(&token);
1093 command->type = COMMAND_F;
1099 tinyobj_vertex_index_t i0 = f[0];
1100 tinyobj_vertex_index_t i1;
1101 tinyobj_vertex_index_t i2 = f[1];
1103 assert(3 * num_f < TINYOBJ_MAX_FACES_PER_F_LINE);
1105 for (k = 2; k < num_f; k++) {
1108 command->f[3 * n + 0] = i0;
1109 command->f[3 * n + 1] = i1;
1110 command->f[3 * n + 2] = i2;
1112 command->f_num_verts[n] = 3;
1115 command->num_f = 3 * n;
1116 command->num_f_num_verts = n;
1120 assert(num_f < TINYOBJ_MAX_FACES_PER_F_LINE);
1121 for (k = 0; k < num_f; k++) {
1122 command->f[k] = f[k];
1125 command->num_f = num_f;
1126 command->f_num_verts[0] = (int)num_f;
1127 command->num_f_num_verts = 1;
1134 if ((0 == strncmp(token, "usemtl", 6)) && IS_SPACE((token[6]))) {
1138 command->material_name = p + (token - linebuf);
1139 command->material_name_len = (unsigned int)length_until_newline(
1140 token, (p_len - (unsigned int)(token - linebuf)) + 1);
1141 command->type = COMMAND_USEMTL;
1147 if ((0 == strncmp(token, "mtllib", 6)) && IS_SPACE((token[6]))) {
1148 /* By specification, `mtllib` should be appear only once in .obj */
1152 command->mtllib_name = p + (token - linebuf);
1153 command->mtllib_name_len = (unsigned int)length_until_newline(
1154 token, p_len - (unsigned int)(token - linebuf)) +
1156 command->type = COMMAND_MTLLIB;
1162 if (token[0] == 'g' && IS_SPACE((token[1]))) {
1163 /* @todo { multiple group name. } */
1166 command->group_name = p + (token - linebuf);
1167 command->group_name_len = (unsigned int)length_until_newline(
1168 token, p_len - (unsigned int)(token - linebuf)) +
1170 command->type = COMMAND_G;
1176 if (token[0] == 'o' && IS_SPACE((token[1]))) {
1177 /* @todo { multiple object name? } */
1180 command->object_name = p + (token - linebuf);
1181 command->object_name_len = (unsigned int)length_until_newline(
1182 token, p_len - (unsigned int)(token - linebuf)) +
1184 command->type = COMMAND_O;
1197 static int is_line_ending(const char *p, unsigned int i, unsigned int end_i) {
1198 if (p[i] == '\0') return 1;
1199 if (p[i] == '\n') return 1; /* this includes \r\n */
1201 if (((i + 1) < end_i) && (p[i + 1] != '\n')) { /* detect only \r case */
1208 int tinyobj_parse_obj(tinyobj_attrib_t *attrib, tinyobj_shape_t **shapes,
1209 unsigned int *num_shapes, tinyobj_material_t **materials_out,
1210 unsigned int *num_materials_out, const char *buf, unsigned int len,
1211 unsigned int flags) {
1212 LineInfo *line_infos = NULL;
1213 Command *commands = NULL;
1214 unsigned int num_lines = 0;
1216 unsigned int num_v = 0;
1217 unsigned int num_vn = 0;
1218 unsigned int num_vt = 0;
1219 unsigned int num_f = 0;
1220 unsigned int num_faces = 0;
1222 int mtllib_line_index = -1;
1224 tinyobj_material_t *materials = NULL;
1225 unsigned int num_materials = 0;
1227 hash_table_t material_table;
1229 if (len < 1) return TINYOBJ_ERROR_INVALID_PARAMETER;
1230 if (attrib == NULL) return TINYOBJ_ERROR_INVALID_PARAMETER;
1231 if (shapes == NULL) return TINYOBJ_ERROR_INVALID_PARAMETER;
1232 if (num_shapes == NULL) return TINYOBJ_ERROR_INVALID_PARAMETER;
1233 if (buf == NULL) return TINYOBJ_ERROR_INVALID_PARAMETER;
1234 if (materials_out == NULL) return TINYOBJ_ERROR_INVALID_PARAMETER;
1235 if (num_materials_out == NULL) return TINYOBJ_ERROR_INVALID_PARAMETER;
1237 tinyobj_attrib_init(attrib);
1238 /* 1. Find '\n' and create line data. */
1241 unsigned int end_idx = len;
1242 unsigned int prev_pos = 0;
1243 unsigned int line_no = 0;
1244 unsigned int last_line_ending = 0;
1246 /* Count # of lines. */
1247 for (i = 0; i < end_idx; i++) {
1248 if (is_line_ending(buf, i, end_idx)) {
1250 last_line_ending = i;
1253 /* The last char from the input may not be a line
1254 * ending character so add an extra line if there
1255 * are more characters after the last line ending
1256 * that was found. */
1257 if (end_idx - last_line_ending > 0) {
1261 if (num_lines == 0) return TINYOBJ_ERROR_EMPTY;
1263 line_infos = (LineInfo *)TINYOBJ_MALLOC(sizeof(LineInfo) * num_lines);
1265 /* Fill line infos. */
1266 for (i = 0; i < end_idx; i++) {
1267 if (is_line_ending(buf, i, end_idx)) {
1268 line_infos[line_no].pos = prev_pos;
1269 line_infos[line_no].len = i - prev_pos;
1274 if (end_idx - last_line_ending > 0) {
1275 line_infos[line_no].pos = prev_pos;
1276 line_infos[line_no].len = end_idx - 1 - last_line_ending;
1280 commands = (Command *)TINYOBJ_MALLOC(sizeof(Command) * num_lines);
1282 create_hash_table(HASH_TABLE_DEFAULT_SIZE, &material_table);
1284 /* 2. parse each line */
1287 for (i = 0; i < num_lines; i++) {
1288 int ret = parseLine(&commands[i], &buf[line_infos[i].pos],
1289 line_infos[i].len, flags & TINYOBJ_FLAG_TRIANGULATE);
1291 if (commands[i].type == COMMAND_V) {
1293 } else if (commands[i].type == COMMAND_VN) {
1295 } else if (commands[i].type == COMMAND_VT) {
1297 } else if (commands[i].type == COMMAND_F) {
1298 num_f += commands[i].num_f;
1299 num_faces += commands[i].num_f_num_verts;
1302 if (commands[i].type == COMMAND_MTLLIB) {
1303 mtllib_line_index = (int)i;
1309 /* line_infos are not used anymore. Release memory. */
1311 TINYOBJ_FREE(line_infos);
1314 /* Load material(if exits) */
1315 if (mtllib_line_index >= 0 && commands[mtllib_line_index].mtllib_name &&
1316 commands[mtllib_line_index].mtllib_name_len > 0) {
1317 char *filename = my_strndup(commands[mtllib_line_index].mtllib_name,
1318 commands[mtllib_line_index].mtllib_name_len);
1320 int ret = tinyobj_parse_and_index_mtl_file(&materials, &num_materials, filename, &material_table);
1322 if (ret != TINYOBJ_SUCCESS) {
1324 fprintf(stderr, "TINYOBJ: Failed to parse material file '%s': %d\n", filename, ret);
1327 TINYOBJ_FREE(filename);
1331 /* Construct attributes */
1334 unsigned int v_count = 0;
1335 unsigned int n_count = 0;
1336 unsigned int t_count = 0;
1337 unsigned int f_count = 0;
1338 unsigned int face_count = 0;
1339 int material_id = -1; /* -1 = default unknown material. */
1342 attrib->vertices = (float *)TINYOBJ_MALLOC(sizeof(float) * num_v * 3);
1343 attrib->num_vertices = (unsigned int)num_v;
1344 attrib->normals = (float *)TINYOBJ_MALLOC(sizeof(float) * num_vn * 3);
1345 attrib->num_normals = (unsigned int)num_vn;
1346 attrib->texcoords = (float *)TINYOBJ_MALLOC(sizeof(float) * num_vt * 2);
1347 attrib->num_texcoords = (unsigned int)num_vt;
1348 attrib->faces = (tinyobj_vertex_index_t *)TINYOBJ_MALLOC(sizeof(tinyobj_vertex_index_t) * num_f);
1349 attrib->face_num_verts = (int *)TINYOBJ_MALLOC(sizeof(int) * num_faces);
1351 attrib->num_faces = (unsigned int)num_faces;
1352 attrib->num_face_num_verts = (unsigned int)num_f;
1354 attrib->material_ids = (int *)TINYOBJ_MALLOC(sizeof(int) * num_faces);
1356 for (i = 0; i < num_lines; i++) {
1357 if (commands[i].type == COMMAND_EMPTY) {
1359 } else if (commands[i].type == COMMAND_USEMTL) {
1361 if (commands[t][i].material_name &&
1362 commands[t][i].material_name_len > 0) {
1363 std::string material_name(commands[t][i].material_name,
1364 commands[t][i].material_name_len);
1366 if (material_map.find(material_name) != material_map.end()) {
1367 material_id = material_map[material_name];
1369 // Assign invalid material ID
1374 if (commands[i].material_name &&
1375 commands[i].material_name_len >0)
1377 /* Create a null terminated string */
1378 char* material_name_null_term = (char*) TINYOBJ_MALLOC(commands[i].material_name_len + 1);
1379 memcpy((void*) material_name_null_term, (const void*) commands[i].material_name, commands[i].material_name_len);
1380 material_name_null_term[commands[i].material_name_len] = 0;
1382 if (hash_table_exists(material_name_null_term, &material_table))
1383 material_id = (int)hash_table_get(material_name_null_term, &material_table);
1387 TINYOBJ_FREE(material_name_null_term);
1389 } else if (commands[i].type == COMMAND_V) {
1390 attrib->vertices[3 * v_count + 0] = commands[i].vx;
1391 attrib->vertices[3 * v_count + 1] = commands[i].vy;
1392 attrib->vertices[3 * v_count + 2] = commands[i].vz;
1394 } else if (commands[i].type == COMMAND_VN) {
1395 attrib->normals[3 * n_count + 0] = commands[i].nx;
1396 attrib->normals[3 * n_count + 1] = commands[i].ny;
1397 attrib->normals[3 * n_count + 2] = commands[i].nz;
1399 } else if (commands[i].type == COMMAND_VT) {
1400 attrib->texcoords[2 * t_count + 0] = commands[i].tx;
1401 attrib->texcoords[2 * t_count + 1] = commands[i].ty;
1403 } else if (commands[i].type == COMMAND_F) {
1405 for (k = 0; k < commands[i].num_f; k++) {
1406 tinyobj_vertex_index_t vi = commands[i].f[k];
1407 int v_idx = fixIndex(vi.v_idx, v_count);
1408 int vn_idx = fixIndex(vi.vn_idx, n_count);
1409 int vt_idx = fixIndex(vi.vt_idx, t_count);
1410 attrib->faces[f_count + k].v_idx = v_idx;
1411 attrib->faces[f_count + k].vn_idx = vn_idx;
1412 attrib->faces[f_count + k].vt_idx = vt_idx;
1415 for (k = 0; k < commands[i].num_f_num_verts; k++) {
1416 attrib->material_ids[face_count + k] = material_id;
1417 attrib->face_num_verts[face_count + k] = commands[i].f_num_verts[k];
1420 f_count += commands[i].num_f;
1421 face_count += commands[i].num_f_num_verts;
1426 /* 5. Construct shape information. */
1428 unsigned int face_count = 0;
1431 unsigned int shape_idx = 0;
1433 const char *shape_name = NULL;
1434 unsigned int shape_name_len = 0;
1435 const char *prev_shape_name = NULL;
1436 unsigned int prev_shape_name_len = 0;
1437 unsigned int prev_shape_face_offset = 0;
1438 unsigned int prev_face_offset = 0;
1439 tinyobj_shape_t prev_shape = {NULL, 0, 0};
1441 /* Find the number of shapes in .obj */
1442 for (i = 0; i < num_lines; i++) {
1443 if (commands[i].type == COMMAND_O || commands[i].type == COMMAND_G) {
1448 /* Allocate array of shapes with maximum possible size(+1 for unnamed
1450 * Actual # of shapes found in .obj is determined in the later */
1451 (*shapes) = (tinyobj_shape_t*)TINYOBJ_MALLOC(sizeof(tinyobj_shape_t) * (n + 1));
1453 for (i = 0; i < num_lines; i++) {
1454 if (commands[i].type == COMMAND_O || commands[i].type == COMMAND_G) {
1455 if (commands[i].type == COMMAND_O) {
1456 shape_name = commands[i].object_name;
1457 shape_name_len = commands[i].object_name_len;
1459 shape_name = commands[i].group_name;
1460 shape_name_len = commands[i].group_name_len;
1463 if (face_count == 0) {
1464 /* 'o' or 'g' appears before any 'f' */
1465 prev_shape_name = shape_name;
1466 prev_shape_name_len = shape_name_len;
1467 prev_shape_face_offset = face_count;
1468 prev_face_offset = face_count;
1470 if (shape_idx == 0) {
1471 /* 'o' or 'g' after some 'v' lines. */
1472 (*shapes)[shape_idx].name = my_strndup(
1473 prev_shape_name, prev_shape_name_len); /* may be NULL */
1474 (*shapes)[shape_idx].face_offset = prev_shape.face_offset;
1475 (*shapes)[shape_idx].length = face_count - prev_face_offset;
1478 prev_face_offset = face_count;
1481 if ((face_count - prev_face_offset) > 0) {
1482 (*shapes)[shape_idx].name =
1483 my_strndup(prev_shape_name, prev_shape_name_len);
1484 (*shapes)[shape_idx].face_offset = prev_face_offset;
1485 (*shapes)[shape_idx].length = face_count - prev_face_offset;
1487 prev_face_offset = face_count;
1491 /* Record shape info for succeeding 'o' or 'g' command. */
1492 prev_shape_name = shape_name;
1493 prev_shape_name_len = shape_name_len;
1494 prev_shape_face_offset = face_count;
1497 if (commands[i].type == COMMAND_F) {
1502 if ((face_count - prev_face_offset) > 0) {
1503 unsigned int length = face_count - prev_shape_face_offset;
1505 (*shapes)[shape_idx].name =
1506 my_strndup(prev_shape_name, prev_shape_name_len);
1507 (*shapes)[shape_idx].face_offset = prev_face_offset;
1508 (*shapes)[shape_idx].length = face_count - prev_face_offset;
1512 /* Guess no 'v' line occurrence after 'o' or 'g', so discards current
1513 * shape information. */
1516 (*num_shapes) = shape_idx;
1520 TINYOBJ_FREE(commands);
1523 destroy_hash_table(&material_table);
1525 (*materials_out) = materials;
1526 (*num_materials_out) = num_materials;
1528 return TINYOBJ_SUCCESS;
1531 void tinyobj_attrib_init(tinyobj_attrib_t *attrib) {
1532 attrib->vertices = NULL;
1533 attrib->num_vertices = 0;
1534 attrib->normals = NULL;
1535 attrib->num_normals = 0;
1536 attrib->texcoords = NULL;
1537 attrib->num_texcoords = 0;
1538 attrib->faces = NULL;
1539 attrib->num_faces = 0;
1540 attrib->face_num_verts = NULL;
1541 attrib->num_face_num_verts = 0;
1542 attrib->material_ids = NULL;
1545 void tinyobj_attrib_free(tinyobj_attrib_t *attrib) {
1546 if (attrib->vertices) TINYOBJ_FREE(attrib->vertices);
1547 if (attrib->normals) TINYOBJ_FREE(attrib->normals);
1548 if (attrib->texcoords) TINYOBJ_FREE(attrib->texcoords);
1549 if (attrib->faces) TINYOBJ_FREE(attrib->faces);
1550 if (attrib->face_num_verts) TINYOBJ_FREE(attrib->face_num_verts);
1551 if (attrib->material_ids) TINYOBJ_FREE(attrib->material_ids);
1554 void tinyobj_shapes_free(tinyobj_shape_t *shapes, unsigned int num_shapes) {
1556 if (shapes == NULL) return;
1558 for (i = 0; i < num_shapes; i++) {
1559 if (shapes[i].name) TINYOBJ_FREE(shapes[i].name);
1562 TINYOBJ_FREE(shapes);
1565 void tinyobj_materials_free(tinyobj_material_t *materials,
1566 unsigned int num_materials) {
1568 if (materials == NULL) return;
1570 for (i = 0; i < num_materials; i++) {
1571 if (materials[i].name) TINYOBJ_FREE(materials[i].name);
1572 if (materials[i].ambient_texname) TINYOBJ_FREE(materials[i].ambient_texname);
1573 if (materials[i].diffuse_texname) TINYOBJ_FREE(materials[i].diffuse_texname);
1574 if (materials[i].specular_texname) TINYOBJ_FREE(materials[i].specular_texname);
1575 if (materials[i].specular_highlight_texname)
1576 TINYOBJ_FREE(materials[i].specular_highlight_texname);
1577 if (materials[i].bump_texname) TINYOBJ_FREE(materials[i].bump_texname);
1578 if (materials[i].displacement_texname)
1579 TINYOBJ_FREE(materials[i].displacement_texname);
1580 if (materials[i].alpha_texname) TINYOBJ_FREE(materials[i].alpha_texname);
1583 TINYOBJ_FREE(materials);
1585 #endif /* TINYOBJ_LOADER_C_IMPLEMENTATION */
1587 #endif /* TINOBJ_LOADER_C_H_ */