24#ifndef TINOBJ_LOADER_C_H_
25#define TINOBJ_LOADER_C_H_
36 float transmittance[3];
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)
97 unsigned int *num_materials,
const char *buf,
unsigned int len,
100 unsigned int *num_materials_out,
101 const char *filename);
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'))
137static void skip_space(
const char **token) {
138 while ((*token)[0] ==
' ' || (*token)[0] ==
'\t') {
143static void skip_space_and_cr(
const char **token) {
144 while ((*token)[0] ==
' ' || (*token)[0] ==
'\t' || (*token)[0] ==
'\r') {
149static 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);
158static unsigned int length_until_newline(
const char *token,
unsigned int n) {
159 unsigned int len = 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'))) {
174static unsigned int length_until_line_feed(
const char *token,
unsigned int n) {
175 unsigned int len = 0;
178 for (len = 0; len < n; len++) {
179 if ((token[len] ==
'\n') || (token[len] ==
'\r')) {
189static int my_atoi(
const char *c) {
192 if (*c ==
'+' || *c ==
'-') {
193 if (*c ==
'-') sign = -1;
196 while (((*c) >=
'0') && ((*c) <=
'9')) {
198 value += (int)(*c -
'0');
205static int fixIndex(
int idx,
unsigned int n) {
206 if (idx > 0)
return idx - 1;
207 if (idx == 0)
return 0;
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] !=
'/') {
252 vi.
vn_idx = my_atoi((*token));
253 while ((*token)[0] !=
'\0' && (*token)[0] !=
'/' && (*token)[0] !=
' ' &&
254 (*token)[0] !=
'\t' && (*token)[0] !=
'\r') {
260static int parseInt(
const char **token) {
263 i = my_atoi((*token));
264 (*token) += until_space((*token));
296static int tryParseDouble(
const char *s,
const char *s_end,
double *result) {
297 double mantissa = 0.0;
313 char const *curr = s;
318 int end_not_reached = 0;
329 if (*curr ==
'+' || *curr ==
'-') {
332 }
else if (IS_DIGIT(*curr)) {
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);
348 if (read == 0)
goto fail;
350 if (!end_not_reached)
goto assemble;
356 end_not_reached = (curr != s_end);
357 while (end_not_reached && IS_DIGIT(*curr)) {
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;
377 if (*curr ==
'e' || *curr ==
'E') {
380 end_not_reached = (curr != s_end);
381 if (end_not_reached && (*curr ==
'+' || *curr ==
'-')) {
384 }
else if (IS_DIGIT(*curr)) {
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;
408 for (i = 0; i < exponent; i++) {
412 for (i = 0; i < exponent; i++) {
416 if (exp_sign ==
'-') {
424 (sign ==
'+' ? 1 : -1) * (mantissa * a * b);
432static float parseFloat(
const char **token) {
437 end = (*token) + until_space((*token));
439 tryParseDouble((*token), end, &val);
445static void parseFloat2(
float *x,
float *y,
const char **token) {
446 (*x) = parseFloat(token);
447 (*y) = parseFloat(token);
450static void parseFloat3(
float *x,
float *y,
float *z,
const char **token) {
451 (*x) = parseFloat(token);
452 (*y) = parseFloat(token);
453 (*z) = parseFloat(token);
456static 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;
461static char *my_strdup(
const char *s,
unsigned int max_length) {
468 len = length_until_line_feed(s, max_length);
473 memcpy(d, s, (
unsigned int)(len));
479static char *my_strndup(
const char *s,
unsigned int len) {
484 if (len == 0)
return NULL;
486 slen = my_strnlen(s, len);
497char *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')) {
514 offset = &((*buf)[old_size - 1]);
516 ret = fgets(offset, (
int)(old_size + 1), file);
517 }
while(ret && (
NULL == strchr(*buf,
'\n')));
532 for (i = 0; i < 3; i++) {
547#define HASH_TABLE_ERROR 1
548#define HASH_TABLE_SUCCESS 0
550#define HASH_TABLE_DEFAULT_SIZE 10
552typedef struct hash_table_entry_t
559 struct hash_table_entry_t* next;
564 unsigned long* hashes;
565 hash_table_entry_t* entries;
566 unsigned int capacity;
570static 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);
582static 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;
592static void destroy_hash_table(hash_table_t* hash_table)
599static 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) {
622 entry->next = start_entry->next;
623 start_entry->next = entry;
626 return HASH_TABLE_SUCCESS;
629static 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;
640static 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)
654static 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);
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);
678 (*hash_table) = new_hash_table;
681static 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;
686static 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;
703 hash_table_maybe_grow(hash_table->n + 1, hash_table);
705 while (hash_table_insert(hash, (
long)val, hash_table) != HASH_TABLE_SUCCESS);
708static 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);
715 unsigned int num_materials,
721 dst[num_materials] = (*new_mat);
726 unsigned int *num_materials_out,
727 const char *filename,
728 hash_table_t* material_table) {
730 unsigned int buffer_size = 128;
733 unsigned int num_materials = 0;
735 int has_previous_material = 0;
736 const char *line_end =
NULL;
738 if (materials_out ==
NULL) {
742 if (num_materials_out ==
NULL) {
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);
756 initMaterial(&material);
759 while (
NULL != dynamic_fgets(&linebuf, &buffer_size, fp)) {
760 const char *token = linebuf;
762 line_end = token + strlen(token);
765 token += strspn(token,
" \t");
768 if (token[0] ==
'\0')
continue;
770 if (token[0] ==
'#')
continue;
773 if ((0 == strncmp(token,
"newmtl", 6)) && IS_SPACE((token[6]))) {
777 if (has_previous_material) {
778 materials = tinyobj_material_add(materials, num_materials, &material);
781 has_previous_material = 1;
785 initMaterial(&material);
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));
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);
815 if (token[0] ==
'K' && token[1] ==
'd' && IS_SPACE((token[2]))) {
818 parseFloat3(&r, &g, &b, &token);
826 if (token[0] ==
'K' && token[1] ==
's' && IS_SPACE((token[2]))) {
829 parseFloat3(&r, &g, &b, &token);
837 if (token[0] ==
'K' && token[1] ==
't' && IS_SPACE((token[2]))) {
840 parseFloat3(&r, &g, &b, &token);
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);
866 if (token[0] ==
'N' && token[1] ==
's' && IS_SPACE(token[2])) {
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])) {
888 material.
dissolve = 1.0f - parseFloat(&token);
893 if ((0 == strncmp(token,
"map_Ka", 6)) && IS_SPACE(token[6])) {
895 material.
ambient_texname = my_strdup(token, (
unsigned int) (line_end - token));
900 if ((0 == strncmp(token,
"map_Kd", 6)) && IS_SPACE(token[6])) {
902 material.
diffuse_texname = my_strdup(token, (
unsigned int) (line_end - token));
907 if ((0 == strncmp(token,
"map_Ks", 6)) && IS_SPACE(token[6])) {
909 material.
specular_texname = my_strdup(token, (
unsigned int) (line_end - token));
914 if ((0 == strncmp(token,
"map_Ns", 6)) && IS_SPACE(token[6])) {
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));
942 if ((0 == strncmp(token,
"disp", 4)) && IS_SPACE(token[4])) {
955 materials = tinyobj_material_add(materials, num_materials, &material);
959 (*num_materials_out) = num_materials;
960 (*materials_out) = materials;
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);
998 int f_num_verts[TINYOBJ_MAX_FACES_PER_F_LINE];
999 unsigned int num_f_num_verts;
1001 const char *group_name;
1002 unsigned int group_name_len;
1005 const char *object_name;
1006 unsigned int object_name_len;
1009 const char *material_name;
1010 unsigned int material_name_len;
1013 const char *mtllib_name;
1014 unsigned int mtllib_name_len;
1019static int parseLine(Command *command,
const char *p,
unsigned int p_len,
1023 assert(p_len < 4095);
1025 memcpy(linebuf, p, p_len);
1026 linebuf[p_len] =
'\0';
1030 command->type = COMMAND_EMPTY;
1036 if (token[0] ==
'\0') {
1040 if (token[0] ==
'#') {
1045 if (token[0] ==
'v' && IS_SPACE((token[1]))) {
1048 parseFloat3(&x, &y, &z, &token);
1052 command->type = COMMAND_V;
1057 if (token[0] ==
'v' && token[1] ==
'n' && IS_SPACE((token[2]))) {
1060 parseFloat3(&x, &y, &z, &token);
1064 command->type = COMMAND_VN;
1069 if (token[0] ==
'v' && token[1] ==
't' && IS_SPACE((token[2]))) {
1072 parseFloat2(&x, &y, &token);
1075 command->type = COMMAND_VT;
1080 if (token[0] ==
'f' && IS_SPACE((token[1]))) {
1081 unsigned int num_f = 0;
1087 while (!IS_NEW_LINE(token[0])) {
1089 skip_space_and_cr(&token);
1095 command->type = COMMAND_F;
1105 assert(3 * num_f < TINYOBJ_MAX_FACES_PER_F_LINE);
1107 for (k = 2; k < num_f; k++) {
1110 command->f[3 * n + 0] = i0;
1111 command->f[3 * n + 1] = i1;
1112 command->f[3 * n + 2] = i2;
1114 command->f_num_verts[n] = 3;
1117 command->num_f = 3 * n;
1118 command->num_f_num_verts = n;
1122 assert(num_f < TINYOBJ_MAX_FACES_PER_F_LINE);
1123 for (k = 0; k < num_f; k++) {
1124 command->f[k] = f[k];
1127 command->num_f = num_f;
1128 command->f_num_verts[0] = (int)num_f;
1129 command->num_f_num_verts = 1;
1136 if ((0 == strncmp(token,
"usemtl", 6)) && IS_SPACE((token[6]))) {
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;
1149 if ((0 == strncmp(token,
"mtllib", 6)) && IS_SPACE((token[6]))) {
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)) +
1158 command->type = COMMAND_MTLLIB;
1164 if (token[0] ==
'g' && IS_SPACE((token[1]))) {
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)) +
1172 command->type = COMMAND_G;
1178 if (token[0] ==
'o' && IS_SPACE((token[1]))) {
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)) +
1186 command->type = COMMAND_O;
1199static 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;
1203 if (((i + 1) < end_i) && (p[i + 1] !=
'\n')) {
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;
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;
1224 int mtllib_line_index = -1;
1227 unsigned int num_materials = 0;
1229 hash_table_t material_table;
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;
1249 for (i = 0; i < end_idx; i++) {
1250 if (is_line_ending(buf, i, end_idx)) {
1252 last_line_ending = i;
1259 if (end_idx - last_line_ending > 0) {
1265 line_infos = (LineInfo *)
TINYOBJ_MALLOC(
sizeof(LineInfo) * num_lines);
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;
1276 if (end_idx - last_line_ending > 0) {
1277 line_infos[line_no].pos = prev_pos;
1278 line_infos[line_no].len = end_idx - 1 - last_line_ending;
1282 commands = (Command *)
TINYOBJ_MALLOC(
sizeof(Command) * num_lines);
1284 create_hash_table(HASH_TABLE_DEFAULT_SIZE, &material_table);
1289 for (i = 0; i < num_lines; i++) {
1290 int ret = parseLine(&commands[i], &buf[line_infos[i].pos],
1293 if (commands[i].type == COMMAND_V) {
1295 }
else if (commands[i].type == COMMAND_VN) {
1297 }
else if (commands[i].type == COMMAND_VT) {
1299 }
else if (commands[i].type == COMMAND_F) {
1300 num_f += commands[i].num_f;
1301 num_faces += commands[i].num_f_num_verts;
1304 if (commands[i].type == COMMAND_MTLLIB) {
1305 mtllib_line_index = (int)i;
1317 if (mtllib_line_index >= 0 && commands[mtllib_line_index].mtllib_name &&
1318 commands[mtllib_line_index].mtllib_name_len > 0) {
1319 char *filename = my_strndup(commands[mtllib_line_index].mtllib_name,
1320 commands[mtllib_line_index].mtllib_name_len);
1322 int ret = tinyobj_parse_and_index_mtl_file(&materials, &num_materials, filename, &material_table);
1326 fprintf(stderr,
"TINYOBJ: Failed to parse material file '%s': %d\n", filename, ret);
1336 unsigned int v_count = 0;
1337 unsigned int n_count = 0;
1338 unsigned int t_count = 0;
1339 unsigned int f_count = 0;
1340 unsigned int face_count = 0;
1341 int material_id = -1;
1353 attrib->
num_faces = (
unsigned int)num_faces;
1358 for (i = 0; i < num_lines; i++) {
1359 if (commands[i].type == COMMAND_EMPTY) {
1361 }
else if (commands[i].type == COMMAND_USEMTL) {
1376 if (commands[i].material_name &&
1377 commands[i].material_name_len >0)
1380 char* material_name_null_term = (
char*)
TINYOBJ_MALLOC(commands[i].material_name_len + 1);
1381 memcpy((
void*) material_name_null_term, (
const void*) commands[i].material_name, commands[i].material_name_len);
1382 material_name_null_term[commands[i].material_name_len] = 0;
1384 if (hash_table_exists(material_name_null_term, &material_table))
1385 material_id = (int)hash_table_get(material_name_null_term, &material_table);
1391 }
else if (commands[i].type == COMMAND_V) {
1392 attrib->
vertices[3 * v_count + 0] = commands[i].vx;
1393 attrib->
vertices[3 * v_count + 1] = commands[i].vy;
1394 attrib->
vertices[3 * v_count + 2] = commands[i].vz;
1396 }
else if (commands[i].type == COMMAND_VN) {
1397 attrib->
normals[3 * n_count + 0] = commands[i].nx;
1398 attrib->
normals[3 * n_count + 1] = commands[i].ny;
1399 attrib->
normals[3 * n_count + 2] = commands[i].nz;
1401 }
else if (commands[i].type == COMMAND_VT) {
1402 attrib->
texcoords[2 * t_count + 0] = commands[i].tx;
1403 attrib->
texcoords[2 * t_count + 1] = commands[i].ty;
1405 }
else if (commands[i].type == COMMAND_F) {
1407 for (k = 0; k < commands[i].num_f; k++) {
1409 int v_idx = fixIndex(vi.
v_idx, v_count);
1410 int vn_idx = fixIndex(vi.
vn_idx, n_count);
1411 int vt_idx = fixIndex(vi.
vt_idx, t_count);
1417 for (k = 0; k < commands[i].num_f_num_verts; k++) {
1419 attrib->
face_num_verts[face_count + k] = commands[i].f_num_verts[k];
1422 f_count += commands[i].num_f;
1423 face_count += commands[i].num_f_num_verts;
1430 unsigned int face_count = 0;
1433 unsigned int shape_idx = 0;
1435 const char *shape_name =
NULL;
1436 unsigned int shape_name_len = 0;
1437 const char *prev_shape_name =
NULL;
1438 unsigned int prev_shape_name_len = 0;
1439 unsigned int prev_shape_face_offset = 0;
1440 unsigned int prev_face_offset = 0;
1444 for (i = 0; i < num_lines; i++) {
1445 if (commands[i].type == COMMAND_O || commands[i].type == COMMAND_G) {
1455 for (i = 0; i < num_lines; i++) {
1456 if (commands[i].type == COMMAND_O || commands[i].type == COMMAND_G) {
1457 if (commands[i].type == COMMAND_O) {
1458 shape_name = commands[i].object_name;
1459 shape_name_len = commands[i].object_name_len;
1461 shape_name = commands[i].group_name;
1462 shape_name_len = commands[i].group_name_len;
1465 if (face_count == 0) {
1467 prev_shape_name = shape_name;
1468 prev_shape_name_len = shape_name_len;
1469 prev_shape_face_offset = face_count;
1470 prev_face_offset = face_count;
1472 if (shape_idx == 0) {
1474 (*shapes)[shape_idx].name = my_strndup(
1475 prev_shape_name, prev_shape_name_len);
1476 (*shapes)[shape_idx].face_offset = prev_shape.
face_offset;
1477 (*shapes)[shape_idx].length = face_count - prev_face_offset;
1480 prev_face_offset = face_count;
1483 if ((face_count - prev_face_offset) > 0) {
1484 (*shapes)[shape_idx].name =
1485 my_strndup(prev_shape_name, prev_shape_name_len);
1486 (*shapes)[shape_idx].face_offset = prev_face_offset;
1487 (*shapes)[shape_idx].length = face_count - prev_face_offset;
1489 prev_face_offset = face_count;
1494 prev_shape_name = shape_name;
1495 prev_shape_name_len = shape_name_len;
1496 prev_shape_face_offset = face_count;
1499 if (commands[i].type == COMMAND_F) {
1504 if ((face_count - prev_face_offset) > 0) {
1505 unsigned int length = face_count - prev_shape_face_offset;
1507 (*shapes)[shape_idx].name =
1508 my_strndup(prev_shape_name, prev_shape_name_len);
1509 (*shapes)[shape_idx].face_offset = prev_face_offset;
1510 (*shapes)[shape_idx].length = face_count - prev_face_offset;
1518 (*num_shapes) = shape_idx;
1525 destroy_hash_table(&material_table);
1527 (*materials_out) = materials;
1528 (*num_materials_out) = num_materials;
1558 if (shapes ==
NULL)
return;
1560 for (i = 0; i < num_shapes; i++) {
1568 unsigned int num_materials) {
1570 if (materials ==
NULL)
return;
1572 for (i = 0; i < num_materials; i++) {
1573 if (materials[i].name)
TINYOBJ_FREE(materials[i].name);
1574 if (materials[i].ambient_texname)
TINYOBJ_FREE(materials[i].ambient_texname);
1575 if (materials[i].diffuse_texname)
TINYOBJ_FREE(materials[i].diffuse_texname);
1576 if (materials[i].specular_texname)
TINYOBJ_FREE(materials[i].specular_texname);
1577 if (materials[i].specular_highlight_texname)
1579 if (materials[i].bump_texname)
TINYOBJ_FREE(materials[i].bump_texname);
1580 if (materials[i].displacement_texname)
1582 if (materials[i].alpha_texname)
TINYOBJ_FREE(materials[i].alpha_texname);
#define TINYOBJ_MALLOC
rmodels - Basic functions to draw 3d shapes and load and draw 3d models
unsigned int num_face_num_verts
tinyobj_vertex_index_t * faces
unsigned int num_texcoords
unsigned int num_vertices
char * specular_highlight_texname
char * displacement_texname
int tinyobj_parse_obj(tinyobj_attrib_t *attrib, tinyobj_shape_t **shapes, unsigned int *num_shapes, tinyobj_material_t **materials, unsigned int *num_materials, const char *buf, unsigned int len, unsigned int flags)
int tinyobj_parse_mtl_file(tinyobj_material_t **materials_out, unsigned int *num_materials_out, const char *filename)
void tinyobj_attrib_free(tinyobj_attrib_t *attrib)
#define TINYOBJ_ERROR_INVALID_PARAMETER
#define TINYOBJ_ERROR_EMPTY
#define TINYOBJ_FLAG_TRIANGULATE
void tinyobj_attrib_init(tinyobj_attrib_t *attrib)
#define TINYOBJ_ERROR_FILE_OPERATION
void tinyobj_shapes_free(tinyobj_shape_t *shapes, unsigned int num_shapes)
void tinyobj_materials_free(tinyobj_material_t *materials, unsigned int num_materials)