commit 35b39239804a82cf3d209c75c339a35930666b88
parent f3aeaf14e109339207e4069ec8c78bd146ebc76f
Author: Vincent Forest <vincent.forest@meso-star.com>
Date: Wed, 25 Mar 2015 10:23:11 +0100
Begin a major refactoring of the internal management of the shape data
Currently the API is no more thread safe
Diffstat:
12 files changed, 846 insertions(+), 597 deletions(-)
diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt
@@ -59,8 +59,8 @@ set(VERSION_MINOR 0)
set(VERSION_PATCH 0)
set(VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH})
-set(S3D_FILES_SRC s3d_device.c s3d_scene.c s3d_shape.c)
-set(S3D_FILES_INC s3d.h s3d_device_c.h s3d_shape_c.h)
+set(S3D_FILES_SRC s3d_device.c s3d_mesh.c s3d_scene.c s3d_shape.c)
+set(S3D_FILES_INC s3d.h s3d_buffer.h s3d_device_c.h s3d_mesh.h s3d_shape_c.h)
# Prepend each file in the `S3D_FILES_<SRC|INC>' list by `S3D_SOURCE_DIR'
rcmake_prepend_path(S3D_FILES_SRC ${S3D_SOURCE_DIR})
diff --git a/src/s3d.h b/src/s3d.h
@@ -68,7 +68,7 @@ enum s3d_attrib_usage {
S3D_ATTRIBS_COUNT__,
/* Unormalized world space face normal. Outward orientation defined with
* respect to the Counter Clock Wise vertex ordering */
- S3D_GEOMETRY_NORMAL
+ S3D_GEOMETRY_NORMAL
};
enum s3d_type {
@@ -83,6 +83,13 @@ enum s3d_transform_space {
S3D_WORLD_TRANSFORM
};
+struct s3d_primitive {
+ /* Internal data */
+ void* ptr;
+ unsigned iprim;
+ unsigned igeom;
+};
+
struct s3d_attrib {
float value[4];
enum s3d_type type;
@@ -109,8 +116,7 @@ static const struct s3d_vertex_data S3D_VERTEX_DATA_NULL =
/* Intersection point */
struct s3d_hit {
- struct s3d_shape* shape; /* Hit shape */
- unsigned iprim[2]; /* Indentifiers of the intersected primitive */
+ struct s3d_primitive prim; /* Intersected primitive */
float normal[3]; /* Unormalized geometry normal */
float uv[2]; /* Barycentric coordinates of the hit onto `iprim' */
float distance; /* Hit distance from the ray origin */
@@ -118,15 +124,16 @@ struct s3d_hit {
/* Constant defining a NULL intersection. Should be used to initialize a hit */
static const struct s3d_hit S3D_HIT_NULL =
-{NULL, {(unsigned)-1, (unsigned)-1}, {0.f,0.f,0.f}, {0.f,0.f}, FLT_MAX};
+{{NULL, (unsigned)-1, (unsigned)-1}, {0.f,0.f,0.f}, {0.f,0.f}, FLT_MAX};
/* Helper macro that defines whether or not the hit is valid, i.e. the ray
* intersects a shape or not */
-#define S3D_HIT_NONE(Hit) ((Hit)->shape == NULL)
+#define S3D_HIT_NONE(Hit) ((Hit)->distance >= FLT_MAX)
/* Forward declaration of s3d opaque data types */
struct s3d_device; /* Entry point of the library */
struct s3d_scene; /* Collection of shapes */
+struct s3d_rt_session; /* Ray tracing session */
struct s3d_shape; /* Untyped geometry */
/* Forward declaration of external data types */
@@ -193,7 +200,14 @@ s3d_scene_instantiate
S3D_API res_T
s3d_scene_attach_shape
(struct s3d_scene* scn,
- struct s3d_shape* shp);
+ struct s3d_shape* shape);
+
+/* Remove the shape from the scene. After its detachment, the scene
+ * release its reference on the shape */
+S3D_API res_T
+s3d_scene_detach_shape
+ (struct s3d_scene* scn,
+ struct s3d_shape* shape);
/* Detach all the shapes from the scene and release the reference that the
* scene takes onto them */
@@ -250,29 +264,21 @@ s3d_shape_is_attached
(struct s3d_shape* shape,
char* is_attached);
-/* Remove the shape from the scene on which it is attached. No error is
- * reported if the shape is not attached to a scene. After its detachment, the
- * scene release its reference on the shape */
-S3D_API res_T
-s3d_shape_detach
- (struct s3d_shape* shape);
-
/* Sample the shape with respect to 3 uniform random variables */
S3D_API res_T
s3d_shape_sample
(struct s3d_shape* shape,
/* Uniform random variables in [0, 1) */
const float u, const float v, const float w,
- unsigned iprim[2], /* Sampled primitive */
- float uv[2]); /* Sampled barycentric coordinate onto `iprim' */
+ struct s3d_primitive* primitive, /* Sampled primitive */
+ float uv[2]); /* Sampled barycentric coordinate onto `primitive' */
/* Retrieve the attribute of the shape prim `iprim' at the barycentric
* coordinates `uv' */
S3D_API res_T
-s3d_shape_get_attrib
- (struct s3d_shape* shape,
+s3d_primitive_get_attrib
+ (struct s3d_primitive* prim,
const enum s3d_attrib_usage attr, /* Attribute to retrieve */
- const unsigned iprim[2], /* Id of the primitive on which `attr' is retrieved */
const float uv[2], /* Barycentric coordinates of `attr' on `iprim' */
struct s3d_attrib* attrib); /* Resulting attrib */
diff --git a/src/s3d_buffer.h b/src/s3d_buffer.h
@@ -0,0 +1,113 @@
+/* Copyright (C) |Meso|Star> 2015 (contact@meso-star.com)
+ *
+ * This software is a computer program whose purpose is to describe a
+ * virtual 3D environment that can be ray-traced and sampled both robustly
+ * and efficiently.
+ *
+ * This software is governed by the CeCILL license under French law and
+ * abiding by the rules of distribution of free software. You can use,
+ * modify and/or redistribute the software under the terms of the CeCILL
+ * license as circulated by CEA, CNRS and INRIA at the following URL
+ * "http://www.cecill.info".
+ *
+ * As a counterpart to the access to the source code and rights to copy,
+ * modify and redistribute granted by the license, users are provided only
+ * with a limited warranty and the software's author, the holder of the
+ * economic rights, and the successive licensors have only limited
+ * liability.
+ *
+ * In this respect, the user's attention is drawn to the risks associated
+ * with loading, using, modifying and/or developing or reproducing the
+ * software by the user in light of its specific status of free software,
+ * that may mean that it is complicated to manipulate, and that also
+ * therefore means that it is reserved for developers and experienced
+ * professionals having in-depth computer knowledge. Users are therefore
+ * encouraged to load and test the software's suitability as regards their
+ * requirements in conditions enabling the security of their systems and/or
+ * data to be ensured and, more generally, to use and operate it in the
+ * same conditions as regards security.
+ *
+ * The fact that you are presently reading this means that you have had
+ * knowledge of the CeCILL license and that you accept its terms. */
+
+#if !defined(BUFFER_NAME) && !defined(BUFFER_DARRAY)
+
+#ifndef S3D_BUFFER_H
+#define S3D_BUFFER_H
+
+#include <rsys/mem_allocator.h>
+#include <rsys/ref_count.h>
+
+#endif /* S3D_BUFFER_H */
+#else
+/*
+ * Generate the buffer type with respect to the following macros:
+ * - BUFFER_NAME: name of the structure and prefix of the functions;
+ * - BUFFER_DARRAY: type of the dynamic array of the buffer;
+ */
+#if !defined(BUFFER_NAME) || !defined(BUFFER_DARRAY)
+ #error "Missing macro definition"
+#endif
+
+#define BUFFER_FUNC__(Func) CONCAT(CONCAT(BUFFER_NAME, _), Func)
+#define BUFFER_DARRAY_FUNC__(Func) CONCAT(CONCAT(BUFFER_DARRAY, _), Func)
+
+struct BUFFER_NAME {
+ BUFFER_DARRAY data;
+ struct mem_allocator* allocator;
+ ref_T ref;
+};
+
+/*******************************************************************************
+ * Helper function
+ ******************************************************************************/
+static INLINE void
+BUFFER_FUNC__(release__)(ref_T* ref)
+{
+ struct BUFFER_NAME* buffer;
+ ASSERT(ref);
+ buffer = CONTAINER_OF(ref, struct BUFFER_NAME, ref);
+ BUFFER_DARRAY_FUNC__(release)(&buffer->data);
+ MEM_FREE(buffer->allocator, buffer);
+}
+
+/*******************************************************************************
+ * Buffer function
+ ******************************************************************************/
+static INLINE res_T
+BUFFER_FUNC__(create)
+ (struct mem_allocator* allocator,
+ struct BUFFER_NAME** out_buffer)
+{
+ struct BUFFER_NAME* buffer;
+ ASSERT(allocator && out_buffer);
+
+ buffer = (struct BUFFER_NAME*)MEM_CALLOC
+ (allocator, 1, sizeof(struct BUFFER_NAME));
+ if(!buffer) return RES_MEM_ERR;
+ BUFFER_DARRAY_FUNC__(init)(allocator, &buffer->data);
+ buffer->allocator = allocator;
+ ref_init(&buffer->ref);
+ *out_buffer = buffer;
+ return RES_OK;
+}
+
+static INLINE void
+BUFFER_FUNC__(ref_get)(struct BUFFER_NAME* buffer)
+{
+ ASSERT(buffer);
+ ref_get(&buffer->ref);
+}
+
+static INLINE void
+BUFFER_FUNC__(ref_put)(struct BUFFER_NAME* buffer)
+{
+ ASSERT(buffer);
+ ref_put(&buffer->ref, BUFFER_FUNC__(release__));
+}
+
+#undef BUFFER_NAME
+#undef BUFFER_DARRAY
+
+#endif /* !BUFFER_NAME || !BUFFER_DARRAY */
+
diff --git a/src/s3d_c.h b/src/s3d_c.h
@@ -33,7 +33,9 @@
#ifndef S3D_C_H
#define S3D_C_H
-#include <embree2/rtcore.h>
+#include "s3d.h"
+#include "s3d_backend.h"
+
#include <rsys/rsys.h>
static FINLINE res_T
@@ -41,7 +43,7 @@ rtc_error_to_res_T(const enum RTCError err)
{
switch(err) {
case RTC_NO_ERROR: return RES_OK;
- case RTC_UNSUPPORTED_ERROR: return RES_UNKNOWN_ERR;
+ case RTC_UNKNOWN_ERROR: return RES_UNKNOWN_ERR;
case RTC_INVALID_ARGUMENT: return RES_BAD_ARG;
case RTC_INVALID_OPERATION: return RES_BAD_ARG;
case RTC_OUT_OF_MEMORY: return RES_MEM_ERR;
@@ -50,5 +52,18 @@ rtc_error_to_res_T(const enum RTCError err)
}
}
+static INLINE unsigned
+s3d_type_get_dimension(const enum s3d_type type)
+{
+ switch(type) {
+ case S3D_FLOAT: return 1;
+ case S3D_FLOAT2: return 2;
+ case S3D_FLOAT3: return 3;
+ case S3D_FLOAT4: return 4;
+ default: FATAL("Unreachable code\n"); break;
+ }
+ return 0;
+}
+
#endif /* S3D_C_H */
diff --git a/src/s3d_mesh.c b/src/s3d_mesh.c
@@ -0,0 +1,357 @@
+/* Copyright (C) |Meso|Star> 2015 (contact@meso-star.com)
+ *
+ * This software is a computer program whose purpose is to describe a
+ * virtual 3D environment that can be ray-traced and sampled both robustly
+ * and efficiently.
+ *
+ * This software is governed by the CeCILL license under French law and
+ * abiding by the rules of distribution of free software. You can use,
+ * modify and/or redistribute the software under the terms of the CeCILL
+ * license as circulated by CEA, CNRS and INRIA at the following URL
+ * "http://www.cecill.info".
+ *
+ * As a counterpart to the access to the source code and rights to copy,
+ * modify and redistribute granted by the license, users are provided only
+ * with a limited warranty and the software's author, the holder of the
+ * economic rights, and the successive licensors have only limited
+ * liability.
+ *
+ * In this respect, the user's attention is drawn to the risks associated
+ * with loading, using, modifying and/or developing or reproducing the
+ * software by the user in light of its specific status of free software,
+ * that may mean that it is complicated to manipulate, and that also
+ * therefore means that it is reserved for developers and experienced
+ * professionals having in-depth computer knowledge. Users are therefore
+ * encouraged to load and test the software's suitability as regards their
+ * requirements in conditions enabling the security of their systems and/or
+ * data to be ensured and, more generally, to use and operate it in the
+ * same conditions as regards security.
+ *
+ * The fact that you are presently reading this means that you have had
+ * knowledge of the CeCILL license and that you accept its terms. */
+
+#include "s3d_c.h"
+#include "s3d_mesh.h"
+
+/*******************************************************************************
+ * Helper functions
+ ******************************************************************************/
+static void
+mesh_setup_indices
+ (struct mesh* mesh,
+ const unsigned ntris,
+ void (*get_indices)(const unsigned itri, unsigned ids[3], void*),
+ const unsigned nverts,
+ void* data)
+{
+ uint32_t* indices;
+ unsigned itri;
+ unsigned ntris_prev;
+ unsigned nverts_new;
+ res_T res;
+ ASSERT(mesh && ntris && nverts);
+
+ ntris_prev = (unsigned)mesh_get_ntris(mesh);
+ ASSERT(get_indices != S3D_KEEP || ntris == ntris_prev);
+
+ if(get_indices == S3D_KEEP)
+ return;
+
+ if(ntris == ntris_prev) {
+ mesh->update_mask |= (INDEX_BUFFER & !mesh->resize_mask);
+ } else {
+ mesh->resize_mask |= INDEX_BUFFER;
+ mesh->update_mask &= !INDEX_BUFFER;
+ }
+
+ if(mesh->indices) { /* Release the old index buffer */
+ index_buffer_ref_put(mesh->indices);
+ mesh->indices = NULL;
+ }
+
+ /* Allocate the new index buffer */
+ res = index_buffer_create(mesh->allocator, &mesh->indices);
+ if(res != RES_OK) FATAL("Unsufficient memory\n");
+ res = darray_u32_resize(&mesh->indices->data, ntris * 3/*# triangle ids*/);
+ if(res != RES_OK) FATAL("Unsufficient memory\n");
+
+ /* Setup the mesh indices */
+ indices = mesh_get_ids(mesh);
+ nverts_new = 0;
+ FOR_EACH(itri, 0, ntris) {
+ uint32_t* ids = indices + itri*3;
+ int i;
+ STATIC_ASSERT(sizeof(unsigned) == sizeof(uint32_t), Unexpected_Type);
+ get_indices(itri, ids, data);
+ FOR_EACH(i, 0, 3) nverts_new = MMAX(nverts_new, ids[i]);
+ }
+ /* Transform nverts from the last vertex id to vertices count */
+ ++nverts_new;
+ if(nverts_new > nverts)
+ FATAL("Out of bound indexation\n");
+}
+
+static void
+mesh_setup_positions
+ (struct mesh* mesh,
+ const unsigned nverts,
+ struct s3d_vertex_data* attr,
+ void* data)
+{
+ float* positions;
+ unsigned ivert, nverts_prev;
+ res_T res;
+ ASSERT(mesh && nverts && attr && attr->usage == S3D_POSITION);
+
+ if(attr->get == S3D_KEEP) {
+ ASSERT(mesh->attribs[S3D_POSITION]);
+ ASSERT(darray_float_size_get(&mesh->attribs[S3D_POSITION]->data) == nverts*3);
+ return;
+ }
+
+ nverts_prev = (unsigned)mesh_get_nverts(mesh);
+ if(nverts == nverts_prev) {
+ mesh->update_mask |= (VERTEX_BUFFER & !mesh->resize_mask);
+ } else {
+ mesh->resize_mask |= VERTEX_BUFFER;
+ mesh->update_mask &= !VERTEX_BUFFER;
+ }
+
+ if(mesh->attribs[S3D_POSITION]) { /* Release the old vertex buffer */
+ vertex_buffer_ref_put(mesh->attribs[S3D_POSITION]);
+ mesh->attribs[S3D_POSITION] = NULL;
+ }
+
+ /* Allocate vertex positions */
+ res = vertex_buffer_create(mesh->allocator, &mesh->attribs[S3D_POSITION]);
+ if(res != RES_OK) FATAL("Insufficient memory\n");
+ res = darray_float_resize(&mesh->attribs[S3D_POSITION]->data, nverts*3);
+ if(res != RES_OK) FATAL("Insufficient memory\n");
+ mesh->attribs_type[S3D_POSITION] = S3D_FLOAT3;
+
+ /* Setup the vertex positions */
+ positions = darray_float_data_get(&mesh->attribs[S3D_POSITION]->data);
+ if(attr->type == S3D_FLOAT3) {
+ FOR_EACH(ivert, 0, nverts) {
+ attr->get(ivert, positions + ivert*3, data);
+ }
+ } else {
+ FOR_EACH(ivert, 0, nverts) {
+ float pos[4];
+ unsigned ipos = ivert * 3;
+ attr->get(ivert, pos, data);
+ switch(attr->type) {
+ case S3D_FLOAT:
+ positions[ipos + 0] = pos[0];
+ positions[ipos + 1] = 0.f;
+ positions[ipos + 2] = 0.f;
+ break;
+ case S3D_FLOAT2:
+ positions[ipos + 0] = pos[0];
+ positions[ipos + 1] = pos[1];
+ positions[ipos + 2] = 0.f;
+ break;
+ case S3D_FLOAT4: /* Homogeneous coordinates */
+ positions[ipos + 0] = pos[0] / pos[3];
+ positions[ipos + 1] = pos[1] / pos[3];
+ positions[ipos + 2] = pos[2] / pos[3];
+ break;
+ default: FATAL("Unreachable code\n"); break;
+ }
+ }
+ }
+}
+
+static void
+mesh_setup_attribs
+ (struct mesh* mesh,
+ const unsigned nverts,
+ const struct s3d_vertex_data* attr,
+ void* data)
+{
+ float* attr_data;
+ unsigned dim;
+ unsigned ivert;
+ res_T res;
+ ASSERT(mesh && nverts && attr);
+ ASSERT(attr->usage >= S3D_ATTRIB_0 && attr->usage < S3D_ATTRIBS_COUNT__);
+
+ dim = s3d_type_get_dimension(attr->type);
+ if(attr->get == S3D_KEEP) {
+ ASSERT(mesh->attribs_type[attr->usage] == attr->type);
+ ASSERT(mesh->attribs[attr->usage]);
+ ASSERT(darray_float_size_get(&mesh->attribs[attr->usage]->data) == nverts*dim);
+ return;
+ }
+
+ if(mesh->attribs[attr->usage]) { /* Release the previous vertex buffer */
+ vertex_buffer_ref_put(mesh->attribs[attr->usage]);
+ mesh->attribs[attr->usage] = NULL;
+ }
+
+ /* Allocate the new vertex buffer */
+ res = vertex_buffer_create(mesh->allocator, &mesh->attribs[attr->usage]);
+ if(res != RES_OK) FATAL("Insufficient memory\n");
+ res = darray_float_resize(&mesh->attribs[attr->usage]->data, nverts * dim);
+ if(res != RES_OK) FATAL("Insufficient memory\n");
+ mesh->attribs_type[attr->usage] = attr->type;
+
+ /* Setup the vertex attrib */
+ attr_data = darray_float_data_get(&mesh->attribs[attr->usage]->data);
+ FOR_EACH(ivert, 0, nverts) {
+ attr->get(ivert, attr_data, data);
+ attr_data += dim;
+ }
+}
+
+/*******************************************************************************
+ * Local functions
+ ******************************************************************************/
+void
+mesh_init(struct mem_allocator* allocator, struct mesh* mesh)
+{
+ ASSERT(allocator && mesh);
+ memset(mesh, 0, sizeof(struct mesh));
+ mesh->allocator = allocator;
+ mesh->rtc_geom = RTC_INVALID_GEOMETRY_ID;
+}
+
+void
+mesh_clear(struct mesh* mesh)
+{
+ size_t iattr;
+ ASSERT(mesh);
+ if(mesh->indices) {
+ index_buffer_ref_put(mesh->indices);
+ mesh->indices = NULL;
+ }
+ FOR_EACH(iattr, 0, S3D_ATTRIBS_COUNT__) {
+ if(mesh->attribs[iattr]) {
+ vertex_buffer_ref_put(mesh->attribs[iattr]);
+ mesh->attribs[iattr] = NULL;
+ }
+ }
+}
+
+size_t
+mesh_get_ntris(const struct mesh* mesh)
+{
+ size_t nids;
+ ASSERT(mesh);
+ if(!mesh->indices)
+ return 0;
+ nids = darray_u32_size_get(&mesh->indices->data);
+ ASSERT(nids % 3 == 0); /* Only triangular meshes are supported */
+ return nids / 3;
+}
+
+size_t
+mesh_get_nverts(const struct mesh* mesh)
+{
+ size_t ncoords;
+ ASSERT(mesh);
+ if(!mesh->attribs[S3D_POSITION])
+ return 0;
+
+ ASSERT(mesh->attribs_type[S3D_POSITION] == S3D_FLOAT3);
+ ncoords = darray_float_size_get(&mesh->attribs[S3D_POSITION]->data);
+ ASSERT(ncoords % 3 == 0);
+ return ncoords / 3;
+}
+
+uint32_t*
+mesh_get_ids(struct mesh* mesh)
+{
+ ASSERT(mesh && mesh->indices);
+ return darray_u32_data_get(&mesh->indices->data);
+}
+
+float*
+mesh_get_pos(struct mesh* mesh)
+{
+ ASSERT(mesh && mesh->attribs[S3D_POSITION]);
+ ASSERT(mesh->attribs_type[S3D_POSITION] == S3D_FLOAT3);
+ return darray_float_data_get(&mesh->attribs[S3D_POSITION]->data);
+}
+
+res_T
+mesh_setup_indexed_vertices
+ (struct mesh* mesh,
+ const unsigned ntris,
+ void (*get_indices)(const unsigned itri, unsigned ids[3], void* ctx),
+ const unsigned nverts,
+ struct s3d_vertex_data attribs[],
+ void* data)
+{
+ unsigned iattr;
+ char has_position = 0;
+ res_T res = RES_OK;
+
+ if(!ntris || !nverts || !attribs) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ /* Check indices description */
+ if(get_indices == S3D_KEEP) {
+ if(!mesh->indices) { /* No indice was previously set */
+ res = RES_BAD_ARG;
+ goto error;
+ } else {
+ const size_t nids_prev = darray_u32_size_get(&mesh->indices->data);
+ const size_t ntris_prev = nids_prev / 3;
+ if(ntris_prev != ntris) { /* Inconsistant data */
+ res = RES_BAD_ARG;
+ goto error;
+ }
+ }
+ }
+
+ /* Check the vertex data description */
+ iattr = 0;
+ has_position = 0;
+ while(attribs[iattr].usage != S3D_ATTRIBS_COUNT__) {
+ if(attribs[iattr].get == S3D_KEEP) {
+ const enum s3d_attrib_usage attr_usage = attribs[iattr].usage;
+ const enum s3d_type type = attribs[iattr].type;
+ if(!mesh->attribs[attr_usage]) { /* The vertex attrib was no set */
+ res = RES_BAD_ARG;
+ goto error;
+ } else {
+ const enum s3d_type type_prev = mesh->attribs_type[attr_usage];
+ const struct darray_float* attr = &mesh->attribs[attr_usage]->data;
+ size_t nverts_prev = darray_float_size_get(attr);
+ nverts_prev /= s3d_type_get_dimension(type_prev);
+ if(type_prev != type || nverts_prev != nverts) { /* Inconsistant data */
+ res = RES_BAD_ARG;
+ goto error;
+ }
+ }
+ }
+ if(attribs[iattr].usage == S3D_POSITION)
+ has_position = 1;
+ ++iattr;
+ }
+
+ if(!has_position) { /* The vertex must have a position */
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ mesh_setup_indices(mesh, ntris, get_indices, nverts, data);
+
+ /* Setup vertex data */
+ for(iattr = 0; attribs[iattr].usage != S3D_ATTRIBS_COUNT__; ++iattr) {
+ if(attribs[iattr].usage == S3D_POSITION) {
+ mesh_setup_positions(mesh, nverts, attribs + iattr, data);
+ } else {
+ mesh_setup_attribs(mesh, nverts, attribs + iattr, data);
+ }
+ }
+
+exit:
+ return res;
+error:
+ goto exit;
+}
+
diff --git a/src/s3d_mesh.h b/src/s3d_mesh.h
@@ -0,0 +1,107 @@
+/* Copyright (C) |Meso|Star> 2015 (contact@meso-star.com)
+ *
+ * This software is a computer program whose purpose is to describe a
+ * virtual 3D environment that can be ray-traced and sampled both robustly
+ * and efficiently.
+ *
+ * This software is governed by the CeCILL license under French law and
+ * abiding by the rules of distribution of free software. You can use,
+ * modify and/or redistribute the software under the terms of the CeCILL
+ * license as circulated by CEA, CNRS and INRIA at the following URL
+ * "http://www.cecill.info".
+ *
+ * As a counterpart to the access to the source code and rights to copy,
+ * modify and redistribute granted by the license, users are provided only
+ * with a limited warranty and the software's author, the holder of the
+ * economic rights, and the successive licensors have only limited
+ * liability.
+ *
+ * In this respect, the user's attention is drawn to the risks associated
+ * with loading, using, modifying and/or developing or reproducing the
+ * software by the user in light of its specific status of free software,
+ * that may mean that it is complicated to manipulate, and that also
+ * therefore means that it is reserved for developers and experienced
+ * professionals having in-depth computer knowledge. Users are therefore
+ * encouraged to load and test the software's suitability as regards their
+ * requirements in conditions enabling the security of their systems and/or
+ * data to be ensured and, more generally, to use and operate it in the
+ * same conditions as regards security.
+ *
+ * The fact that you are presently reading this means that you have had
+ * knowledge of the CeCILL license and that you accept its terms. */
+
+#include "s3d.h"
+
+#include "s3d_buffer.h"
+
+#include <rsys/dynamic_array_u32.h>
+#include <rsys/dynamic_array_float.h>
+#include <rsys/ref_count.h>
+
+#ifndef S3D_MESH_H
+#define S3D_MESH_H
+
+/* Generate the index buffer data type */
+#define BUFFER_NAME index_buffer
+#define BUFFER_DARRAY darray_u32
+#include "s3d_buffer.h"
+
+/* Generate the vertex buffer data type */
+#define BUFFER_NAME vertex_buffer
+#define BUFFER_DARRAY darray_float
+#include "s3d_buffer.h"
+
+enum buffer_type {
+ INDEX_BUFFER = BIT(0),
+ VERTEX_BUFFER = BIT(1)
+};
+
+struct mesh { /* Triangular mesh */
+ struct index_buffer* indices;
+ struct vertex_buffer* attribs[S3D_ATTRIBS_COUNT__];
+ enum s3d_type attribs_type[S3D_ATTRIBS_COUNT__];
+
+ int resize_mask; /* Combination of buffer_type */
+ int update_mask; /* Combination of buffer_type */
+
+ unsigned rtc_geom; /* Embree geometry */
+
+ struct mem_allocator* allocator;
+};
+
+extern LOCAL_SYM void
+mesh_init
+ (struct mem_allocator* allocator,
+ struct mesh* mesh);
+
+extern LOCAL_SYM void
+mesh_clear
+ (struct mesh* mesh);
+
+extern LOCAL_SYM size_t
+mesh_get_ntris
+ (const struct mesh* mesh);
+
+extern LOCAL_SYM size_t
+mesh_get_nverts
+ (const struct mesh* mesh);
+
+extern LOCAL_SYM uint32_t*
+mesh_get_ids
+ (struct mesh* mesh);
+
+extern LOCAL_SYM float*
+mesh_get_pos
+ (struct mesh* mesh);
+
+extern LOCAL_SYM res_T
+mesh_setup_indexed_vertices
+ (struct mesh* mesh,
+ const unsigned ntris,
+ void (*get_indices)(const unsigned itri, unsigned ids[3], void* ctx),
+ const unsigned nverts,
+ struct s3d_vertex_data attribs[],
+ void* data);
+
+#endif /* S3D_MESH_H */
+
diff --git a/src/s3d_scene.c b/src/s3d_scene.c
@@ -43,102 +43,160 @@
/*******************************************************************************
* Helper functions
******************************************************************************/
-static INLINE void
-delete_rtc_geometry(struct s3d_scene* scn, struct s3d_shape* shape)
-{
- ASSERT(scn && shape && shape->rtc_geom != RTC_INVALID_GEOMETRY_ID);
- rtcDeleteGeometry(scn->rtc_scn, shape->rtc_geom);
- darray_geom2shape_data_get(&scn->geom2shape)[shape->rtc_geom] = NULL;
- shape->rtc_geom = RTC_INVALID_GEOMETRY_ID;
-}
-
-/* Map the Embree geometry to its s3d shape */
-static INLINE res_T
-shape_register_rtc_geom(struct s3d_scene* scn, struct s3d_shape* shape)
+static res_T
+scene_create_mesh_rtc_geometry(struct s3d_scene* scn, struct mesh* mesh)
{
- ASSERT(scn && shape && shape->rtc_geom != RTC_INVALID_GEOMETRY_ID);
+ ASSERT(scn && mesh && mesh->rtc_geom == RTC_INVALID_GEOMETRY_ID);
+ mesh->rtc_geom = rtcNewTriangleMesh(scn->rtc_scn, RTC_GEOMETRY_DYNAMIC,
+ mesh_get_ntris(mesh), mesh_get_nverts(mesh));
- /* Map the Embree geometry to its shape */
- if(shape->rtc_geom >= darray_geom2shape_size_get(&scn->geom2shape)) {
- res_T res = darray_geom2shape_resize(&scn->geom2shape, shape->rtc_geom+1);
- if(res != RES_OK) return res;
+ /* Map the Embree geometry to its scene mesh */
+ if(mesh->rtc_geom >= darray_geom2mesh_size_get(&scn->geom2mesh)) {
+ res_T res = darray_geom2mesh_resize(&scn->geom2mesh, mesh->rtc_geom + 1);
+ if(res != RES_OK) {
+ rtcDeleteGeometry(scn->rtc_scn, mesh->rtc_geom);
+ mesh->rtc_geom = RTC_INVALID_GEOMETRY_ID;
+ return res;
+ } else {
+ /* Check that no other mesh is mapped to this Embree geometry */
+ ASSERT(!darray_geom2mesh_data_get(&scn->geom2mesh)[mesh->rtc_geom]);
+ }
+ darray_geom2mesh_data_get(&scn->geom2mesh)[mesh->rtc_geom] = mesh;
+ scn->is_rtc_scn_outdated = 1;
}
- darray_geom2shape_data_get(&scn->geom2shape)[shape->rtc_geom] = shape;
return RES_OK;
}
+static void
+scene_delete_mesh_rtc_geometry(struct s3d_scene* scn, struct mesh* mesh)
+{
+ ASSERT(scn && mesh && mesh->rtc_geom != RTC_INVALID_GEOMETRY_ID);
+ ASSERT(darray_geom2mesh_size_get(&scn->geom2mesh) > mesh->rtc_geom);
+ darray_geom2mesh_data_get(&scn->geom2mesh)[mesh->rtc_geom] = NULL;
+ rtcDeleteGeometry(scn->rtc_scn, mesh->rtc_geom);
+ mesh->rtc_geom = RTC_INVALID_GEOMETRY_ID;
+ scn->is_rtc_scn_outdated = 1;
+}
+
static res_T
-shape_mesh_setup(struct s3d_scene* scn, struct s3d_shape* shape)
+scene_setup_shape_mesh
+ (struct s3d_scene* scn,
+ struct s3d_shape* shape)
{
- uint32_t* ids;
- float* pos;
- size_t ntris, nverts;
- res_T res;
- ASSERT(scn && shape && shape->type == SHAPE_MESH);
+ struct mesh** pmesh = NULL;
+ struct mesh* mesh = NULL;
+ size_t iattr;
+ char upd_pos, upd_ids;
+
+ res_T res = RES_OK;
+ ASSERT(shape && shape->type == SHAPE_MESH);
- ids = darray_u32_data_get(&shape->data.mesh.indices);
- pos = darray_float_data_get(&shape->data.mesh.attribs[S3D_POSITION]);
- ntris = darray_u32_size_get(&shape->data.mesh.indices)/3;
- nverts = darray_float_size_get(&shape->data.mesh.attribs[S3D_POSITION])/3;
+ pmesh = htable_mesh_find(&scn->meshes, &shape);
+ /* Create the scene mesh of the shape if necessary */
+ if(pmesh) {
+ mesh = *pmesh;
+ } else {
+ mesh = (struct mesh*)MEM_ALLOC(scn->dev->allocator, sizeof(struct mesh));
+ if(!mesh) {
+ res = RES_MEM_ERR;
+ goto error;
+ }
+ mesh_init(scn->dev->allocator, mesh);
+ res = htable_mesh_set(&scn->meshes, &shape, &mesh);
+ if(res != RES_OK) goto error;
+ }
- mutex_rw_rlock(shape->lock); /* Prevent concurrent shape update */
+ /* Discard the shape mesh that is not geometrically valid */
+ if(!shape->data.mesh.indices || !shape->data.mesh.attribs[S3D_POSITION]) {
+ if(mesh->rtc_geom != RTC_INVALID_GEOMETRY_ID)
+ scene_delete_mesh_rtc_geometry(scn, mesh);
+ mesh_clear(mesh);
+ goto exit;
+ }
- /* The Embree geometry is no more valid */
- if(shape->data.mesh.resize_mask
- && shape->rtc_geom != RTC_INVALID_GEOMETRY_ID) {
- rtcDeleteGeometry(scn->rtc_scn, shape->rtc_geom);
- shape->rtc_geom = RTC_INVALID_GEOMETRY_ID;
- shape->data.mesh.resize_mask = 0;
+ /* Define which Embree geometry buffer must be updated */
+ upd_ids = mesh->indices != shape->data.mesh.indices
+ || ((shape->data.mesh.update_mask & INDEX_BUFFER) != 0);
+ upd_pos = mesh->attribs[S3D_POSITION] != shape->data.mesh.attribs[S3D_POSITION]
+ || ((shape->data.mesh.update_mask == VERTEX_BUFFER) != 0);
+
+ /* Get a reference onto the shape mesh indices */
+ if(mesh->indices != shape->data.mesh.indices) {
+ if(mesh->indices) {
+ index_buffer_ref_put(mesh->indices);
+ mesh->indices = NULL;
+ }
+ ASSERT(shape->data.mesh.indices);
+ index_buffer_ref_get(shape->data.mesh.indices);
+ mesh->indices = shape->data.mesh.indices;
}
- if(shape->rtc_geom != RTC_INVALID_GEOMETRY_ID) {
- /* 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_VERTEX_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]));
-
- /* Map the Embree geometry to its shape */
- res = shape_register_rtc_geom(scn, shape);
- if(res != RES_OK) {
- mutex_rw_unlock(shape->lock);
- return res;
+
+ /* Get the reference onto the shape mesh attribs */
+ FOR_EACH(iattr, 0, S3D_ATTRIBS_COUNT__) {
+ if(mesh->attribs[iattr] == shape->data.mesh.attribs[iattr])
+ continue;
+
+ if(mesh->attribs[iattr]) {
+ vertex_buffer_ref_put(mesh->attribs[iattr]);
+ mesh->attribs[iattr] = NULL;
}
+ if(!shape->data.mesh.attribs[iattr])
+ continue;
+
+ vertex_buffer_ref_get(shape->data.mesh.attribs[iattr]);
+ mesh->attribs[iattr] = shape->data.mesh.attribs[iattr];
+ mesh->attribs_type[iattr] = shape->data.mesh.attribs_type[iattr];
}
- mutex_rw_unlock(shape->lock);
- return RES_OK;
-}
+ /* The shape mesh was resize => the Embree geometry is no more valid */
+ if(shape->data.mesh.resize_mask && mesh->rtc_geom!=RTC_INVALID_GEOMETRY_ID) {
+ scene_delete_mesh_rtc_geometry(scn, mesh);
+ }
-static res_T
-scene_setup(struct s3d_scene* scn);
+ /* Create the Embree geometry of the scene mesh */
+ if(mesh->rtc_geom == RTC_INVALID_GEOMETRY_ID) {
+ res = scene_create_mesh_rtc_geometry(scn, mesh);
+ if(res != RES_OK) goto error;
+ }
+ if(upd_pos) { /* Update the Embree vertex buffer if necessary */
+ rtcSetBuffer(scn->rtc_scn, mesh->rtc_geom, RTC_VERTEX_BUFFER,
+ mesh_get_pos(mesh), 0, sizeof(float[3]));
+ rtcUpdateBuffer(scn->rtc_scn, mesh->rtc_geom, RTC_VERTEX_BUFFER);
+ scn->is_rtc_scn_outdated = 1;
+ }
+ if(upd_ids) { /* Update the Embree index buffer if necessary */
+ rtcSetBuffer(scn->rtc_scn, mesh->rtc_geom, RTC_INDEX_BUFFER,
+ mesh_get_ids(mesh), 0, sizeof(uint32_t[3]));
+ rtcUpdateBuffer(scn->rtc_scn, mesh->rtc_geom, RTC_INDEX_BUFFER);
+ scn->is_rtc_scn_outdated = 1;
+ }
+
+ /* Flush the shape mesh states */
+ shape->data.mesh.resize_mask = 0;
+ shape->data.mesh.update_mask = 0;
+
+exit:
+ return res;
+error:
+ goto exit;
+}
+
+#if 0
static res_T
shape_instance_setup(struct s3d_scene* scn, struct s3d_shape* shape)
{
res_T res;
ASSERT(scn && shape && shape->type == SHAPE_INSTANCE);
-
/* Recursuvely update the scene */
S3D(scene_build(shape->data.instance.scene));
- mutex_rw_rlock(shape->lock);
-
if(shape->rtc_geom == RTC_INVALID_GEOMETRY_ID) {
shape->rtc_geom = rtcNewInstance
(scn->rtc_scn, shape->data.instance.scene->rtc_scn);
res = shape_register_rtc_geom(scn, shape);
if(res != RES_OK) {
- mutex_rw_unlock(shape->lock);
return res;
}
}
@@ -150,58 +208,26 @@ shape_instance_setup(struct s3d_scene* scn, struct s3d_shape* shape)
shape->data.instance.transform);
shape->data.instance.update_transform = 1;
}
-
- mutex_rw_unlock(shape->lock);
return RES_OK;
}
-
-res_T
-scene_setup(struct s3d_scene* scn)
-{
- struct list_node* node;
- res_T res = RES_OK;
- ASSERT(scn);
-
- LIST_FOR_EACH(node, &scn->shapes) {
- struct s3d_shape* shape = CONTAINER_OF
- (node, struct s3d_shape, scene_attachment);
- switch(shape->type) {
- case SHAPE_INSTANCE:
- res = shape_instance_setup(scn, shape);
- break;
- case SHAPE_MESH:
- res = shape_mesh_setup(scn, shape);
- break;
- default: FATAL("Unreachable code\n"); break;
- }
- if(res != RES_OK)
- goto error;
- }
-
-exit:
- return res;
-error:
- LIST_FOR_EACH(node, &scn->shapes) {
- struct s3d_shape* shape = CONTAINER_OF
- (node, struct s3d_shape, scene_attachment);
- if(shape->rtc_geom != RTC_INVALID_GEOMETRY_ID)
- delete_rtc_geometry(scn, shape);
- }
- goto exit;
-}
+#endif
static INLINE void
-scene_remove_shape_unsafe(struct s3d_scene* scn, struct s3d_shape* shape)
+scene_remove_shape(struct s3d_scene* scn, struct s3d_shape* shape)
{
- mutex_rw_wlock(shape->lock);
- ASSERT(shape->scn == scn);
- if(is_list_empty(&shape->scene_attachment)) /* No more attached */
- return;
- if(shape->rtc_geom != RTC_INVALID_GEOMETRY_ID)
- delete_rtc_geometry(scn, shape);
+ struct mesh** pmesh;
+ ASSERT(scn && shape && !is_list_empty(&shape->scene_attachment));
+
+ pmesh = htable_mesh_find(&scn->meshes, &shape);
+ if(pmesh) { /* The shape mesh is registered into the scene */
+ struct mesh* mesh = *pmesh;
+ if(mesh->rtc_geom != RTC_INVALID_GEOMETRY_ID) /* Rm the Embree geometry */
+ scene_delete_mesh_rtc_geometry(scn, mesh);
+ mesh_clear(mesh); /* Release the mesh buffers */
+ MEM_FREE(scn->dev->allocator, mesh);
+ htable_mesh_erase(&scn->meshes, &shape);
+ }
list_del(&shape->scene_attachment);
- shape->scn = NULL;
- mutex_rw_unlock(shape->lock);
S3D(shape_ref_put(shape));
}
@@ -215,44 +241,13 @@ scene_release(ref_T* ref)
S3D(scene_clear(scn));
dev = scn->dev;
if(scn->rtc_scn) rtcDeleteScene(scn->rtc_scn);
- if(scn->lock) mutex_destroy(scn->lock);
- if(scn->lock_rtc) mutex_rw_destroy(scn->lock_rtc);
- darray_geom2shape_release(&scn->geom2shape);
+ htable_mesh_release(&scn->meshes);
+ darray_geom2mesh_release(&scn->geom2mesh);
MEM_FREE(dev->allocator, scn);
S3D(device_ref_put(dev));
}
/*******************************************************************************
- * Local function
- ******************************************************************************/
-struct s3d_shape*
-scene_shape_from_rtc_geom(struct s3d_scene* scn, const unsigned igeom)
-{
- struct s3d_shape* shape = NULL;
- res_T res = RES_OK;
- ASSERT(scn);
-
- mutex_lock(scn->lock);
- if(darray_geom2shape_size_get(&scn->geom2shape) <= igeom)
- goto error;
- shape = darray_geom2shape_data_get(&scn->geom2shape)[igeom];
-error:
- mutex_unlock(scn->lock);
- return shape;
-exit:
- goto error;
-}
-
-void
-scene_remove_shape(struct s3d_scene* scn, struct s3d_shape* shape)
-{
- ASSERT(scn);
- mutex_lock(scn->lock);
- scene_remove_shape_unsafe(scn, shape);
- mutex_unlock(scn->lock);
-}
-
-/*******************************************************************************
* Exported s3d_scene functions
******************************************************************************/
res_T
@@ -271,12 +266,12 @@ s3d_scene_create(struct s3d_device* dev, struct s3d_scene** out_scn)
res = RES_MEM_ERR;
goto error;
}
- darray_geom2shape_init(dev->allocator, &scn->geom2shape);
list_init(&scn->shapes);
+ htable_mesh_init(dev->allocator, &scn->meshes);
+ darray_geom2mesh_init(dev->allocator, &scn->geom2mesh);
ref_init(&scn->ref);
S3D(device_ref_get(dev));
scn->dev = dev;
- scn->is_outdated = 0;
scn->rtc_scn = rtcNewScene
(RTC_SCENE_DYNAMIC | RTC_SCENE_INCOHERENT,
RTC_INTERSECT1 | RTC_INTERSECT4);
@@ -284,16 +279,6 @@ s3d_scene_create(struct s3d_device* dev, struct s3d_scene** out_scn)
res = RES_MEM_ERR;
goto error;
}
- scn->lock = mutex_create();
- if(!scn->lock) {
- res = RES_MEM_ERR;
- goto error;
- }
- scn->lock_rtc = mutex_rw_create();
- if(!scn->lock_rtc) {
- res = RES_MEM_ERR;
- goto error;
- }
/* Commit empty scene => the scene can be ray traced without any build */
rtcCommit(scn->rtc_scn);
@@ -352,34 +337,37 @@ s3d_scene_attach_shape(struct s3d_scene* scn, struct s3d_shape* shape)
{
if(!scn || !shape)
return RES_BAD_ARG;
-
- /* Prevent the scene_<clear|build|remove_shape> operations */
- mutex_lock(scn->lock);
- /* Prevent the shape_<attach|detach|setup> operations */
- mutex_rw_wlock(shape->lock);
-
- if(!is_list_empty(&shape->scene_attachment)) {
- mutex_unlock(scn->lock);
- mutex_rw_unlock(shape->lock);
+ if(!is_list_empty(&shape->scene_attachment))
return RES_BAD_ARG;
- }
- ASSERT(shape->scn == NULL);
- ASSERT(shape->rtc_geom == RTC_INVALID_GEOMETRY_ID);
list_add_tail(&scn->shapes, &shape->scene_attachment);
- shape->scn = scn;
- scn->is_outdated = 1;
-
- mutex_unlock(scn->lock);
- mutex_rw_unlock(shape->lock);
-
- /* The shape should be own by the caller. Consequently, the shape cannot be
- * deleted before the end of the attach process */
S3D(shape_ref_get(shape));
return RES_OK;
}
res_T
+s3d_scene_detach_shape(struct s3d_scene* scn, struct s3d_shape* shape)
+{
+ char is_attached;
+ if(!scn || !shape) return RES_BAD_ARG;
+ if(!(S3D(shape_is_attached(shape, &is_attached)), is_attached))
+ return RES_BAD_ARG;
+#ifndef NDEBUG
+ {
+ struct list_node* node;
+ char is_found = 0;
+ LIST_FOR_EACH(node, &scn->shapes) {
+ if(node == &shape->scene_attachment)
+ is_found = 1;
+ }
+ ASSERT(is_found);
+ }
+#endif
+ scene_remove_shape(scn, shape);
+ return RES_OK;
+}
+
+res_T
s3d_scene_clear(struct s3d_scene* scn)
{
struct list_node* node, *tmp;
@@ -387,41 +375,45 @@ s3d_scene_clear(struct s3d_scene* scn)
return RES_BAD_ARG;
/* Prevent the scene_<attach_shape|remove_shape|build> operations */
- mutex_lock(scn->lock);
LIST_FOR_EACH_SAFE(node, tmp, &scn->shapes) {
struct s3d_shape* shape = CONTAINER_OF
(node, struct s3d_shape, scene_attachment);
- scene_remove_shape_unsafe(scn, shape);
+ scene_remove_shape(scn, shape);
}
- mutex_unlock(scn->lock);
return RES_OK;
}
res_T
s3d_scene_build(struct s3d_scene* scn)
{
+ struct list_node* node;
res_T res = RES_OK;
- if(!scn)
- return RES_BAD_ARG;
-
- /* Prevent the scene_<attach/remove_shape|clear|build> operations */
- mutex_lock(scn->lock);
- /* Prevent concurrent ray tracing */
- mutex_rw_wlock(scn->lock_rtc);
+ if(!scn) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
- if(ATOMIC_GET(&scn->is_outdated)) {
- res = scene_setup(scn);
+ LIST_FOR_EACH(node, &scn->shapes) {
+ struct s3d_shape* shape = CONTAINER_OF
+ (node, struct s3d_shape, scene_attachment);
+ switch(shape->type) {
+ case SHAPE_INSTANCE: /*res = shape_instance_setup(scn, shape);*/ break;
+ case SHAPE_MESH: res = scene_setup_shape_mesh(scn, shape); break;
+ default: FATAL("Unreachable code\n"); break;
+ }
+ if(res != RES_OK)
+ goto error;
+ }
+ if(scn->is_rtc_scn_outdated) {
rtcCommit(scn->rtc_scn);
- scn->is_outdated = res != RES_OK;
+ scn->is_rtc_scn_outdated = 0;
}
- mutex_unlock(scn->lock);
- mutex_rw_unlock(scn->lock_rtc);
-
- if(res != RES_OK)
- return res;
- return RES_OK;
+exit:
+ return res;
+error:
+ goto exit;
}
res_T
@@ -452,40 +444,29 @@ s3d_scene_trace_ray
ray.mask = 0xFFFFFFFF;
ray.time = 0.f;
- /* Prevent concurrent modifications on the Embree scene */
- mutex_rw_rlock(scn->lock_rtc);
rtcIntersect(scn->rtc_scn, ray);
- if((unsigned)ray.geomID == RTC_INVALID_GEOMETRY_ID) {
+ if((unsigned)ray.geomID == RTC_INVALID_GEOMETRY_ID) { /* No hit */
*hit = S3D_HIT_NULL;
} else {
f3_set(hit->normal, ray.Ng);
hit->uv[0] = ray.u;
hit->uv[1] = ray.v;
hit->distance = ray.tfar;
- hit->iprim[TRIANGLE_ID] = ray.primID;
if((unsigned)ray.instID == RTC_INVALID_GEOMETRY_ID) {
- ASSERT((unsigned)ray.geomID
- < darray_geom2shape_size_get(&scn->geom2shape));
- hit->shape = darray_geom2shape_data_get(&scn->geom2shape)[ray.geomID];
- ASSERT(hit->shape != NULL && (unsigned)ray.geomID == hit->shape->rtc_geom);
- ASSERT(hit->shape->type == SHAPE_MESH);
- hit->iprim[GEOMETRY_ID] = RTC_INVALID_GEOMETRY_ID;
+ ASSERT((unsigned)ray.geomID < darray_geom2mesh_size_get(&scn->geom2mesh));
+ hit->prim.ptr = darray_geom2mesh_data_get(&scn->geom2mesh)[ray.geomID];
+ hit->prim.iprim = ray.primID;
+ hit->prim.igeom = RTC_INVALID_GEOMETRY_ID;
} else { /* The hit shape is instantiated */
/* Retrieve the hit instance */
- ASSERT((unsigned)ray.instID
- < darray_geom2shape_size_get(&scn->geom2shape));
- hit->shape = darray_geom2shape_data_get(&scn->geom2shape)[ray.instID];
- ASSERT(hit->shape->type == SHAPE_INSTANCE);
- ASSERT(hit->shape != NULL && (unsigned)ray.instID == hit->shape->rtc_geom);
- ASSERT((unsigned)ray.geomID
- < darray_geom2shape_size_get(&hit->shape->data.instance.scene->geom2shape));
- hit->iprim[GEOMETRY_ID] = ray.geomID;
+ ASSERT((unsigned)ray.instID < darray_geom2mesh_size_get(&scn->geom2mesh));
+ /*hit->prim.ptr = TODO */
+ hit->prim.igeom = ray.geomID;
+ hit->prim.iprim = ray.primID;
}
}
- mutex_rw_unlock(scn->lock_rtc);
return RES_OK;
}
-
diff --git a/src/s3d_scene_c.h b/src/s3d_scene_c.h
@@ -36,36 +36,29 @@
#include "s3d_backend.h"
#include <rsys/dynamic_array.h>
+#include <rsys/hash_table.h>
#include <rsys/list.h>
#include <rsys/ref_count.h>
-#define DARRAY_NAME geom2shape
-#define DARRAY_DATA struct s3d_shape*
+#define DARRAY_NAME geom2mesh
+#define DARRAY_DATA struct mesh*
#include <rsys/dynamic_array.h>
+#define HTABLE_NAME mesh
+#define HTABLE_DATA struct mesh*
+#define HTABLE_KEY struct s3d_shape*
+#include <rsys/hash_table.h>
+
struct s3d_scene {
struct list_node shapes; /* List of attached shapes */
- struct darray_geom2shape geom2shape;
+ struct htable_mesh meshes; /* List of meshes associated to a shape */
+ struct darray_geom2mesh geom2mesh;
RTCScene rtc_scn;
- int is_outdated; /* Flag defining if the scene description was updated */
-
- struct mutex* lock;
- struct mutex_rw* lock_rtc;
+ char is_rtc_scn_outdated;
struct s3d_device* dev;
ref_T ref;
};
-/* Return NULL if `rtc_geom' is not registered into `scn' */
-extern LOCAL_SYM struct s3d_shape*
-scene_shape_from_rtc_geom
- (struct s3d_scene* scn,
- const unsigned rtc_geom);
-
-extern LOCAL_SYM void
-scene_remove_shape
- (struct s3d_scene* scn,
- struct s3d_shape* shape);
-
#endif /* S3D_SCENE_C_H */
diff --git a/src/s3d_shape.c b/src/s3d_shape.c
@@ -41,202 +41,12 @@
/*******************************************************************************
* Helper functions
******************************************************************************/
-static INLINE unsigned
-get_s3d_type_dimension(const enum s3d_type type)
-{
- switch(type) {
- case S3D_FLOAT: return 1;
- case S3D_FLOAT2: return 2;
- case S3D_FLOAT3: return 3;
- case S3D_FLOAT4: return 4;
- default: FATAL("Unreachable code\n"); break;
- }
- return 0;
-}
-
-static void
-mesh_init(struct mem_allocator* allocator, struct mesh* mesh)
-{
- int iattr;
- ASSERT(mesh);
- darray_u32_init(allocator, &mesh->indices);
- FOR_EACH(iattr, 0, S3D_ATTRIBS_COUNT__) {
- darray_float_init(allocator, &mesh->attribs[iattr]);
- mesh->attribs_type[iattr] = S3D_FLOAT;
- }
- /* The vertex positions are always float3 */
- mesh->attribs_type[S3D_POSITION] = S3D_FLOAT3;
-
- mesh->update_mask = mesh->resize_mask = 0;
-}
-
-static void
-mesh_release(struct mesh* mesh)
-{
- int iattr;
- ASSERT(mesh);
- darray_u32_release(&mesh->indices);
- FOR_EACH(iattr, 0, S3D_ATTRIBS_COUNT__) {
- darray_float_release(&mesh->attribs[iattr]);
- }
-}
-
-static void
-mesh_setup_indices
- (struct mesh* mesh,
- const unsigned ntris,
- void (*get_indices)(const unsigned itri, unsigned ids[3], void*),
- const unsigned nverts,
- void* data)
-{
- uint32_t* indices;
- unsigned itri;
- unsigned nids, nids_prev;
- unsigned nverts_new;
- res_T res;
- ASSERT(mesh && ntris && get_indices && nverts);
-
- nids = ntris * 3;
- nids_prev = (unsigned)darray_u32_size_get(&mesh->indices);
- ASSERT(get_indices != S3D_KEEP || nids == nids_prev);
-
- if(get_indices == S3D_KEEP) {
- ASSERT(nids == nids_prev);
- return;
- }
-
- 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) FATAL("Unsufficient memory\n");
- }
-
- /* Setup the mesh indices */
- indices = darray_u32_data_get(&mesh->indices);
- nverts_new = 0;
- FOR_EACH(itri, 0, ntris) {
- uint32_t* ids = indices + itri*3;
- int i;
- STATIC_ASSERT(sizeof(unsigned) == sizeof(uint32_t), Unexpected_Type);
- get_indices(itri, ids, data);
- FOR_EACH(i, 0, 3) nverts_new = MMAX(nverts_new, ids[i]);
- }
- /* Transform nverts from the last vertex id to vertices count */
- ++nverts_new;
- if(nverts_new > nverts)
- FATAL("Out of bound indexation\n");
-}
-
-static void
-mesh_setup_positions
- (struct mesh* mesh,
- const unsigned nverts,
- struct s3d_vertex_data* attr,
- void* data)
-{
- float* positions;
- unsigned ivert, nverts_prev;
- res_T res;
- ASSERT(mesh && nverts && attr && attr->usage == S3D_POSITION);
-
- nverts_prev = (unsigned)darray_float_size_get(&mesh->attribs[S3D_POSITION]);
- ASSERT(nverts_prev % 3 == 0);
- nverts_prev /= 3;
- if(attr->get == S3D_KEEP) {
- ASSERT(nverts == nverts_prev);
- return;
- }
-
- /* 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->attribs[S3D_POSITION], nverts*3);
- if(res != RES_OK) FATAL("Unsufficient memory\n");
- }
-
- /* Setup the vertex positions */
- positions = darray_float_data_get(&mesh->attribs[S3D_POSITION]);
- if(attr->type == S3D_FLOAT3) {
- FOR_EACH(ivert, 0, nverts) {
- attr->get(ivert, positions + ivert*3, data);
- }
- } else {
- FOR_EACH(ivert, 0, nverts) {
- float pos[4];
- unsigned ipos = ivert * 3;
- attr->get(ivert, pos, data);
- switch(attr->type) {
- case S3D_FLOAT:
- positions[ipos + 0] = pos[0];
- positions[ipos + 1] = 0.f;
- positions[ipos + 2] = 0.f;
- break;
- case S3D_FLOAT2:
- positions[ipos + 0] = pos[0];
- positions[ipos + 1] = pos[1];
- positions[ipos + 2] = 0.f;
- break;
- case S3D_FLOAT4: /* Homogeneous coordinates */
- positions[ipos + 0] = pos[0] / pos[3];
- positions[ipos + 1] = pos[1] / pos[3];
- positions[ipos + 2] = pos[2] / pos[3];
- break;
- default: FATAL("Unreachable code\n"); break;
- }
- }
- }
-}
-
-static void
-mesh_setup_attribs
- (struct mesh* mesh,
- const unsigned nverts,
- const struct s3d_vertex_data* attr,
- void* data)
-{
- float* attr_data;
- unsigned attr_dimension;
- unsigned ivert, nverts_prev;
- res_T res;
- ASSERT(mesh && nverts && attr);
- ASSERT(attr->usage >= S3D_ATTRIB_0 && attr->usage < S3D_ATTRIBS_COUNT__);
-
- nverts_prev = (unsigned)darray_float_size_get(&mesh->attribs[attr->usage]);
- attr_dimension = get_s3d_type_dimension(mesh->attribs_type[attr->usage]);
- ASSERT(nverts_prev % attr_dimension == 0);
- nverts_prev /= attr_dimension;
-
- if(attr->get == S3D_KEEP) {
- ASSERT(mesh->attribs_type[attr->usage] == attr->type);
- ASSERT(nverts == nverts_prev);
- return;
- }
-
- attr_dimension = get_s3d_type_dimension(attr->type);
- res = darray_float_resize(&mesh->attribs[attr->usage], nverts*attr_dimension);
- if(res != RES_OK) FATAL("Unsufficient memory\n");
-
- /* Setup the vertex attrib */
- attr_data = darray_float_data_get(&mesh->attribs[attr->usage]);
- FOR_EACH(ivert, 0, nverts) {
- attr->get(ivert, attr_data, data);
- attr_data += attr_dimension;
- }
- mesh->attribs_type[attr->usage] = attr->type;
-}
-
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_MESH: mesh_clear(&shape->data.mesh); break;
case SHAPE_NONE: /* Do nothing */ break;
case SHAPE_INSTANCE: S3D(scene_ref_put(shape->data.instance.scene)); break;
default: FATAL("Unreachable code\n"); break;
@@ -253,19 +63,9 @@ shape_release(ref_T* ref)
shape = CONTAINER_OF(ref, struct s3d_shape, ref);
dev = shape->dev;
- /* It is unacessary to prevent the concurrent read/write of the
- * scene_attachment, scn and rtc_geom fields since the shape should not be
- * shared when it is released */
-
/* The shape should not be attached */
ASSERT(is_list_empty(&shape->scene_attachment));
- ASSERT(shape->scn == NULL);
- /* The rtc_geom should be invalid */
- ASSERT(shape->rtc_geom == RTC_INVALID_GEOMETRY_ID);
-
shape_release_data(shape);
- if(shape->lock)
- mutex_rw_destroy(shape->lock);
MEM_FREE(dev->allocator, shape);
S3D(device_ref_put(dev));
}
@@ -290,16 +90,9 @@ shape_create(struct s3d_device* dev, struct s3d_shape** out_shape)
goto error;
}
list_init(&shape->scene_attachment);
- shape->rtc_geom = RTC_INVALID_GEOMETRY_ID;
- shape->scn = NULL;
S3D(device_ref_get(dev));
shape->dev = dev;
ref_init(&shape->ref);
- shape->lock = mutex_rw_create();
- if(!shape->lock) {
- res = RES_MEM_ERR;
- goto error;
- }
exit:
if(out_shape) *out_shape = shape;
@@ -357,23 +150,7 @@ s3d_shape_is_attached(struct s3d_shape* shape, char* is_attached)
{
if(!shape || !is_attached)
return RES_BAD_ARG;
- /* Prevent the shape from its concurrent attachment/detachment. Anyway,
- * several threads can concurrently query for its attachement status */
- mutex_rw_rlock(shape->lock);
*is_attached = !is_list_empty(&shape->scene_attachment);
- ASSERT(!*is_attached || shape->scn);
- mutex_rw_unlock(shape->lock);
- return RES_OK;
-}
-
-res_T
-s3d_shape_detach(struct s3d_shape* shape)
-{
- char is_attached;
- if(!shape) return RES_BAD_ARG;
- if(!(S3D(shape_is_attached(shape, &is_attached)), is_attached))
- return RES_OK;
- scene_remove_shape(shape->scn, shape);
return RES_OK;
}
@@ -391,6 +168,7 @@ s3d_shape_sample
return RES_OK;
}
+#if 0
res_T
s3d_shape_get_attrib
(const struct s3d_shape* shape,
@@ -414,8 +192,6 @@ s3d_shape_get_attrib
if(uv[0] < 0.f || uv[1] < 0.f || !eq_eps(w, 1.f, 1.e-6f))
return RES_BAD_ARG;
- mutex_rw_rlock(shape->lock);
-
switch(shape->type) {
case SHAPE_INSTANCE:
mesh = scene_shape_from_rtc_geom
@@ -495,11 +271,11 @@ s3d_shape_get_attrib
}
}
exit:
- mutex_rw_unlock(shape->lock);
return res;
error:
goto exit;
}
+#endif
res_T
s3d_instance_set_position
@@ -508,7 +284,6 @@ s3d_instance_set_position
float axis[3];
if(!shape || shape->type != SHAPE_INSTANCE || !position)
return RES_BAD_ARG;
- mutex_rw_wlock(shape->lock);
shape->data.instance.transform[9] =
f3_dot(f33_row(axis, shape->data.instance.transform, 0), position);
shape->data.instance.transform[10] =
@@ -516,9 +291,6 @@ s3d_instance_set_position
shape->data.instance.transform[11] =
f3_dot(f33_row(axis, shape->data.instance.transform, 2), position);
shape->data.instance.update_transform = 1;
- if(shape->scn)
- ATOMIC_SET(&shape->scn->is_outdated, 1);
- mutex_rw_unlock(shape->lock);
return RES_OK;
}
@@ -532,24 +304,18 @@ s3d_instance_translate
return RES_BAD_ARG;
if(space == S3D_LOCAL_TRANSFORM) {
float vec[3];
- mutex_rw_wlock(shape->lock);
f33_mulf3(vec, shape->data.instance.transform, translation);
f3_add
(shape->data.instance.transform + 9,
shape->data.instance.transform + 9,
vec);
shape->data.instance.update_transform = 1;
- if(shape->scn) ATOMIC_SET(&shape->scn->is_outdated, 1);
- mutex_rw_unlock(shape->lock);
} else if(space == S3D_WORLD_TRANSFORM) {
- mutex_rw_wlock(shape->lock);
f3_add
(shape->data.instance.transform + 9,
shape->data.instance.transform + 9,
translation);
shape->data.instance.update_transform = 1;
- if(shape->scn) ATOMIC_SET(&shape->scn->is_outdated, 1);
- mutex_rw_unlock(shape->lock);
} else {
return RES_BAD_ARG;
}
@@ -565,73 +331,9 @@ s3d_mesh_setup_indexed_vertices
struct s3d_vertex_data attribs[],
void* data)
{
- unsigned iattr;
- char has_position = 0;
- res_T res = RES_OK;
-
- if(!shape || shape->type != SHAPE_MESH || !ntris || !nverts || !attribs)
+ if(!shape || shape->type != SHAPE_MESH)
return RES_BAD_ARG;
-
- /* Prevent the shape from its concurrent attachment/detachment & pull */
- mutex_rw_wlock(shape->lock);
-
- /* Check indices description */
- if(get_indices == S3D_KEEP) {
- const unsigned nids_prev = (unsigned)
- darray_u32_size_get(&shape->data.mesh.indices);
- const unsigned ntris_prev = nids_prev / 3;
- if(ntris_prev != ntris) { /* Inconsistant data */
- res = RES_BAD_ARG;
- goto error;
- }
- }
-
- /* Check the vertex data description */
- iattr = 0;
- has_position = 0;
- do {
- if(attribs[iattr].get == S3D_KEEP) {
- const enum s3d_attrib_usage attr_usage = attribs[iattr].usage;
- const enum s3d_type type = attribs[iattr].type;
- const enum s3d_type type_prev = shape->data.mesh.attribs_type[attr_usage];
- const struct darray_float* attr = shape->data.mesh.attribs + attr_usage;
- size_t nverts_prev = darray_float_size_get(attr);
- nverts_prev /= get_s3d_type_dimension(type_prev);
- if(type_prev != type || nverts_prev != nverts) { /* Inconsistant data */
- res = RES_BAD_ARG;
- goto error;
- }
- }
- if(attribs[iattr].usage == S3D_POSITION)
- has_position = 1;
- ++iattr;
- } while(attribs[iattr].usage != S3D_ATTRIBS_COUNT__);
-
- if(!has_position) { /* The vertex must have a position */
- res = RES_BAD_ARG;
- goto error;
- }
-
- /* Setup indices */
- if(get_indices != S3D_KEEP) {
- mesh_setup_indices(&shape->data.mesh, ntris, get_indices, nverts, data);
- }
- /* Setup vertex data */
- for(iattr = 0; attribs[iattr].usage != S3D_ATTRIBS_COUNT__; ++iattr) {
- if(attribs[iattr].usage == S3D_POSITION) {
- mesh_setup_positions(&shape->data.mesh, nverts, attribs + iattr, data);
- } else {
- mesh_setup_attribs(&shape->data.mesh, nverts, attribs + iattr, data);
- }
- }
-
- if(shape->scn) /* Notify the shape update */
- ATOMIC_SET(&shape->scn->is_outdated, 1);
-
-exit:
- mutex_rw_unlock(shape->lock);
- return res;
-error:
- goto exit;
+ return mesh_setup_indexed_vertices
+ (&shape->data.mesh, ntris, get_indices, nverts, attribs, data);
}
diff --git a/src/s3d_shape_c.h b/src/s3d_shape_c.h
@@ -33,21 +33,16 @@
#ifndef S3D_SHAPE_C_H
#define S3D_SHAPE_C_H
+#include "s3d_mesh.h"
+
#include <rsys/dynamic_array_u32.h>
#include <rsys/dynamic_array_float.h>
#include <rsys/list.h>
#include <rsys/mutex.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_MESH,
SHAPE_INSTANCE,
@@ -55,34 +50,15 @@ enum shape_type {
SHAPE_NONE = SHAPE_TYPES_COUNT__
};
-/* Helper constants use as synthactic sugar to index the primitive identifiers */
-enum {
- GEOMETRY_ID, /* Index toward the Embree Geometry ID of the primtive */
- TRIANGLE_ID /* Index toward the Embree triangle ID of the primitive*/
-};
-
-struct mesh { /* Triangular mesh */
- darray_u32 indices;
- darray_float attribs[S3D_ATTRIBS_COUNT__];
- enum s3d_type attribs_type[S3D_ATTRIBS_COUNT__];
-
- /* Combination of shape_buffer */
- int update_mask; /* Define which shape buffers were updated */
- int resize_mask; /* Define which shape buffers were [re]allocateod */
-};
-
struct instance {
struct s3d_scene* scene;
float transform[12]; /* local to world 3x4 column major matrix */
- int update_transform; /* Define if the transform matrix was updated or not */
+ char update_transform;
};
struct s3d_shape {
struct list_node scene_attachment;
enum shape_type type;
- unsigned rtc_geom; /* Embree geometry id */
- struct s3d_scene* scn; /* The scene on which the shape is attached */
- struct mutex_rw* lock;
union {
struct instance instance;
diff --git a/src/test_s3d_shape.c b/src/test_s3d_shape.c
@@ -63,11 +63,6 @@ main(int argc, char** argv)
CHECK(s3d_shape_is_attached(shape, &c), RES_OK);
CHECK(c, 0);
- CHECK(s3d_shape_detach(NULL), RES_BAD_ARG);
- CHECK(s3d_shape_detach(shape), RES_OK);
- CHECK(s3d_shape_is_attached(shape, &c), RES_OK);
- CHECK(c, 0);
-
CHECK(s3d_scene_attach_shape(NULL, NULL), RES_BAD_ARG);
CHECK(s3d_scene_attach_shape(scn, NULL), RES_BAD_ARG);
CHECK(s3d_scene_attach_shape(NULL, shape), RES_BAD_ARG);
@@ -75,7 +70,10 @@ main(int argc, char** argv)
CHECK(s3d_shape_is_attached(shape, &c), RES_OK);
NCHECK(c, 0);
- CHECK(s3d_shape_detach(shape), RES_OK);
+ CHECK(s3d_scene_detach_shape(NULL, NULL), RES_BAD_ARG);
+ CHECK(s3d_scene_detach_shape(scn, NULL), RES_BAD_ARG);
+ CHECK(s3d_scene_detach_shape(NULL, shape), RES_BAD_ARG);
+ CHECK(s3d_scene_detach_shape(scn, shape), RES_OK);
CHECK(s3d_shape_is_attached(shape, &c), RES_OK);
CHECK(c, 0);
diff --git a/src/test_s3d_trace_ray.c b/src/test_s3d_trace_ray.c
@@ -48,8 +48,8 @@ struct camera {
static void
camera_init(struct camera* cam)
{
- const float pos[3] = { 0.f, -1500.f, 0.f };
- const float tgt[3] = { 0.f, 0.f, 0.f };
+ const float pos[3] = { 178.f, -1000.f, 273.f };
+ const float tgt[3] = { 178.f, 0.f, 273.f };
const float up[3] = { 0.f, 0.f, 1.f };
const float proj_ratio = (float)IMG_WIDTH/(float)IMG_HEIGHT;
const float fov_x = (float)PI * 0.25f;
@@ -157,6 +157,7 @@ main(int argc, char** argv)
CHECK(s3d_scene_trace_ray(scn, org, dir, range, &hit), RES_BAD_ARG);
+ /*
CHECK(s3d_scene_create(dev, &scn2), RES_OK);
f3(org, -555.f, 0.f, -551.f);
@@ -183,7 +184,7 @@ main(int argc, char** argv)
CHECK(s3d_scene_attach_shape(scn2, shape), RES_OK);
CHECK(s3d_instance_set_position(shape, org), RES_OK);
- CHECK(s3d_scene_build(scn2), RES_OK);
+ CHECK(s3d_scene_build(scn2), RES_OK);*/
camera_init(&cam);
FOR_EACH(iy, 0, IMG_HEIGHT) {
@@ -195,7 +196,7 @@ main(int argc, char** argv)
pixel[0] = (float)ix/(float)IMG_WIDTH;
camera_ray(&cam, pixel, org, dir);
- CHECK(s3d_scene_trace_ray(scn2, org, dir, range, &hit), RES_OK);
+ CHECK(s3d_scene_trace_ray(scn, org, dir, range, &hit), RES_OK);
if(!img)
continue;
@@ -224,7 +225,7 @@ main(int argc, char** argv)
CHECK(s3d_device_ref_put(dev), RES_OK);
CHECK(s3d_shape_ref_put(shape), RES_OK);
CHECK(s3d_scene_ref_put(scn), RES_OK);
- CHECK(s3d_scene_ref_put(scn2), RES_OK);
+ /*CHECK(s3d_scene_ref_put(scn2), RES_OK);*/
check_memory_allocator(&allocator);
mem_shutdown_proxy_allocator(&allocator);