star-3daw

Create star-3d geometries from OBJ files
git clone git://git.meso-star.fr/star-3daw.git
Log | Files | Refs | README | LICENSE

commit 35594b78e23cd2efe675d0dc1df6305723eac601
parent 6ecfe2b77b4b1467c2dee0d8e8c596160130f999
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Tue, 26 May 2015 16:03:00 +0200

Implement and test the s3daw_load function

Diffstat:
Msrc/s3daw.c | 376+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/test_s3daw.c | 160++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 535 insertions(+), 1 deletion(-)

diff --git a/src/s3daw.c b/src/s3daw.c @@ -26,14 +26,62 @@ * The fact that you are presently reading this means that you have had * knowledge of the CeCILL license and that you accept its terms. */ +#define _POSIX_C_SOURCE 200112L /* dirname support */ + #include "s3daw.h" +#include <rsys/dynamic_array_char.h> +#include <rsys/dynamic_array_size_t.h> +#include <rsys/float3.h> +#include <rsys/float4.h> +#include <rsys/hash_table.h> #include <rsys/logger.h> #include <rsys/mem_allocator.h> #include <rsys/ref_count.h> + #include <star/s3d.h> + #include <aw.h> #include <polygon.h> +#include <string.h> +#include <libgen.h> /* dirname */ + +#define DARRAY_NAME shape +#define DARRAY_DATA struct s3d_shape* +#include <rsys/dynamic_array.h> + +#define DARRAY_NAME vertex +#define DARRAY_DATA struct aw_obj_vertex +#include <rsys/dynamic_array.h> + +static FINLINE char +vertex_eq(const struct aw_obj_vertex* v0, const struct aw_obj_vertex* v1) +{ + return f4_eq_eps(v0->position, v1->position, 1.e-6f) + && f3_eq_eps(v0->normal, v1->normal, 1.e-6f) + && f3_eq_eps(v0->texcoord, v1->texcoord, 1.e-6f); +} + +#define HTABLE_NAME vertex_id +#define HTABLE_DATA uint64_t +#define HTABLE_KEY struct aw_obj_vertex +#define HTABLE_KEY_FUNCTOR_EQ vertex_eq +#include <rsys/hash_table.h> + +#define HTABLE_NAME material +#define HTABLE_DATA struct aw_material +#define HTABLE_KEY struct str +#define HTABLE_KEY_FUNCTOR_EQ str_eq +#define HTABLE_KEY_FUNCTOR_HASH str_hash +#define HTABLE_KEY_FUNCTOR_INIT str_init +#define HTABLE_KEY_FUNCTOR_COPY str_copy +#define HTABLE_KEY_FUNCTOR_COPY_AND_RELEASE str_copy_and_release +#define HTABLE_KEY_FUNCTOR_RELEASE str_release +#define HTABLE_DATA_FUNCTOR_INIT aw_material_init +#define HTABLE_DATA_FUNCTOR_COPY aw_material_copy +#define HTABLE_DATA_FUNCTOR_COPY_AND_RELEASE aw_material_copy_and_release +#define HTABME_DATA_FUNCTOR_RELEASE aw_material_release +#include <rsys/hash_table.h> struct s3daw { ref_T ref; @@ -44,12 +92,269 @@ struct s3daw { struct aw_mtl* loader_mtl; struct polygon* polygon; struct s3d_device* s3d; + struct darray_shape shapes; + + /* Scratch data structure used during shape registration */ + struct aw_obj_named_group mtl; /* Material of primitive soup */ + struct darray_size_t indices; /* Triangle indices */ + struct darray_vertex vertices; /* float[3], vertex coordinates */ + struct darray_char mtllib; /* Temp buffer for the mtllib filename */ + struct htable_vertex_id vertices_ids; /* Map a aw_obj_vertex to an id */ + struct htable_material materials; /* Map a material name to an aw_material */ }; /******************************************************************************* * Helper functions ******************************************************************************/ static void +get_indices(const unsigned itri, unsigned ids[3], void* ctx) +{ + struct s3daw* s3daw = ctx; + const size_t* indices; + ASSERT(ids && ctx && itri < darray_size_t_size_get(&s3daw->indices)/3); + indices = darray_size_t_cdata_get(&s3daw->indices) + itri * 3; + ids[0] = (unsigned)indices[0]; + ids[1] = (unsigned)indices[1]; + ids[2] = (unsigned)indices[2]; +} + +static void +get_position(const unsigned ivert, float pos[3], void* ctx) +{ + struct s3daw* s3daw = ctx; + const struct aw_obj_vertex* vertex; + ASSERT(ctx && pos && ivert < darray_vertex_size_get(&s3daw->vertices)); + vertex = darray_vertex_cdata_get(&s3daw->vertices) + ivert; + f3_set(pos, vertex->position); +} + +static INLINE res_T +get_dirname(const char* filename, struct darray_char* filedir) +{ + const char* dir; + size_t dir_len; + res_T res = RES_OK; + ASSERT(filename && filedir); + + res = darray_char_resize(filedir, strlen(filename) + 1/*'\0'*/); + if(res != RES_OK) goto error; + + strcpy(darray_char_data_get(filedir), filename); + dir = dirname(darray_char_data_get(filedir)); + dir_len = strlen(dir); + + res = darray_char_resize(filedir, dir_len + 1/*'/'*/); + if(res != RES_OK) goto error; + + memmove(darray_char_data_get(filedir), dir, dir_len); + darray_char_data_get(filedir)[dir_len] = '/'; + +exit: + return res; +error: + darray_char_clear(filedir); + goto exit; +} + +static res_T +shape_register(struct s3daw* s3daw, const struct aw_obj_named_group* obj_mtl) +{ + struct s3d_vertex_data vertex_data[2]; + struct s3d_shape* shape = NULL; + size_t iface; + size_t ntris, nverts; + + res_T res = RES_OK; + ASSERT(s3daw && obj_mtl); + + /* Reset the scrach shape data */ + darray_vertex_clear(&s3daw->vertices); + darray_size_t_clear(&s3daw->indices); + htable_vertex_id_clear(&s3daw->vertices_ids); + + FOR_EACH(iface, obj_mtl->face_id, obj_mtl->face_id + obj_mtl->faces_count) { + struct aw_obj_face face; + struct aw_obj_vertex vertex; + const uint32_t* tri_ids; + uint32_t ntri_ids; + size_t ivertex; + size_t i; + + AW(obj_face_get(s3daw->loader_obj, iface, &face)); + + /* Triangulate the face */ + POLYGON(clear(s3daw->polygon)); + FOR_EACH(ivertex, face.vertex_id, face.vertex_id + face.vertices_count) { + struct aw_obj_vertex vertex; + AW(obj_vertex_get(s3daw->loader_obj, ivertex, &vertex)); + res = polygon_vertex_add(s3daw->polygon, vertex.position); + if(res != RES_OK) goto error; + } + res = polygon_triangulate(s3daw->polygon, &tri_ids, &ntri_ids); + if(res != RES_OK) goto error; + + FOR_EACH(i, 0, ntri_ids) { + size_t* ivertex_registered; + + /* Define if the obj_face vertex is already registered */ + ivertex = tri_ids[i] + face.vertex_id; + AW(obj_vertex_get(s3daw->loader_obj, ivertex, &vertex)); + ivertex_registered = htable_vertex_id_find(&s3daw->vertices_ids, &vertex); + + if(ivertex_registered) { + /* Vertex is registered. Simply add its id to the indices */ + res = darray_size_t_push_back(&s3daw->indices, ivertex_registered); + if(res != RES_OK) goto error; + } else { + /* Vertex is not registered. Register it and add its id to the indices */ + const size_t ivertex_new = darray_vertex_size_get(&s3daw->vertices); + res = darray_vertex_push_back(&s3daw->vertices, &vertex); + if(res != RES_OK) goto error; + res = darray_size_t_push_back(&s3daw->indices, &ivertex_new); + if(res != RES_OK) goto error; + res = htable_vertex_id_set(&s3daw->vertices_ids, &vertex, &ivertex_new); + if(res != RES_OK) goto error; + } + } + } + + /* Create the S3D shape */ + + res = s3d_shape_create_mesh(s3daw->s3d, &shape); + if(res != RES_OK) goto error; + + nverts = darray_vertex_size_get(&s3daw->vertices); + ntris = darray_size_t_size_get(&s3daw->indices); + ASSERT(ntris); + ntris /= 3; + + vertex_data[0].usage = S3D_POSITION; + vertex_data[0].type = S3D_FLOAT3; + vertex_data[0].get = get_position; + vertex_data[1] = S3D_VERTEX_DATA_NULL; + + res = s3d_mesh_setup_indexed_vertices + (shape, (unsigned)ntris, get_indices, (unsigned)nverts, vertex_data, s3daw); + if(res != RES_OK) goto error; + + res = darray_shape_push_back(&s3daw->shapes, &shape); + if(res != RES_OK) goto error; + +exit: + return res; +error: + if(shape) S3D(shape_ref_put(shape)); + goto exit; +} + +static void +materials_clear(struct htable_material* materials) +{ + struct htable_material_iterator it, end; + ASSERT(materials); + + htable_material_begin(materials, &it); + htable_material_end(materials, &end); + while(!htable_material_iterator_eq(&it, &end)) { + struct aw_material* material = htable_material_iterator_data_get(&it); + htable_material_iterator_next(&it); + AW(material_release(material)); + } + htable_material_clear(materials); +} + +static INLINE void +shapes_clear(struct darray_shape* shapes) +{ + size_t i; + ASSERT(shapes); + FOR_EACH(i, 0, darray_shape_size_get(shapes)) { + S3D(shape_ref_put(darray_shape_data_get(shapes)[i])); + } + darray_shape_clear(shapes); +} + +static res_T +shapes_create(struct s3daw* s3daw, const char* filename) +{ + struct aw_obj_desc obj_desc; + size_t imtl; + res_T res = RES_OK; + ASSERT(s3daw && filename); + + AW(obj_desc_get(s3daw->loader_obj, &obj_desc)); + shapes_clear(&s3daw->shapes); + + if(!obj_desc.usemtls_count) { /* No material grouping => triangle soup */ + str_clear(&s3daw->mtl.name); + s3daw->mtl.face_id = 0; + s3daw->mtl.faces_count = obj_desc.faces_count; + res = shape_register(s3daw, &s3daw->mtl); + if(res != RES_OK) goto error; + } else { + size_t imtllib; + size_t dirname_len; + + /* Setup the `mtllib' buffer with the directory of the filename */ + res = get_dirname(filename, &s3daw->mtllib); + if(res != RES_OK) goto error; + dirname_len = darray_char_size_get(&s3daw->mtllib); + + /* Load the materials */ + FOR_EACH(imtllib, 0, obj_desc.mtllibs_count) { + const char* mtllib; + size_t mtllib_len; + size_t nmtls; + + AW(obj_mtllib_get(s3daw->loader_obj, imtllib, &mtllib)); + + /* Append the mtllib name to the directory stored into the `mtllib' */ + mtllib_len = strlen(mtllib); + res = darray_char_resize(&s3daw->mtllib, mtllib_len + 1/*'\0'*/); + if(res != RES_OK) goto error; + strncpy(darray_char_data_get(&s3daw->mtllib) + dirname_len + 1, + mtllib, mtllib_len + 1); + + /* Load the material library */ + res = aw_mtl_load(s3daw->loader_mtl, darray_char_cdata_get(&s3daw->mtllib)); + if(res != RES_OK && res != RES_IO_ERR/*The mtl lib may not be found*/) + goto error; + + /* Register the materials of the material library */ + AW(mtl_materials_count_get(s3daw->loader_mtl, &nmtls)); + FOR_EACH(imtl, 0, nmtls) { + struct aw_material material; + AW(material_init(NULL, &material)); + AW(mtl_material_get(s3daw->loader_mtl, imtl, &material)); + res = htable_material_set(&s3daw->materials, &material.name, &material); + if(res != RES_OK) goto error; + } + } + + /* Create the S3D shapes */ + FOR_EACH(imtl, 0, obj_desc.usemtls_count) { + const struct aw_material* material; + AW(obj_mtl_get(s3daw->loader_obj, imtl, &s3daw->mtl)); + material = htable_material_find(&s3daw->materials, &s3daw->mtl.name); + if(!material) { /* Some material may not be loaded */ + logger_print(s3daw->logger, LOG_WARNING, + "The material `%s' is not loaded\n", str_cget(&s3daw->mtl.name)); + str_clear(&s3daw->mtl.name); + } + res = shape_register(s3daw, &s3daw->mtl); + if(res != RES_OK) goto error; + } + } + +exit: + return res; +error: + shapes_clear(&s3daw->shapes); + materials_clear(&s3daw->materials); + goto exit; +} + +static void release_s3daw(ref_T* ref) { struct s3daw* s3daw = CONTAINER_OF(ref, struct s3daw, ref); @@ -60,6 +365,18 @@ release_s3daw(ref_T* ref) if(s3daw->polygon) POLYGON(ref_put(s3daw->polygon)); if(s3daw->s3d) S3D(device_ref_put(s3daw->s3d)); + shapes_clear(&s3daw->shapes); + materials_clear(&s3daw->materials); + + darray_shape_release(&s3daw->shapes); + + AW(obj_named_group_release(&s3daw->mtl)); + darray_size_t_release(&s3daw->indices); + darray_vertex_release(&s3daw->vertices); + darray_char_release(&s3daw->mtllib); + htable_vertex_id_release(&s3daw->vertices_ids); + htable_material_release(&s3daw->materials); + MEM_RM(s3daw->allocator, s3daw); } @@ -95,6 +412,13 @@ s3daw_create s3daw->logger = logger ? logger : LOGGER_DEFAULT; S3D(device_ref_get(s3d)); s3daw->s3d = s3d; + darray_shape_init(s3daw->allocator, &s3daw->shapes); + AW(obj_named_group_init(allocator, &s3daw->mtl)); + darray_size_t_init(s3daw->allocator, &s3daw->indices); + darray_vertex_init(s3daw->allocator, &s3daw->vertices); + darray_char_init(s3daw->allocator, &s3daw->mtllib); + htable_vertex_id_init(s3daw->allocator, &s3daw->vertices_ids); + htable_material_init(s3daw->allocator, &s3daw->materials); res = polygon_create(s3daw->allocator, &s3daw->polygon); if(res != RES_OK) goto error; @@ -151,3 +475,55 @@ s3daw_get_loaders(struct s3daw* s3daw, struct aw_obj** obj, struct aw_mtl** mtl) return RES_OK; } +res_T +s3daw_load(struct s3daw* s3daw, const char* filename) +{ + res_T res; + + if(!s3daw || !filename) return RES_BAD_ARG; + + res = aw_obj_load(s3daw->loader_obj, filename); + if(res != RES_OK) return res; + return shapes_create(s3daw, filename); +} + +res_T +s3daw_clear(struct s3daw* s3daw) +{ + if(!s3daw) return RES_BAD_ARG; + + /* Clear intermediary data structures */ + POLYGON(clear(s3daw->polygon)); + + /* Clear shape */ + shapes_clear(&s3daw->shapes); + + /* Clear scratch data structures */ + darray_size_t_clear(&s3daw->indices); + darray_vertex_clear(&s3daw->vertices); + darray_char_clear(&s3daw->mtllib); + htable_vertex_id_clear(&s3daw->vertices_ids); + + AW(obj_clear(s3daw->loader_obj)); + AW(mtl_clear(s3daw->loader_mtl)); + + return RES_OK; +} + +res_T +s3daw_get_shapes_count(const struct s3daw* s3daw, size_t* nshapes) +{ + if(!s3daw || !nshapes) return RES_BAD_ARG; + *nshapes = darray_shape_size_get(&s3daw->shapes); + return RES_OK; +} + +res_T +s3daw_get_shape(struct s3daw* s3daw, const size_t ishape, struct s3d_shape** shape) +{ + if(!s3daw || !shape || ishape >= darray_shape_size_get(&s3daw->shapes)) + return RES_BAD_ARG; + *shape = darray_shape_data_get(&s3daw->shapes)[ishape]; + return RES_OK; +} + diff --git a/src/test_s3daw.c b/src/test_s3daw.c @@ -32,6 +32,163 @@ #include <rsys/mem_allocator.h> #include <star/s3d.h> +static void +test_cbox(struct s3daw* s3daw) +{ + const char* cbox_obj = + "mtllib cbox.mtl cbox2.mtl\n" + "v -1.01 0 0.99\n" + "v 1 0 0.99\n" + "v 1 0 -1.04\n" + "v -0.99 0 -1.04\n" + "g floor\n" + "usemtl floor\n" + "f -4 -3 -2 -1\n" + + "v -1.02 1.99 0.99\n" + "v -1.02 1.99 -1.04\n" + "v 1 1.99 -1.04\n" + "v 1 1.99 0.99\n" + "g ceiling\n" + "usemtl ceiling\n" + "f -4 -3 -2 -1\n" + + "v -0.99 0 -1.04\n" + "v 1 0 -1.04\n" + "v 1 1.99 -1.04\n" + "v -1.02 1.99 -1.04\n" + "g back\n" + "usemtl back\n" + "f -4 -3 -2 -1\n" + + "v 1 0 -1.04\n" + "v 1 0 0.99\n" + "v 1 1.99 0.99\n" + "v 1 1.99 -1.04\n" + "g right\n" + "usemtl right\n" + "f -4 -3 -2 -1\n" + + "v -1.01 0 0.99\n" + "v -0.99 0 -1.04\n" + "v -1.02 1.99 -1.04\n" + "v -1.02 1.99 0.99\n" + "g left\n" + "usemtl left\n" + "f -4 -3 -2 -1\n"; + + const char* cbox_mtl = + "newmtl left\n" + "Ns 10\n" + "Ni 1.5\n" + "illum 2\n" + "Ka 0.63 0.065 0.05\n" + "Kd 0.63 0.065 0.05\n" + "Ks 0 0 0\n" + "Ke 0 0 0\n" + + "newmtl right\n" + "Ns 10\n" + "Ni 1.5\n" + "illum 2\n" + "Ka 0.14 0.45 0.091\n" + "Kd 0.14 0.45 0.091\n" + "Ks 0 0 0\n" + "Ke 0 0 0\n" + + "newmtl floor\n" + "Ns 10\n" + "Ni 1\n" + "illum 2\n" + "Ka 0.725 0.71 0.68\n" + "Kd 0.725 0.71 0.68\n" + "Ks 0 0 0\n" + "Ke 0 0 0\n" + + "newmtl ceiling\n" + "Ns 10\n" + "Ni 1\n" + "illum 2\n" + "Ka 0.725 0.71 0.68\n" + "Kd 0.725 0.71 0.68\n" + "Ks 0 0 0\n" + "Ke 0 0 0\n"; + + const char* cbox2_mtl = + "newmtl back\n" + "Ns 10\n" + "Ni 1\n" + "illum 2\n" + "Ka 0.725 0.71 0.68\n" + "Kd 0.725 0.71 0.68\n" + "Ks 0 0 0\n" + "Ke 0 0 0\n"; + + FILE* file; + struct s3d_shape* shape; + size_t ishape, nshapes; + + ASSERT(s3daw); + + file = fopen("cbox.obj", "w"); + + NCHECK(file, NULL); + fwrite(cbox_obj, sizeof(char), strlen(cbox_obj), file); + fclose(file); + + file = fopen("cbox.mtl", "w"); + NCHECK(file, NULL); + fwrite(cbox_mtl, sizeof(char), strlen(cbox_mtl), file); + fclose(file); + + remove("cbox2.mtl"); + + CHECK(s3daw_load(NULL, NULL), RES_BAD_ARG); + CHECK(s3daw_load(s3daw, NULL), RES_BAD_ARG); + CHECK(s3daw_load(NULL, "cbox.obj"), RES_BAD_ARG); + CHECK(s3daw_load(s3daw, "cbox.obj__"), RES_IO_ERR); + CHECK(s3daw_load(s3daw, "cbox.obj"), RES_OK); + + file = fopen("cbox2.mtl", "w"); + NCHECK(file, NULL); + fwrite(cbox2_mtl, sizeof(char), strlen(cbox2_mtl), file); + fclose(file); + + CHECK(s3daw_load(s3daw, "cbox.obj"), RES_OK); + + CHECK(s3daw_get_shapes_count(NULL, NULL), RES_BAD_ARG); + CHECK(s3daw_get_shapes_count(s3daw, NULL), RES_BAD_ARG); + CHECK(s3daw_get_shapes_count(NULL, &nshapes), RES_BAD_ARG); + CHECK(s3daw_get_shapes_count(s3daw, &nshapes), RES_OK); + CHECK(nshapes, 5); + + CHECK(s3daw_get_shape(NULL, 0, NULL), RES_BAD_ARG); + CHECK(s3daw_get_shape(s3daw, 0, NULL), RES_BAD_ARG); + CHECK(s3daw_get_shape(NULL, 0, &shape), RES_BAD_ARG); + FOR_EACH(ishape, 0, nshapes) + CHECK(s3daw_get_shape(s3daw, 0, &shape), RES_OK); + CHECK(s3daw_get_shape(s3daw, nshapes, &shape), RES_BAD_ARG); + + CHECK(s3daw_clear(NULL), RES_BAD_ARG); + CHECK(s3daw_clear(s3daw), RES_OK); + CHECK(s3daw_get_shapes_count(s3daw, &nshapes), RES_OK); + CHECK(nshapes, 0); + CHECK(s3daw_get_shape(s3daw, 0, &shape), RES_BAD_ARG); + + file = fopen("cbox.obj", "r"); + NCHECK(file, NULL); + +#if 0 + CHECK(s3daw_load_stream(s3daw, file), RES_OK); + CHECK(s3daw_shapes_count_get(s3daw, &nshapes), RES_OK); + CHECK(nshapes, 5); + FOR_EACH(ishape, 0, nshapes) + CHECK(s3daw_shape_get(s3daw, 0, &shape), RES_OK); +#endif + + fclose(file); +} + int main(int argc, char** argv) { @@ -75,8 +232,9 @@ main(int argc, char** argv) CHECK(s3daw_get_loaders(NULL, &aw_obj, &aw_mtl), RES_BAD_ARG); CHECK(s3daw_get_loaders(s3daw, &aw_obj, &aw_mtl), RES_OK); - CHECK(s3daw_ref_put(s3daw), RES_OK); + test_cbox(s3daw); + CHECK(s3daw_ref_put(s3daw), RES_OK); CHECK(s3d_device_ref_put(s3d), RES_OK); if(MEM_ALLOCATED_SIZE(&allocator_proxy)) {