Logo

index : raylib-jai

---

  • summary
  • about
  • tree
  • log
  • branches
<< path: root/public/raylib-jai.git/html/Raylib/raylib/src/external/tinyobj_loader_c.h blob: 88b2acbdc00ba417f0fe1dbddc808c344af15b51 [raw] [clear marker]

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