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:
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);