loader_aw

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

commit cf43b267d0bd0104c38c1008e725ffc6d428f99b
parent 6be9c358b64f45604a9875c25d56df66e30cd0e0
Author: vaplv <vaplv@free.fr>
Date:   Sun,  6 Jul 2014 14:21:15 +0200

Change the `obj' project prefix by `aw'

AW means for Alias/Wavefront

Diffstat:
Mcmake/CMakeLists.txt | 32++++++++++++++++----------------
Asrc/aw.h | 150+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/aw_mtl.c | 77+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/aw_obj.c | 743+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dsrc/obj.c | 741-------------------------------------------------------------------------------
Dsrc/obj.h | 130-------------------------------------------------------------------------------
Asrc/test_aw_obj.c | 345+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dsrc/test_obj.c | 345-------------------------------------------------------------------------------
8 files changed, 1331 insertions(+), 1232 deletions(-)

diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt @@ -1,9 +1,9 @@ cmake_minimum_required(VERSION 2.6) -project(loader_obj C) +project(loader_aw C) cmake_policy(SET CMP0011 NEW) enable_testing() -set(OBJ_SOURCE_DIR ${PROJECT_SOURCE_DIR}/../src) +set(AW_SOURCE_DIR ${PROJECT_SOURCE_DIR}/../src) ################################################################################ # Dependencies @@ -17,38 +17,38 @@ include(rcmake) ################################################################################ # Define targets ################################################################################ -set(OBJ_FILES_SRC obj.c) -set(OBJ_FILES_INC obj.h) -rcmake_prepend_path(OBJ_FILES_SRC ${OBJ_SOURCE_DIR}) -rcmake_prepend_path(OBJ_FILES_INC ${OBJ_SOURCE_DIR}) +set(AW_FILES_SRC aw_obj.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}) -add_library(loader-obj SHARED ${OBJ_FILES_SRC} ${OBJ_FILES_INC}) -target_link_libraries(loader-obj RSys) +add_library(loader-aw SHARED ${AW_FILES_SRC} ${AW_FILES_INC}) +target_link_libraries(loader-aw RSys) set(VERSION_MAJOR 0) set(VERSION_MINOR 0) set(VERSION_PATCH 0) set(VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}) -set_target_properties(loader-obj PROPERTIES - DEFINE_SYMBOL OBJ_SHARED_BUILD +set_target_properties(loader-aw PROPERTIES + DEFINE_SYMBOL AW_SHARED_BUILD VERSION ${VERSION} SOVERSION ${VERSION_MAJOR}) -rcmake_setup_devel(LoaderObj loader-obj ${VERSION} obj/obj_version.h) +rcmake_setup_devel(LoaderAW loader-aw ${VERSION} aw/aw_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) +add_executable(test_aw_obj ${AW_SOURCE_DIR}/test_aw_obj.c) +target_link_libraries(test_aw_obj loader-aw) +add_test(test_aw_obj test_aw_obj) ################################################################################ # Install directories ################################################################################ -install(TARGETS loader-obj +install(TARGETS loader-aw ARCHIVE DESTINATION lib LIBRARY DESTINATION lib RUNTIME DESTINATION bin) -install(FILES ${OBJ_FILES_INC} DESTINATION include/obj) +install(FILES ${AW_FILES_INC} DESTINATION include/aw) diff --git a/src/aw.h b/src/aw.h @@ -0,0 +1,150 @@ +#ifndef AW_H +#define AW_H + +#include <rsys/rsys.h> + +#ifdef AW_SHARED_BUILD + #define AW_API extern EXPORT_SYM +#elif defined(AW_STATIC_BUILD) + #define AW_API extern LOCAL_SYM +#else + #define AW_API extern IMPORT_SYM +#endif + +#ifndef NDEBUG + #define AW(Func) ASSERT(aw_##Func == AW_OK) +#else + #define AW(Func) aw_##Func +#endif + +#define AW_ID_NONE ((size_t)(-1)) + +enum aw_result { + AW_BAD_ARGUMENT, + AW_IO_ERROR, + AW_MEMORY_ERROR, + AW_OK +}; + +struct aw_obj_desc { + size_t faces_count; + size_t groups_count; + size_t smooth_groups_count; + size_t usemtls_count; + size_t mtllibs_count; +}; + +struct aw_obj_face { + size_t vertex_id; /* Index of the first face vertex */ + size_t vertices_count; + size_t group_id; /* Index of the face group */ + size_t smooth_group_id; /* Index of the face smooth group */ + size_t mtl_id; /* Index of the face material */ +}; + +struct aw_obj_group { + const char* name; + size_t face_id; /* Index of the first group face */ + size_t faces_count; +}; + +struct aw_obj_smooth_group { + char is_smoothed; + size_t face_id; /* Index of the first smooth group face */ + size_t faces_count; +}; + +struct aw_obj_mtl { + const char* name; + size_t face_id; + size_t faces_count; +}; + +struct aw_obj_vertex { + float position[4]; + float normal[3]; + float texcoord[3]; +}; + +struct aw_obj; +struct aw_mtl; +struct mem_allocator; + +BEGIN_DECLS + +/******************************************************************************* + * Obj functions + ******************************************************************************/ +AW_API enum aw_result +aw_obj_create + (struct mem_allocator* allocator, /* NULL <=> use default allocator */ + struct aw_obj** obj); + +AW_API enum aw_result +aw_obj_ref_get + (struct aw_obj* obj); + +AW_API enum aw_result +aw_obj_ref_put + (struct aw_obj* obj); + +AW_API enum aw_result +aw_obj_load + (struct aw_obj* obj, + const char* filename); + +AW_API enum aw_result +aw_obj_desc_get + (struct aw_obj* obj, + struct aw_obj_desc* desc); + +AW_API enum aw_result +aw_obj_face_get + (const struct aw_obj* obj, + const size_t face_id, + struct aw_obj_face* face); + +AW_API enum aw_result +aw_obj_group_get + (const struct aw_obj* obj, + const size_t group_id, + struct aw_obj_group* group); + +AW_API enum aw_result +aw_obj_smooth_group_get + (const struct aw_obj* obj, + const size_t smooth_group_id, + struct aw_obj_smooth_group* smooth_group); + +AW_API enum aw_result +aw_obj_mtl_get + (const struct aw_obj* obj, + const size_t mtl_id, + struct aw_obj_mtl* mtl); + +AW_API enum aw_result +aw_obj_vertex_get + (const struct aw_obj* obj, + const size_t vertex_id, + struct aw_obj_vertex* vertex); + +/******************************************************************************* + * Mtl functions + ******************************************************************************/ +AW_API enum aw_result +aw_mtl_create + (struct mem_allocator* allocator, /* NULL <=> use default allocator */ + struct aw_mtl* mtl); + +AW_API enum aw_result +aw_mtl_ref_get + (struct aw_mtl* mtl); + +AW_API enum aw_result +aw_mtl_ref_put + (struct aw_mtl* mtl); + +END_DECLS + +#endif /* AW_H */ + diff --git a/src/aw_mtl.c b/src/aw_mtl.c @@ -0,0 +1,77 @@ +#define _POSIX_C_SOURCE 200112L /* strtok_r support */ + +#include "obj.h" + +#include <rsys/mem_allocator.h> +#include <rsys/ref_count.h> + +struct mtl { + ref_T ref; + struct mem_allocator* allocator; +}; + +/******************************************************************************* + * Helper functions + ******************************************************************************/ +static void +obj_mtl_release(ref_T* ref) +{ + struct obj_mtl* mtl = CONTAINER_OF(ref, struct obj_mtl, ref); + ASSERT(ref); + MEM_FREE(mtl->allocator, mtl); +} + +/******************************************************************************* + * Exported functions + ******************************************************************************/ +enum obj_result +obj_mtl_create + (struct mem_allocator* mem_allocator, + struct obj_mtl** mtl_out) +{ + struct mem_allocator* allocator; + struct obj_mtl* mtl = NULL; + enum obj_result res = OBJ_OK; + + if(!mtl_out) { + res = OBJ_BAD_ARGUMENT; + goto error; + } + allocator = mem_allocator ? mem_allocator : &mem_default_allocator; + mtl = MEM_CALLOC(allocator, 1, sizeof(struct obj_mtl)); + if(!mtl) { + res = OBJ_MEMORY_ERROR; + goto error; + } + obj->allocator = allocator; + ref_init(&mtl->ref); + +exit: + if(mtl_out) + *mtl_out = mtl; + return res; +error: + if(mtl) { + OBJ(mtl_ref_put(mtl)); + mtl = NULL; + } + goto exit; +} + +enum obj_result +obj_mtl_ref_get(struct obj_mtl* mtl) +{ + if(!mtl) + return OBJ_BAD_ARGUMENT; + ref_get(&mtl->ref); + return OBJ_OK; +} + +enum obj_result +obj_mtl_ref_put(struct obj_mtl* mtl) +{ + if(!mtl) + return OBJ_BAD_ARGUMENT; + ref_put(&mtl->ref, obj_mtl_release); + return OBJ_OK; +} diff --git a/src/aw_obj.c b/src/aw_obj.c @@ -0,0 +1,743 @@ +#define _POSIX_C_SOURCE 200112L /* strtok_r support */ + +#include "aw.h" + +#include <rsys/dynamic_array_float.h> +#include <rsys/float3.h> +#include <rsys/float4.h> +#include <rsys/mem_allocator.h> +#include <rsys/ref_count.h> +#include <rsys/str.h> + +#include <float.h> + +struct vertex { + size_t iposition; + size_t inormal; + size_t itexcoord; +}; +static const struct vertex VERTEX_NULL = { AW_ID_NONE, AW_ID_NONE, AW_ID_NONE }; + +struct named_group { + struct str name; + 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 aw_obj_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 aw_obj_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 aw_obj { + struct darray_float positions; /* float4 */ + struct darray_float normals; /* float3 */ + struct darray_float texcoords; /* float3 */ + 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; + + size_t igroups_active; /* Index toward the first active group */ + + ref_T ref; + struct mem_allocator* allocator; +}; + +/******************************************************************************* + * 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) +{ + size_t nfaces, ngrps; + ASSERT(obj); + if(0 == (nfaces = darray_face_size_get(&obj->faces))) + return; + if(0 == (ngrps = darray_named_group_size_get(&obj->groups))) + return; + if(obj->igroups_active >= ngrps) + return; + FOR_EACH(obj->igroups_active, obj->igroups_active, ngrps) { + struct named_group* grp; + grp = darray_named_group_data_get(&obj->groups) + obj->igroups_active; + ASSERT(grp->iface <= nfaces); + grp->nfaces = nfaces - grp->iface; + } +} + +static FINLINE void +flush_usemtl(struct aw_obj* obj) +{ + struct named_group* mtl; + size_t nfaces, ngrps; + ASSERT(obj); + if(0 == (nfaces = darray_face_size_get(&obj->faces))) + return; + if(0 == (ngrps = darray_named_group_size_get(&obj->usemtls))) + return; + mtl = darray_named_group_data_get(&obj->usemtls) + (ngrps - 1); + ASSERT(mtl->iface <= nfaces); + mtl->nfaces = nfaces - mtl->iface; +} + +static FINLINE void +flush_smooth_group(struct aw_obj* obj) +{ + struct aw_obj_smooth_group* grp; + size_t nfaces, ngrps; + ASSERT(obj); + if(0 == (nfaces = darray_face_size_get(&obj->faces))) + return; + if(0 == (ngrps = darray_smooth_group_size_get(&obj->smooth_groups))) + return; + grp = darray_smooth_group_data_get(&obj->smooth_groups) + (ngrps - 1); + ASSERT(grp->face_id <= nfaces); + grp->faces_count = nfaces - grp->face_id; +} + +static enum aw_result +parse_floatX + (char** word_tk, + const unsigned count_min, + const unsigned count_max, + const float default_value, + struct darray_float* dst) +{ + unsigned i; + enum aw_result res = AW_OK; + ASSERT(word_tk && dst && count_min <= count_max); + + FOR_EACH(i, 0, count_max) { + char* real; + float f; + if(NULL == (real = strtok_r(NULL, " ", word_tk))) + break; + res = string_to_float(real, &f); + if(res != AW_OK) + goto error; + if(darray_float_push_back(dst, &f)) { + res = AW_MEMORY_ERROR; + goto error; + } + } + if(i < count_min) { + res = AW_BAD_ARGUMENT; + goto error; + } + + FOR_EACH(i, i, count_max) { + if(darray_float_push_back(dst, &default_value)) { + res = AW_MEMORY_ERROR; + goto error; + } + } + +exit: + return res; +error: + FOR_EACH_REVERSE(i, i, 0) + darray_float_pop_back(dst); + goto exit; +} + +static enum aw_result +parse_face(struct aw_obj* obj, char** word_tk) +{ + struct aw_obj_face face; + char* word; + enum aw_result res = AW_OK; + ASSERT(obj && word_tk); + + face.vertex_id = darray_vertex_size_get(&obj->vertices); + face.vertices_count = 0; + face.group_id = darray_named_group_size_get(&obj->groups) - 1; + face.smooth_group_id = darray_smooth_group_size_get(&obj->smooth_groups) - 1; + face.mtl_id = darray_named_group_size_get(&obj->usemtls) - 1; + + /* Note that the obj indexation starts at 1 rather than 0. We thus subtract 1 + * to the vertex attribute indices in order to match the C memory layout */ + while((word = strtok_r(NULL, " ", word_tk))) { + char* id, *id_tk, *id_pos; + struct vertex vert = VERTEX_NULL; + + /* position index */ + id_pos = strtok_r(word, "/", &id_tk); + res = string_to_size_t(id_pos, &vert.iposition); + if(res != AW_OK) + goto error; + --vert.iposition; /* Match C memory layout */ + if((id = strtok_r(NULL, "/", &id_tk))) { + id_pos += strlen(id_pos); + if( id > id_pos + 3) /* Unexpected N `/' separators with N > 2 */ + goto error; + if(id == id_pos + 2) { /* `//' separator => No tex */ + /* normal index */ + if(AW_OK != (res = string_to_size_t(id, &vert.inormal))) + goto error; + --vert.inormal; /* Match C memory layout */ + } else { + /* texcoord index */ + if(AW_OK != (res = string_to_size_t(id, &vert.itexcoord))) + goto error; + --vert.itexcoord; /* Match C memory latout */ + /* normal index */ + if((id = strtok_r(NULL, "/", &id_tk))) { + if(AW_OK != (res = string_to_size_t(id, &vert.inormal))) + goto error; + --vert.inormal; /* Match C memory layout */ + } + } + } + if(darray_vertex_push_back(&obj->vertices, &vert)) { + res = AW_MEMORY_ERROR; + goto error; + } + ++face.vertices_count; + } + if(darray_face_push_back(&obj->faces, &face)) { + res = AW_MEMORY_ERROR; + goto error; + } + +exit: + return res; +error: + FOR_EACH_REVERSE(face.vertices_count, face.vertices_count, 0) + darray_vertex_pop_back(&obj->vertices); + goto exit; +} + +static enum aw_result +parse_group(struct aw_obj* obj, char** word_tk) +{ + char* word; + size_t ngrps = 0, igrp = 0; + enum aw_result res = AW_OK; + ASSERT(obj && word_tk); + + flush_groups(obj); + + word = strtok_r(NULL, " ", word_tk); + if(!word) { + res = AW_BAD_ARGUMENT; + goto error; + } + #define CALL(Func) { if(Func) { res = AW_MEMORY_ERROR; goto error; } }(void)0 + ngrps = igrp = darray_named_group_size_get(&obj->groups); + obj->igroups_active = ngrps; + do { + struct named_group* grp = NULL; + if(darray_named_group_resize(&obj->groups, igrp + 1)) { + res = AW_MEMORY_ERROR; + goto error; + } + grp = darray_named_group_data_get(&obj->groups) + ngrps; + ++igrp; + if(str_set(&grp->name, word)) { + res = AW_MEMORY_ERROR; + goto error; + } + grp->iface = darray_face_size_get(&obj->faces); + grp->nfaces = 0; + } while((word = strtok_r(NULL, " ", word_tk))); + +exit: + return res; +error: + if(igrp != ngrps) + CHECK(darray_named_group_resize(&obj->groups, ngrps), 0); + goto exit; +} + +static enum aw_result +parse_smooth_group(struct aw_obj* obj, char** word_tk) +{ + char* word; + struct aw_obj_smooth_group grp; + enum aw_result res; + size_t i; + ASSERT(obj && word_tk); + + flush_smooth_group(obj); + + word = strtok_r(NULL, " ", word_tk); + if(!strcmp(word, "off")) { + grp.is_smoothed = 0; + } else if(!strcmp(word, "on")) { + grp.is_smoothed = 1; + } else { + res = string_to_size_t(word, &i); + if(res != AW_OK) + return res; + grp.is_smoothed = i != 0; + } + grp.face_id = darray_face_size_get(&obj->faces); + grp.faces_count = 0; + + if(darray_smooth_group_push_back(&obj->smooth_groups, &grp)) + return AW_MEMORY_ERROR; + + return AW_OK; +} + +static enum aw_result +parse_mtllib(struct aw_obj* obj, char** word_tk) +{ + char* word; + size_t imtllib = 0; + size_t nmtllibs = 0; + enum aw_result res = AW_OK; + ASSERT(obj && word_tk); + + word = strtok_r(NULL, " ", word_tk); + if(!word) { + res = AW_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 = AW_MEMORY_ERROR; + goto error; + } + str = darray_mtllib_data_get(&obj->mtllibs) + imtllib; + ++imtllib; + if(str_set(str, word)) { + res = AW_MEMORY_ERROR; + goto error; + } + } while((word = strtok_r(NULL, " ", word_tk))); + +exit: + return res; +error: + if(imtllib != nmtllibs) + CHECK(darray_mtllib_resize(&obj->mtllibs, nmtllibs), 0); + goto exit; +} + +static enum aw_result +parse_usemtl(struct aw_obj* obj, char** word_tk) +{ + char* word; + struct named_group* mtl = NULL; + size_t nmtls; + enum aw_result res = AW_OK; + ASSERT(obj && word_tk); + + flush_usemtl(obj); + + word= strtok_r(NULL, " ", word_tk); + if(!word_tk) { + res = AW_BAD_ARGUMENT; + goto error; + } + + nmtls = darray_named_group_size_get(&obj->usemtls); + if(darray_named_group_resize(&obj->usemtls, nmtls + 1)) { + res = AW_MEMORY_ERROR; + goto error; + } + mtl = darray_named_group_data_get(&obj->usemtls) + nmtls; + if(str_set(&mtl->name, word)) { + res = AW_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; +} + +static enum aw_result +parse_file(struct aw_obj* obj, const char* path, char* content) +{ + char* line, *line_tk; + unsigned long iline; + enum aw_result res = AW_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, 4, 1.f, &obj->positions); + } else if(!strcmp(word, "vn")) { /* Vertex normal */ + res = parse_floatX(&word_tk, 3, 3, 0.f, &obj->normals); + } else if(!strcmp(word, "vt")) { /* Vertex texture coordinates */ + res = parse_floatX(&word_tk, 1, 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 */ + 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 */ + res = parse_usemtl(obj, &word_tk); + } else { + res = AW_BAD_ARGUMENT; + } + if(res != AW_OK) + goto error; + word = strtok_r(NULL, " ", &word_tk); + } + line = strtok_r(NULL, "\n", &line_tk); + ++iline; + } + flush_groups(obj); + flush_smooth_group(obj); + flush_usemtl(obj); +exit: + return res; +error: + fprintf(stderr, "%s:%lu: error: parsing failed\n", path, iline); + goto exit; +} + +static void +aw_clear(struct aw_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); + obj->igroups_active = 0; +} + +static void +aw_release(ref_T* ref) +{ + struct aw_obj* obj = CONTAINER_OF(ref, struct aw_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); +} + +/******************************************************************************* + * Exported functions + ******************************************************************************/ +enum aw_result +aw_obj_create + (struct mem_allocator* mem_allocator, + struct aw_obj** aw_out) +{ + struct mem_allocator* allocator; + struct aw_obj* obj = NULL; + enum aw_result res = AW_OK; + + if(!aw_out) { + res = AW_BAD_ARGUMENT; + goto error; + } + allocator = mem_allocator ? mem_allocator : &mem_default_allocator; + obj = MEM_CALLOC(allocator, 1, sizeof(struct aw_obj)); + if(!obj) { + res = AW_MEMORY_ERROR; + goto error; + } + 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: + if(aw_out) + *aw_out = obj; + return res; +error: + if(obj) { + AW(obj_ref_put(obj)); + obj = NULL; + } + goto exit; +} + +enum aw_result +aw_obj_ref_get(struct aw_obj* obj) +{ + if(!obj) + return AW_BAD_ARGUMENT; + ref_get(&obj->ref); + return AW_OK; +} + +enum aw_result +aw_obj_ref_put(struct aw_obj* obj) +{ + if(!obj) + return AW_BAD_ARGUMENT; + ref_put(&obj->ref, aw_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; + 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))) + goto error; + +exit: + if(file) + fclose(file); + if(file_content) + MEM_FREE(obj->allocator, file_content); + return res; +error: + goto exit; +} + +enum aw_result +aw_obj_desc_get(struct aw_obj* obj, struct aw_obj_desc* desc) +{ + if(!obj || !desc) + return AW_BAD_ARGUMENT; + desc->faces_count = darray_face_size_get(&obj->faces); + desc->groups_count = darray_named_group_size_get(&obj->groups); + desc->smooth_groups_count = darray_smooth_group_size_get(&obj->smooth_groups); + desc->usemtls_count = darray_named_group_size_get(&obj->usemtls); + desc->mtllibs_count = darray_mtllib_size_get(&obj->mtllibs); + return AW_OK; +} + +enum aw_result +aw_obj_face_get + (const struct aw_obj* obj, const size_t iface, struct aw_obj_face* face) +{ + if(!obj || !face || iface >= darray_face_size_get(&obj->faces)) + return AW_BAD_ARGUMENT; + *face = darray_face_cdata_get(&obj->faces)[iface]; + return AW_OK; +} + +enum aw_result +aw_obj_group_get + (const struct aw_obj* obj, const size_t igroup, struct aw_obj_group* grp) +{ + const struct named_group* group; + if(!obj || !grp || igroup >= darray_named_group_size_get(&obj->groups)) + return AW_BAD_ARGUMENT; + group = darray_named_group_cdata_get(&obj->groups) + igroup; + grp->name = str_cget(&group->name); + grp->face_id = group->iface; + grp->faces_count = group->nfaces; + return AW_OK; +} + +enum aw_result +aw_obj_smooth_group_get + (const struct aw_obj* obj, + const size_t ismooth_group, + struct aw_obj_smooth_group* group) +{ + if(!obj || !group + || ismooth_group >= darray_smooth_group_size_get(&obj->smooth_groups)) + return AW_BAD_ARGUMENT; + *group = darray_smooth_group_cdata_get(&obj->smooth_groups)[ismooth_group]; + return AW_OK; +} + +enum aw_result +aw_obj_mtl_get + (const struct aw_obj* obj, const size_t imtl, struct aw_obj_mtl* mtl) +{ + const struct named_group* group; + if(!obj || !mtl || imtl >= darray_named_group_size_get(&obj->usemtls)) + return AW_BAD_ARGUMENT; + group = darray_named_group_cdata_get(&obj->usemtls) + imtl; + mtl->name = str_cget(&group->name); + mtl->face_id = group->iface; + mtl->faces_count = group->nfaces; + return AW_OK; +} + +enum aw_result +aw_obj_vertex_get + (const struct aw_obj* obj, const size_t ivertex, struct aw_obj_vertex* vertex) +{ + const struct vertex* vert; + const float* data; + if(!obj || !vertex || ivertex >= darray_vertex_size_get(&obj->vertices)) + return AW_BAD_ARGUMENT; + vert = darray_vertex_cdata_get(&obj->vertices) + ivertex; + + /* Fetch vertex position */ + ASSERT(vert->iposition != VERTEX_NULL.iposition); + data = darray_float_cdata_get(&obj->positions) + vert->iposition * 4; + f4_set(vertex->position, data); + /* Setup vertex texcoord */ + if(vert->itexcoord == VERTEX_NULL.itexcoord) { + f3_splat(vertex->texcoord, 0.f); + } else { + data = darray_float_cdata_get(&obj->texcoords) + vert->itexcoord * 3; + f3_set(vertex->texcoord, data); + } + /* Setup vertex normal */ + if(vert->inormal == VERTEX_NULL.inormal) { + f3_splat(vertex->normal, 0.f); + } else { + data = darray_float_cdata_get(&obj->normals) + vert->inormal * 3; + f3_set(vertex->normal, data); + } + return AW_OK; +} + diff --git a/src/obj.c b/src/obj.c @@ -1,741 +0,0 @@ -#define _POSIX_C_SOURCE 200112L /* strtok_r support */ - -#include "obj.h" - -#include <rsys/dynamic_array_float.h> -#include <rsys/float3.h> -#include <rsys/float4.h> -#include <rsys/mem_allocator.h> -#include <rsys/ref_count.h> -#include <rsys/str.h> - -#include <float.h> - -struct vertex { - size_t iposition; - size_t inormal; - size_t itexcoord; -}; -static const struct vertex VERTEX_NULL = - { OBJ_ID_NONE, OBJ_ID_NONE, OBJ_ID_NONE }; - -struct named_group { - struct str name; - 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 obj_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 obj_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; /* float4 */ - struct darray_float normals; /* float3 */ - struct darray_float texcoords; /* float3 */ - 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; - - size_t igroups_active; /* Index toward the first active group */ - - ref_T ref; - struct mem_allocator* allocator; -}; - -/******************************************************************************* - * 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_groups(struct obj* obj) -{ - size_t nfaces, ngrps; - ASSERT(obj); - if(0 == (nfaces = darray_face_size_get(&obj->faces))) - return; - if(0 == (ngrps = darray_named_group_size_get(&obj->groups))) - return; - if(obj->igroups_active >= ngrps) - return; - FOR_EACH(obj->igroups_active, obj->igroups_active, ngrps) { - struct named_group* grp; - grp = darray_named_group_data_get(&obj->groups) + obj->igroups_active; - ASSERT(grp->iface <= nfaces); - grp->nfaces = nfaces - grp->iface; - } -} - -static FINLINE void -flush_usemtl(struct obj* obj) -{ - struct named_group* mtl; - size_t nfaces, ngrps; - ASSERT(obj); - if(0 == (nfaces = darray_face_size_get(&obj->faces))) - return; - if(0 == (ngrps = darray_named_group_size_get(&obj->usemtls))) - return; - mtl = darray_named_group_data_get(&obj->usemtls) + (ngrps - 1); - ASSERT(mtl->iface <= nfaces); - mtl->nfaces = nfaces - mtl->iface; -} - -static FINLINE void -flush_smooth_group(struct obj* obj) -{ - struct obj_smooth_group* grp; - size_t nfaces, ngrps; - ASSERT(obj); - if(0 == (nfaces = darray_face_size_get(&obj->faces))) - return; - if(0 == (ngrps = darray_smooth_group_size_get(&obj->smooth_groups))) - return; - grp = darray_smooth_group_data_get(&obj->smooth_groups) + (ngrps - 1); - ASSERT(grp->face_id <= nfaces); - grp->faces_count = nfaces - grp->face_id; -} - -static enum obj_result -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 && count_min <= count_max); - - FOR_EACH(i, 0, count_max) { - char* real; - float f; - 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: - 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 obj_face face; - char* word; - enum obj_result res = OBJ_OK; - ASSERT(obj && word_tk); - - face.vertex_id = darray_vertex_size_get(&obj->vertices); - face.vertices_count = 0; - face.group_id = darray_named_group_size_get(&obj->groups) - 1; - face.smooth_group_id = darray_smooth_group_size_get(&obj->smooth_groups) - 1; - face.mtl_id = darray_named_group_size_get(&obj->usemtls) - 1; - - /* Note that the obj indexation starts at 1 rather than 0. We thus subtract 1 - * to the vertex attribute indices in order to match the C memory layout */ - while((word = strtok_r(NULL, " ", word_tk))) { - char* id, *id_tk, *id_pos; - struct vertex vert = VERTEX_NULL; - - /* position index */ - id_pos = strtok_r(word, "/", &id_tk); - res = string_to_size_t(id_pos, &vert.iposition); - if(res != OBJ_OK) - goto error; - --vert.iposition; /* Match C memory layout */ - if((id = strtok_r(NULL, "/", &id_tk))) { - id_pos += strlen(id_pos); - if( id > id_pos + 3) /* Unexpected N `/' separators with N > 2 */ - goto error; - if(id == id_pos + 2) { /* `//' separator => No tex */ - /* normal index */ - if(OBJ_OK != (res = string_to_size_t(id, &vert.inormal))) - goto error; - --vert.inormal; /* Match C memory layout */ - } else { - /* texcoord index */ - if(OBJ_OK != (res = string_to_size_t(id, &vert.itexcoord))) - goto error; - --vert.itexcoord; /* Match C memory latout */ - /* normal index */ - if((id = strtok_r(NULL, "/", &id_tk))) { - if(OBJ_OK != (res = string_to_size_t(id, &vert.inormal))) - goto error; - --vert.inormal; /* Match C memory layout */ - } - } - } - if(darray_vertex_push_back(&obj->vertices, &vert)) { - res = OBJ_MEMORY_ERROR; - goto error; - } - ++face.vertices_count; - } - if(darray_face_push_back(&obj->faces, &face)) { - res = OBJ_MEMORY_ERROR; - goto error; - } - -exit: - return res; -error: - FOR_EACH_REVERSE(face.vertices_count, face.vertices_count, 0) - darray_vertex_pop_back(&obj->vertices); - goto exit; -} - -static enum obj_result -parse_group(struct obj* obj, char** word_tk) -{ - char* word; - size_t ngrps = 0, igrp = 0; - enum obj_result res = OBJ_OK; - ASSERT(obj && word_tk); - - flush_groups(obj); - - 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 = igrp = darray_named_group_size_get(&obj->groups); - obj->igroups_active = ngrps; - do { - struct named_group* grp = NULL; - if(darray_named_group_resize(&obj->groups, igrp + 1)) { - res = OBJ_MEMORY_ERROR; - goto error; - } - grp = darray_named_group_data_get(&obj->groups) + ngrps; - ++igrp; - if(str_set(&grp->name, word)) { - res = OBJ_MEMORY_ERROR; - goto error; - } - grp->iface = darray_face_size_get(&obj->faces); - grp->nfaces = 0; - } while((word = strtok_r(NULL, " ", word_tk))); - -exit: - return res; -error: - if(igrp != ngrps) - CHECK(darray_named_group_resize(&obj->groups, ngrps), 0); - goto exit; -} - -static enum obj_result -parse_smooth_group(struct obj* obj, char** word_tk) -{ - char* word; - struct obj_smooth_group grp; - enum obj_result res; - size_t i; - ASSERT(obj && word_tk); - - flush_smooth_group(obj); - - word = strtok_r(NULL, " ", word_tk); - if(!strcmp(word, "off")) { - grp.is_smoothed = 0; - } else if(!strcmp(word, "on")) { - grp.is_smoothed = 1; - } else { - res = string_to_size_t(word, &i); - if(res != OBJ_OK) - return res; - grp.is_smoothed = i != 0; - } - grp.face_id = darray_face_size_get(&obj->faces); - grp.faces_count = 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 = 0; - size_t nmtllibs = 0; - 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: - 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_usemtl(obj); - - 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; -} - -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, 4, 1.f, &obj->positions); - } else if(!strcmp(word, "vn")) { /* Vertex normal */ - res = parse_floatX(&word_tk, 3, 3, 0.f, &obj->normals); - } else if(!strcmp(word, "vt")) { /* Vertex texture coordinates */ - res = parse_floatX(&word_tk, 1, 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 */ - 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 */ - res = parse_usemtl(obj, &word_tk); - } 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_groups(obj); - flush_smooth_group(obj); - flush_usemtl(obj); -exit: - return res; -error: - fprintf(stderr, "%s:%lu: error: parsing failed\n", path, iline); - goto exit; -} - -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); - obj->igroups_active = 0; -} - -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); -} - -/******************************************************************************* - * Exported functions - ******************************************************************************/ -enum obj_result -obj_create - (struct mem_allocator* mem_allocator, - struct obj** obj_out) -{ - struct mem_allocator* allocator; - struct obj* obj = NULL; - enum obj_result res = OBJ_OK; - - if(!obj_out) { - res = OBJ_BAD_ARGUMENT; - goto error; - } - allocator = mem_allocator ? mem_allocator : &mem_default_allocator; - obj = MEM_CALLOC(allocator, 1, sizeof(struct obj)); - if(!obj) { - res = OBJ_MEMORY_ERROR; - goto error; - } - 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: - if(obj_out) - *obj_out = obj; - return res; -error: - if(obj) { - OBJ(ref_put(obj)); - obj = NULL; - } - goto exit; -} - -enum obj_result -obj_ref_get(struct obj* obj) -{ - if(!obj) - return OBJ_BAD_ARGUMENT; - ref_get(&obj->ref); - return OBJ_OK; -} - -enum obj_result -obj_ref_put(struct obj* obj) -{ - if(!obj) - return OBJ_BAD_ARGUMENT; - ref_put(&obj->ref, obj_release); - return OBJ_OK; -} - -enum obj_result -obj_load(struct obj* obj, const char* filename) -{ - FILE* file = NULL; - char* file_content = NULL; - long file_size = 0; - size_t len = 0; - enum obj_result res = OBJ_OK; - (void)len; - - if(!obj || !filename) { - res = OBJ_BAD_ARGUMENT; - goto error; - } - - file = fopen(filename, "r"); - if(!file) { - res = OBJ_IO_ERROR; - 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 = OBJ_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; - - obj_clear(obj); - if(OBJ_OK != (res = parse_file(obj, filename, file_content))) - goto error; - -exit: - if(file) - fclose(file); - if(file_content) - MEM_FREE(obj->allocator, file_content); - return res; -error: - goto exit; -} - -enum obj_result -obj_desc_get(struct obj* obj, struct obj_desc* desc) -{ - if(!obj || !desc) - return OBJ_BAD_ARGUMENT; - desc->faces_count = darray_face_size_get(&obj->faces); - desc->groups_count = darray_named_group_size_get(&obj->groups); - desc->smooth_groups_count = darray_smooth_group_size_get(&obj->smooth_groups); - desc->usemtls_count = darray_named_group_size_get(&obj->usemtls); - desc->mtllibs_count = darray_mtllib_size_get(&obj->mtllibs); - return OBJ_OK; -} - -enum obj_result -obj_face_get(const struct obj* obj, const size_t iface, struct obj_face* face) -{ - if(!obj || !face || iface >= darray_face_size_get(&obj->faces)) - return OBJ_BAD_ARGUMENT; - *face = darray_face_cdata_get(&obj->faces)[iface]; - return OBJ_OK; -} - -enum obj_result -obj_group_get(const struct obj* obj, const size_t igroup, struct obj_group* grp) -{ - const struct named_group* group; - if(!obj || !grp || igroup >= darray_named_group_size_get(&obj->groups)) - return OBJ_BAD_ARGUMENT; - group = darray_named_group_cdata_get(&obj->groups) + igroup; - grp->name = str_cget(&group->name); - grp->face_id = group->iface; - grp->faces_count = group->nfaces; - return OBJ_OK; -} - -enum obj_result -obj_smooth_group_get - (const struct obj* obj, - const size_t ismooth_group, - struct obj_smooth_group* group) -{ - if(!obj || !group - || ismooth_group >= darray_smooth_group_size_get(&obj->smooth_groups)) - return OBJ_BAD_ARGUMENT; - *group = darray_smooth_group_cdata_get(&obj->smooth_groups)[ismooth_group]; - return OBJ_OK; -} - -enum obj_result -obj_mtl_get(const struct obj* obj, const size_t imtl, struct obj_mtl* mtl) -{ - const struct named_group* group; - if(!obj || !mtl || imtl >= darray_named_group_size_get(&obj->usemtls)) - return OBJ_BAD_ARGUMENT; - group = darray_named_group_cdata_get(&obj->usemtls) + imtl; - mtl->name = str_cget(&group->name); - mtl->face_id = group->iface; - mtl->faces_count = group->nfaces; - return OBJ_OK; -} - -enum obj_result -obj_vertex_get - (const struct obj* obj, const size_t ivertex, struct obj_vertex* vertex) -{ - const struct vertex* vert; - const float* data; - if(!obj || !vertex || ivertex >= darray_vertex_size_get(&obj->vertices)) - return OBJ_BAD_ARGUMENT; - vert = darray_vertex_cdata_get(&obj->vertices) + ivertex; - - /* Fetch vertex position */ - ASSERT(vert->iposition != VERTEX_NULL.iposition); - data = darray_float_cdata_get(&obj->positions) + vert->iposition * 4; - f4_set(vertex->position, data); - /* Setup vertex texcoord */ - if(vert->itexcoord == VERTEX_NULL.itexcoord) { - f3_splat(vertex->texcoord, 0.f); - } else { - data = darray_float_cdata_get(&obj->texcoords) + vert->itexcoord * 3; - f3_set(vertex->texcoord, data); - } - /* Setup vertex normal */ - if(vert->inormal == VERTEX_NULL.inormal) { - f3_splat(vertex->normal, 0.f); - } else { - data = darray_float_cdata_get(&obj->normals) + vert->inormal * 3; - f3_set(vertex->normal, data); - } - return OBJ_OK; -} - diff --git a/src/obj.h b/src/obj.h @@ -1,130 +0,0 @@ -#ifndef OBJ_H -#define OBJ_H - -#include <rsys/rsys.h> - -#ifdef OBJ_SHARED_BUILD - #define OBJ_API extern EXPORT_SYM -#elif defined(OBJ_STATIC_BUILD) - #define OBJ_API extern LOCAL_SYM -#else - #define OBJ_API extern IMPORT_SYM -#endif - -#ifndef NDEBUG - #define OBJ(Func) ASSERT(obj_##Func == OBJ_OK) -#else - #define OBJ(Func) obj_##Func -#endif - -#define OBJ_ID_NONE ((size_t)(-1)) - -enum obj_result { - OBJ_BAD_ARGUMENT, - OBJ_IO_ERROR, - OBJ_MEMORY_ERROR, - OBJ_OK -}; - -struct obj_desc { - size_t faces_count; - size_t groups_count; - size_t smooth_groups_count; - size_t usemtls_count; - size_t mtllibs_count; -}; - -struct obj_face { - size_t vertex_id; /* Index of the first face vertex */ - size_t vertices_count; - size_t group_id; /* Index of the face group */ - size_t smooth_group_id; /* Index of the face smooth group */ - size_t mtl_id; /* Index of the face material */ -}; - -struct obj_group { - const char* name; - size_t face_id; /* Index of the first group face */ - size_t faces_count; -}; - -struct obj_smooth_group { - char is_smoothed; - size_t face_id; /* Index of the first smooth group face */ - size_t faces_count; -}; - -struct obj_mtl { - const char* name; - size_t face_id; - size_t faces_count; -}; - -struct obj_vertex { - float position[4]; - float normal[3]; - float texcoord[3]; -}; - -struct obj; -struct mem_allocator; - -BEGIN_DECLS - -OBJ_API enum obj_result -obj_create - (struct mem_allocator* allocator, /* NULL <=> use default allocator */ - struct obj** obj); - -OBJ_API enum obj_result -obj_ref_get - (struct obj* obj); - -OBJ_API enum obj_result -obj_ref_put - (struct obj* obj); - -OBJ_API enum obj_result -obj_load - (struct obj* obj, - const char* filename); - -OBJ_API enum obj_result -obj_desc_get - (struct obj* obj, - struct obj_desc* desc); - -OBJ_API enum obj_result -obj_face_get - (const struct obj* obj, - const size_t face_id, - struct obj_face* face); - -OBJ_API enum obj_result -obj_group_get - (const struct obj* obj, - const size_t group_id, - struct obj_group* group); - -OBJ_API enum obj_result -obj_smooth_group_get - (const struct obj* obj, - const size_t smooth_group_id, - struct obj_smooth_group* smooth_group); - -OBJ_API enum obj_result -obj_mtl_get - (const struct obj* obj, - const size_t mtl_id, - struct obj_mtl* mtl); - -OBJ_API enum obj_result -obj_vertex_get - (const struct obj* obj, - const size_t vertex_id, - struct obj_vertex* vertex); - -END_DECLS - -#endif /* OBJ_H */ - diff --git a/src/test_aw_obj.c b/src/test_aw_obj.c @@ -0,0 +1,345 @@ +#include "aw.h" + +#include <rsys/float3.h> +#include <rsys/float4.h> +#include <rsys/mem_allocator.h> + +#include <string.h> + +static void +test_plane(struct aw_obj* obj) +{ + 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"; + float v4[4]; + struct aw_obj_desc desc; + struct aw_obj_face face; + struct aw_obj_mtl mtl; + struct aw_obj_vertex vertex; + FILE* file; + + NCHECK(obj, NULL); + + file = fopen("test_obj_plane.obj", "w"); + NCHECK(file, NULL); + fwrite(plane_obj, sizeof(char), strlen(plane_obj), file); + fclose(file); + + CHECK(aw_obj_load(NULL, NULL), AW_BAD_ARGUMENT); + CHECK(aw_obj_load(obj, NULL), AW_BAD_ARGUMENT); + CHECK(aw_obj_load(NULL, "test_obj_plane.obj"), AW_BAD_ARGUMENT); + CHECK(aw_obj_load(obj, "none.obj"), AW_IO_ERROR); + CHECK(aw_obj_load(obj, "test_obj_plane.obj"), AW_OK); + + CHECK(aw_obj_desc_get(NULL, NULL), AW_BAD_ARGUMENT); + CHECK(aw_obj_desc_get(obj, NULL), AW_BAD_ARGUMENT); + CHECK(aw_obj_desc_get(NULL, &desc), AW_BAD_ARGUMENT); + CHECK(aw_obj_desc_get(obj, &desc), AW_OK); + CHECK(desc.faces_count, 1); + CHECK(desc.groups_count, 0); + CHECK(desc.smooth_groups_count, 0); + CHECK(desc.usemtls_count, 1); + CHECK(desc.mtllibs_count, 1); + + CHECK(aw_obj_face_get(NULL, AW_ID_NONE, NULL), AW_BAD_ARGUMENT); + CHECK(aw_obj_face_get(obj, AW_ID_NONE, NULL), AW_BAD_ARGUMENT); + CHECK(aw_obj_face_get(NULL, 0, NULL), AW_BAD_ARGUMENT); + CHECK(aw_obj_face_get(obj, 0, NULL), AW_BAD_ARGUMENT); + CHECK(aw_obj_face_get(NULL, AW_ID_NONE, &face), AW_BAD_ARGUMENT); + CHECK(aw_obj_face_get(obj, AW_ID_NONE, &face), AW_BAD_ARGUMENT); + CHECK(aw_obj_face_get(NULL, 0, &face), AW_BAD_ARGUMENT); + CHECK(aw_obj_face_get(obj, 0, &face), AW_OK); + CHECK(face.vertex_id, 0); + CHECK(face.vertices_count, 4); + CHECK(face.group_id, AW_ID_NONE); + CHECK(face.smooth_group_id, AW_ID_NONE); + CHECK(face.mtl_id, 0); + + CHECK(aw_obj_mtl_get(NULL, AW_ID_NONE, NULL), AW_BAD_ARGUMENT); + CHECK(aw_obj_mtl_get(obj, AW_ID_NONE, NULL), AW_BAD_ARGUMENT); + CHECK(aw_obj_mtl_get(NULL, 0, NULL), AW_BAD_ARGUMENT); + CHECK(aw_obj_mtl_get(obj, 0, NULL), AW_BAD_ARGUMENT); + CHECK(aw_obj_mtl_get(NULL, AW_ID_NONE, &mtl), AW_BAD_ARGUMENT); + CHECK(aw_obj_mtl_get(obj, AW_ID_NONE, &mtl), AW_BAD_ARGUMENT); + CHECK(aw_obj_mtl_get(NULL, 0, &mtl), AW_BAD_ARGUMENT); + CHECK(aw_obj_mtl_get(obj, 0, &mtl), AW_OK); + CHECK(strcmp(mtl.name, "wood"), 0); + CHECK(mtl.face_id, 0); + CHECK(mtl.faces_count, 1); + + CHECK(aw_obj_vertex_get(NULL, AW_ID_NONE, NULL), AW_BAD_ARGUMENT); + CHECK(aw_obj_vertex_get(obj, AW_ID_NONE, NULL), AW_BAD_ARGUMENT); + CHECK(aw_obj_vertex_get(NULL, 0, NULL), AW_BAD_ARGUMENT); + CHECK(aw_obj_vertex_get(obj, 0, NULL), AW_BAD_ARGUMENT); + CHECK(aw_obj_vertex_get(NULL, AW_ID_NONE, &vertex), AW_BAD_ARGUMENT); + CHECK(aw_obj_vertex_get(obj, AW_ID_NONE, &vertex), AW_BAD_ARGUMENT); + CHECK(aw_obj_vertex_get(NULL, 0, &vertex), AW_BAD_ARGUMENT); + + CHECK(aw_obj_vertex_get(obj, 0, &vertex), AW_OK); + CHECK(f4_eq(vertex.position, f4(v4, 0.f, 2.f, 0.f, 1.f)), 1); + CHECK(f3_eq(vertex.texcoord, f3(v4, 0.f, 1.f, 0.f)), 1); + CHECK(aw_obj_vertex_get(obj, 1, &vertex), AW_OK); + CHECK(f4_eq(vertex.position, f4(v4, 0.f, 0.f, 0.f, 1.f)), 1); + CHECK(f3_eq(vertex.texcoord, f3(v4, 0.f, 0.f, 0.f)), 1); + CHECK(aw_obj_vertex_get(obj, 2, &vertex), AW_OK); + CHECK(f4_eq(vertex.position, f4(v4, 2.f, 0.f, 0.f, 1.f)), 1); + CHECK(f3_eq(vertex.texcoord, f3(v4, 1.f, 0.f, 0.f)), 1); + CHECK(aw_obj_vertex_get(obj, 3, &vertex), AW_OK); + CHECK(f4_eq(vertex.position, f4(v4, 2.f, 2.f, 0.f, 1.f)), 1); + CHECK(f3_eq(vertex.texcoord, f3(v4, 1.f, 1.f, 0.f)), 1); +} + +static void +test_squares(struct aw_obj* obj) +{ + static const char* squares_obj = + "v 0.000000 2.000000 0.000000\n" + "v 0.000000 0.000000 0.000000\n" + "v 2.000000 0.000000 0.000000\n" + "v 2.000000 2.000000 0.000000\n" + "v 4.000000 0.000000 -1.255298\n" + "v 4.000000 2.000000 -1.255298\n" + "vn 0.000000 0.000000 1.000000\n" + "vn 0.000000 0.000000 1.000000\n" + "vn 0.276597 0.000000 0.960986\n" + "vn 0.276597 0.000000 0.960986\n" + "vn 0.531611 0.000000 0.846988\n" + "vn 0.531611 0.000000 0.846988\n" + "# 6 vertices\n" + "\n" + "# 6 normals\n" + "\n" + "g all\n" + "s 1\n" + "f 1//1 2//2 3//3 4//4\n" + "f 4//4 3//3 5//5 6//6\n" + "# 2 elements\n"; + float v4[4]; + struct aw_obj_desc desc; + struct aw_obj_face face; + struct aw_obj_group group; + struct aw_obj_smooth_group sgroup; + struct aw_obj_mtl mtl; + struct aw_obj_vertex vertex; + FILE* file; + + NCHECK(obj, NULL); + + file = fopen("test_obj_squares.obj", "w"); + NCHECK(file, NULL); + fwrite(squares_obj, sizeof(char), strlen(squares_obj), file); + fclose(file); + CHECK(aw_obj_load(obj, "test_obj_squares.obj"), AW_OK); + + CHECK(aw_obj_desc_get(obj, &desc), AW_OK); + CHECK(desc.faces_count, 2); + CHECK(desc.groups_count, 1); + CHECK(desc.smooth_groups_count, 1); + CHECK(desc.usemtls_count, 0); + CHECK(desc.mtllibs_count, 0); + + CHECK(aw_obj_face_get(obj, 0, &face), AW_OK); + CHECK(face.vertex_id, 0); + CHECK(face.vertices_count, 4); + CHECK(face.group_id, 0); + CHECK(face.smooth_group_id, 0); + CHECK(face.mtl_id, AW_ID_NONE); + + CHECK(aw_obj_vertex_get(obj, 0, &vertex), AW_OK); + CHECK(f4_eq(vertex.position, f4(v4, 0.f, 2.f, 0.f, 1.f)), 1); + CHECK(f3_eq(vertex.normal, f3(v4, 0.f, 0.f, 1.f)), 1); + CHECK(aw_obj_vertex_get(obj, 1, &vertex), AW_OK); + CHECK(f4_eq(vertex.position, f4(v4, 0.f, 0.f, 0.f, 1.f)), 1); + CHECK(f3_eq(vertex.normal, f3(v4, 0.f, 0.f, 1.f)), 1); + CHECK(aw_obj_vertex_get(obj, 2, &vertex), AW_OK); + CHECK(f4_eq(vertex.position, f4(v4, 2.f, 0.f, 0.f, 1.f)), 1); + CHECK(f3_eq(vertex.normal, f3(v4, 0.276597f, 0.f, 0.960986f)), 1); + CHECK(aw_obj_vertex_get(obj, 3, &vertex), AW_OK); + CHECK(f4_eq(vertex.position, f4(v4, 2.f, 2.f, 0.f, 1.f)), 1); + CHECK(f3_eq(vertex.normal, f3(v4, 0.276597f, 0.f, 0.960986f)), 1); + + CHECK(aw_obj_face_get(obj, 1, &face), AW_OK); + CHECK(face.vertex_id, 4); + CHECK(face.vertices_count, 4); + CHECK(face.group_id, 0); + CHECK(face.smooth_group_id, 0); + CHECK(face.mtl_id, AW_ID_NONE); + + CHECK(aw_obj_vertex_get(obj, 4, &vertex), AW_OK); + CHECK(f4_eq(vertex.position, f4(v4, 2.f, 2.f, 0.f, 1.f)), 1); + CHECK(f3_eq(vertex.normal, f3(v4, 0.276597f, 0.f, 0.960986f)), 1); + CHECK(aw_obj_vertex_get(obj, 5, &vertex), AW_OK); + CHECK(f4_eq(vertex.position, f4(v4, 2.f, 0.f, 0.f, 1.f)), 1); + CHECK(f3_eq(vertex.normal, f3(v4, 0.276597f, 0.f, 0.960986f)), 1); + CHECK(aw_obj_vertex_get(obj, 6, &vertex), AW_OK); + CHECK(f4_eq(vertex.position, f4(v4, 4.f, 0.f, -1.255298f, 1.f)), 1); + CHECK(f3_eq(vertex.normal, f3(v4, 0.531611f, 0.f, 0.846988f)), 1); + CHECK(aw_obj_vertex_get(obj, 7, &vertex), AW_OK); + CHECK(f4_eq(vertex.position, f4(v4, 4.f, 2.f, -1.255298f, 1.f)), 1); + CHECK(f3_eq(vertex.normal, f3(v4, 0.531611f, 0.f, 0.846988f)), 1); + + CHECK(aw_obj_group_get(NULL, AW_ID_NONE, NULL), AW_BAD_ARGUMENT); + CHECK(aw_obj_group_get(obj, AW_ID_NONE, NULL), AW_BAD_ARGUMENT); + CHECK(aw_obj_group_get(NULL, 0, NULL), AW_BAD_ARGUMENT); + CHECK(aw_obj_group_get(obj, 0, NULL), AW_BAD_ARGUMENT); + CHECK(aw_obj_group_get(NULL, AW_ID_NONE, &group), AW_BAD_ARGUMENT); + CHECK(aw_obj_group_get(obj, AW_ID_NONE, &group), AW_BAD_ARGUMENT); + CHECK(aw_obj_group_get(NULL, 0, &group), AW_BAD_ARGUMENT); + CHECK(aw_obj_group_get(obj, 0, &group), AW_OK); + CHECK(strcmp(group.name, "all"), 0); + CHECK(group.face_id, 0); + CHECK(group.faces_count, 2); + + CHECK(aw_obj_smooth_group_get(NULL, AW_ID_NONE, NULL), AW_BAD_ARGUMENT); + CHECK(aw_obj_smooth_group_get(obj, AW_ID_NONE, NULL), AW_BAD_ARGUMENT); + CHECK(aw_obj_smooth_group_get(NULL, 0, NULL), AW_BAD_ARGUMENT); + CHECK(aw_obj_smooth_group_get(obj, 0, NULL), AW_BAD_ARGUMENT); + CHECK(aw_obj_smooth_group_get(NULL, AW_ID_NONE, &sgroup), AW_BAD_ARGUMENT); + CHECK(aw_obj_smooth_group_get(obj, AW_ID_NONE, &sgroup), AW_BAD_ARGUMENT); + CHECK(aw_obj_smooth_group_get(NULL, 0, &sgroup), AW_BAD_ARGUMENT); + CHECK(aw_obj_smooth_group_get(obj, 0, &sgroup), AW_OK); + CHECK(sgroup.is_smoothed, 1); + CHECK(sgroup.face_id, 0); + CHECK(sgroup.faces_count, 2); + + CHECK(aw_obj_mtl_get(obj, 0, &mtl), AW_BAD_ARGUMENT); +} + +static void +test_cube(struct aw_obj* obj) +{ + 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"; + const char* group_names[6] = + { "front", "back", "right", "top", "left", "bottom" }; + const char* mtl_names[6] = + { "red", "blue", "green", "gold", "orange", "purple" }; + struct aw_obj_desc desc; + FILE* file; + size_t i; + + NCHECK(obj, NULL); + + file = fopen("test_obj_cube.obj", "w"); + NCHECK(file, NULL); + fwrite(cube_obj, sizeof(char), strlen(cube_obj), file); + fclose(file); + CHECK(aw_obj_load(obj, "test_obj_cube.obj"), AW_OK); + + CHECK(aw_obj_desc_get(obj, &desc), AW_OK); + CHECK(desc.faces_count, 6); + CHECK(desc.groups_count, 6); + CHECK(desc.smooth_groups_count, 0); + CHECK(desc.usemtls_count, 6); + CHECK(desc.mtllibs_count, 1); + + FOR_EACH(i, 0, 6) { + struct aw_obj_face face; + CHECK(aw_obj_face_get(obj, i, &face), AW_OK); + CHECK(face.vertex_id, i*4); + CHECK(face.vertices_count, 4); + CHECK(face.group_id, i); + CHECK(face.smooth_group_id, AW_ID_NONE); + CHECK(face.mtl_id, i); + } + + FOR_EACH(i, 0, 6) { + struct aw_obj_group group; + CHECK(aw_obj_group_get(obj, i, &group), AW_OK); + CHECK(strcmp(group.name, group_names[i]), 0); + CHECK(group.face_id, i); + CHECK(group.faces_count, 1); + } + + FOR_EACH(i, 0, 6) { + struct aw_obj_mtl mtl; + CHECK(aw_obj_mtl_get(obj, i, &mtl), AW_OK); + CHECK(strcmp(mtl.name, mtl_names[i]), 0); + CHECK(mtl.face_id, i); + CHECK(mtl.faces_count, 1); + } +} + +int +main(int argc, char** argv) +{ + struct mem_allocator allocator; + struct aw_obj* obj; + int i; + + mem_init_proxy_allocator(&allocator, &mem_default_allocator); + + CHECK(aw_obj_create(NULL, NULL), AW_BAD_ARGUMENT); + CHECK(aw_obj_create(&allocator, NULL), AW_BAD_ARGUMENT); + CHECK(aw_obj_create(NULL, &obj), AW_OK); + + CHECK(aw_obj_ref_get(NULL), AW_BAD_ARGUMENT); + CHECK(aw_obj_ref_get(obj), AW_OK); + CHECK(aw_obj_ref_put(NULL), AW_BAD_ARGUMENT); + CHECK(aw_obj_ref_put(obj), AW_OK); + CHECK(aw_obj_ref_put(obj), AW_OK); + + CHECK(aw_obj_create(&allocator, &obj), AW_OK); + + test_plane(obj); + test_squares(obj); + test_cube(obj); + FOR_EACH(i, 1, argc) + CHECK(aw_obj_load(obj, argv[i]), AW_OK); + + CHECK(aw_obj_ref_put(obj), AW_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; +} + diff --git a/src/test_obj.c b/src/test_obj.c @@ -1,345 +0,0 @@ -#include "obj.h" - -#include <rsys/float3.h> -#include <rsys/float4.h> -#include <rsys/mem_allocator.h> - -#include <string.h> - -static void -test_plane(struct obj* obj) -{ - 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"; - float v4[4]; - struct obj_desc desc; - struct obj_face face; - struct obj_mtl mtl; - struct obj_vertex vertex; - FILE* file; - - NCHECK(obj, NULL); - - file = fopen("test_obj_plane.obj", "w"); - NCHECK(file, NULL); - fwrite(plane_obj, sizeof(char), strlen(plane_obj), file); - fclose(file); - - CHECK(obj_load(NULL, NULL), OBJ_BAD_ARGUMENT); - CHECK(obj_load(obj, NULL), OBJ_BAD_ARGUMENT); - CHECK(obj_load(NULL, "test_obj_plane.obj"), OBJ_BAD_ARGUMENT); - CHECK(obj_load(obj, "none.obj"), OBJ_IO_ERROR); - CHECK(obj_load(obj, "test_obj_plane.obj"), OBJ_OK); - - CHECK(obj_desc_get(NULL, NULL), OBJ_BAD_ARGUMENT); - CHECK(obj_desc_get(obj, NULL), OBJ_BAD_ARGUMENT); - CHECK(obj_desc_get(NULL, &desc), OBJ_BAD_ARGUMENT); - CHECK(obj_desc_get(obj, &desc), OBJ_OK); - CHECK(desc.faces_count, 1); - CHECK(desc.groups_count, 0); - CHECK(desc.smooth_groups_count, 0); - CHECK(desc.usemtls_count, 1); - CHECK(desc.mtllibs_count, 1); - - CHECK(obj_face_get(NULL, OBJ_ID_NONE, NULL), OBJ_BAD_ARGUMENT); - CHECK(obj_face_get(obj, OBJ_ID_NONE, NULL), OBJ_BAD_ARGUMENT); - CHECK(obj_face_get(NULL, 0, NULL), OBJ_BAD_ARGUMENT); - CHECK(obj_face_get(obj, 0, NULL), OBJ_BAD_ARGUMENT); - CHECK(obj_face_get(NULL, OBJ_ID_NONE, &face), OBJ_BAD_ARGUMENT); - CHECK(obj_face_get(obj, OBJ_ID_NONE, &face), OBJ_BAD_ARGUMENT); - CHECK(obj_face_get(NULL, 0, &face), OBJ_BAD_ARGUMENT); - CHECK(obj_face_get(obj, 0, &face), OBJ_OK); - CHECK(face.vertex_id, 0); - CHECK(face.vertices_count, 4); - CHECK(face.group_id, OBJ_ID_NONE); - CHECK(face.smooth_group_id, OBJ_ID_NONE); - CHECK(face.mtl_id, 0); - - CHECK(obj_mtl_get(NULL, OBJ_ID_NONE, NULL), OBJ_BAD_ARGUMENT); - CHECK(obj_mtl_get(obj, OBJ_ID_NONE, NULL), OBJ_BAD_ARGUMENT); - CHECK(obj_mtl_get(NULL, 0, NULL), OBJ_BAD_ARGUMENT); - CHECK(obj_mtl_get(obj, 0, NULL), OBJ_BAD_ARGUMENT); - CHECK(obj_mtl_get(NULL, OBJ_ID_NONE, &mtl), OBJ_BAD_ARGUMENT); - CHECK(obj_mtl_get(obj, OBJ_ID_NONE, &mtl), OBJ_BAD_ARGUMENT); - CHECK(obj_mtl_get(NULL, 0, &mtl), OBJ_BAD_ARGUMENT); - CHECK(obj_mtl_get(obj, 0, &mtl), OBJ_OK); - CHECK(strcmp(mtl.name, "wood"), 0); - CHECK(mtl.face_id, 0); - CHECK(mtl.faces_count, 1); - - CHECK(obj_vertex_get(NULL, OBJ_ID_NONE, NULL), OBJ_BAD_ARGUMENT); - CHECK(obj_vertex_get(obj, OBJ_ID_NONE, NULL), OBJ_BAD_ARGUMENT); - CHECK(obj_vertex_get(NULL, 0, NULL), OBJ_BAD_ARGUMENT); - CHECK(obj_vertex_get(obj, 0, NULL), OBJ_BAD_ARGUMENT); - CHECK(obj_vertex_get(NULL, OBJ_ID_NONE, &vertex), OBJ_BAD_ARGUMENT); - CHECK(obj_vertex_get(obj, OBJ_ID_NONE, &vertex), OBJ_BAD_ARGUMENT); - CHECK(obj_vertex_get(NULL, 0, &vertex), OBJ_BAD_ARGUMENT); - - CHECK(obj_vertex_get(obj, 0, &vertex), OBJ_OK); - CHECK(f4_eq(vertex.position, f4(v4, 0.f, 2.f, 0.f, 1.f)), 1); - CHECK(f3_eq(vertex.texcoord, f3(v4, 0.f, 1.f, 0.f)), 1); - CHECK(obj_vertex_get(obj, 1, &vertex), OBJ_OK); - CHECK(f4_eq(vertex.position, f4(v4, 0.f, 0.f, 0.f, 1.f)), 1); - CHECK(f3_eq(vertex.texcoord, f3(v4, 0.f, 0.f, 0.f)), 1); - CHECK(obj_vertex_get(obj, 2, &vertex), OBJ_OK); - CHECK(f4_eq(vertex.position, f4(v4, 2.f, 0.f, 0.f, 1.f)), 1); - CHECK(f3_eq(vertex.texcoord, f3(v4, 1.f, 0.f, 0.f)), 1); - CHECK(obj_vertex_get(obj, 3, &vertex), OBJ_OK); - CHECK(f4_eq(vertex.position, f4(v4, 2.f, 2.f, 0.f, 1.f)), 1); - CHECK(f3_eq(vertex.texcoord, f3(v4, 1.f, 1.f, 0.f)), 1); -} - -static void -test_squares(struct obj* obj) -{ - static const char* squares_obj = - "v 0.000000 2.000000 0.000000\n" - "v 0.000000 0.000000 0.000000\n" - "v 2.000000 0.000000 0.000000\n" - "v 2.000000 2.000000 0.000000\n" - "v 4.000000 0.000000 -1.255298\n" - "v 4.000000 2.000000 -1.255298\n" - "vn 0.000000 0.000000 1.000000\n" - "vn 0.000000 0.000000 1.000000\n" - "vn 0.276597 0.000000 0.960986\n" - "vn 0.276597 0.000000 0.960986\n" - "vn 0.531611 0.000000 0.846988\n" - "vn 0.531611 0.000000 0.846988\n" - "# 6 vertices\n" - "\n" - "# 6 normals\n" - "\n" - "g all\n" - "s 1\n" - "f 1//1 2//2 3//3 4//4\n" - "f 4//4 3//3 5//5 6//6\n" - "# 2 elements\n"; - float v4[4]; - struct obj_desc desc; - struct obj_face face; - struct obj_group group; - struct obj_smooth_group sgroup; - struct obj_mtl mtl; - struct obj_vertex vertex; - FILE* file; - - NCHECK(obj, NULL); - - file = fopen("test_obj_squares.obj", "w"); - NCHECK(file, NULL); - fwrite(squares_obj, sizeof(char), strlen(squares_obj), file); - fclose(file); - CHECK(obj_load(obj, "test_obj_squares.obj"), OBJ_OK); - - CHECK(obj_desc_get(obj, &desc), OBJ_OK); - CHECK(desc.faces_count, 2); - CHECK(desc.groups_count, 1); - CHECK(desc.smooth_groups_count, 1); - CHECK(desc.usemtls_count, 0); - CHECK(desc.mtllibs_count, 0); - - CHECK(obj_face_get(obj, 0, &face), OBJ_OK); - CHECK(face.vertex_id, 0); - CHECK(face.vertices_count, 4); - CHECK(face.group_id, 0); - CHECK(face.smooth_group_id, 0); - CHECK(face.mtl_id, OBJ_ID_NONE); - - CHECK(obj_vertex_get(obj, 0, &vertex), OBJ_OK); - CHECK(f4_eq(vertex.position, f4(v4, 0.f, 2.f, 0.f, 1.f)), 1); - CHECK(f3_eq(vertex.normal, f3(v4, 0.f, 0.f, 1.f)), 1); - CHECK(obj_vertex_get(obj, 1, &vertex), OBJ_OK); - CHECK(f4_eq(vertex.position, f4(v4, 0.f, 0.f, 0.f, 1.f)), 1); - CHECK(f3_eq(vertex.normal, f3(v4, 0.f, 0.f, 1.f)), 1); - CHECK(obj_vertex_get(obj, 2, &vertex), OBJ_OK); - CHECK(f4_eq(vertex.position, f4(v4, 2.f, 0.f, 0.f, 1.f)), 1); - CHECK(f3_eq(vertex.normal, f3(v4, 0.276597f, 0.f, 0.960986f)), 1); - CHECK(obj_vertex_get(obj, 3, &vertex), OBJ_OK); - CHECK(f4_eq(vertex.position, f4(v4, 2.f, 2.f, 0.f, 1.f)), 1); - CHECK(f3_eq(vertex.normal, f3(v4, 0.276597f, 0.f, 0.960986f)), 1); - - CHECK(obj_face_get(obj, 1, &face), OBJ_OK); - CHECK(face.vertex_id, 4); - CHECK(face.vertices_count, 4); - CHECK(face.group_id, 0); - CHECK(face.smooth_group_id, 0); - CHECK(face.mtl_id, OBJ_ID_NONE); - - CHECK(obj_vertex_get(obj, 4, &vertex), OBJ_OK); - CHECK(f4_eq(vertex.position, f4(v4, 2.f, 2.f, 0.f, 1.f)), 1); - CHECK(f3_eq(vertex.normal, f3(v4, 0.276597f, 0.f, 0.960986f)), 1); - CHECK(obj_vertex_get(obj, 5, &vertex), OBJ_OK); - CHECK(f4_eq(vertex.position, f4(v4, 2.f, 0.f, 0.f, 1.f)), 1); - CHECK(f3_eq(vertex.normal, f3(v4, 0.276597f, 0.f, 0.960986f)), 1); - CHECK(obj_vertex_get(obj, 6, &vertex), OBJ_OK); - CHECK(f4_eq(vertex.position, f4(v4, 4.f, 0.f, -1.255298f, 1.f)), 1); - CHECK(f3_eq(vertex.normal, f3(v4, 0.531611f, 0.f, 0.846988f)), 1); - CHECK(obj_vertex_get(obj, 7, &vertex), OBJ_OK); - CHECK(f4_eq(vertex.position, f4(v4, 4.f, 2.f, -1.255298f, 1.f)), 1); - CHECK(f3_eq(vertex.normal, f3(v4, 0.531611f, 0.f, 0.846988f)), 1); - - CHECK(obj_group_get(NULL, OBJ_ID_NONE, NULL), OBJ_BAD_ARGUMENT); - CHECK(obj_group_get(obj, OBJ_ID_NONE, NULL), OBJ_BAD_ARGUMENT); - CHECK(obj_group_get(NULL, 0, NULL), OBJ_BAD_ARGUMENT); - CHECK(obj_group_get(obj, 0, NULL), OBJ_BAD_ARGUMENT); - CHECK(obj_group_get(NULL, OBJ_ID_NONE, &group), OBJ_BAD_ARGUMENT); - CHECK(obj_group_get(obj, OBJ_ID_NONE, &group), OBJ_BAD_ARGUMENT); - CHECK(obj_group_get(NULL, 0, &group), OBJ_BAD_ARGUMENT); - CHECK(obj_group_get(obj, 0, &group), OBJ_OK); - CHECK(strcmp(group.name, "all"), 0); - CHECK(group.face_id, 0); - CHECK(group.faces_count, 2); - - CHECK(obj_smooth_group_get(NULL, OBJ_ID_NONE, NULL), OBJ_BAD_ARGUMENT); - CHECK(obj_smooth_group_get(obj, OBJ_ID_NONE, NULL), OBJ_BAD_ARGUMENT); - CHECK(obj_smooth_group_get(NULL, 0, NULL), OBJ_BAD_ARGUMENT); - CHECK(obj_smooth_group_get(obj, 0, NULL), OBJ_BAD_ARGUMENT); - CHECK(obj_smooth_group_get(NULL, OBJ_ID_NONE, &sgroup), OBJ_BAD_ARGUMENT); - CHECK(obj_smooth_group_get(obj, OBJ_ID_NONE, &sgroup), OBJ_BAD_ARGUMENT); - CHECK(obj_smooth_group_get(NULL, 0, &sgroup), OBJ_BAD_ARGUMENT); - CHECK(obj_smooth_group_get(obj, 0, &sgroup), OBJ_OK); - CHECK(sgroup.is_smoothed, 1); - CHECK(sgroup.face_id, 0); - CHECK(sgroup.faces_count, 2); - - CHECK(obj_mtl_get(obj, 0, &mtl), OBJ_BAD_ARGUMENT); -} - -static void -test_cube(struct obj* obj) -{ - 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"; - const char* group_names[6] = - { "front", "back", "right", "top", "left", "bottom" }; - const char* mtl_names[6] = - { "red", "blue", "green", "gold", "orange", "purple" }; - struct obj_desc desc; - FILE* file; - size_t i; - - NCHECK(obj, NULL); - - file = fopen("test_obj_cube.obj", "w"); - NCHECK(file, NULL); - fwrite(cube_obj, sizeof(char), strlen(cube_obj), file); - fclose(file); - CHECK(obj_load(obj, "test_obj_cube.obj"), OBJ_OK); - - CHECK(obj_desc_get(obj, &desc), OBJ_OK); - CHECK(desc.faces_count, 6); - CHECK(desc.groups_count, 6); - CHECK(desc.smooth_groups_count, 0); - CHECK(desc.usemtls_count, 6); - CHECK(desc.mtllibs_count, 1); - - FOR_EACH(i, 0, 6) { - struct obj_face face; - CHECK(obj_face_get(obj, i, &face), OBJ_OK); - CHECK(face.vertex_id, i*4); - CHECK(face.vertices_count, 4); - CHECK(face.group_id, i); - CHECK(face.smooth_group_id, OBJ_ID_NONE); - CHECK(face.mtl_id, i); - } - - FOR_EACH(i, 0, 6) { - struct obj_group group; - CHECK(obj_group_get(obj, i, &group), OBJ_OK); - CHECK(strcmp(group.name, group_names[i]), 0); - CHECK(group.face_id, i); - CHECK(group.faces_count, 1); - } - - FOR_EACH(i, 0, 6) { - struct obj_mtl mtl; - CHECK(obj_mtl_get(obj, i, &mtl), OBJ_OK); - CHECK(strcmp(mtl.name, mtl_names[i]), 0); - CHECK(mtl.face_id, i); - CHECK(mtl.faces_count, 1); - } -} - -int -main(int argc, char** argv) -{ - struct mem_allocator allocator; - struct obj* obj; - int i; - - 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); - - test_plane(obj); - test_squares(obj); - test_cube(obj); - FOR_EACH(i, 1, argc) - CHECK(obj_load(obj, argv[i]), 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; -} -