loader_aw

Load OBJ/MTL file formats
git clone git://git.meso-star.fr/loader_aw.git
Log | Files | Refs | README | LICENSE

commit a4fde9c937876a4cd828173cacec08ac684901d7
parent 4731fce957c755f180bfe70136e66d15211a55e4
Author: vaplv <vaplv@free.fr>
Date:   Sun,  6 Jul 2014 16:35:34 +0200

Parse the newmtl, Ka, Kd and Ks mtl directives

Diffstat:
Mcmake/CMakeLists.txt | 2+-
Asrc/aw.c | 47+++++++++++++++++++++++++++++++++++++++++++++++
Msrc/aw.h | 5+++++
Asrc/aw_c.h | 42++++++++++++++++++++++++++++++++++++++++++
Msrc/aw_mtl.c | 226+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
Msrc/aw_obj.c | 78+++++++++++++++---------------------------------------------------------------
6 files changed, 333 insertions(+), 67 deletions(-)

diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt @@ -17,7 +17,7 @@ include(rcmake) ################################################################################ # Define targets ################################################################################ -set(AW_FILES_SRC aw_obj.c aw_mtl.c) +set(AW_FILES_SRC aw_c.h aw.c aw_obj.c aw_mtl.c) set(AW_FILES_INC aw.h) rcmake_prepend_path(AW_FILES_SRC ${AW_SOURCE_DIR}) rcmake_prepend_path(AW_FILES_INC ${AW_SOURCE_DIR}) diff --git a/src/aw.c b/src/aw.c @@ -0,0 +1,47 @@ +#include "aw_c.h" +#include <rsys/mem_allocator.h> + +/******************************************************************************* + * Local functions + ******************************************************************************/ +enum aw_result +read_file(struct mem_allocator* allocator, const char* filename, char** out) +{ + FILE* file = NULL; + char* content = NULL; + long size = 0; + size_t len = 0; + enum aw_result res = AW_OK; + ASSERT(allocator && filename && out); + (void)len; + + file = fopen(filename, "r"); + if(!file) { + res = AW_IO_ERROR; + goto error; + } + fseek(file, 0, SEEK_END); + size = ftell(file); + fseek(file, 0, SEEK_SET); + + content = MEM_ALLOC(allocator, (size_t)size + 1 /* Null char */); + if(!content) { + res = AW_MEMORY_ERROR; + goto error; + } + len = fread(content, 1, (size_t)size, file); + content[size] = '\0'; + ASSERT(len == (size_t)size); + +exit: + if(file) + fclose(file); + *out = content; + return res; +error: + if(content) { + MEM_FREE(allocator, content); + content = NULL; + } + goto exit; +} diff --git a/src/aw.h b/src/aw.h @@ -144,6 +144,11 @@ AW_API enum aw_result aw_mtl_ref_put (struct aw_mtl* mtl); +AW_API enum aw_result +aw_mtl_load + (struct aw_mtl* mtl, + const char* filename); + END_DECLS #endif /* AW_H */ diff --git a/src/aw_c.h b/src/aw_c.h @@ -0,0 +1,42 @@ +#ifndef AW_C_H +#define AW_C_H + +#include "aw.h" + +static FINLINE enum aw_result +string_to_float(const char* str, float* f) +{ + char* ptr = NULL; + ASSERT(f); + if(!str) + return AW_BAD_ARGUMENT; + *f = (float)strtod(str, &ptr); + if(ptr == str || *ptr != '\0') + return AW_BAD_ARGUMENT; + return AW_OK; +} + +static FINLINE enum aw_result +string_to_size_t(const char* str, size_t* i) +{ + char* ptr = NULL; + ASSERT(i); + if(!str) + return AW_BAD_ARGUMENT; + *i = (size_t)strtol(str, &ptr, 10); + if(ptr == str || *ptr != '\0') + return AW_BAD_ARGUMENT; + return AW_OK; +} + +/* Read `filename' and store its content in `file'. `file' is internally + * allocated by the provided allocator and consequentle the callee should + * deallocate it by using the same allocator */ +extern LOCAL_SYM enum aw_result +read_file + (struct mem_allocator* allocator, + const char* filename, + char** file); + +#endif /* AW_C_H */ + diff --git a/src/aw_mtl.c b/src/aw_mtl.c @@ -1,11 +1,76 @@ #define _POSIX_C_SOURCE 200112L /* strtok_r support */ -#include "aw.h" +#include "aw_c.h" +#include <rsys/dynamic_array.h> +#include <rsys/float3.h> #include <rsys/mem_allocator.h> #include <rsys/ref_count.h> +#include <rsys/str.h> + +struct color { + float value[3]; + char is_CIEXYZ; +}; + +static FINLINE void +color_copy(struct color* dst, const struct color* src) +{ + ASSERT(dst && src); + f3_set(dst->value, src->value); + dst->is_CIEXYZ = src->is_CIEXYZ; +} + +struct material { + struct str name; + struct color ambient; + struct color diffuse; + struct color specular; +}; + +static FINLINE void +material_init(struct mem_allocator* allocator, struct material* mtl) +{ + ASSERT(mtl); + str_init(allocator, &mtl->name); +} + +static FINLINE void +material_release(struct material* mtl) +{ + ASSERT(mtl); + str_release(&mtl->name); +} + +static FINLINE int +material_copy(struct material* dst, const struct material* src) +{ + ASSERT(dst && src); + color_copy(&dst->ambient, &src->ambient); + return str_copy(&dst->name, &src->name); +} + +static FINLINE int +material_copy_and_release(struct material* dst, struct material* src) +{ + ASSERT(dst && src); + color_copy(&dst->ambient, &src->ambient); + return str_copy_and_release(&dst->name, &src->name); +} + +/* Generate the darray_mtl data structure */ +#define DARRAY_NAME material +#define DARRAY_DATA struct material +#define DARRAY_FUNCTOR_INIT material_init +#define DARRAY_FUNCTOR_RELEASE material_release +#define DARRAY_FUNCTOR_COPY material_copy +#define DARRAY_FUNCTOR_COPY_AND_RELEASE material_copy_and_release +#include <rsys/dynamic_array.h> struct aw_mtl { + struct darray_material materials; + struct material* newmtl; /* Pointer toward the current material */ + ref_T ref; struct mem_allocator* allocator; }; @@ -13,11 +78,137 @@ struct aw_mtl { /******************************************************************************* * Helper functions ******************************************************************************/ +static enum aw_result +parse_newmtl(struct aw_mtl* mtl, char** word_tk) +{ + char* word; + size_t nmtls; + ASSERT(mtl && word_tk); + + word = strtok_r(NULL, "\n", word_tk); + if(!word) + return AW_BAD_ARGUMENT; + + nmtls = darray_material_size_get(&mtl->materials); + if(darray_material_resize(&mtl->materials, nmtls + 1)) + return AW_MEMORY_ERROR; + + mtl->newmtl = darray_material_data_get(&mtl->materials) + nmtls; + if(str_set(&mtl->newmtl->name, word)) { + darray_material_pop_back(&mtl->materials); + mtl->newmtl = NULL; + return AW_MEMORY_ERROR; + } + return AW_OK; +} + +static enum aw_result +parse_color(struct color* col, char** word_tk) +{ + char* word; + enum aw_result res = AW_OK; + ASSERT(col && word_tk); + + word = strtok_r(NULL, " ", word_tk); + if(!word) + return AW_BAD_ARGUMENT; + + if(!strcmp(word, "spectral")) { + fprintf(stderr, "spectral colors are not supported\n"); + return AW_BAD_ARGUMENT; + } + + col->is_CIEXYZ = strcmp(word, "xyz") == 0; + if(col->is_CIEXYZ && !(word = strtok_r(NULL, " ", word_tk))) + return AW_BAD_ARGUMENT; + + if(AW_OK != (res = string_to_float(word, &col->value[0]))) + return res; + + /* If only the first component is defined the second and third ones are + * assumed to be equal to the first one */ + word = strtok_r(NULL, " ", word_tk); + if(!word) { + col->value[1] = col->value[2] = col->value[0]; + } else { + if(AW_OK != (res = string_to_float(word, &col->value[1]))) + return res; + word = strtok_r(NULL, " ", word_tk); + if(AW_OK != (res = string_to_float(word, &col->value[2]))) + return res; + } + return AW_OK; +} + +static enum aw_result +parse_mtl_file(struct aw_mtl* mtl, const char* path, char* content) +{ + char* line, *line_tk; + unsigned long iline; + enum aw_result res = AW_OK; + ASSERT(mtl && path && content); + + line = strtok_r(content, "\n", &line_tk); + iline = 1; + while(line) { + char* word, *word_tk; + word = strtok_r(line, " ", &word_tk); + while(word) { + if(!strcmp(word, "newmtl")) { /* Material name declaration */ + res = parse_newmtl(mtl, &word_tk); + } else if(mtl->newmtl == NULL) { + res = AW_BAD_ARGUMENT; + } else if(!strcmp(word, "Ka")) { /* Ambient reflectivity */ + res = parse_color(&mtl->newmtl->ambient, &word_tk); + } else if(!strcmp(word, "Kd")) { /* Diffuse reflectivity */ + res = parse_color(&mtl->newmtl->diffuse, &word_tk); + } else if(!strcmp(word, "Ks")) { /* Specular reflectivity */ + res = parse_color(&mtl->newmtl->specular, &word_tk); + } else if(!strcmp(word, "Tf")) { /* Transimission filter */ + } else if(!strcmp(word, "Ns")) { /* Specular exponent */ + } else if(!strcmp(word, "Ni")) { /* Refraction index */ + } else if(!strcmp(word, "illum")) { /* Illumination model */ + } else if(!strcmp(word, "d")) { /* Dissolve factor */ + } else if(!strcmp(word, "sharpness")) { /* Reflection sharpness */ + } else if(!strcmp(word, "map_Ka")) { /* Ambient texture */ + } else if(!strcmp(word, "map_Kd")) { /* Diffuse texture */ + } else if(!strcmp(word, "map_Ks")) { /* Specular texture */ + } else if(!strcmp(word, "map_Ns")) { /* Specular exponent texture */ + } else if(!strcmp(word, "map_d")) { /* Dissolve texture */ + } else if(!strcmp(word, "bump")) { /* Bump map */ + } else { + res = AW_OK; + fprintf(stderr, "%s:%lu: warning: ignored or malformed directive %s\n", + path, iline, word); + } + if(res != AW_OK) + goto error; + word = strtok_r(NULL, " ", &word_tk); + } + line = strtok_r(NULL, "\n", &line_tk); + ++iline; + } +exit: + return res; +error: + fprintf(stderr, "%s:%lu: error: parsing failed\n", path, iline); + goto exit; +} + +static void +mtl_clear(struct aw_mtl* mtl) +{ + ASSERT(mtl); + darray_material_clear(&mtl->materials); + mtl->newmtl = NULL; +} + static void -aw_mtl_release(ref_T* ref) +mtl_release(ref_T* ref) { struct aw_mtl* mtl = CONTAINER_OF(ref, struct aw_mtl, ref); ASSERT(ref); + darray_material_release(&mtl->materials); MEM_FREE(mtl->allocator, mtl); } @@ -45,6 +236,8 @@ aw_mtl_create } mtl->allocator = allocator; ref_init(&mtl->ref); + darray_material_init(allocator, &mtl->materials); + mtl->newmtl = NULL; exit: if(mtl_out) @@ -72,6 +265,33 @@ aw_mtl_ref_put(struct aw_mtl* mtl) { if(!mtl) return AW_BAD_ARGUMENT; - ref_put(&mtl->ref, aw_mtl_release); + ref_put(&mtl->ref, mtl_release); return AW_OK; } + +enum aw_result +aw_mtl_load(struct aw_mtl* mtl, const char* filename) +{ + char* file_content = NULL; + enum aw_result res = AW_OK; + + if(!mtl || !filename) { + res = AW_BAD_ARGUMENT; + goto error; + } + + if(AW_OK != (res = read_file(mtl->allocator, filename, &file_content))) + goto error; + + mtl_clear(mtl); + if(AW_OK != (res = parse_mtl_file(mtl, filename, file_content))) + goto error; + +exit: + if(file_content) + MEM_FREE(mtl->allocator, file_content); + return res; +error: + goto exit; +} + diff --git a/src/aw_obj.c b/src/aw_obj.c @@ -1,6 +1,6 @@ #define _POSIX_C_SOURCE 200112L /* strtok_r support */ -#include "aw.h" +#include "aw_c.h" #include <rsys/dynamic_array_float.h> #include <rsys/float3.h> @@ -111,32 +111,6 @@ struct aw_obj { /******************************************************************************* * Helper functions ******************************************************************************/ -static FINLINE enum aw_result -string_to_float(const char* str, float* f) -{ - char* ptr = NULL; - ASSERT(f); - if(!str) - return AW_BAD_ARGUMENT; - *f = (float)strtod(str, &ptr); - if(ptr == str || *ptr != '\0') - return AW_BAD_ARGUMENT; - return AW_OK; -} - -static FINLINE enum aw_result -string_to_size_t(const char* str, size_t* i) -{ - char* ptr = NULL; - ASSERT(i); - if(!str) - return AW_BAD_ARGUMENT; - *i = (size_t)strtol(str, &ptr, 10); - if(ptr == str || *ptr != '\0') - return AW_BAD_ARGUMENT; - return AW_OK; -} - static FINLINE void flush_groups(struct aw_obj* obj) { @@ -447,7 +421,7 @@ error: } static enum aw_result -parse_file(struct aw_obj* obj, const char* path, char* content) +parse_obj_file(struct aw_obj* obj, const char* path, char* content) { char* line, *line_tk; unsigned long iline; @@ -480,7 +454,9 @@ parse_file(struct aw_obj* obj, const char* path, char* content) } else if(!strcmp(word, "usemtl")) { /* Use the mtl library */ res = parse_usemtl(obj, &word_tk); } else { - res = AW_BAD_ARGUMENT; + res = AW_OK; + fprintf(stderr, "%s:%lu: warning: ignored or malformed directive %s", + path, iline, word); } if(res != AW_OK) goto error; @@ -500,7 +476,7 @@ error: } static void -aw_clear(struct aw_obj* obj) +obj_clear(struct aw_obj* obj) { ASSERT(obj); darray_float_clear(&obj->positions); @@ -516,7 +492,7 @@ aw_clear(struct aw_obj* obj) } static void -aw_release(ref_T* ref) +obj_release(ref_T* ref) { struct aw_obj* obj = CONTAINER_OF(ref, struct aw_obj, ref); ASSERT(ref); @@ -538,13 +514,13 @@ aw_release(ref_T* ref) enum aw_result aw_obj_create (struct mem_allocator* mem_allocator, - struct aw_obj** aw_out) + struct aw_obj** obj_out) { struct mem_allocator* allocator; struct aw_obj* obj = NULL; enum aw_result res = AW_OK; - if(!aw_out) { + if(!obj_out) { res = AW_BAD_ARGUMENT; goto error; } @@ -567,8 +543,8 @@ aw_obj_create darray_mtllib_init(mem_allocator, &obj->mtllibs); exit: - if(aw_out) - *aw_out = obj; + if(obj_out) + *obj_out = obj; return res; error: if(obj) { @@ -592,53 +568,29 @@ aw_obj_ref_put(struct aw_obj* obj) { if(!obj) return AW_BAD_ARGUMENT; - ref_put(&obj->ref, aw_release); + ref_put(&obj->ref, obj_release); return AW_OK; } enum aw_result aw_obj_load(struct aw_obj* obj, const char* filename) { - FILE* file = NULL; char* file_content = NULL; - long file_size = 0; - size_t len = 0; enum aw_result res = AW_OK; - (void)len; if(!obj || !filename) { res = AW_BAD_ARGUMENT; goto error; } - file = fopen(filename, "r"); - if(!file) { - res = AW_IO_ERROR; + if(AW_OK != (res = read_file(obj->allocator, filename, &file_content))) goto error; - } - fseek(file, 0, SEEK_END); - file_size = ftell(file); - fseek(file, 0, SEEK_SET); - - file_content = MEM_ALLOC(obj->allocator, (size_t)file_size + 1); - if(!file_content) { - res = AW_MEMORY_ERROR; - goto error; - } - len = fread(file_content, 1, (size_t)file_size, file); - file_content[file_size] = '\0'; - ASSERT(len == (size_t)file_size); - fclose(file); - file = NULL; - - aw_clear(obj); - if(AW_OK != (res = parse_file(obj, filename, file_content))) + obj_clear(obj); + if(AW_OK != (res = parse_obj_file(obj, filename, file_content))) goto error; exit: - if(file) - fclose(file); if(file_content) MEM_FREE(obj->allocator, file_content); return res;