loader_aw

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

commit 7258e67ab614621883bdd98b4bd9a562460fc5d1
parent f9f0af5ede06568db14492f4ba4ca71da3b18765
Author: vaplv <vaplv@free.fr>
Date:   Sun, 29 Jun 2014 22:00:21 +0200

Test the obj loader

Diffstat:
Mcmake/CMakeLists.txt | 7+++++++
Msrc/obj.c | 118+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------
Asrc/test_obj.c | 104+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 209 insertions(+), 20 deletions(-)

diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt @@ -37,6 +37,13 @@ set_target_properties(loader-obj PROPERTIES rcmake_setup_devel(LoaderObj loader-obj ${VERSION} obj/obj_version.h) ################################################################################ +# Define tests +################################################################################ +add_executable(test_obj ${OBJ_SOURCE_DIR}/test_obj.c) +target_link_libraries(test_obj loader-obj) +add_test(test_obj test_obj) + +################################################################################ # Install directories ################################################################################ install(TARGETS loader-obj diff --git a/src/obj.c b/src/obj.c @@ -98,9 +98,9 @@ named_group_copy_and_release(struct named_group* dst, struct named_group* src) #include <rsys/dynamic_array.h> struct obj { - struct darray_float positions; /* float3 */ + struct darray_float positions; /* float4 */ struct darray_float normals; /* float3 */ - struct darray_float texcoords; /* float2 */ + struct darray_float texcoords; /* float3 */ struct darray_vertex vertices; struct darray_face faces; struct darray_named_group groups; @@ -174,25 +174,42 @@ flush_smooth_group } static enum obj_result -parse_floatX(char** word_tk, const unsigned count, struct darray_float* dst) +parse_floatX + (char** word_tk, + const unsigned count_min, + const unsigned count_max, + const float default_value, + struct darray_float* dst) { unsigned i; enum obj_result res = OBJ_OK; - ASSERT(word_tk && dst); + ASSERT(word_tk && dst && count_min <= count_max); - FOR_EACH(i, 0, count) { + FOR_EACH(i, 0, count_max) { char* real; float f; - real = strtok_r(NULL, " ", word_tk); + if(NULL == (real = strtok_r(NULL, " ", word_tk))) + break; 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; } } + if(i < count_min) { + res = OBJ_BAD_ARGUMENT; + goto error; + } + + FOR_EACH(i, i, count_max) { + if(darray_float_push_back(dst, &default_value)) { + res = OBJ_MEMORY_ERROR; + goto error; + } + } + exit: return res; error: @@ -221,16 +238,18 @@ parse_face(struct obj* obj, char** word_tk) goto error; /* texcoord index */ id = strtok_r(NULL, "/", &id_tk); - if(*id != '\0') { - res = string_to_size_t(id, &vertex.itexcoord); + if(id) { + 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; } - /* 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; @@ -318,8 +337,8 @@ static enum obj_result parse_mtllib(struct obj* obj, char** word_tk) { char* word; - size_t imtllib; - size_t nmtllibs; + size_t imtllib = 0; + size_t nmtllibs = 0; enum obj_result res = OBJ_OK; ASSERT(obj && word_tk); @@ -346,7 +365,46 @@ parse_mtllib(struct obj* obj, char** word_tk) exit: return res; error: - CHECK(darray_mtllib_resize(&obj->mtllibs, nmtllibs), 0); + if(imtllib != nmtllibs) + CHECK(darray_mtllib_resize(&obj->mtllibs, nmtllibs), 0); + goto exit; +} + +static enum obj_result +parse_usemtl(struct obj* obj, char** word_tk) +{ + char* word; + struct named_group* mtl = NULL; + size_t nmtls; + enum obj_result res = OBJ_OK; + ASSERT(obj && word_tk); + + flush_named_group(&obj->usemtls, &obj->faces); + + word= strtok_r(NULL, " ", word_tk); + if(!word_tk) { + res = OBJ_BAD_ARGUMENT; + goto error; + } + + nmtls = darray_named_group_size_get(&obj->usemtls); + if(darray_named_group_resize(&obj->usemtls, nmtls + 1)) { + res = OBJ_MEMORY_ERROR; + goto error; + } + mtl = darray_named_group_data_get(&obj->usemtls) + nmtls; + if(str_set(&mtl->name, word)) { + res = OBJ_MEMORY_ERROR; + goto error; + } + mtl->iface = darray_face_size_get(&obj->faces); + mtl->nfaces = 0; + +exit: + return res; +error: + if(mtl) + darray_named_group_pop_back(&obj->usemtls); goto exit; } @@ -368,11 +426,11 @@ parse_file(struct obj* obj, const char* path, char* content) if(word[0] == '#') { /* Comment */ break; } else if(!strcmp(word, "v")) { /* Vertex position */ - res = parse_floatX(&word_tk, 3, &obj->positions); + res = parse_floatX(&word_tk, 3, 4, 1.f, &obj->positions); } else if(!strcmp(word, "vn")) { /* Vertex normal */ - res = parse_floatX(&word_tk, 3, &obj->normals); + res = parse_floatX(&word_tk, 3, 3, 0.f, &obj->normals); } else if(!strcmp(word, "vt")) { /* Vertex texture coordinates */ - res = parse_floatX(&word_tk, 2, &obj->texcoords); + res = parse_floatX(&word_tk, 2, 3, 0.f, &obj->texcoords); } else if(!strcmp(word, "f") || !strcmp(word, "fo")) { /* face element */ res = parse_face(obj, &word_tk); } else if(!strcmp(word, "g")) { /* Grouping */ @@ -382,6 +440,7 @@ parse_file(struct obj* obj, const char* path, char* content) } else if(!strcmp(word, "mtllib")) { /* Mtl library */ res = parse_mtllib(obj, &word_tk); } else if(!strcmp(word, "usemtl")) { /* Use the mtl library */ + res = parse_usemtl(obj, &word_tk); } else { res = OBJ_BAD_ARGUMENT; } @@ -403,6 +462,21 @@ error: } static void +obj_clear(struct obj* obj) +{ + ASSERT(obj); + darray_float_clear(&obj->positions); + darray_float_clear(&obj->normals); + darray_float_clear(&obj->texcoords); + darray_vertex_clear(&obj->vertices); + darray_face_clear(&obj->faces); + darray_named_group_clear(&obj->groups); + darray_named_group_clear(&obj->usemtls); + darray_smooth_group_clear(&obj->smooth_groups); + darray_mtllib_clear(&obj->mtllibs); +} + +static void obj_release(ref_T* ref) { struct obj* obj = CONTAINER_OF(ref, struct obj, ref); @@ -454,6 +528,8 @@ obj_create darray_mtllib_init(mem_allocator, &obj->mtllibs); exit: + if(obj_out) + *obj_out = obj; return res; error: if(obj) { @@ -489,6 +565,7 @@ obj_load(struct obj* obj, const char* filename) long file_size = 0; size_t len = 0; enum obj_result res = OBJ_OK; + (void)len; if(!obj || !filename) { res = OBJ_BAD_ARGUMENT; @@ -516,6 +593,7 @@ obj_load(struct obj* obj, const char* filename) fclose(file); file = NULL; + obj_clear(obj); if(OBJ_OK != (res = parse_file(obj, filename, file_content))) goto error; diff --git a/src/test_obj.c b/src/test_obj.c @@ -0,0 +1,104 @@ +#include "obj.h" +#include <rsys/mem_allocator.h> +#include <string.h> + +static const char* plane_obj = + "mtllib master.mtl" + "\n" + "v 0.0000 2.0000 0.0000\n" + "v 0.0000 0.0000 0.0000\n" + "v 2.0000 0.0000 0.0000\n" + "v 2.0000 2.0000 0.0000\n" + "vt 0.0000 1.0000 0.0000\n" + "vt 0.0000 0.0000 0.0000\n" + "vt 1.0000 0.0000 0.0000\n" + "vt 1.0000 1.0000 0.0000\n" + "# 4 vertices\n" + "\n" + "usemtl wood\n" + "f 1/1 2/2 3/3 4/4\n" + "# 1 element\n"; + +static const char* cube_obj = + "# Cube with a different material applied to each of its faces\n" + "mtllib master.mtl\n" + "v 0.0000 2.0000 2.0000\n" + "v 0.0000 0.0000 2.0000\n" + "v 2.0000 0.0000 2.0000\n" + "v 2.0000 2.0000 2.0000\n" + "v 0.0000 2.0000 0.0000\n" + "v 0.0000 0.0000 0.0000\n" + "v 2.0000 0.0000 0.0000\n" + "v 2.0000 2.0000 0.0000\n" + "# 8 vertices\n" + "g front\n" + "usemtl red\n" + "f 1 2 3 4\n" + "g back\n" + "usemtl blue\n" + "f 8 7 6 5\n" + "g right\n" + "usemtl green\n" + "f 4 3 7 8\n" + "g top\n" + "usemtl gold\n" + "f 5 1 4 8\n" + "g left\n" + "usemtl orange\n" + "f 5 6 2 1\n" + "g bottom\n" + "usemtl purple\n" + "f 2 6 7 3\n" + "# 6 elements\n"; + +int +main(int argc, char** argv) +{ + struct mem_allocator allocator; + struct obj* obj; + FILE* file; + (void)argc, (void)argv; + + file = fopen("test_obj_cube.obj", "w"); + NCHECK(file, NULL); + fwrite(cube_obj, sizeof(char), strlen(cube_obj), file); + fclose(file); + + file = fopen("test_obj_plane.obj", "w"); + NCHECK(file, NULL); + fwrite(plane_obj, sizeof(char), strlen(plane_obj), file); + fclose(file); + + mem_init_proxy_allocator(&allocator, &mem_default_allocator); + + CHECK(obj_create(NULL, NULL), OBJ_BAD_ARGUMENT); + CHECK(obj_create(&allocator, NULL), OBJ_BAD_ARGUMENT); + CHECK(obj_create(NULL, &obj), OBJ_OK); + + CHECK(obj_ref_get(NULL), OBJ_BAD_ARGUMENT); + CHECK(obj_ref_get(obj), OBJ_OK); + CHECK(obj_ref_put(NULL), OBJ_BAD_ARGUMENT); + CHECK(obj_ref_put(obj), OBJ_OK); + CHECK(obj_ref_put(obj), OBJ_OK); + + CHECK(obj_create(&allocator, &obj), OBJ_OK); + + CHECK(obj_load(NULL, NULL), OBJ_BAD_ARGUMENT); + CHECK(obj_load(obj, NULL), OBJ_BAD_ARGUMENT); + CHECK(obj_load(NULL, "test_obj_cube.obj"), OBJ_BAD_ARGUMENT); + CHECK(obj_load(obj, "none.obj"), OBJ_IO_ERROR); + CHECK(obj_load(obj, "test_obj_cube.obj"), OBJ_OK); + CHECK(obj_load(obj, "test_obj_plane.obj"), OBJ_OK); + + CHECK(obj_ref_put(obj), OBJ_OK); + + if(MEM_ALLOCATED_SIZE(&allocator)) { + char dump[512]; + MEM_DUMP(&allocator, dump, sizeof(dump)/sizeof(char)); + fprintf(stderr, "%s\n", dump); + FATAL("Memory leaks\n"); + } + mem_shutdown_proxy_allocator(&allocator); + CHECK(mem_allocated_size(), 0); + return 0; +}