loader_aw

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

commit f9f0af5ede06568db14492f4ba4ca71da3b18765
parent 2c70a8e7a998f0c036ed2abe2c67e868537cb424
Author: vaplv <vaplv@free.fr>
Date:   Sat, 28 Jun 2014 16:07:20 +0200

Begin the implementation of the obj file parsing

Diffstat:
Msrc/obj.c | 414++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 413 insertions(+), 1 deletion(-)

diff --git a/src/obj.c b/src/obj.c @@ -1,8 +1,113 @@ +#define _POSIX_C_SOURCE 200112L /* strtok_r support */ + #include "obj.h" + +#include <rsys/dynamic_array_float.h> #include <rsys/mem_allocator.h> #include <rsys/ref_count.h> +#include <rsys/str.h> + +struct vertex { + size_t iposition; + size_t inormal; + size_t itexcoord; +}; + +struct face { + size_t ivertex; /* Index toward the first vertex */ + size_t nvertices; /* Vertex count */ +}; + +struct named_group { + struct str name; + size_t iface; /* Index toward the first face */ + size_t nfaces; /* Faces count in the group */ +}; + +struct smooth_group { + char is_enabled; + size_t iface; /* Index toward the first face */ + size_t nfaces; /* Faces count in the group */ +}; + +static INLINE void +named_group_init(struct mem_allocator* allocator, struct named_group* grp) +{ + ASSERT(grp); + str_init(allocator, &grp->name); + grp->iface = 0; + grp->nfaces = 0; +} + +static INLINE void +named_group_release(struct named_group* grp) +{ + ASSERT(grp); + str_release(&grp->name); +} + +static INLINE int +named_group_copy(struct named_group* dst, const struct named_group* src) +{ + ASSERT(dst && src); + dst->iface = src->iface; + dst->nfaces = src->nfaces; + return str_copy(&dst->name, &src->name); +} + +static INLINE int +named_group_copy_and_release(struct named_group* dst, struct named_group* src) +{ + ASSERT(dst && src); + dst->iface = src->iface; + dst->nfaces = src->nfaces; + return str_copy_and_release(&dst->name, &src->name); +} + +/* Generate the darray_vertex data structure */ +#define DARRAY_NAME vertex +#define DARRAY_DATA struct vertex +#include <rsys/dynamic_array.h> + +/* Generate the darray_face data structure */ +#define DARRAY_NAME face +#define DARRAY_DATA struct face +#include <rsys/dynamic_array.h> + +/* Generate the darray_named_group data structure */ +#define DARRAY_NAME named_group +#define DARRAY_DATA struct named_group +#define DARRAY_FUNCTOR_INIT named_group_init +#define DARRAY_FUNCTOR_RELEASE named_group_release +#define DARRAY_FUNCTOR_COPY named_group_copy +#define DARRAY_FUNCTOR_COPY_AND_RELEASE named_group_copy_and_release +#include <rsys/dynamic_array.h> + +/* Generate the darray_smooth_group data structure */ +#define DARRAY_NAME smooth_group +#define DARRAY_DATA struct smooth_group +#include <rsys/dynamic_array.h> + +/* Generate the darray_mtllib data structure */ +#define DARRAY_NAME mtllib +#define DARRAY_DATA struct str +#define DARRAY_FUNCTOR_INIT str_init +#define DARRAY_FUNCTOR_RELEASE str_release +#define DARRAY_FUNCTOR_COPY str_copy +#define DARRAY_FUNCTOR_COPY_AND_RELEASE str_copy_and_release +#include <rsys/dynamic_array.h> struct obj { + struct darray_float positions; /* float3 */ + struct darray_float normals; /* float3 */ + struct darray_float texcoords; /* float2 */ + struct darray_vertex vertices; + struct darray_face faces; + struct darray_named_group groups; + struct darray_smooth_group smooth_groups; + struct darray_named_group usemtls; + struct darray_mtllib mtllibs; + ref_T ref; struct mem_allocator* allocator; }; @@ -10,11 +115,307 @@ struct obj { /******************************************************************************* * Helper functions ******************************************************************************/ +static FINLINE enum obj_result +string_to_float(const char* str, float* f) +{ + char* ptr = NULL; + ASSERT(f); + if(!str) + return OBJ_BAD_ARGUMENT; + *f = (float)strtod(str, &ptr); + if(ptr == str || *ptr != '\0') + return OBJ_BAD_ARGUMENT; + return OBJ_OK; +} + +static FINLINE enum obj_result +string_to_size_t(const char* str, size_t* i) +{ + char* ptr = NULL; + ASSERT(i); + if(!str) + return OBJ_BAD_ARGUMENT; + *i = (size_t)strtol(str, &ptr, 10); + if(ptr == str || *ptr != '\0') + return OBJ_BAD_ARGUMENT; + return OBJ_OK; +} + +static FINLINE void +flush_named_group + (struct darray_named_group* groups, const struct darray_face* faces) +{ + struct named_group* grp; + size_t nfaces, ngrps; + ASSERT(groups && faces); + if(0 == (nfaces = darray_face_size_get(faces))) + return; + if( 0 == (ngrps = darray_named_group_size_get(groups))) + return; + grp = darray_named_group_data_get(groups) + (ngrps - 1); + ASSERT(grp->iface < nfaces); + grp->nfaces =nfaces - grp->iface; +} + +static FINLINE void +flush_smooth_group + (struct darray_smooth_group* groups, const struct darray_face* faces) +{ + struct smooth_group* grp; + size_t nfaces, ngrps; + ASSERT(groups && faces); + if(0 == (nfaces = darray_face_size_get(faces))) + return; + if( 0 == (ngrps = darray_smooth_group_size_get(groups))) + return; + grp = darray_smooth_group_data_get(groups) + (ngrps - 1); + ASSERT(grp->iface < nfaces); + grp->nfaces =nfaces - grp->iface; +} + +static enum obj_result +parse_floatX(char** word_tk, const unsigned count, struct darray_float* dst) +{ + unsigned i; + enum obj_result res = OBJ_OK; + ASSERT(word_tk && dst); + + FOR_EACH(i, 0, count) { + char* real; + float f; + real = strtok_r(NULL, " ", word_tk); + res = string_to_float(real, &f); + if(res != OBJ_OK) + goto error; + + if(darray_float_push_back(dst, &f)) { + res = OBJ_MEMORY_ERROR; + goto error; + } + } +exit: + return res; +error: + FOR_EACH_REVERSE(i, i, 0) + darray_float_pop_back(dst); + goto exit; +} + +static enum obj_result +parse_face(struct obj* obj, char** word_tk) +{ + struct face face; + char* word; + enum obj_result res = OBJ_OK; + ASSERT(obj && word_tk); + + face.ivertex = darray_vertex_size_get(&obj->vertices); + face.nvertices = 0; + while((word = strtok_r(NULL, " ", word_tk))) { + char* id, *id_tk; + struct vertex vertex; + /* position index */ + id = strtok_r(word, "/", &id_tk); + res = string_to_size_t(id, &vertex.iposition); + if(res != OBJ_OK) + goto error; + /* texcoord index */ + id = strtok_r(NULL, "/", &id_tk); + if(*id != '\0') { + res = string_to_size_t(id, &vertex.itexcoord); + if(res != OBJ_OK) + goto error; + } + /* normal index */ + id = strtok_r(NULL, "/", &id_tk); + res = string_to_size_t(id, &vertex.inormal); + if(res != OBJ_OK) + goto error; + + if(darray_vertex_push_back(&obj->vertices, &vertex)) { + res = OBJ_MEMORY_ERROR; + goto error; + } + ++face.nvertices; + } + + if(darray_face_push_back(&obj->faces, &face)) { + res = OBJ_MEMORY_ERROR; + goto error; + } + +exit: + return res; +error: + FOR_EACH_REVERSE(face.nvertices, face.nvertices, 0) + darray_vertex_pop_back(&obj->vertices); + goto exit; +} + +static enum obj_result +parse_group(struct obj* obj, char** word_tk) +{ + char* word; + struct named_group* grp = NULL; + size_t ngrps; + enum obj_result res = OBJ_OK; + ASSERT(obj && word_tk); + + flush_named_group(&obj->groups, &obj->faces); + + word = strtok_r(NULL, " ", word_tk); + if(!word) { + res = OBJ_BAD_ARGUMENT; + goto error; + } + #define CALL(Func) { if(Func) { res = OBJ_MEMORY_ERROR; goto error; } }(void)0 + ngrps = darray_named_group_size_get(&obj->groups); + CALL(darray_named_group_resize(&obj->groups, ngrps + 1)); + grp = darray_named_group_data_get(&obj->groups) + ngrps; + CALL(str_set(&grp->name, word)); + while((word = strtok_r(NULL, " ", word_tk))) { + CALL(str_append_char(&grp->name, ' ')); + CALL(str_append(&grp->name, word)); + } + #undef CALL + grp->iface = darray_face_size_get(&obj->faces); + grp->nfaces = 0; +exit: + return res; +error: + if(grp) + darray_named_group_pop_back(&obj->groups); + goto exit; +} + +static enum obj_result +parse_smooth_group(struct obj* obj, char** word_tk) +{ + char* word; + struct smooth_group grp; + enum obj_result res; + size_t i; + ASSERT(obj && word_tk); + + flush_smooth_group(&obj->smooth_groups, &obj->faces); + + word = strtok_r(NULL, " ", word_tk); + res = string_to_size_t(word, &i); + if(res != OBJ_OK) + return res; + + grp.is_enabled = i != 0; + grp.iface = darray_face_size_get(&obj->faces); + grp.nfaces = 0; + + if(darray_smooth_group_push_back(&obj->smooth_groups, &grp)) + return OBJ_MEMORY_ERROR; + + return OBJ_OK; +} + +static enum obj_result +parse_mtllib(struct obj* obj, char** word_tk) +{ + char* word; + size_t imtllib; + size_t nmtllibs; + enum obj_result res = OBJ_OK; + ASSERT(obj && word_tk); + + word = strtok_r(NULL, " ", word_tk); + if(!word) { + res = OBJ_BAD_ARGUMENT; + goto error; + } + nmtllibs = imtllib = darray_mtllib_size_get(&obj->mtllibs); + do { + struct str* str; + if(darray_mtllib_resize(&obj->mtllibs, imtllib + 1)) { + res = OBJ_MEMORY_ERROR; + goto error; + } + str = darray_mtllib_data_get(&obj->mtllibs) + imtllib; + ++imtllib; + if(str_set(str, word)) { + res = OBJ_MEMORY_ERROR; + goto error; + } + } while((word = strtok_r(NULL, " ", word_tk))); + +exit: + return res; +error: + CHECK(darray_mtllib_resize(&obj->mtllibs, nmtllibs), 0); + goto exit; +} + +static enum obj_result +parse_file(struct obj* obj, const char* path, char* content) +{ + char* line, *line_tk; + unsigned long iline; + enum obj_result res = OBJ_OK; + ASSERT(obj && 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(word[0] == '#') { /* Comment */ + break; + } else if(!strcmp(word, "v")) { /* Vertex position */ + res = parse_floatX(&word_tk, 3, &obj->positions); + } else if(!strcmp(word, "vn")) { /* Vertex normal */ + res = parse_floatX(&word_tk, 3, &obj->normals); + } else if(!strcmp(word, "vt")) { /* Vertex texture coordinates */ + res = parse_floatX(&word_tk, 2, &obj->texcoords); + } else if(!strcmp(word, "f") || !strcmp(word, "fo")) { /* face element */ + res = parse_face(obj, &word_tk); + } else if(!strcmp(word, "g")) { /* Grouping */ + res = parse_group(obj, &word_tk); + } else if(!strcmp(word, "s")) { /* Smooth group */ + res = parse_smooth_group(obj, &word_tk); + } else if(!strcmp(word, "mtllib")) { /* Mtl library */ + res = parse_mtllib(obj, &word_tk); + } else if(!strcmp(word, "usemtl")) { /* Use the mtl library */ + } else { + res = OBJ_BAD_ARGUMENT; + } + if(res != OBJ_OK) + goto error; + word = strtok_r(NULL, " ", &word_tk); + } + line = strtok_r(NULL, "\n", &line_tk); + ++iline; + } + flush_named_group(&obj->groups, &obj->faces); + flush_named_group(&obj->usemtls, &obj->faces); + flush_smooth_group(&obj->smooth_groups, &obj->faces); +exit: + return res; +error: + fprintf(stderr, "%s:%lu: error: parsing failed\n", path, iline); + goto exit; +} + static void obj_release(ref_T* ref) { struct obj* obj = CONTAINER_OF(ref, struct obj, ref); ASSERT(ref); + darray_float_release(&obj->positions); + darray_float_release(&obj->normals); + darray_float_release(&obj->texcoords); + darray_vertex_release(&obj->vertices); + darray_face_release(&obj->faces); + darray_named_group_release(&obj->groups); + darray_named_group_release(&obj->usemtls); + darray_smooth_group_release(&obj->smooth_groups); + darray_mtllib_release(&obj->mtllibs); MEM_FREE(obj->allocator, obj); } @@ -42,6 +443,15 @@ obj_create } obj->allocator = allocator; ref_init(&obj->ref); + darray_float_init(mem_allocator, &obj->positions); + darray_float_init(mem_allocator, &obj->normals); + darray_float_init(mem_allocator, &obj->texcoords); + darray_vertex_init(mem_allocator, &obj->vertices); + darray_face_init(mem_allocator, &obj->faces); + darray_named_group_init(mem_allocator, &obj->groups); + darray_named_group_init(mem_allocator, &obj->usemtls); + darray_smooth_group_init(mem_allocator, &obj->smooth_groups); + darray_mtllib_init(mem_allocator, &obj->mtllibs); exit: return res; @@ -106,7 +516,8 @@ obj_load(struct obj* obj, const char* filename) fclose(file); file = NULL; - /* TODO parse file content */ + if(OBJ_OK != (res = parse_file(obj, filename, file_content))) + goto error; exit: if(file) @@ -118,3 +529,4 @@ error: goto exit; } +