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:
| M | src/s3daw.c | | | 376 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| M | src/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)) {