star-3d

Surface structuring for efficient 3D geometric queries
git clone git://git.meso-star.fr/star-3d.git
Log | Files | Refs | README | LICENSE

commit 1d08c433b451cc2b6c47526f625fe2e175ae71d1
parent d9e8cabe258130741b8f2103b3efcaf495e579d7
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Wed, 11 Mar 2015 14:55:11 +0100

Refactoring of the shape data management

Diffstat:
Msrc/s3d.h | 28++++++++++++++++++----------
Msrc/s3d_scene.c | 42+++++++++++++++++++++++++++++++-----------
Msrc/s3d_shape.c | 328+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------
Msrc/s3d_shape_c.h | 33++++++++++++++++++++++++++++-----
Msrc/test_s3d_shape.c | 19+++++++++++++++++++
5 files changed, 358 insertions(+), 92 deletions(-)

diff --git a/src/s3d.h b/src/s3d.h @@ -75,16 +75,24 @@ struct s3d_attrib { /* Descriptor of a triangular mesh */ struct s3d_trimesh_desc { - unsigned (*get_tricount)(void*); /* # triangles */ - void (*get_indices) /* Get the 3 vertex indices of the triangle `itri' */ - (const unsigned itri, unsigned ids[3], void*); - void (*get_position) /* Get the 3D position of the vertex `ivert' */ - (const unsigned ivert, float position[3], void*); - void (*get_normal) /* Get the world space normal of `ivert'. May be NULL */ - (const unsigned ivert, float normal[3], void*); - void (*get_uv) /* Get the parametric coordinates of `ivert'. May be NULL */ - (const unsigned ivert, float uv[2], void*); - void* data;/* Pointer to user data; last argument of the previous callbacks */ + /* # triangles. May be NULL if get_indices is NULL */ + unsigned (*get_tricount)(void*); + /* Get the 3 vertex indices of the triangle `itri'. May be NULL if the shape + * was already setuped as a triangular mesh <=> does not update the shape + * indices */ + void (*get_indices)(const unsigned itri, unsigned ids[3], void*); + /* Get the 3D position of the vertex `ivert'. May be NULL if the shape was + * already setuped as a triangular mesh <=> does not update the shape + * coordinates */ + void (*get_position)(const unsigned ivert, float position[3], void*); + /* Get the world space normal of `ivert'. May be NULL <=> does not setup + * normals data to the shape or does not update them */ + void (*get_normal)(const unsigned ivert, float normal[3], void*); + /* Get the parametric coordinates of `ivert'. May be NULL <=> does not setup + * uvs data to the shape or does not update them */ + void (*get_uv)(const unsigned ivert, float uv[2], void*); + /* Pointer to user data; last argument of the previous callbacks */ + void* data; }; static const struct s3d_trimesh_desc S3D_TRIMESH_DESC_NULL = { diff --git a/src/s3d_scene.c b/src/s3d_scene.c @@ -55,20 +55,37 @@ scene_setup(struct s3d_scene* scn) LIST_FOR_EACH(node, &scn->shapes) { struct s3d_shape* shape = CONTAINER_OF (node, struct s3d_shape, scene_attachment); - uint32_t* ids = darray_u32_data_get(&shape->indices); - float* pos = darray_float_data_get(&shape->positions); - const size_t ntris = darray_u32_size_get(&shape->indices) / 3; - const size_t nverts = darray_float_size_get(&shape->positions) / 3; + uint32_t* ids = darray_u32_data_get(&shape->data.mesh.indices); + float* pos = darray_float_data_get(&shape->data.mesh.positions); + const size_t ntris = darray_u32_size_get(&shape->data.mesh.indices)/3; + const size_t nverts = darray_float_size_get(&shape->data.mesh.positions)/3; ASSERT(IS_ALIGNED(ids, 16)); - ASSERT(shape->type == SHAPE_TRIMESH); - shape->rtc_geom = rtcNewTriangleMesh - (scn->rtc_scn, RTC_GEOMETRY_STATIC, ntris, nverts); - rtcSetBuffer(scn->rtc_scn, shape->rtc_geom, RTC_INDEX_BUFFER, ids, 0, - sizeof(uint32_t[3])); - rtcSetBuffer(scn->rtc_scn, shape->rtc_geom, RTC_VERTEX_BUFFER, pos, 0, - sizeof(float[3])); + /* The Embree geometry is no more valid */ + if(shape->data.mesh.resize_mask && shape->rtc_geom!=INVALID_RTC_GEOMETRY) { + rtcDeleteGeometry(scn->rtc_scn, shape->rtc_geom); + shape->rtc_geom = INVALID_RTC_GEOMETRY; + shape->data.mesh.resize_mask = 0; + } + + if(shape->rtc_geom != INVALID_RTC_GEOMETRY) { + /* Update the Embree geometry if required */ + if(shape->data.mesh.update_mask & MESH_INDEX_BUFFER) + rtcUpdateBuffer(scn->rtc_scn, shape->rtc_geom, RTC_INDEX_BUFFER); + if(shape->data.mesh.update_mask & MESH_INDEX_BUFFER) + rtcUpdateBuffer(scn->rtc_scn, shape->rtc_geom, RTC_VERTEX_BUFFER); + shape->data.mesh.update_mask = 0; + } else { + /* Setup a new Embree geometry */ + shape->rtc_geom = rtcNewTriangleMesh + (scn->rtc_scn, RTC_GEOMETRY_STATIC, ntris, nverts); + rtcSetBuffer(scn->rtc_scn, shape->rtc_geom, RTC_INDEX_BUFFER, ids, 0, + sizeof(uint32_t[3])); + rtcSetBuffer(scn->rtc_scn, shape->rtc_geom, RTC_VERTEX_BUFFER, pos, 0, + sizeof(float[3])); + } } + /* Commit the scene updates */ rtcCommit(scn->rtc_scn); } @@ -150,8 +167,11 @@ s3d_scene_attach_shape(struct s3d_scene* scn, struct s3d_shape* shape) { if(!scn || !shape || !is_list_empty(&shape->scene_attachment)) return RES_BAD_ARG; + ASSERT(shape->rtc_scn == NULL); + ASSERT(shape->rtc_geom == INVALID_RTC_GEOMETRY); S3D(shape_ref_get(shape)); list_add_tail(&scn->shapes, &shape->scene_attachment); + shape->rtc_scn = scn->rtc_scn; return RES_OK; } diff --git a/src/s3d_shape.c b/src/s3d_shape.c @@ -39,6 +39,251 @@ /******************************************************************************* * Helper functions ******************************************************************************/ +static FINLINE void +shape_delete_rtc_geometry(struct s3d_shape* shape) +{ + ASSERT(shape && shape->rtc_geom != INVALID_RTC_GEOMETRY && shape->rtc_scn); + rtcDeleteGeometry(shape->rtc_scn, shape->rtc_geom); + shape->rtc_geom = INVALID_RTC_GEOMETRY; +} + +static void +mesh_init(struct mem_allocator* allocator, struct mesh* mesh) +{ + ASSERT(mesh); + darray_u32_init(allocator, &mesh->indices); + darray_float_init(allocator, &mesh->positions); + darray_float_init(allocator, &mesh->normals); + darray_float_init(allocator, &mesh->uvs); + mesh->update_mask = mesh->resize_mask = 0; +} + +static void +mesh_release(struct mesh* mesh) +{ + ASSERT(mesh); + darray_u32_release(&mesh->indices); + darray_float_release(&mesh->positions); + darray_float_release(&mesh->normals); + darray_float_release(&mesh->uvs); +} + +static res_T +mesh_setup_indices + (struct mesh* mesh, + const struct s3d_trimesh_desc* desc, + unsigned* nvertices) +{ + uint32_t* indices; + unsigned itri, ntris, nids, nids_prev, nverts; + res_T res; + ASSERT(mesh && desc && nvertices); + + nids_prev = darray_u32_size_get(&mesh->indices); + if(!desc->get_indices) { + if(!nids_prev) { /* The indices were not previously setuped */ + return RES_BAD_ARG; + } else { /* Keep the previously setuped indices */ + nverts = darray_float_size_get(&mesh->positions); + ASSERT(nverts % 3 == 0); + *nvertices = nverts / 3; + return RES_OK; + } + } + + if(!desc->get_tricount) + return RES_BAD_ARG; + + ntris = desc->get_tricount(desc->data); + if(!ntris) + return RES_BAD_ARG; + + nids = ntris * 3; + if(nids == nids_prev) { + mesh->update_mask |= (MESH_INDEX_BUFFER & !mesh->resize_mask); + } else { + mesh->resize_mask |= MESH_INDEX_BUFFER; + mesh->update_mask &= !MESH_INDEX_BUFFER; + res = darray_u32_resize(&mesh->indices, nids); + if(res != RES_OK) return res; + } + + /* Setup the mesh indices */ + indices = darray_u32_data_get(&mesh->indices); + nverts = 0; + FOR_EACH(itri, 0, ntris) { + uint32_t* ids = indices + itri*3; + int i; + STATIC_ASSERT(sizeof(unsigned) == sizeof(uint32_t), Unexpected_Type); + desc->get_indices(itri, ids, desc->data); + FOR_EACH(i, 0, 3) if(ids[i] >= nverts) nverts = ids[i]; + } + /* Transform nverts from last vertex id to vertices count */ + *nvertices = nverts + 1; + return RES_OK; +} + +static res_T +mesh_setup_positions + (struct mesh* mesh, + const struct s3d_trimesh_desc* desc, + const unsigned nverts) +{ + float* positions; + unsigned i, nverts_prev; + res_T res; + ASSERT(mesh && desc && nverts); + + nverts_prev = darray_float_size_get(&mesh->positions); + ASSERT(nverts_prev % 3 == 0); + nverts_prev /= 3; + + if(!desc->get_position) { + if(!nverts_prev) { /* The vertex positions were not already setuped */ + return RES_BAD_ARG; + } else if(nverts != nverts_prev) { /* Inconsistant descriptor */ + return RES_BAD_ARG; + } else { /* Keep the previous positions */ + return RES_OK; + } + } + + /* Allocate vertex positions */ + if(nverts == nverts_prev) { + mesh->update_mask |= (MESH_VERTEX_BUFFER & mesh->resize_mask); + } else { + mesh->resize_mask |= MESH_VERTEX_BUFFER; + mesh->update_mask &= !MESH_VERTEX_BUFFER; /* The vertices are no more updated */ + res = darray_float_resize(&mesh->positions, nverts*3); + if(res != RES_OK) return res; + } + + /* Setup the vertex positions */ + positions = darray_float_data_get(&mesh->positions); + memset(positions, 0xFF, sizeof(float[3])*nverts); + FOR_EACH(i, 0, darray_u32_size_get(&mesh->indices)) { + union { float f; uint32_t i; } ucast; + const unsigned ivert = darray_u32_cdata_get(&mesh->indices)[i]; + const unsigned ipos = ivert * 3; + + ucast.f = positions[ipos]; + if(ucast.i == 0xFFFFFFFF) /* The vertex was not setuped */ + desc->get_position(ivert, positions + ipos, desc->data); + } + return RES_OK; +} + +static res_T +mesh_setup_normals + (struct mesh* mesh, + const struct s3d_trimesh_desc* desc, + const unsigned nverts) +{ + float* normals; + unsigned i, nverts_prev; + res_T res; + ASSERT(mesh && desc && nverts); + + nverts_prev = darray_float_size_get(&mesh->normals); + ASSERT(nverts_prev % 3 == 0); + nverts_prev /= 3; + + if(!desc->get_normal) { + if(nverts_prev && nverts != nverts_prev) { /* Inconsistant descriptor */ + return RES_BAD_ARG; + } else { /* Keep the previous normals or do not setup them */ + return RES_OK; + } + } + + res = darray_float_resize(&mesh->normals, nverts*3); + if(res != RES_OK) return res; + + /* Setup the vertex normals */ + normals = darray_float_data_get(&mesh->normals); + memset(normals, 0xFF, sizeof(float[3])*nverts); + FOR_EACH(i, 0, darray_u32_size_get(&mesh->indices)) { + union { float f; uint32_t i; } ucast; + const unsigned ivert = darray_u32_cdata_get(&mesh->indices)[i]; + const unsigned inormal = ivert * 3; + + ucast.f = normals[inormal]; + if(ucast.i == 0xFFFFFFFF) /* The normal was not setuped */ + desc->get_normal(ivert, normals + inormal, desc->data); + } + return RES_OK; +} + +static res_T +mesh_setup_uvs + (struct mesh* mesh, + const struct s3d_trimesh_desc* desc, + const unsigned nverts) +{ + float* uvs; + unsigned i, nverts_prev; + res_T res; + ASSERT(mesh && desc && nverts); + + nverts_prev = darray_float_size_get(&mesh->uvs); + ASSERT(nverts_prev % 2 == 0); + nverts_prev /= 2; + + if(!desc->get_uv) { + if(nverts_prev && nverts != nverts_prev) { /* Inconsistant descriptor */ + return RES_BAD_ARG; + } else { /* Keep the previous normals or do not setup them */ + return RES_OK; + } + } + + res = darray_float_resize(&mesh->uvs, nverts*2); + if(res != RES_OK) return res; + + /* Setup the vertex normals */ + uvs = darray_float_data_get(&mesh->uvs); + memset(uvs, 0xFF, sizeof(float[2])*nverts); + FOR_EACH(i, 0, darray_u32_size_get(&mesh->indices)) { + union { float f; uint32_t i; } ucast; + const unsigned ivert = darray_u32_cdata_get(&mesh->indices)[i]; + const unsigned iuv = ivert * 3; + + ucast.f = uvs[iuv]; + if(ucast.i == 0xFFFFFFFF) /* The normal was not setuped */ + desc->get_uv(ivert, uvs + iuv, desc->data); + } + return RES_OK; +} + +static res_T +mesh_setup(struct mesh* mesh, const struct s3d_trimesh_desc* desc) +{ + unsigned nverts; + res_T res = RES_OK; + + res = mesh_setup_indices(mesh, desc, &nverts); + if(res != RES_OK) return res; + res = mesh_setup_positions(mesh, desc, nverts); + if(res != RES_OK) return res; + res = mesh_setup_normals(mesh, desc, nverts); + if(res != RES_OK) return res; + res = mesh_setup_uvs(mesh, desc, nverts); + if(res != RES_OK) return res; + return RES_OK; +} + +static void +shape_release_data(struct s3d_shape* shape) +{ + ASSERT(shape); + switch(shape->type) { + case SHAPE_MESH: mesh_release(&shape->data.mesh); break; + case SHAPE_NONE: /* DO nothing */ break; + default: FATAL("Unreachable code\n"); break; + } + shape->type = SHAPE_NONE; +} + static void shape_release(ref_T* ref) { @@ -49,10 +294,10 @@ shape_release(ref_T* ref) dev = shape->dev; /* The shape should not be attached */ ASSERT(is_list_empty(&shape->scene_attachment)); - darray_u32_release(&shape->indices); - darray_float_release(&shape->positions); - darray_float_release(&shape->normals); - darray_float_release(&shape->uvs); + ASSERT(shape->rtc_scn == NULL); + /* The rtc_geom should be invalid */ + ASSERT(shape->rtc_geom == INVALID_RTC_GEOMETRY); + shape_release_data(shape); MEM_FREE(dev->allocator, shape); S3D(device_ref_put(dev)); } @@ -78,10 +323,7 @@ s3d_shape_create(struct s3d_device* dev, struct s3d_shape** out_shape) } list_init(&shape->scene_attachment); shape->type = SHAPE_NONE; - darray_u32_init(dev->allocator, &shape->indices); - darray_float_init(dev->allocator, &shape->positions); - darray_float_init(dev->allocator, &shape->normals); - darray_float_init(dev->allocator, &shape->uvs); + shape->rtc_geom = INVALID_RTC_GEOMETRY; S3D(device_ref_get(dev)); shape->dev = dev; ref_init(&shape->ref); @@ -119,7 +361,11 @@ s3d_shape_detach(struct s3d_shape* shape) if(!shape) return RES_BAD_ARG; if(is_list_empty(&shape->scene_attachment)) /* The shape is not attached */ return RES_OK; + ASSERT(shape->rtc_scn); + if(shape->rtc_geom != INVALID_RTC_GEOMETRY) + shape_delete_rtc_geometry(shape); list_del(&shape->scene_attachment); + shape->rtc_scn = NULL; S3D(shape_ref_put(shape)); return RES_OK; } @@ -129,76 +375,26 @@ s3d_shape_setup_trimesh (struct s3d_shape* shape, const struct s3d_trimesh_desc* desc) { - uint32_t* indices; - float* positions = NULL; - float* normals = NULL; - float* uvs = NULL; - unsigned itri, ntris, nverts, nids; - size_t i; res_T res = RES_OK; + if(!shape || !desc) { res = RES_BAD_ARG; goto error; } - if(!desc->get_tricount || !desc->get_indices || !desc->get_position) { - res = RES_BAD_ARG; - goto error; - } - - ntris = desc->get_tricount(desc->data); - nids = ntris * 3; - res = darray_u32_resize(&shape->indices, nids); - if(res != RES_OK) goto error; - indices = darray_u32_data_get(&shape->indices); - - /* Get the mesh indices */ - nverts = 0; - FOR_EACH(itri, 0, ntris) { - uint32_t* ids = indices + itri*3; - STATIC_ASSERT(sizeof(unsigned) == sizeof(uint32_t), Unexpected_Type); - desc->get_indices(itri, ids, desc->data); - FOR_EACH(i, 0, 3) - if(ids[i] >= nverts) nverts = ids[i]; + if(shape->type != SHAPE_MESH) { + shape_release_data(shape); + mesh_init(shape->dev->allocator, &shape->data.mesh); + shape->type = SHAPE_MESH; } - ++nverts; /* Transform nverts from last vertex id to vertices count */ - /* Allocate the vertex data */ - res = darray_float_resize(&shape->positions, nverts*3); + res = mesh_setup(&shape->data.mesh, desc); if(res != RES_OK) goto error; - positions = darray_float_data_get(&shape->positions); - if(desc->get_normal) { - res = darray_float_resize(&shape->normals, nverts*3); - if(res != RES_OK) goto error; - normals = darray_float_data_get(&shape->normals); - } - if(desc->get_uv) { - res = darray_float_resize(&shape->uvs, nverts*2); - if(res != RES_OK) goto error; - uvs = darray_float_data_get(&shape->uvs); - } - - /* Setup the vertex data */ - memset(positions, 0xFF, sizeof(float[3])*nverts); - FOR_EACH(i, 0, nids) { - union { float f; uint32_t i; } ucast; - const unsigned ivert = indices[i]; - const unsigned iposition = ivert * 3; - const unsigned inormal = iposition; - const unsigned iuv = indices[i] * 2; - - ucast.f = positions[iposition]; - if(ucast.i != 0xFFFFFFFF) /* The vertex was already setuped */ - continue; - - desc->get_position(ivert, positions + iposition, desc->data); - if(normals) desc->get_normal(ivert, normals + inormal, desc->data); - if(uvs) desc->get_uv(ivert, uvs + iuv, desc->data); - } - shape->type = SHAPE_TRIMESH; exit: return res; error: + if(shape && desc) + shape_release_data(shape); goto exit; } diff --git a/src/s3d_shape_c.h b/src/s3d_shape_c.h @@ -38,22 +38,45 @@ #include <rsys/list.h> #include <rsys/ref_count.h> +#include <embree2/rtcore.h> + +#include <limits.h> + +enum mesh_buffer { + MESH_INDEX_BUFFER = BIT(0), + MESH_VERTEX_BUFFER = BIT(1) +}; + enum shape_type { - SHAPE_TRIMESH, + SHAPE_MESH, SHAPE_SCENE, SHAPE_TYPES_COUNT__, SHAPE_NONE = SHAPE_TYPES_COUNT__ }; +struct mesh { /* Triangular mesh */ + darray_u32 indices; + darray_float positions; /* list of 3 floats */ + darray_float normals; /* list of 3 floats */ + darray_float uvs; /* list of 2 floats */ + + /* Combination of shape_buffer */ + int update_mask; /* Define which shape buffers were updated */ + int resize_mask; /* Define which shape buffers were [re]allocateod */ +}; + +#define INVALID_RTC_GEOMETRY UINT_MAX + struct s3d_shape { struct list_node scene_attachment; enum shape_type type; unsigned rtc_geom; /* Embree geometry id */ + RTCScene rtc_scn; /* The RTC scene from which the rtc_geom was created */ - darray_u32 indices; - darray_float positions; /* list of 3 floats */ - darray_float normals; /* list of 3 floats */ - darray_float uvs; /* list of 2 floats */ + union { + struct s3d_scene* scene; + struct mesh mesh; + } data; struct s3d_device* dev; ref_T ref; diff --git a/src/test_s3d_shape.c b/src/test_s3d_shape.c @@ -71,6 +71,25 @@ main(int argc, char** argv) trimesh.get_uv = cbox_get_uv; CHECK(s3d_shape_setup_trimesh(shape, &trimesh), RES_OK); + trimesh = S3D_TRIMESH_DESC_NULL; + trimesh.get_tricount = cbox_get_ntris; + trimesh.get_indices = cbox_get_ids; + CHECK(s3d_shape_setup_trimesh(shape, &trimesh), RES_OK); + trimesh.get_tricount = NULL; + trimesh.get_indices = NULL; + trimesh.get_position = cbox_get_position; + CHECK(s3d_shape_setup_trimesh(shape, &trimesh), RES_OK); + trimesh.get_position = NULL; + trimesh.get_normal = cbox_get_normal; + CHECK(s3d_shape_setup_trimesh(shape, &trimesh), RES_OK); + trimesh.get_normal = NULL; + trimesh.get_uv = cbox_get_uv; + CHECK(s3d_shape_setup_trimesh(shape, &trimesh), RES_OK); + + trimesh = S3D_TRIMESH_DESC_NULL; + trimesh.get_indices = cbox_get_ids; + CHECK(s3d_shape_setup_trimesh(shape, &trimesh), RES_BAD_ARG); + CHECK(s3d_shape_ref_get(NULL), RES_BAD_ARG); CHECK(s3d_shape_ref_get(shape), RES_OK); CHECK(s3d_shape_ref_put(NULL), RES_BAD_ARG);