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