Wise&mystical  1.0
Project about Europe
Loading...
Searching...
No Matches
tinyobj_loader_c.h
Go to the documentation of this file.
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 */
54
55typedef struct {
56 char *name; /* group name or object name. */
57 unsigned int face_offset;
58 unsigned int length;
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;
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 */
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
105extern void tinyobj_shapes_free(tinyobj_shape_t *shapes, unsigned int num_shapes);
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) {
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;
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) {
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) {
740 }
741
742 if (num_materials_out == NULL) {
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);
753 }
754
755 /* Create a default material */
756 initMaterial(&material);
757
758 linebuf = (char*)TINYOBJ_MALLOC(buffer_size);
759 while (NULL != dynamic_fgets(&linebuf, &buffer_size, fp)) {
760 const char *token = linebuf;
761
762 line_end = token + strlen(token);
763
764 /* Skip leading space. */
765 token += strspn(token, " \t");
766
767 assert(token);
768 if (token[0] == '\0') continue; /* empty line */
769
770 if (token[0] == '#') continue; /* comment line */
771
772 /* new mtl */
773 if ((0 == strncmp(token, "newmtl", 6)) && IS_SPACE((token[6]))) {
774 char namebuf[4096];
775
776 /* flush previous material. */
777 if (has_previous_material) {
778 materials = tinyobj_material_add(materials, num_materials, &material);
779 num_materials++;
780 } else {
781 has_previous_material = 1;
782 }
783
784 /* initial temporary material */
785 initMaterial(&material);
786
787 /* set new mtl name */
788 token += 7;
789#ifdef _MSC_VER
790 sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf));
791#else
792 sscanf(token, "%s", namebuf);
793#endif
794 material.name = my_strdup(namebuf, (unsigned int) (line_end - token));
795
796 /* Add material to material table */
797 if (material_table)
798 hash_table_set(material.name, num_materials, material_table);
799
800 continue;
801 }
802
803 /* ambient */
804 if (token[0] == 'K' && token[1] == 'a' && IS_SPACE((token[2]))) {
805 float r, g, b;
806 token += 2;
807 parseFloat3(&r, &g, &b, &token);
808 material.ambient[0] = r;
809 material.ambient[1] = g;
810 material.ambient[2] = b;
811 continue;
812 }
813
814 /* diffuse */
815 if (token[0] == 'K' && token[1] == 'd' && IS_SPACE((token[2]))) {
816 float r, g, b;
817 token += 2;
818 parseFloat3(&r, &g, &b, &token);
819 material.diffuse[0] = r;
820 material.diffuse[1] = g;
821 material.diffuse[2] = b;
822 continue;
823 }
824
825 /* specular */
826 if (token[0] == 'K' && token[1] == 's' && IS_SPACE((token[2]))) {
827 float r, g, b;
828 token += 2;
829 parseFloat3(&r, &g, &b, &token);
830 material.specular[0] = r;
831 material.specular[1] = g;
832 material.specular[2] = b;
833 continue;
834 }
835
836 /* transmittance */
837 if (token[0] == 'K' && token[1] == 't' && IS_SPACE((token[2]))) {
838 float r, g, b;
839 token += 2;
840 parseFloat3(&r, &g, &b, &token);
841 material.transmittance[0] = r;
842 material.transmittance[1] = g;
843 material.transmittance[2] = b;
844 continue;
845 }
846
847 /* ior(index of refraction) */
848 if (token[0] == 'N' && token[1] == 'i' && IS_SPACE((token[2]))) {
849 token += 2;
850 material.ior = parseFloat(&token);
851 continue;
852 }
853
854 /* emission */
855 if (token[0] == 'K' && token[1] == 'e' && IS_SPACE(token[2])) {
856 float r, g, b;
857 token += 2;
858 parseFloat3(&r, &g, &b, &token);
859 material.emission[0] = r;
860 material.emission[1] = g;
861 material.emission[2] = b;
862 continue;
863 }
864
865 /* shininess */
866 if (token[0] == 'N' && token[1] == 's' && IS_SPACE(token[2])) {
867 token += 2;
868 material.shininess = parseFloat(&token);
869 continue;
870 }
871
872 /* illum model */
873 if (0 == strncmp(token, "illum", 5) && IS_SPACE(token[5])) {
874 token += 6;
875 material.illum = parseInt(&token);
876 continue;
877 }
878
879 /* dissolve */
880 if ((token[0] == 'd' && IS_SPACE(token[1]))) {
881 token += 1;
882 material.dissolve = parseFloat(&token);
883 continue;
884 }
885 if (token[0] == 'T' && token[1] == 'r' && IS_SPACE(token[2])) {
886 token += 2;
887 /* Invert value of Tr(assume Tr is in range [0, 1]) */
888 material.dissolve = 1.0f - parseFloat(&token);
889 continue;
890 }
891
892 /* ambient texture */
893 if ((0 == strncmp(token, "map_Ka", 6)) && IS_SPACE(token[6])) {
894 token += 7;
895 material.ambient_texname = my_strdup(token, (unsigned int) (line_end - token));
896 continue;
897 }
898
899 /* diffuse texture */
900 if ((0 == strncmp(token, "map_Kd", 6)) && IS_SPACE(token[6])) {
901 token += 7;
902 material.diffuse_texname = my_strdup(token, (unsigned int) (line_end - token));
903 continue;
904 }
905
906 /* specular texture */
907 if ((0 == strncmp(token, "map_Ks", 6)) && IS_SPACE(token[6])) {
908 token += 7;
909 material.specular_texname = my_strdup(token, (unsigned int) (line_end - token));
910 continue;
911 }
912
913 /* specular highlight texture */
914 if ((0 == strncmp(token, "map_Ns", 6)) && IS_SPACE(token[6])) {
915 token += 7;
916 material.specular_highlight_texname = my_strdup(token, (unsigned int) (line_end - token));
917 continue;
918 }
919
920 /* bump texture */
921 if ((0 == strncmp(token, "map_bump", 8)) && IS_SPACE(token[8])) {
922 token += 9;
923 material.bump_texname = my_strdup(token, (unsigned int) (line_end - token));
924 continue;
925 }
926
927 /* alpha texture */
928 if ((0 == strncmp(token, "map_d", 5)) && IS_SPACE(token[5])) {
929 token += 6;
930 material.alpha_texname = my_strdup(token, (unsigned int) (line_end - token));
931 continue;
932 }
933
934 /* bump texture */
935 if ((0 == strncmp(token, "bump", 4)) && IS_SPACE(token[4])) {
936 token += 5;
937 material.bump_texname = my_strdup(token, (unsigned int) (line_end - token));
938 continue;
939 }
940
941 /* displacement texture */
942 if ((0 == strncmp(token, "disp", 4)) && IS_SPACE(token[4])) {
943 token += 5;
944 material.displacement_texname = my_strdup(token, (unsigned int) (line_end - token));
945 continue;
946 }
947
948 /* @todo { unknown parameter } */
949 }
950
951 fclose(fp);
952
953 if (material.name) {
954 /* Flush last material element */
955 materials = tinyobj_material_add(materials, num_materials, &material);
956 num_materials++;
957 }
958
959 (*num_materials_out) = num_materials;
960 (*materials_out) = materials;
961
962 if (linebuf) {
963 TINYOBJ_FREE(linebuf);
964 }
965
966 return TINYOBJ_SUCCESS;
967}
968
970 unsigned int *num_materials_out,
971 const char *filename) {
972 return tinyobj_parse_and_index_mtl_file(materials_out, num_materials_out, filename, NULL);
973}
974
975
976typedef enum {
977 COMMAND_EMPTY,
978 COMMAND_V,
979 COMMAND_VN,
980 COMMAND_VT,
981 COMMAND_F,
982 COMMAND_G,
983 COMMAND_O,
984 COMMAND_USEMTL,
985 COMMAND_MTLLIB
986
987} CommandType;
988
989typedef struct {
990 float vx, vy, vz;
991 float nx, ny, nz;
992 float tx, ty;
993
994 /* @todo { Use dynamic array } */
995 tinyobj_vertex_index_t f[TINYOBJ_MAX_FACES_PER_F_LINE];
996 unsigned int num_f;
997
998 int f_num_verts[TINYOBJ_MAX_FACES_PER_F_LINE];
999 unsigned int num_f_num_verts;
1000
1001 const char *group_name;
1002 unsigned int group_name_len;
1003 int pad0;
1004
1005 const char *object_name;
1006 unsigned int object_name_len;
1007 int pad1;
1008
1009 const char *material_name;
1010 unsigned int material_name_len;
1011 int pad2;
1012
1013 const char *mtllib_name;
1014 unsigned int mtllib_name_len;
1015
1016 CommandType type;
1017} Command;
1018
1019static int parseLine(Command *command, const char *p, unsigned int p_len,
1020 int triangulate) {
1021 char linebuf[4096];
1022 const char *token;
1023 assert(p_len < 4095);
1024
1025 memcpy(linebuf, p, p_len);
1026 linebuf[p_len] = '\0';
1027
1028 token = linebuf;
1029
1030 command->type = COMMAND_EMPTY;
1031
1032 /* Skip leading space. */
1033 skip_space(&token);
1034
1035 assert(token);
1036 if (token[0] == '\0') { /* empty line */
1037 return 0;
1038 }
1039
1040 if (token[0] == '#') { /* comment line */
1041 return 0;
1042 }
1043
1044 /* vertex */
1045 if (token[0] == 'v' && IS_SPACE((token[1]))) {
1046 float x, y, z;
1047 token += 2;
1048 parseFloat3(&x, &y, &z, &token);
1049 command->vx = x;
1050 command->vy = y;
1051 command->vz = z;
1052 command->type = COMMAND_V;
1053 return 1;
1054 }
1055
1056 /* normal */
1057 if (token[0] == 'v' && token[1] == 'n' && IS_SPACE((token[2]))) {
1058 float x, y, z;
1059 token += 3;
1060 parseFloat3(&x, &y, &z, &token);
1061 command->nx = x;
1062 command->ny = y;
1063 command->nz = z;
1064 command->type = COMMAND_VN;
1065 return 1;
1066 }
1067
1068 /* texcoord */
1069 if (token[0] == 'v' && token[1] == 't' && IS_SPACE((token[2]))) {
1070 float x, y;
1071 token += 3;
1072 parseFloat2(&x, &y, &token);
1073 command->tx = x;
1074 command->ty = y;
1075 command->type = COMMAND_VT;
1076 return 1;
1077 }
1078
1079 /* face */
1080 if (token[0] == 'f' && IS_SPACE((token[1]))) {
1081 unsigned int num_f = 0;
1082
1083 tinyobj_vertex_index_t f[TINYOBJ_MAX_FACES_PER_F_LINE];
1084 token += 2;
1085 skip_space(&token);
1086
1087 while (!IS_NEW_LINE(token[0])) {
1088 tinyobj_vertex_index_t vi = parseRawTriple(&token);
1089 skip_space_and_cr(&token);
1090
1091 f[num_f] = vi;
1092 num_f++;
1093 }
1094
1095 command->type = COMMAND_F;
1096
1097 if (triangulate) {
1098 unsigned int k;
1099 unsigned int n = 0;
1100
1101 tinyobj_vertex_index_t i0 = f[0];
1103 tinyobj_vertex_index_t i2 = f[1];
1104
1105 assert(3 * num_f < TINYOBJ_MAX_FACES_PER_F_LINE);
1106
1107 for (k = 2; k < num_f; k++) {
1108 i1 = i2;
1109 i2 = f[k];
1110 command->f[3 * n + 0] = i0;
1111 command->f[3 * n + 1] = i1;
1112 command->f[3 * n + 2] = i2;
1113
1114 command->f_num_verts[n] = 3;
1115 n++;
1116 }
1117 command->num_f = 3 * n;
1118 command->num_f_num_verts = n;
1119
1120 } else {
1121 unsigned int k = 0;
1122 assert(num_f < TINYOBJ_MAX_FACES_PER_F_LINE);
1123 for (k = 0; k < num_f; k++) {
1124 command->f[k] = f[k];
1125 }
1126
1127 command->num_f = num_f;
1128 command->f_num_verts[0] = (int)num_f;
1129 command->num_f_num_verts = 1;
1130 }
1131
1132 return 1;
1133 }
1134
1135 /* use mtl */
1136 if ((0 == strncmp(token, "usemtl", 6)) && IS_SPACE((token[6]))) {
1137 token += 7;
1138
1139 skip_space(&token);
1140 command->material_name = p + (token - linebuf);
1141 command->material_name_len = (unsigned int)length_until_newline(
1142 token, (p_len - (unsigned int)(token - linebuf)) + 1);
1143 command->type = COMMAND_USEMTL;
1144
1145 return 1;
1146 }
1147
1148 /* load mtl */
1149 if ((0 == strncmp(token, "mtllib", 6)) && IS_SPACE((token[6]))) {
1150 /* By specification, `mtllib` should be appear only once in .obj */
1151 token += 7;
1152
1153 skip_space(&token);
1154 command->mtllib_name = p + (token - linebuf);
1155 command->mtllib_name_len = (unsigned int)length_until_newline(
1156 token, p_len - (unsigned int)(token - linebuf)) +
1157 1;
1158 command->type = COMMAND_MTLLIB;
1159
1160 return 1;
1161 }
1162
1163 /* group name */
1164 if (token[0] == 'g' && IS_SPACE((token[1]))) {
1165 /* @todo { multiple group name. } */
1166 token += 2;
1167
1168 command->group_name = p + (token - linebuf);
1169 command->group_name_len = (unsigned int)length_until_newline(
1170 token, p_len - (unsigned int)(token - linebuf)) +
1171 1;
1172 command->type = COMMAND_G;
1173
1174 return 1;
1175 }
1176
1177 /* object name */
1178 if (token[0] == 'o' && IS_SPACE((token[1]))) {
1179 /* @todo { multiple object name? } */
1180 token += 2;
1181
1182 command->object_name = p + (token - linebuf);
1183 command->object_name_len = (unsigned int)length_until_newline(
1184 token, p_len - (unsigned int)(token - linebuf)) +
1185 1;
1186 command->type = COMMAND_O;
1187
1188 return 1;
1189 }
1190
1191 return 0;
1192}
1193
1194typedef struct {
1195 unsigned int pos;
1196 unsigned int len;
1197} LineInfo;
1198
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; /* this includes \r\n */
1202 if (p[i] == '\r') {
1203 if (((i + 1) < end_i) && (p[i + 1] != '\n')) { /* detect only \r case */
1204 return 1;
1205 }
1206 }
1207 return 0;
1208}
1209
1211 unsigned int *num_shapes, tinyobj_material_t **materials_out,
1212 unsigned int *num_materials_out, const char *buf, unsigned int len,
1213 unsigned int flags) {
1214 LineInfo *line_infos = NULL;
1215 Command *commands = NULL;
1216 unsigned int num_lines = 0;
1217
1218 unsigned int num_v = 0;
1219 unsigned int num_vn = 0;
1220 unsigned int num_vt = 0;
1221 unsigned int num_f = 0;
1222 unsigned int num_faces = 0;
1223
1224 int mtllib_line_index = -1;
1225
1226 tinyobj_material_t *materials = NULL;
1227 unsigned int num_materials = 0;
1228
1229 hash_table_t material_table;
1230
1231 if (len < 1) return TINYOBJ_ERROR_INVALID_PARAMETER;
1232 if (attrib == NULL) return TINYOBJ_ERROR_INVALID_PARAMETER;
1233 if (shapes == NULL) return TINYOBJ_ERROR_INVALID_PARAMETER;
1234 if (num_shapes == NULL) return TINYOBJ_ERROR_INVALID_PARAMETER;
1235 if (buf == NULL) return TINYOBJ_ERROR_INVALID_PARAMETER;
1236 if (materials_out == NULL) return TINYOBJ_ERROR_INVALID_PARAMETER;
1237 if (num_materials_out == NULL) return TINYOBJ_ERROR_INVALID_PARAMETER;
1238
1239 tinyobj_attrib_init(attrib);
1240 /* 1. Find '\n' and create line data. */
1241 {
1242 unsigned int i;
1243 unsigned int end_idx = len;
1244 unsigned int prev_pos = 0;
1245 unsigned int line_no = 0;
1246 unsigned int last_line_ending = 0;
1247
1248 /* Count # of lines. */
1249 for (i = 0; i < end_idx; i++) {
1250 if (is_line_ending(buf, i, end_idx)) {
1251 num_lines++;
1252 last_line_ending = i;
1253 }
1254 }
1255 /* The last char from the input may not be a line
1256 * ending character so add an extra line if there
1257 * are more characters after the last line ending
1258 * that was found. */
1259 if (end_idx - last_line_ending > 0) {
1260 num_lines++;
1261 }
1262
1263 if (num_lines == 0) return TINYOBJ_ERROR_EMPTY;
1264
1265 line_infos = (LineInfo *)TINYOBJ_MALLOC(sizeof(LineInfo) * num_lines);
1266
1267 /* Fill line infos. */
1268 for (i = 0; i < end_idx; i++) {
1269 if (is_line_ending(buf, i, end_idx)) {
1270 line_infos[line_no].pos = prev_pos;
1271 line_infos[line_no].len = i - prev_pos;
1272 prev_pos = i + 1;
1273 line_no++;
1274 }
1275 }
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;
1279 }
1280 }
1281
1282 commands = (Command *)TINYOBJ_MALLOC(sizeof(Command) * num_lines);
1283
1284 create_hash_table(HASH_TABLE_DEFAULT_SIZE, &material_table);
1285
1286 /* 2. parse each line */
1287 {
1288 unsigned int i = 0;
1289 for (i = 0; i < num_lines; i++) {
1290 int ret = parseLine(&commands[i], &buf[line_infos[i].pos],
1291 line_infos[i].len, flags & TINYOBJ_FLAG_TRIANGULATE);
1292 if (ret) {
1293 if (commands[i].type == COMMAND_V) {
1294 num_v++;
1295 } else if (commands[i].type == COMMAND_VN) {
1296 num_vn++;
1297 } else if (commands[i].type == COMMAND_VT) {
1298 num_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;
1302 }
1303
1304 if (commands[i].type == COMMAND_MTLLIB) {
1305 mtllib_line_index = (int)i;
1306 }
1307 }
1308 }
1309 }
1310
1311 /* line_infos are not used anymore. Release memory. */
1312 if (line_infos) {
1313 TINYOBJ_FREE(line_infos);
1314 }
1315
1316 /* Load material(if exits) */
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);
1321
1322 int ret = tinyobj_parse_and_index_mtl_file(&materials, &num_materials, filename, &material_table);
1323
1324 if (ret != TINYOBJ_SUCCESS) {
1325 /* warning. */
1326 fprintf(stderr, "TINYOBJ: Failed to parse material file '%s': %d\n", filename, ret);
1327 }
1328
1329 TINYOBJ_FREE(filename);
1330
1331 }
1332
1333 /* Construct attributes */
1334
1335 {
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; /* -1 = default unknown material. */
1342 unsigned int i = 0;
1343
1344 attrib->vertices = (float *)TINYOBJ_MALLOC(sizeof(float) * num_v * 3);
1345 attrib->num_vertices = (unsigned int)num_v;
1346 attrib->normals = (float *)TINYOBJ_MALLOC(sizeof(float) * num_vn * 3);
1347 attrib->num_normals = (unsigned int)num_vn;
1348 attrib->texcoords = (float *)TINYOBJ_MALLOC(sizeof(float) * num_vt * 2);
1349 attrib->num_texcoords = (unsigned int)num_vt;
1351 attrib->face_num_verts = (int *)TINYOBJ_MALLOC(sizeof(int) * num_faces);
1352
1353 attrib->num_faces = (unsigned int)num_faces;
1354 attrib->num_face_num_verts = (unsigned int)num_f;
1355
1356 attrib->material_ids = (int *)TINYOBJ_MALLOC(sizeof(int) * num_faces);
1357
1358 for (i = 0; i < num_lines; i++) {
1359 if (commands[i].type == COMMAND_EMPTY) {
1360 continue;
1361 } else if (commands[i].type == COMMAND_USEMTL) {
1362 /* @todo
1363 if (commands[t][i].material_name &&
1364 commands[t][i].material_name_len > 0) {
1365 std::string material_name(commands[t][i].material_name,
1366 commands[t][i].material_name_len);
1367
1368 if (material_map.find(material_name) != material_map.end()) {
1369 material_id = material_map[material_name];
1370 } else {
1371 // Assign invalid material ID
1372 material_id = -1;
1373 }
1374 }
1375 */
1376 if (commands[i].material_name &&
1377 commands[i].material_name_len >0)
1378 {
1379 /* Create a null terminated string */
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;
1383
1384 if (hash_table_exists(material_name_null_term, &material_table))
1385 material_id = (int)hash_table_get(material_name_null_term, &material_table);
1386 else
1387 material_id = -1;
1388
1389 TINYOBJ_FREE(material_name_null_term);
1390 }
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;
1395 v_count++;
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;
1400 n_count++;
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;
1404 t_count++;
1405 } else if (commands[i].type == COMMAND_F) {
1406 unsigned int k = 0;
1407 for (k = 0; k < commands[i].num_f; k++) {
1408 tinyobj_vertex_index_t vi = commands[i].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);
1412 attrib->faces[f_count + k].v_idx = v_idx;
1413 attrib->faces[f_count + k].vn_idx = vn_idx;
1414 attrib->faces[f_count + k].vt_idx = vt_idx;
1415 }
1416
1417 for (k = 0; k < commands[i].num_f_num_verts; k++) {
1418 attrib->material_ids[face_count + k] = material_id;
1419 attrib->face_num_verts[face_count + k] = commands[i].f_num_verts[k];
1420 }
1421
1422 f_count += commands[i].num_f;
1423 face_count += commands[i].num_f_num_verts;
1424 }
1425 }
1426 }
1427
1428 /* 5. Construct shape information. */
1429 {
1430 unsigned int face_count = 0;
1431 unsigned int i = 0;
1432 unsigned int n = 0;
1433 unsigned int shape_idx = 0;
1434
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;
1441 tinyobj_shape_t prev_shape = {NULL, 0, 0};
1442
1443 /* Find the number of shapes in .obj */
1444 for (i = 0; i < num_lines; i++) {
1445 if (commands[i].type == COMMAND_O || commands[i].type == COMMAND_G) {
1446 n++;
1447 }
1448 }
1449
1450 /* Allocate array of shapes with maximum possible size(+1 for unnamed
1451 * group/object).
1452 * Actual # of shapes found in .obj is determined in the later */
1453 (*shapes) = (tinyobj_shape_t*)TINYOBJ_MALLOC(sizeof(tinyobj_shape_t) * (n + 1));
1454
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;
1460 } else {
1461 shape_name = commands[i].group_name;
1462 shape_name_len = commands[i].group_name_len;
1463 }
1464
1465 if (face_count == 0) {
1466 /* 'o' or 'g' appears before any 'f' */
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;
1471 } else {
1472 if (shape_idx == 0) {
1473 /* 'o' or 'g' after some 'v' lines. */
1474 (*shapes)[shape_idx].name = my_strndup(
1475 prev_shape_name, prev_shape_name_len); /* may be NULL */
1476 (*shapes)[shape_idx].face_offset = prev_shape.face_offset;
1477 (*shapes)[shape_idx].length = face_count - prev_face_offset;
1478 shape_idx++;
1479
1480 prev_face_offset = face_count;
1481
1482 } else {
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;
1488 shape_idx++;
1489 prev_face_offset = face_count;
1490 }
1491 }
1492
1493 /* Record shape info for succeeding 'o' or 'g' command. */
1494 prev_shape_name = shape_name;
1495 prev_shape_name_len = shape_name_len;
1496 prev_shape_face_offset = face_count;
1497 }
1498 }
1499 if (commands[i].type == COMMAND_F) {
1500 face_count++;
1501 }
1502 }
1503
1504 if ((face_count - prev_face_offset) > 0) {
1505 unsigned int length = face_count - prev_shape_face_offset;
1506 if (length > 0) {
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;
1511 shape_idx++;
1512 }
1513 } else {
1514 /* Guess no 'v' line occurrence after 'o' or 'g', so discards current
1515 * shape information. */
1516 }
1517
1518 (*num_shapes) = shape_idx;
1519 }
1520
1521 if (commands) {
1522 TINYOBJ_FREE(commands);
1523 }
1524
1525 destroy_hash_table(&material_table);
1526
1527 (*materials_out) = materials;
1528 (*num_materials_out) = num_materials;
1529
1530 return TINYOBJ_SUCCESS;
1531}
1532
1534 attrib->vertices = NULL;
1535 attrib->num_vertices = 0;
1536 attrib->normals = NULL;
1537 attrib->num_normals = 0;
1538 attrib->texcoords = NULL;
1539 attrib->num_texcoords = 0;
1540 attrib->faces = NULL;
1541 attrib->num_faces = 0;
1542 attrib->face_num_verts = NULL;
1543 attrib->num_face_num_verts = 0;
1544 attrib->material_ids = NULL;
1545}
1546
1548 if (attrib->vertices) TINYOBJ_FREE(attrib->vertices);
1549 if (attrib->normals) TINYOBJ_FREE(attrib->normals);
1550 if (attrib->texcoords) TINYOBJ_FREE(attrib->texcoords);
1551 if (attrib->faces) TINYOBJ_FREE(attrib->faces);
1552 if (attrib->face_num_verts) TINYOBJ_FREE(attrib->face_num_verts);
1553 if (attrib->material_ids) TINYOBJ_FREE(attrib->material_ids);
1554}
1555
1556void tinyobj_shapes_free(tinyobj_shape_t *shapes, unsigned int num_shapes) {
1557 unsigned int i;
1558 if (shapes == NULL) return;
1559
1560 for (i = 0; i < num_shapes; i++) {
1561 if (shapes[i].name) TINYOBJ_FREE(shapes[i].name);
1562 }
1563
1564 TINYOBJ_FREE(shapes);
1565}
1566
1568 unsigned int num_materials) {
1569 unsigned int i;
1570 if (materials == NULL) return;
1571
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)
1578 TINYOBJ_FREE(materials[i].specular_highlight_texname);
1579 if (materials[i].bump_texname) TINYOBJ_FREE(materials[i].bump_texname);
1580 if (materials[i].displacement_texname)
1581 TINYOBJ_FREE(materials[i].displacement_texname);
1582 if (materials[i].alpha_texname) TINYOBJ_FREE(materials[i].alpha_texname);
1583 }
1584
1585 TINYOBJ_FREE(materials);
1586}
1587#endif /* TINYOBJ_LOADER_C_IMPLEMENTATION */
1588
1589#endif /* TINOBJ_LOADER_C_H_ */
#define NULL
Definition: miniaudio.h:3718
#define TINYOBJ_CALLOC
Definition: rmodels.c:63
#define TINYOBJ_REALLOC
Definition: rmodels.c:64
#define TINYOBJ_FREE
Definition: rmodels.c:65
#define TINYOBJ_MALLOC
rmodels - Basic functions to draw 3d shapes and load and draw 3d models
Definition: rmodels.c:62
unsigned int num_faces
unsigned int num_face_num_verts
tinyobj_vertex_index_t * faces
unsigned int num_texcoords
unsigned int num_vertices
unsigned int num_normals
char * specular_highlight_texname
unsigned int length
unsigned int face_offset
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_SUCCESS
#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)