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 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:
Mcmake/CMakeLists.txt | 4++--
Msrc/s3d.h | 42++++++++++++++++++++++++------------------
Asrc/s3d_buffer.h | 113+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/s3d_c.h | 19+++++++++++++++++--
Asrc/s3d_mesh.c | 357+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/s3d_mesh.h | 107+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/s3d_scene.c | 411++++++++++++++++++++++++++++++++++++++-----------------------------------------
Msrc/s3d_scene_c.h | 29+++++++++++------------------
Msrc/s3d_shape.c | 310++-----------------------------------------------------------------------------
Msrc/s3d_shape_c.h | 30+++---------------------------
Msrc/test_s3d_shape.c | 10++++------
Msrc/test_s3d_trace_ray.c | 11++++++-----
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);