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 | |
30 | typedef struct { |
31 | char *name; |
32 | |
33 | float ambient[3]; |
34 | float diffuse[3]; |
35 | float specular[3]; |
36 | float transmittance[3]; |
37 | float emission[3]; |
38 | float shininess; |
39 | float ior; /* index of refraction */ |
40 | float dissolve; /* 1 == opaque; 0 == fully transparent */ |
41 | /* illumination model (see http://www.fileformat.info/format/material/) */ |
42 | int illum; |
43 | |
44 | int pad0; |
45 | |
46 | char *ambient_texname; /* map_Ka */ |
47 | char *diffuse_texname; /* map_Kd */ |
48 | char *specular_texname; /* map_Ks */ |
49 | char *specular_highlight_texname; /* map_Ns */ |
50 | char *bump_texname; /* map_bump, bump */ |
51 | char *displacement_texname; /* disp */ |
52 | char *alpha_texname; /* map_d */ |
53 | } tinyobj_material_t; |
54 | |
55 | typedef struct { |
56 | char *name; /* group name or object name. */ |
57 | unsigned int face_offset; |
58 | unsigned int length; |
59 | } tinyobj_shape_t; |
60 | |
61 | typedef struct { int v_idx, vt_idx, vn_idx; } tinyobj_vertex_index_t; |
62 | |
63 | typedef struct { |
64 | unsigned int num_vertices; |
65 | unsigned int num_normals; |
66 | unsigned int num_texcoords; |
67 | unsigned int num_faces; |
68 | unsigned int num_face_num_verts; |
69 | |
70 | int pad0; |
71 | |
72 | float *vertices; |
73 | float *normals; |
74 | float *texcoords; |
75 | tinyobj_vertex_index_t *faces; |
76 | int *face_num_verts; |
77 | int *material_ids; |
78 | } tinyobj_attrib_t; |
79 | |
80 | |
81 | #define TINYOBJ_FLAG_TRIANGULATE (1 << 0) |
82 | |
83 | #define TINYOBJ_INVALID_INDEX (0x80000000) |
84 | |
85 | #define TINYOBJ_SUCCESS (0) |
86 | #define TINYOBJ_ERROR_EMPTY (-1) |
87 | #define TINYOBJ_ERROR_INVALID_PARAMETER (-2) |
88 | #define TINYOBJ_ERROR_FILE_OPERATION (-3) |
89 | |
90 | /* Parse wavefront .obj(.obj string data is expanded to linear char array `buf') |
91 | * flags are combination of TINYOBJ_FLAG_*** |
92 | * Returns TINYOBJ_SUCCESS if things goes well. |
93 | * Returns TINYOBJ_ERR_*** when there is an error. |
94 | */ |
95 | extern int tinyobj_parse_obj(tinyobj_attrib_t *attrib, tinyobj_shape_t **shapes, |
96 | unsigned int *num_shapes, tinyobj_material_t **materials, |
97 | unsigned int *num_materials, const char *buf, unsigned int len, |
98 | unsigned int flags); |
99 | extern int tinyobj_parse_mtl_file(tinyobj_material_t **materials_out, |
100 | unsigned int *num_materials_out, |
101 | const char *filename); |
102 | |
103 | extern void tinyobj_attrib_init(tinyobj_attrib_t *attrib); |
104 | extern void tinyobj_attrib_free(tinyobj_attrib_t *attrib); |
105 | extern void tinyobj_shapes_free(tinyobj_shape_t *shapes, unsigned int num_shapes); |
106 | extern void tinyobj_materials_free(tinyobj_material_t *materials, |
107 | unsigned int num_materials); |
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 | |
137 | static void skip_space(const char **token) { |
138 | while ((*token)[0] == ' ' || (*token)[0] == '\t') { |
139 | (*token)++; |
140 | } |
141 | } |
142 | |
143 | static void skip_space_and_cr(const char **token) { |
144 | while ((*token)[0] == ' ' || (*token)[0] == '\t' || (*token)[0] == '\r') { |
145 | (*token)++; |
146 | } |
147 | } |
148 | |
149 | static int until_space(const char *token) { |
150 | const char *p = token; |
151 | while (p[0] != '\0' && p[0] != ' ' && p[0] != '\t' && p[0] != '\r') { |
152 | p++; |
153 | } |
154 | |
155 | return (int)(p - token); |
156 | } |
157 | |
158 | static 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 | |
174 | static 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 | */ |
189 | static 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. */ |
205 | static int fixIndex(int idx, unsigned int n) { |
206 | if (idx > 0) return idx - 1; |
207 | if (idx == 0) return 0; |
208 | return (int)n + idx; /* negative value = relative */ |
209 | } |
210 | |
211 | /* Parse raw triples: i, i/j/k, i//k, i/j */ |
212 | static tinyobj_vertex_index_t parseRawTriple(const char **token) { |
213 | tinyobj_vertex_index_t vi; |
214 | /* 0x80000000 = -2147483648 = invalid */ |
215 | vi.v_idx = (int)(0x80000000); |
216 | vi.vn_idx = (int)(0x80000000); |
217 | vi.vt_idx = (int)(0x80000000); |
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 | |
260 | static 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 | */ |
296 | static int tryParseDouble(const char *s, const char *s_end, double *result) { |
297 | double mantissa = 0.0; |
298 | /* This exponent is base 2 rather than 10. |
299 | * However the exponent we parse is supposed to be one of ten, |
300 | * thus we must take care to convert the exponent/and or the |
301 | * mantissa to a * 2^E, where a is the mantissa and E is the |
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 | |
402 | assemble : |
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; |
428 | fail: |
429 | return 0; |
430 | } |
431 | |
432 | static 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 | |
445 | static void parseFloat2(float *x, float *y, const char **token) { |
446 | (*x) = parseFloat(token); |
447 | (*y) = parseFloat(token); |
448 | } |
449 | |
450 | static void parseFloat3(float *x, float *y, float *z, const char **token) { |
451 | (*x) = parseFloat(token); |
452 | (*y) = parseFloat(token); |
453 | (*z) = parseFloat(token); |
454 | } |
455 | |
456 | static unsigned int my_strnlen(const char *s, unsigned int n) { |
457 | const char *p = memchr(s, 0, n); |
458 | return p ? (unsigned int)(p - s) : n; |
459 | } |
460 | |
461 | static 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 | |
479 | static 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 | |
497 | char *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 | |
522 | static void initMaterial(tinyobj_material_t *material) { |
523 | int i; |
524 | material->name = NULL; |
525 | material->ambient_texname = NULL; |
526 | material->diffuse_texname = NULL; |
527 | material->specular_texname = NULL; |
528 | material->specular_highlight_texname = NULL; |
529 | material->bump_texname = NULL; |
530 | material->displacement_texname = NULL; |
531 | material->alpha_texname = NULL; |
532 | for (i = 0; i < 3; i++) { |
533 | material->ambient[i] = 0.f; |
534 | material->diffuse[i] = 0.f; |
535 | material->specular[i] = 0.f; |
536 | material->transmittance[i] = 0.f; |
537 | material->emission[i] = 0.f; |
538 | } |
539 | material->illum = 0; |
540 | material->dissolve = 1.f; |
541 | material->shininess = 1.f; |
542 | material->ior = 1.f; |
543 | } |
544 | |
545 | /* Implementation of string to int hashtable */ |
546 | |
547 | #define HASH_TABLE_ERROR 1 |
548 | #define HASH_TABLE_SUCCESS 0 |
549 | |
550 | #define HASH_TABLE_DEFAULT_SIZE 10 |
551 | |
552 | typedef 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 | |
562 | typedef 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 | |
570 | static 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 | |
582 | static 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 | |
592 | static 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 */ |
599 | static 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 | |
629 | static 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 | |
640 | static 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 | |
654 | static 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 | |
681 | static 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 | |
686 | static 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 | |
708 | static 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 | |
714 | static tinyobj_material_t *tinyobj_material_add(tinyobj_material_t *prev, |
715 | unsigned int num_materials, |
716 | tinyobj_material_t *new_mat) { |
717 | tinyobj_material_t *dst; |
718 | dst = (tinyobj_material_t *)TINYOBJ_REALLOC( |
719 | prev, sizeof(tinyobj_material_t) * (num_materials + 1)); |
720 | |
721 | dst[num_materials] = (*new_mat); /* Just copy pointer for char* members */ |
722 | return dst; |
723 | } |
724 | |
725 | static int tinyobj_parse_and_index_mtl_file(tinyobj_material_t **materials_out, |
726 | unsigned int *num_materials_out, |
727 | const char *filename, |
728 | hash_table_t* material_table) { |
729 | tinyobj_material_t material; |
730 | unsigned int buffer_size = 128; |
731 | char *linebuf; |
732 | FILE *fp; |
733 | unsigned int num_materials = 0; |
734 | tinyobj_material_t *materials = NULL; |
735 | int has_previous_material = 0; |
736 | const char *line_end = NULL; |
737 | |
738 | if (materials_out == NULL) { |
739 | return TINYOBJ_ERROR_INVALID_PARAMETER; |
740 | } |
741 | |
742 | if (num_materials_out == NULL) { |
743 | return TINYOBJ_ERROR_INVALID_PARAMETER; |
744 | } |
745 | |
746 | (*materials_out) = NULL; |
747 | (*num_materials_out) = 0; |
748 | |
749 | fp = fopen(filename, "r" ); |
750 | if (!fp) { |
751 | //fprintf(stderr, "TINYOBJ: Error reading file '%s': %s (%d)\n", filename, strerror(errno), errno); // @raysan5: commented |
752 | return TINYOBJ_ERROR_FILE_OPERATION; |
753 | } |
754 | |
755 | /* Create a default material */ |
756 | initMaterial(&material); |
757 | |
758 | linebuf = (char*)TINYOBJ_MALLOC(buffer_size); |
759 | while (NULL != dynamic_fgets(&linebuf, &buffer_size, fp)) { |
760 | const char *token = linebuf; |
761 | |
762 | line_end = token + strlen(token); |
763 | |
764 | /* Skip leading space. */ |
765 | token += strspn(token, " \t" ); |
766 | |
767 | assert(token); |
768 | if (token[0] == '\0') continue; /* empty line */ |
769 | |
770 | if (token[0] == '#') continue; /* comment line */ |
771 | |
772 | /* new mtl */ |
773 | if ((0 == strncmp(token, "newmtl" , 6)) && IS_SPACE((token[6]))) { |
774 | char namebuf[4096]; |
775 | |
776 | /* flush previous material. */ |
777 | if (has_previous_material) { |
778 | materials = tinyobj_material_add(materials, num_materials, &material); |
779 | num_materials++; |
780 | } else { |
781 | has_previous_material = 1; |
782 | } |
783 | |
784 | /* initial temporary material */ |
785 | initMaterial(&material); |
786 | |
787 | /* set new mtl name */ |
788 | token += 7; |
789 | #ifdef _MSC_VER |
790 | sscanf_s(token, "%s" , namebuf, (unsigned)_countof(namebuf)); |
791 | #else |
792 | sscanf(token, "%s" , namebuf); |
793 | #endif |
794 | material.name = my_strdup(namebuf, (unsigned int) (line_end - token)); |
795 | |
796 | /* Add material to material table */ |
797 | if (material_table) |
798 | hash_table_set(material.name, num_materials, material_table); |
799 | |
800 | continue; |
801 | } |
802 | |
803 | /* ambient */ |
804 | if (token[0] == 'K' && token[1] == 'a' && IS_SPACE((token[2]))) { |
805 | float r, g, b; |
806 | token += 2; |
807 | parseFloat3(&r, &g, &b, &token); |
808 | material.ambient[0] = r; |
809 | material.ambient[1] = g; |
810 | material.ambient[2] = b; |
811 | continue; |
812 | } |
813 | |
814 | /* diffuse */ |
815 | if (token[0] == 'K' && token[1] == 'd' && IS_SPACE((token[2]))) { |
816 | float r, g, b; |
817 | token += 2; |
818 | parseFloat3(&r, &g, &b, &token); |
819 | material.diffuse[0] = r; |
820 | material.diffuse[1] = g; |
821 | material.diffuse[2] = b; |
822 | continue; |
823 | } |
824 | |
825 | /* specular */ |
826 | if (token[0] == 'K' && token[1] == 's' && IS_SPACE((token[2]))) { |
827 | float r, g, b; |
828 | token += 2; |
829 | parseFloat3(&r, &g, &b, &token); |
830 | material.specular[0] = r; |
831 | material.specular[1] = g; |
832 | material.specular[2] = b; |
833 | continue; |
834 | } |
835 | |
836 | /* transmittance */ |
837 | if (token[0] == 'K' && token[1] == 't' && IS_SPACE((token[2]))) { |
838 | float r, g, b; |
839 | token += 2; |
840 | parseFloat3(&r, &g, &b, &token); |
841 | material.transmittance[0] = r; |
842 | material.transmittance[1] = g; |
843 | material.transmittance[2] = b; |
844 | continue; |
845 | } |
846 | |
847 | /* ior(index of refraction) */ |
848 | if (token[0] == 'N' && token[1] == 'i' && IS_SPACE((token[2]))) { |
849 | token += 2; |
850 | material.ior = parseFloat(&token); |
851 | continue; |
852 | } |
853 | |
854 | /* emission */ |
855 | if (token[0] == 'K' && token[1] == 'e' && IS_SPACE(token[2])) { |
856 | float r, g, b; |
857 | token += 2; |
858 | parseFloat3(&r, &g, &b, &token); |
859 | material.emission[0] = r; |
860 | material.emission[1] = g; |
861 | material.emission[2] = b; |
862 | continue; |
863 | } |
864 | |
865 | /* shininess */ |
866 | if (token[0] == 'N' && token[1] == 's' && IS_SPACE(token[2])) { |
867 | token += 2; |
868 | material.shininess = parseFloat(&token); |
869 | continue; |
870 | } |
871 | |
872 | /* illum model */ |
873 | if (0 == strncmp(token, "illum" , 5) && IS_SPACE(token[5])) { |
874 | token += 6; |
875 | material.illum = parseInt(&token); |
876 | continue; |
877 | } |
878 | |
879 | /* dissolve */ |
880 | if ((token[0] == 'd' && IS_SPACE(token[1]))) { |
881 | token += 1; |
882 | material.dissolve = parseFloat(&token); |
883 | continue; |
884 | } |
885 | if (token[0] == 'T' && token[1] == 'r' && IS_SPACE(token[2])) { |
886 | token += 2; |
887 | /* Invert value of Tr(assume Tr is in range [0, 1]) */ |
888 | material.dissolve = 1.0f - parseFloat(&token); |
889 | continue; |
890 | } |
891 | |
892 | /* ambient texture */ |
893 | if ((0 == strncmp(token, "map_Ka" , 6)) && IS_SPACE(token[6])) { |
894 | token += 7; |
895 | material.ambient_texname = my_strdup(token, (unsigned int) (line_end - token)); |
896 | continue; |
897 | } |
898 | |
899 | /* diffuse texture */ |
900 | if ((0 == strncmp(token, "map_Kd" , 6)) && IS_SPACE(token[6])) { |
901 | token += 7; |
902 | material.diffuse_texname = my_strdup(token, (unsigned int) (line_end - token)); |
903 | continue; |
904 | } |
905 | |
906 | /* specular texture */ |
907 | if ((0 == strncmp(token, "map_Ks" , 6)) && IS_SPACE(token[6])) { |
908 | token += 7; |
909 | material.specular_texname = my_strdup(token, (unsigned int) (line_end - token)); |
910 | continue; |
911 | } |
912 | |
913 | /* specular highlight texture */ |
914 | if ((0 == strncmp(token, "map_Ns" , 6)) && IS_SPACE(token[6])) { |
915 | token += 7; |
916 | material.specular_highlight_texname = my_strdup(token, (unsigned int) (line_end - token)); |
917 | continue; |
918 | } |
919 | |
920 | /* bump texture */ |
921 | if ((0 == strncmp(token, "map_bump" , 8)) && IS_SPACE(token[8])) { |
922 | token += 9; |
923 | material.bump_texname = my_strdup(token, (unsigned int) (line_end - token)); |
924 | continue; |
925 | } |
926 | |
927 | /* alpha texture */ |
928 | if ((0 == strncmp(token, "map_d" , 5)) && IS_SPACE(token[5])) { |
929 | token += 6; |
930 | material.alpha_texname = my_strdup(token, (unsigned int) (line_end - token)); |
931 | continue; |
932 | } |
933 | |
934 | /* bump texture */ |
935 | if ((0 == strncmp(token, "bump" , 4)) && IS_SPACE(token[4])) { |
936 | token += 5; |
937 | material.bump_texname = my_strdup(token, (unsigned int) (line_end - token)); |
938 | continue; |
939 | } |
940 | |
941 | /* displacement texture */ |
942 | if ((0 == strncmp(token, "disp" , 4)) && IS_SPACE(token[4])) { |
943 | token += 5; |
944 | material.displacement_texname = my_strdup(token, (unsigned int) (line_end - token)); |
945 | continue; |
946 | } |
947 | |
948 | /* @todo { unknown parameter } */ |
949 | } |
950 | |
951 | if (material.name) { |
952 | /* Flush last material element */ |
953 | materials = tinyobj_material_add(materials, num_materials, &material); |
954 | num_materials++; |
955 | } |
956 | |
957 | (*num_materials_out) = num_materials; |
958 | (*materials_out) = materials; |
959 | |
960 | if (linebuf) { |
961 | TINYOBJ_FREE(linebuf); |
962 | } |
963 | |
964 | return TINYOBJ_SUCCESS; |
965 | } |
966 | |
967 | int tinyobj_parse_mtl_file(tinyobj_material_t **materials_out, |
968 | unsigned int *num_materials_out, |
969 | const char *filename) { |
970 | return tinyobj_parse_and_index_mtl_file(materials_out, num_materials_out, filename, NULL); |
971 | } |
972 | |
973 | |
974 | typedef enum { |
975 | COMMAND_EMPTY, |
976 | COMMAND_V, |
977 | COMMAND_VN, |
978 | COMMAND_VT, |
979 | COMMAND_F, |
980 | COMMAND_G, |
981 | COMMAND_O, |
982 | COMMAND_USEMTL, |
983 | COMMAND_MTLLIB |
984 | |
985 | } CommandType; |
986 | |
987 | typedef struct { |
988 | float vx, vy, vz; |
989 | float nx, ny, nz; |
990 | float tx, ty; |
991 | |
992 | /* @todo { Use dynamic array } */ |
993 | tinyobj_vertex_index_t f[TINYOBJ_MAX_FACES_PER_F_LINE]; |
994 | unsigned int num_f; |
995 | |
996 | int f_num_verts[TINYOBJ_MAX_FACES_PER_F_LINE]; |
997 | unsigned int num_f_num_verts; |
998 | |
999 | const char *group_name; |
1000 | unsigned int group_name_len; |
1001 | int pad0; |
1002 | |
1003 | const char *object_name; |
1004 | unsigned int object_name_len; |
1005 | int pad1; |
1006 | |
1007 | const char *material_name; |
1008 | unsigned int material_name_len; |
1009 | int pad2; |
1010 | |
1011 | const char *mtllib_name; |
1012 | unsigned int mtllib_name_len; |
1013 | |
1014 | CommandType type; |
1015 | } Command; |
1016 | |
1017 | static int parseLine(Command *command, const char *p, unsigned int p_len, |
1018 | int triangulate) { |
1019 | char linebuf[4096]; |
1020 | const char *token; |
1021 | assert(p_len < 4095); |
1022 | |
1023 | memcpy(linebuf, p, p_len); |
1024 | linebuf[p_len] = '\0'; |
1025 | |
1026 | token = linebuf; |
1027 | |
1028 | command->type = COMMAND_EMPTY; |
1029 | |
1030 | /* Skip leading space. */ |
1031 | skip_space(&token); |
1032 | |
1033 | assert(token); |
1034 | if (token[0] == '\0') { /* empty line */ |
1035 | return 0; |
1036 | } |
1037 | |
1038 | if (token[0] == '#') { /* comment line */ |
1039 | return 0; |
1040 | } |
1041 | |
1042 | /* vertex */ |
1043 | if (token[0] == 'v' && IS_SPACE((token[1]))) { |
1044 | float x, y, z; |
1045 | token += 2; |
1046 | parseFloat3(&x, &y, &z, &token); |
1047 | command->vx = x; |
1048 | command->vy = y; |
1049 | command->vz = z; |
1050 | command->type = COMMAND_V; |
1051 | return 1; |
1052 | } |
1053 | |
1054 | /* normal */ |
1055 | if (token[0] == 'v' && token[1] == 'n' && IS_SPACE((token[2]))) { |
1056 | float x, y, z; |
1057 | token += 3; |
1058 | parseFloat3(&x, &y, &z, &token); |
1059 | command->nx = x; |
1060 | command->ny = y; |
1061 | command->nz = z; |
1062 | command->type = COMMAND_VN; |
1063 | return 1; |
1064 | } |
1065 | |
1066 | /* texcoord */ |
1067 | if (token[0] == 'v' && token[1] == 't' && IS_SPACE((token[2]))) { |
1068 | float x, y; |
1069 | token += 3; |
1070 | parseFloat2(&x, &y, &token); |
1071 | command->tx = x; |
1072 | command->ty = y; |
1073 | command->type = COMMAND_VT; |
1074 | return 1; |
1075 | } |
1076 | |
1077 | /* face */ |
1078 | if (token[0] == 'f' && IS_SPACE((token[1]))) { |
1079 | unsigned int num_f = 0; |
1080 | |
1081 | tinyobj_vertex_index_t f[TINYOBJ_MAX_FACES_PER_F_LINE]; |
1082 | token += 2; |
1083 | skip_space(&token); |
1084 | |
1085 | while (!IS_NEW_LINE(token[0])) { |
1086 | tinyobj_vertex_index_t vi = parseRawTriple(&token); |
1087 | skip_space_and_cr(&token); |
1088 | |
1089 | f[num_f] = vi; |
1090 | num_f++; |
1091 | } |
1092 | |
1093 | command->type = COMMAND_F; |
1094 | |
1095 | if (triangulate) { |
1096 | unsigned int k; |
1097 | unsigned int n = 0; |
1098 | |
1099 | tinyobj_vertex_index_t i0 = f[0]; |
1100 | tinyobj_vertex_index_t i1; |
1101 | tinyobj_vertex_index_t i2 = f[1]; |
1102 | |
1103 | assert(3 * num_f < TINYOBJ_MAX_FACES_PER_F_LINE); |
1104 | |
1105 | for (k = 2; k < num_f; k++) { |
1106 | i1 = i2; |
1107 | i2 = f[k]; |
1108 | command->f[3 * n + 0] = i0; |
1109 | command->f[3 * n + 1] = i1; |
1110 | command->f[3 * n + 2] = i2; |
1111 | |
1112 | command->f_num_verts[n] = 3; |
1113 | n++; |
1114 | } |
1115 | command->num_f = 3 * n; |
1116 | command->num_f_num_verts = n; |
1117 | |
1118 | } else { |
1119 | unsigned int k = 0; |
1120 | assert(num_f < TINYOBJ_MAX_FACES_PER_F_LINE); |
1121 | for (k = 0; k < num_f; k++) { |
1122 | command->f[k] = f[k]; |
1123 | } |
1124 | |
1125 | command->num_f = num_f; |
1126 | command->f_num_verts[0] = (int)num_f; |
1127 | command->num_f_num_verts = 1; |
1128 | } |
1129 | |
1130 | return 1; |
1131 | } |
1132 | |
1133 | /* use mtl */ |
1134 | if ((0 == strncmp(token, "usemtl" , 6)) && IS_SPACE((token[6]))) { |
1135 | token += 7; |
1136 | |
1137 | skip_space(&token); |
1138 | command->material_name = p + (token - linebuf); |
1139 | command->material_name_len = (unsigned int)length_until_newline( |
1140 | token, (p_len - (unsigned int)(token - linebuf)) + 1); |
1141 | command->type = COMMAND_USEMTL; |
1142 | |
1143 | return 1; |
1144 | } |
1145 | |
1146 | /* load mtl */ |
1147 | if ((0 == strncmp(token, "mtllib" , 6)) && IS_SPACE((token[6]))) { |
1148 | /* By specification, `mtllib` should be appear only once in .obj */ |
1149 | token += 7; |
1150 | |
1151 | skip_space(&token); |
1152 | command->mtllib_name = p + (token - linebuf); |
1153 | command->mtllib_name_len = (unsigned int)length_until_newline( |
1154 | token, p_len - (unsigned int)(token - linebuf)) + |
1155 | 1; |
1156 | command->type = COMMAND_MTLLIB; |
1157 | |
1158 | return 1; |
1159 | } |
1160 | |
1161 | /* group name */ |
1162 | if (token[0] == 'g' && IS_SPACE((token[1]))) { |
1163 | /* @todo { multiple group name. } */ |
1164 | token += 2; |
1165 | |
1166 | command->group_name = p + (token - linebuf); |
1167 | command->group_name_len = (unsigned int)length_until_newline( |
1168 | token, p_len - (unsigned int)(token - linebuf)) + |
1169 | 1; |
1170 | command->type = COMMAND_G; |
1171 | |
1172 | return 1; |
1173 | } |
1174 | |
1175 | /* object name */ |
1176 | if (token[0] == 'o' && IS_SPACE((token[1]))) { |
1177 | /* @todo { multiple object name? } */ |
1178 | token += 2; |
1179 | |
1180 | command->object_name = p + (token - linebuf); |
1181 | command->object_name_len = (unsigned int)length_until_newline( |
1182 | token, p_len - (unsigned int)(token - linebuf)) + |
1183 | 1; |
1184 | command->type = COMMAND_O; |
1185 | |
1186 | return 1; |
1187 | } |
1188 | |
1189 | return 0; |
1190 | } |
1191 | |
1192 | typedef struct { |
1193 | unsigned int pos; |
1194 | unsigned int len; |
1195 | } LineInfo; |
1196 | |
1197 | static int is_line_ending(const char *p, unsigned int i, unsigned int end_i) { |
1198 | if (p[i] == '\0') return 1; |
1199 | if (p[i] == '\n') return 1; /* this includes \r\n */ |
1200 | if (p[i] == '\r') { |
1201 | if (((i + 1) < end_i) && (p[i + 1] != '\n')) { /* detect only \r case */ |
1202 | return 1; |
1203 | } |
1204 | } |
1205 | return 0; |
1206 | } |
1207 | |
1208 | int tinyobj_parse_obj(tinyobj_attrib_t *attrib, tinyobj_shape_t **shapes, |
1209 | unsigned int *num_shapes, tinyobj_material_t **materials_out, |
1210 | unsigned int *num_materials_out, const char *buf, unsigned int len, |
1211 | unsigned int flags) { |
1212 | LineInfo *line_infos = NULL; |
1213 | Command *commands = NULL; |
1214 | unsigned int num_lines = 0; |
1215 | |
1216 | unsigned int num_v = 0; |
1217 | unsigned int num_vn = 0; |
1218 | unsigned int num_vt = 0; |
1219 | unsigned int num_f = 0; |
1220 | unsigned int num_faces = 0; |
1221 | |
1222 | int mtllib_line_index = -1; |
1223 | |
1224 | tinyobj_material_t *materials = NULL; |
1225 | unsigned int num_materials = 0; |
1226 | |
1227 | hash_table_t material_table; |
1228 | |
1229 | if (len < 1) return TINYOBJ_ERROR_INVALID_PARAMETER; |
1230 | if (attrib == NULL) return TINYOBJ_ERROR_INVALID_PARAMETER; |
1231 | if (shapes == NULL) return TINYOBJ_ERROR_INVALID_PARAMETER; |
1232 | if (num_shapes == NULL) return TINYOBJ_ERROR_INVALID_PARAMETER; |
1233 | if (buf == NULL) return TINYOBJ_ERROR_INVALID_PARAMETER; |
1234 | if (materials_out == NULL) return TINYOBJ_ERROR_INVALID_PARAMETER; |
1235 | if (num_materials_out == NULL) return TINYOBJ_ERROR_INVALID_PARAMETER; |
1236 | |
1237 | tinyobj_attrib_init(attrib); |
1238 | /* 1. Find '\n' and create line data. */ |
1239 | { |
1240 | unsigned int i; |
1241 | unsigned int end_idx = len; |
1242 | unsigned int prev_pos = 0; |
1243 | unsigned int line_no = 0; |
1244 | unsigned int last_line_ending = 0; |
1245 | |
1246 | /* Count # of lines. */ |
1247 | for (i = 0; i < end_idx; i++) { |
1248 | if (is_line_ending(buf, i, end_idx)) { |
1249 | num_lines++; |
1250 | last_line_ending = i; |
1251 | } |
1252 | } |
1253 | /* The last char from the input may not be a line |
1254 | * ending character so add an extra line if there |
1255 | * are more characters after the last line ending |
1256 | * that was found. */ |
1257 | if (end_idx - last_line_ending > 0) { |
1258 | num_lines++; |
1259 | } |
1260 | |
1261 | if (num_lines == 0) return TINYOBJ_ERROR_EMPTY; |
1262 | |
1263 | line_infos = (LineInfo *)TINYOBJ_MALLOC(sizeof(LineInfo) * num_lines); |
1264 | |
1265 | /* Fill line infos. */ |
1266 | for (i = 0; i < end_idx; i++) { |
1267 | if (is_line_ending(buf, i, end_idx)) { |
1268 | line_infos[line_no].pos = prev_pos; |
1269 | line_infos[line_no].len = i - prev_pos; |
1270 | prev_pos = i + 1; |
1271 | line_no++; |
1272 | } |
1273 | } |
1274 | if (end_idx - last_line_ending > 0) { |
1275 | line_infos[line_no].pos = prev_pos; |
1276 | line_infos[line_no].len = end_idx - 1 - last_line_ending; |
1277 | } |
1278 | } |
1279 | |
1280 | commands = (Command *)TINYOBJ_MALLOC(sizeof(Command) * num_lines); |
1281 | |
1282 | create_hash_table(HASH_TABLE_DEFAULT_SIZE, &material_table); |
1283 | |
1284 | /* 2. parse each line */ |
1285 | { |
1286 | unsigned int i = 0; |
1287 | for (i = 0; i < num_lines; i++) { |
1288 | int ret = parseLine(&commands[i], &buf[line_infos[i].pos], |
1289 | line_infos[i].len, flags & TINYOBJ_FLAG_TRIANGULATE); |
1290 | if (ret) { |
1291 | if (commands[i].type == COMMAND_V) { |
1292 | num_v++; |
1293 | } else if (commands[i].type == COMMAND_VN) { |
1294 | num_vn++; |
1295 | } else if (commands[i].type == COMMAND_VT) { |
1296 | num_vt++; |
1297 | } else if (commands[i].type == COMMAND_F) { |
1298 | num_f += commands[i].num_f; |
1299 | num_faces += commands[i].num_f_num_verts; |
1300 | } |
1301 | |
1302 | if (commands[i].type == COMMAND_MTLLIB) { |
1303 | mtllib_line_index = (int)i; |
1304 | } |
1305 | } |
1306 | } |
1307 | } |
1308 | |
1309 | /* line_infos are not used anymore. Release memory. */ |
1310 | if (line_infos) { |
1311 | TINYOBJ_FREE(line_infos); |
1312 | } |
1313 | |
1314 | /* Load material(if exits) */ |
1315 | if (mtllib_line_index >= 0 && commands[mtllib_line_index].mtllib_name && |
1316 | commands[mtllib_line_index].mtllib_name_len > 0) { |
1317 | char *filename = my_strndup(commands[mtllib_line_index].mtllib_name, |
1318 | commands[mtllib_line_index].mtllib_name_len); |
1319 | |
1320 | int ret = tinyobj_parse_and_index_mtl_file(&materials, &num_materials, filename, &material_table); |
1321 | |
1322 | if (ret != TINYOBJ_SUCCESS) { |
1323 | /* warning. */ |
1324 | //fprintf(stderr, "TINYOBJ: Failed to parse material file '%s': %d\n", filename, ret); // @raysan5: commented |
1325 | } |
1326 | |
1327 | TINYOBJ_FREE(filename); |
1328 | |
1329 | } |
1330 | |
1331 | /* Construct attributes */ |
1332 | |
1333 | { |
1334 | unsigned int v_count = 0; |
1335 | unsigned int n_count = 0; |
1336 | unsigned int t_count = 0; |
1337 | unsigned int f_count = 0; |
1338 | unsigned int face_count = 0; |
1339 | int material_id = -1; /* -1 = default unknown material. */ |
1340 | unsigned int i = 0; |
1341 | |
1342 | attrib->vertices = (float *)TINYOBJ_MALLOC(sizeof(float) * num_v * 3); |
1343 | attrib->num_vertices = (unsigned int)num_v; |
1344 | attrib->normals = (float *)TINYOBJ_MALLOC(sizeof(float) * num_vn * 3); |
1345 | attrib->num_normals = (unsigned int)num_vn; |
1346 | attrib->texcoords = (float *)TINYOBJ_MALLOC(sizeof(float) * num_vt * 2); |
1347 | attrib->num_texcoords = (unsigned int)num_vt; |
1348 | attrib->faces = (tinyobj_vertex_index_t *)TINYOBJ_MALLOC(sizeof(tinyobj_vertex_index_t) * num_f); |
1349 | attrib->face_num_verts = (int *)TINYOBJ_MALLOC(sizeof(int) * num_faces); |
1350 | |
1351 | attrib->num_faces = (unsigned int)num_faces; |
1352 | attrib->num_face_num_verts = (unsigned int)num_f; |
1353 | |
1354 | attrib->material_ids = (int *)TINYOBJ_MALLOC(sizeof(int) * num_faces); |
1355 | |
1356 | for (i = 0; i < num_lines; i++) { |
1357 | if (commands[i].type == COMMAND_EMPTY) { |
1358 | continue; |
1359 | } else if (commands[i].type == COMMAND_USEMTL) { |
1360 | /* @todo |
1361 | if (commands[t][i].material_name && |
1362 | commands[t][i].material_name_len > 0) { |
1363 | std::string material_name(commands[t][i].material_name, |
1364 | commands[t][i].material_name_len); |
1365 | |
1366 | if (material_map.find(material_name) != material_map.end()) { |
1367 | material_id = material_map[material_name]; |
1368 | } else { |
1369 | // Assign invalid material ID |
1370 | material_id = -1; |
1371 | } |
1372 | } |
1373 | */ |
1374 | if (commands[i].material_name && |
1375 | commands[i].material_name_len >0) |
1376 | { |
1377 | /* Create a null terminated string */ |
1378 | char* material_name_null_term = (char*) TINYOBJ_MALLOC(commands[i].material_name_len + 1); |
1379 | memcpy((void*) material_name_null_term, (const void*) commands[i].material_name, commands[i].material_name_len); |
1380 | material_name_null_term[commands[i].material_name_len - 1] = 0; |
1381 | |
1382 | if (hash_table_exists(material_name_null_term, &material_table)) |
1383 | material_id = (int)hash_table_get(material_name_null_term, &material_table); |
1384 | else |
1385 | material_id = -1; |
1386 | |
1387 | TINYOBJ_FREE(material_name_null_term); |
1388 | } |
1389 | } else if (commands[i].type == COMMAND_V) { |
1390 | attrib->vertices[3 * v_count + 0] = commands[i].vx; |
1391 | attrib->vertices[3 * v_count + 1] = commands[i].vy; |
1392 | attrib->vertices[3 * v_count + 2] = commands[i].vz; |
1393 | v_count++; |
1394 | } else if (commands[i].type == COMMAND_VN) { |
1395 | attrib->normals[3 * n_count + 0] = commands[i].nx; |
1396 | attrib->normals[3 * n_count + 1] = commands[i].ny; |
1397 | attrib->normals[3 * n_count + 2] = commands[i].nz; |
1398 | n_count++; |
1399 | } else if (commands[i].type == COMMAND_VT) { |
1400 | attrib->texcoords[2 * t_count + 0] = commands[i].tx; |
1401 | attrib->texcoords[2 * t_count + 1] = commands[i].ty; |
1402 | t_count++; |
1403 | } else if (commands[i].type == COMMAND_F) { |
1404 | unsigned int k = 0; |
1405 | for (k = 0; k < commands[i].num_f; k++) { |
1406 | tinyobj_vertex_index_t vi = commands[i].f[k]; |
1407 | int v_idx = fixIndex(vi.v_idx, v_count); |
1408 | int vn_idx = fixIndex(vi.vn_idx, n_count); |
1409 | int vt_idx = fixIndex(vi.vt_idx, t_count); |
1410 | attrib->faces[f_count + k].v_idx = v_idx; |
1411 | attrib->faces[f_count + k].vn_idx = vn_idx; |
1412 | attrib->faces[f_count + k].vt_idx = vt_idx; |
1413 | } |
1414 | |
1415 | for (k = 0; k < commands[i].num_f_num_verts; k++) { |
1416 | attrib->material_ids[face_count + k] = material_id; |
1417 | attrib->face_num_verts[face_count + k] = commands[i].f_num_verts[k]; |
1418 | } |
1419 | |
1420 | f_count += commands[i].num_f; |
1421 | face_count += commands[i].num_f_num_verts; |
1422 | } |
1423 | } |
1424 | } |
1425 | |
1426 | /* 5. Construct shape information. */ |
1427 | { |
1428 | unsigned int face_count = 0; |
1429 | unsigned int i = 0; |
1430 | unsigned int n = 0; |
1431 | unsigned int shape_idx = 0; |
1432 | |
1433 | const char *shape_name = NULL; |
1434 | unsigned int shape_name_len = 0; |
1435 | const char *prev_shape_name = NULL; |
1436 | unsigned int prev_shape_name_len = 0; |
1437 | unsigned int prev_shape_face_offset = 0; |
1438 | unsigned int prev_face_offset = 0; |
1439 | tinyobj_shape_t prev_shape = {NULL, 0, 0}; |
1440 | |
1441 | /* Find the number of shapes in .obj */ |
1442 | for (i = 0; i < num_lines; i++) { |
1443 | if (commands[i].type == COMMAND_O || commands[i].type == COMMAND_G) { |
1444 | n++; |
1445 | } |
1446 | } |
1447 | |
1448 | /* Allocate array of shapes with maximum possible size(+1 for unnamed |
1449 | * group/object). |
1450 | * Actual # of shapes found in .obj is determined in the later */ |
1451 | (*shapes) = (tinyobj_shape_t*)TINYOBJ_MALLOC(sizeof(tinyobj_shape_t) * (n + 1)); |
1452 | |
1453 | for (i = 0; i < num_lines; i++) { |
1454 | if (commands[i].type == COMMAND_O || commands[i].type == COMMAND_G) { |
1455 | if (commands[i].type == COMMAND_O) { |
1456 | shape_name = commands[i].object_name; |
1457 | shape_name_len = commands[i].object_name_len; |
1458 | } else { |
1459 | shape_name = commands[i].group_name; |
1460 | shape_name_len = commands[i].group_name_len; |
1461 | } |
1462 | |
1463 | if (face_count == 0) { |
1464 | /* 'o' or 'g' appears before any 'f' */ |
1465 | prev_shape_name = shape_name; |
1466 | prev_shape_name_len = shape_name_len; |
1467 | prev_shape_face_offset = face_count; |
1468 | prev_face_offset = face_count; |
1469 | } else { |
1470 | if (shape_idx == 0) { |
1471 | /* 'o' or 'g' after some 'v' lines. */ |
1472 | (*shapes)[shape_idx].name = my_strndup( |
1473 | prev_shape_name, prev_shape_name_len); /* may be NULL */ |
1474 | (*shapes)[shape_idx].face_offset = prev_shape.face_offset; |
1475 | (*shapes)[shape_idx].length = face_count - prev_face_offset; |
1476 | shape_idx++; |
1477 | |
1478 | prev_face_offset = face_count; |
1479 | |
1480 | } else { |
1481 | if ((face_count - prev_face_offset) > 0) { |
1482 | (*shapes)[shape_idx].name = |
1483 | my_strndup(prev_shape_name, prev_shape_name_len); |
1484 | (*shapes)[shape_idx].face_offset = prev_face_offset; |
1485 | (*shapes)[shape_idx].length = face_count - prev_face_offset; |
1486 | shape_idx++; |
1487 | prev_face_offset = face_count; |
1488 | } |
1489 | } |
1490 | |
1491 | /* Record shape info for succeeding 'o' or 'g' command. */ |
1492 | prev_shape_name = shape_name; |
1493 | prev_shape_name_len = shape_name_len; |
1494 | prev_shape_face_offset = face_count; |
1495 | } |
1496 | } |
1497 | if (commands[i].type == COMMAND_F) { |
1498 | face_count++; |
1499 | } |
1500 | } |
1501 | |
1502 | if ((face_count - prev_face_offset) > 0) { |
1503 | unsigned int length = face_count - prev_shape_face_offset; |
1504 | if (length > 0) { |
1505 | (*shapes)[shape_idx].name = |
1506 | my_strndup(prev_shape_name, prev_shape_name_len); |
1507 | (*shapes)[shape_idx].face_offset = prev_face_offset; |
1508 | (*shapes)[shape_idx].length = face_count - prev_face_offset; |
1509 | shape_idx++; |
1510 | } |
1511 | } else { |
1512 | /* Guess no 'v' line occurrence after 'o' or 'g', so discards current |
1513 | * shape information. */ |
1514 | } |
1515 | |
1516 | (*num_shapes) = shape_idx; |
1517 | } |
1518 | |
1519 | if (commands) { |
1520 | TINYOBJ_FREE(commands); |
1521 | } |
1522 | |
1523 | destroy_hash_table(&material_table); |
1524 | |
1525 | (*materials_out) = materials; |
1526 | (*num_materials_out) = num_materials; |
1527 | |
1528 | return TINYOBJ_SUCCESS; |
1529 | } |
1530 | |
1531 | void tinyobj_attrib_init(tinyobj_attrib_t *attrib) { |
1532 | attrib->vertices = NULL; |
1533 | attrib->num_vertices = 0; |
1534 | attrib->normals = NULL; |
1535 | attrib->num_normals = 0; |
1536 | attrib->texcoords = NULL; |
1537 | attrib->num_texcoords = 0; |
1538 | attrib->faces = NULL; |
1539 | attrib->num_faces = 0; |
1540 | attrib->face_num_verts = NULL; |
1541 | attrib->num_face_num_verts = 0; |
1542 | attrib->material_ids = NULL; |
1543 | } |
1544 | |
1545 | void tinyobj_attrib_free(tinyobj_attrib_t *attrib) { |
1546 | if (attrib->vertices) TINYOBJ_FREE(attrib->vertices); |
1547 | if (attrib->normals) TINYOBJ_FREE(attrib->normals); |
1548 | if (attrib->texcoords) TINYOBJ_FREE(attrib->texcoords); |
1549 | if (attrib->faces) TINYOBJ_FREE(attrib->faces); |
1550 | if (attrib->face_num_verts) TINYOBJ_FREE(attrib->face_num_verts); |
1551 | if (attrib->material_ids) TINYOBJ_FREE(attrib->material_ids); |
1552 | } |
1553 | |
1554 | void tinyobj_shapes_free(tinyobj_shape_t *shapes, unsigned int num_shapes) { |
1555 | unsigned int i; |
1556 | if (shapes == NULL) return; |
1557 | |
1558 | for (i = 0; i < num_shapes; i++) { |
1559 | if (shapes[i].name) TINYOBJ_FREE(shapes[i].name); |
1560 | } |
1561 | |
1562 | TINYOBJ_FREE(shapes); |
1563 | } |
1564 | |
1565 | void tinyobj_materials_free(tinyobj_material_t *materials, |
1566 | unsigned int num_materials) { |
1567 | unsigned int i; |
1568 | if (materials == NULL) return; |
1569 | |
1570 | for (i = 0; i < num_materials; i++) { |
1571 | if (materials[i].name) TINYOBJ_FREE(materials[i].name); |
1572 | if (materials[i].ambient_texname) TINYOBJ_FREE(materials[i].ambient_texname); |
1573 | if (materials[i].diffuse_texname) TINYOBJ_FREE(materials[i].diffuse_texname); |
1574 | if (materials[i].specular_texname) TINYOBJ_FREE(materials[i].specular_texname); |
1575 | if (materials[i].specular_highlight_texname) |
1576 | TINYOBJ_FREE(materials[i].specular_highlight_texname); |
1577 | if (materials[i].bump_texname) TINYOBJ_FREE(materials[i].bump_texname); |
1578 | if (materials[i].displacement_texname) |
1579 | TINYOBJ_FREE(materials[i].displacement_texname); |
1580 | if (materials[i].alpha_texname) TINYOBJ_FREE(materials[i].alpha_texname); |
1581 | } |
1582 | |
1583 | TINYOBJ_FREE(materials); |
1584 | } |
1585 | #endif /* TINYOBJ_LOADER_C_IMPLEMENTATION */ |
1586 | |
1587 | #endif /* TINOBJ_LOADER_C_H_ */ |
1588 | |