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 b6d9b307a4c735229076f321d9c8fdd9ad484dbe
parent 6a611f84688294e853355714e5a29972877cffc3
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Thu, 10 Sep 2015 11:33:31 +0200

Remove the shape_<compute_area|compute_volume|primitives_count> funcs

These functionalities are now supported by the scene rather than the
shape.

Diffstat:
Mcmake/CMakeLists.txt | 1-
Msrc/s3d.h | 37++++++++++++++++++-------------------
Msrc/s3d_instance.c | 38--------------------------------------
Msrc/s3d_instance.h | 10+---------
Dsrc/s3d_sampler.c | 582------------------------------------------------------------------------------
Msrc/s3d_scene.c | 150+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/s3d_shape.c | 52----------------------------------------------------
Msrc/test_s3d_scene.c | 135+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/test_s3d_shape.c | 86++-----------------------------------------------------------------------------
Msrc/test_s3d_trace_ray.c | 14+++++---------
10 files changed, 311 insertions(+), 794 deletions(-)

diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt @@ -67,7 +67,6 @@ set(S3D_FILES_SRC s3d_instance.c s3d_mesh.c s3d_primitive.c - s3d_sampler.c s3d_scene.c s3d_shape.c) set(S3D_FILES_INC_API s3d.h) diff --git a/src/s3d.h b/src/s3d.h @@ -304,6 +304,24 @@ s3d_scene_sample struct s3d_primitive* primitive, /* sampled primitive */ float uv[2]); +/* Retrieve the number of scene primitives */ +S3D_API res_T +s3d_scene_primitives_count + (struct s3d_scene* scn, + size_t* primitives_count); + +S3D_API res_T +s3d_scene_compute_area + (struct s3d_scene* scn, + float* area); + +/* This function assumes that the scene defines a closed volume and that the + * normals point into the volume */ +S3D_API res_T +s3d_scene_compute_volume + (struct s3d_scene* scn, + float* volume); + /******************************************************************************* * Shape API - A shape defines a geometry that can be attached to *one* scene. ******************************************************************************/ @@ -352,25 +370,6 @@ S3D_API res_T s3d_shape_flip_surface (struct s3d_shape* shape); -/* Get the number of primitives */ -S3D_API res_T -s3d_shape_primitives_count - (struct s3d_shape* shape, - size_t* primitives_count); - -/* Return 0 if the shape is disabled */ -S3D_API res_T -s3d_shape_compute_area - (struct s3d_shape* shape, - float* area); - -/* This function assumes that the shape defines a closed volume and that the - * normals point into the volume. Return 0 if the shape is disabled */ -S3D_API res_T -s3d_shape_compute_volume - (struct s3d_shape* shape, - float* volume); - /******************************************************************************* * Primitive API - Define a geometric primitive of a shape ******************************************************************************/ diff --git a/src/s3d_instance.c b/src/s3d_instance.c @@ -103,44 +103,6 @@ instance_ref_put(struct instance* inst) ref_put(&inst->ref, instance_release); } -size_t -instance_get_ntris(struct instance* inst) -{ - struct list_node* node; - size_t ntris = 0; - LIST_FOR_EACH(node, &inst->scene->shapes) { - struct s3d_shape* shape = CONTAINER_OF - (node, struct s3d_shape, scene_attachment); - switch(shape->type) { - case GEOM_INSTANCE: - ntris += instance_get_ntris(shape->data.instance); - break; - case GEOM_MESH: - ntris += mesh_get_ntris(shape->data.mesh); - break; - default: FATAL("Unreachable code\n"); break; - } - } - return ntris; -} - -float -instance_compute_area(struct instance* inst) -{ - struct list_node* node; - float area = 0.f; - ASSERT(inst); - - /* TODO take into account the scale factor of the instance */ - LIST_FOR_EACH(node, &inst->scene->shapes) { - struct s3d_shape* shape = CONTAINER_OF - (node, struct s3d_shape, scene_attachment); - ASSERT(shape->type == GEOM_MESH); /* One instancing level is supported */ - area += mesh_compute_area(shape->data.mesh); - } - return area; -} - float instance_compute_volume(struct instance* inst, const char flip_surface) { diff --git a/src/s3d_instance.h b/src/s3d_instance.h @@ -57,18 +57,10 @@ extern LOCAL_SYM void instance_ref_put (struct instance* inst); -extern LOCAL_SYM size_t -instance_get_ntris - (struct instance* inst); - -extern LOCAL_SYM float -instance_compute_area - (struct instance* inst); - extern LOCAL_SYM float instance_compute_volume (struct instance* inst, - const char flip_surface); /* Flip the instance surfaces or not */ + const char flip_surface); #endif /* S3D_INSTANCE_H */ diff --git a/src/s3d_sampler.c b/src/s3d_sampler.c @@ -1,582 +0,0 @@ -/* 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_device_c.h" -#include "s3d_scene_c.h" -#include "s3d_shape_c.h" - -#include <rsys/dynamic_array_float.h> -#include <rsys/dynamic_array_size_t.h> -#include <rsys/float33.h> -#include <rsys/logger.h> - -#include <algorithm> - -/* The sampler is used to compute uniform random variates of a shape */ -#if 0 -struct s3d_sampler { - struct s3d_shape* shape; /* The shape to sample */ - - /* Register the shape mesh to sample. For a SHAPE_INSTANCE, it lists the - * instantiated shape mesh while for SHAPE_MESH it stores only one mesh, i.e. - * the mesh of the shape. Actually, this cache is synchronised with the shape - * data on s3d_sampler_begin_sampling invocation by creating meshes that get - * a reference onto the shape mesh data. This makes the sampling robust to - * shape updates until the s3d_sampler_end_sampling invocation */ - struct darray_mesh cached_meshes; - - /* Register the shape instance data. If the shape is not a SHAPE_INSTANCE, - * the cached_instance is NULL. The cached instance data are synchronised on - * s3d_sampler_begin invocation by copying the shape instance data to the - * cache instance. This makes the sampling robust to the updates of shape - * instance data until the s3d_sampler_end_sampling invocation */ - struct instance* cached_instance; - - /* List of cached_mesh CDF ordered with respect to the cached_mesh list, i.e. - * the first value is the CDF of the first cached mesh, the second value the - * CDF of the second cached mesh, etc.*/ - struct darray_float meshes_cdf; - /* Map a cached mesh id to its 1st triangle CDF */ - struct darray_size_t mesh2triangle; - /* List of triangle CDF ordered with respect to the cached_mesh list, i.e. - * the first N values stores the CDF of the N triangles of the first cached - * mesh, the next M values are the CDF of the M triangles of the second - * cached mesh, etc. Use the mesh2triangle data structure to retrieve the - * list of triangle CDF of a specific cached mesh */ - struct darray_float triangles_cdf; - - char is_sync; /* Define if the sampler is in a sampling process */ - ref_T ref; -}; - -/******************************************************************************* - * Helper functions - ******************************************************************************/ -/* The `cache' get a reference onto the `mesh' data. Return 1 if the `cache' - * *geometry* data are updated and 0 otherwise */ -static char -cache_mesh(struct mesh* cache, struct mesh* mesh) -{ - char upd_geom = 0; - size_t i; - ASSERT(cache && mesh); - - /* Get a reference onto the shape mesh indices */ - if(cache->indices != mesh->indices) { - if(cache->indices) { /* Release previous cached indices */ - index_buffer_ref_put(cache->indices); - cache->indices = NULL; - } - index_buffer_ref_get(mesh->indices); - cache->indices = mesh->indices; - upd_geom = 1; - } - - /* Get a reference onto the shape mesh attribs */ - FOR_EACH(i, 0, S3D_ATTRIBS_COUNT__) { - if(cache->attribs[i] == mesh->attribs[i]) - continue; - - if(i == S3D_POSITION) - upd_geom = 1; - - if(cache->attribs[i]) { /* Release previous cached attribs */ - vertex_buffer_ref_put(cache->attribs[i]); - cache->attribs[i] = NULL; - } - - if(!mesh->attribs[i]) - continue; - - vertex_buffer_ref_get(mesh->attribs[i]); - cache->attribs[i] = mesh->attribs[i]; - cache->attribs_type[i] = mesh->attribs_type[i]; - } - cache->flip_surface = mesh->flip_surface; - return upd_geom; -} - -/* Release all cached meshes */ -static void -sampler_clear_cached_meshes(struct s3d_sampler* sampler) -{ - size_t i = 0; - ASSERT(sampler); - FOR_EACH(i, 0, darray_mesh_size_get(&sampler->cached_meshes)) - mesh_ref_put(darray_mesh_data_get(&sampler->cached_meshes)[i]); - darray_mesh_clear(&sampler->cached_meshes); -} - -/* Clean-up the CDF */ -static void -sampler_clear_cdf(struct s3d_sampler* sampler) -{ - ASSERT(sampler); - darray_float_clear(&sampler->meshes_cdf); - darray_float_clear(&sampler->triangles_cdf); - darray_size_t_clear(&sampler->mesh2triangle); -} - -/* Clean-up the synchronised data */ -static void -sampler_clear(struct s3d_sampler* sampler) -{ - ASSERT(sampler); - sampler_clear_cached_meshes(sampler); - sampler_clear_cdf(sampler); - sampler->is_sync = 0; -} - -/* Compute the CDF of the cached meshes */ -static res_T -sampler_compute_cdf(struct s3d_sampler* sampler) -{ - struct mesh** meshes = NULL; - size_t* mesh2triangle = NULL; - float* meshes_cdf = NULL; - size_t imesh, nmeshes; - size_t itri; - float area = 0.f, rcp_area = 0.f; - res_T res = RES_OK; - ASSERT(sampler); - - /* Fetched cached meshes */ - meshes = darray_mesh_data_get(&sampler->cached_meshes); - nmeshes = darray_mesh_size_get(&sampler->cached_meshes); - ASSERT(nmeshes); - - /* Reserve meshes CDF memory space */ - sampler_clear_cdf(sampler); - res = darray_float_resize(&sampler->meshes_cdf, nmeshes); - if(res != RES_OK) goto error; - res = darray_size_t_resize(&sampler->mesh2triangle, nmeshes); - if(res != RES_OK) goto error; - - /* Fetch meshes CDF data */ - meshes_cdf = darray_float_data_get(&sampler->meshes_cdf); - mesh2triangle = darray_size_t_data_get(&sampler->mesh2triangle); - - FOR_EACH(imesh, 0, nmeshes) { - const size_t ntris = mesh_get_ntris(meshes[imesh]); - const uint32_t* ids = mesh_get_ids(meshes[imesh]); - const float* verts = mesh_get_pos(meshes[imesh]); - - mesh2triangle[imesh] = darray_float_size_get(&sampler->triangles_cdf); - res = darray_float_reserve - (&sampler->triangles_cdf, mesh2triangle[imesh] + ntris); - if(res != RES_OK) goto error; - - FOR_EACH(itri, 0, ntris ) { - int i; - const float* positions[3]; - float edges[2][3], normal[3]; - float triangle_area; - - FOR_EACH(i, 0, 3) { /* Fetch vertex positions */ - const uint32_t id = ids[itri*3 + i]; - ASSERT(id < mesh_get_nverts(meshes[imesh])); - positions[i] = verts + id * 3/*#coords*/; - } - /* Compute triangle area. Note that actually, we compute the double of - * the triangle area since it saves computation times whithout affecting - * the sampling result */ - f3_sub(edges[0], positions[1], positions[0]); - f3_sub(edges[1], positions[2], positions[0]); - f3_cross(normal, edges[0], edges[1]); - triangle_area = f3_len(normal); - /* TODO take into account the scale instance transformation in the - * computation of the triangle area */ - - /* Store the CDF value of the triangle `itri' */ - area += triangle_area; - darray_float_push_back(&sampler->triangles_cdf, &area); - } - /* Store the CDF value of the mesh `imesh' */ - meshes_cdf[imesh] = area; - } - - if(eq_eps(area, 0, 1.e-6f)) { - res = RES_BAD_ARG; - goto error; - } - - /* Normalize the meshes/triangles CDF */ - rcp_area = 1.f / area; - FOR_EACH(imesh, 0, nmeshes) { - meshes_cdf[imesh] *= rcp_area; - } - FOR_EACH(itri, 0, darray_float_size_get(&sampler->triangles_cdf)) { - darray_float_data_get(&sampler->triangles_cdf)[itri] *= rcp_area; - } - -exit: - return res; -error: - sampler_clear_cdf(sampler); - goto exit; -} - -/* Cache the sampler shape mesh */ -static res_T -sampler_cache_mesh(struct s3d_sampler* sampler, char* upd_geom) -{ - struct mesh* mesh; - char is_enabled; - res_T res = RES_OK; - ASSERT(sampler && sampler->shape->type == SHAPE_MESH && upd_geom); - - if(!sampler->shape->data.mesh->indices - || !sampler->shape->data.mesh->attribs[S3D_POSITION]) { - res = RES_BAD_ARG; - goto error; - } - - if(!(S3D(shape_is_enabled(sampler->shape, &is_enabled)), is_enabled)) { - if(sampler->shape->dev->verbose) { - logger_print(sampler->shape->dev->logger, LOG_ERROR, - "Couldn't sample anything. The mesh to sample is disabled\n"); - } - res = RES_BAD_ARG; - goto error; - } - - if(darray_mesh_size_get(&sampler->cached_meshes)) { - /* Fetch the cached entry */ - mesh = darray_mesh_data_get(&sampler->cached_meshes)[0]; - } else { - /* 1st sync process => create the cached entry */ - res = mesh_create(sampler->shape->dev, &mesh); - if(res != RES_OK) goto error; - res = darray_mesh_push_back(&sampler->cached_meshes, &mesh); - if(res != RES_OK) goto error; - } - /* Cache the instance mesh data */ - *upd_geom = cache_mesh(mesh, sampler->shape->data.mesh); - -exit: - return res; -error: - sampler_clear_cached_meshes(sampler); - goto exit; -} - -/* Cache the sampler instance */ -static res_T -sampler_cache_instance(struct s3d_sampler* sampler, char* upd_geom) -{ - struct list_node* node; - struct mesh* mesh; - size_t ishape = 0; - size_t nshapes; - res_T res = RES_OK; - ASSERT(sampler && sampler->shape->type == SHAPE_INSTANCE && upd_geom); - - /* Check that the instance is ready to be sampled, i.e. it is enabled */ - if(!sampler->shape->data.instance->geom.is_enabled) { - if(sampler->shape->dev->verbose) { - logger_print(sampler->shape->dev->logger, LOG_ERROR, - "Couldn't sample anything. The instance to sample is disabled\n"); - } - res = RES_BAD_ARG; - goto error; - } - - /* Setup the Id of the cached instance */ - sampler->cached_instance->id = sampler->shape->name.index; - - f33_set /* Cache the instance rotation */ - (sampler->cached_instance->transform, - sampler->shape->data.instance->transform); - f3_set /* Cache the instance translation */ - (sampler->cached_instance->transform + 9, - sampler->shape->data.instance->transform + 9); - - LIST_FOR_EACH(node, &sampler->shape->data.instance->scene->shapes) { - char is_enabled; - struct s3d_shape* shape = CONTAINER_OF - (node, struct s3d_shape, scene_attachment); - ASSERT(shape->type == SHAPE_MESH); - - /* Discard disabled shapes */ - if(!(S3D(shape_is_enabled(shape, &is_enabled)), is_enabled)) - continue; - - /* Discard shapes with no geometry */ - if(!shape->data.mesh->indices - || !shape->data.mesh->attribs[S3D_POSITION]) - continue; - - if(ishape < darray_mesh_size_get(&sampler->cached_meshes)) { - /* Fetch the previous cache entry */ - mesh = darray_mesh_data_get(&sampler->cached_meshes)[ishape]; - } else { - /* Create a new cache entry */ - res = mesh_create(sampler->shape->dev, &mesh); - if(res != RES_OK) goto error; - res = darray_mesh_push_back(&sampler->cached_meshes, &mesh); - if(res != RES_OK) goto error; - } - mesh->id = shape->name.index; /* Setup the Id of the cached mesh */ - /* Cache the instance mesh data */ - *upd_geom = cache_mesh(mesh, shape->data.mesh) || *upd_geom; - ++ishape; - } - nshapes = ishape; - - if(!nshapes) { - if(sampler->shape->dev->verbose) { - logger_print(sampler->shape->dev->logger, LOG_ERROR, - "Couldn't sample anything. " - "All instantiated shapes to sample are disabled\n"); - } - res = RES_BAD_ARG; - goto error; - } - - /* Flush the "no more in use" cache entries */ - FOR_EACH(ishape, ishape, darray_mesh_size_get(&sampler->cached_meshes)) - mesh_ref_put(darray_mesh_data_get(&sampler->cached_meshes)[ishape]); - darray_mesh_resize(&sampler->cached_meshes, nshapes); - - /* TODO take into account the scale transformation in the upd_geom flag */ - *upd_geom = *upd_geom || (nshapes != ishape); - -exit: - return res; -error: - sampler_clear_cached_meshes(sampler); - goto exit; -} - -/* Synchronise the sampler with the shape data */ -static res_T -sampler_sync(struct s3d_sampler* sampler) -{ - char upd_geom = 0; - res_T res = RES_OK; - ASSERT(sampler); - - if(sampler->is_sync) - return RES_BAD_OP; - - switch(sampler->shape->type) { - case SHAPE_MESH: res = sampler_cache_mesh(sampler, &upd_geom); break; - case SHAPE_INSTANCE: res = sampler_cache_instance(sampler, &upd_geom); break; - default: FATAL("Unreachable code\n"); break; - } - if(res != RES_OK) goto error; - - if(upd_geom) { - res = sampler_compute_cdf(sampler); - if(res != RES_OK) goto error; - } - sampler->is_sync = 1; - -exit: - return res; -error: - sampler_clear_cached_meshes(sampler); - goto exit; -} - -static void -sampler_release(ref_T* ref) -{ - struct s3d_sampler* sampler; - struct s3d_shape* shape; - - ASSERT(ref); - sampler = CONTAINER_OF(ref, struct s3d_sampler, ref); - shape = sampler->shape; - - sampler_clear_cached_meshes(sampler); - darray_mesh_release(&sampler->cached_meshes); - darray_float_release(&sampler->triangles_cdf); - darray_float_release(&sampler->meshes_cdf); - darray_size_t_release(&sampler->mesh2triangle); - - if(sampler->cached_instance) - instance_ref_put(sampler->cached_instance); - - MEM_RM(shape->dev->allocator, sampler); - S3D(shape_ref_put(shape)); -} - -/******************************************************************************* - * Exported functions - ******************************************************************************/ -res_T -s3d_sampler_create(struct s3d_shape* shape, struct s3d_sampler** out_sampler) -{ - struct s3d_sampler* sampler = NULL; - res_T res = RES_OK; - - if(!out_sampler || !shape) { - res = RES_BAD_ARG; - goto error; - } - sampler = (struct s3d_sampler*) - MEM_CALLOC(shape->dev->allocator, 1, sizeof(struct s3d_sampler)); - if(!sampler) { - res = RES_MEM_ERR; - goto error; - } - ref_init(&sampler->ref); - S3D(shape_ref_get(shape)); - sampler->shape = shape; - darray_mesh_init(shape->dev->allocator, &sampler->cached_meshes); - darray_float_init(shape->dev->allocator, &sampler->triangles_cdf); - darray_float_init(shape->dev->allocator, &sampler->meshes_cdf); - darray_size_t_init(shape->dev->allocator, &sampler->mesh2triangle); - - if(shape->type == SHAPE_INSTANCE) { - res = instance_create(shape->data.instance->scene, &sampler->cached_instance); - if(res != RES_OK) goto error; - } - -exit: - if(out_sampler) *out_sampler = sampler; - return res; -error: - if(sampler) { - S3D(sampler_ref_put(sampler)); - sampler = NULL; - } - goto exit; -} - -res_T -s3d_sampler_ref_get(struct s3d_sampler* sampler) -{ - if(!sampler) return RES_BAD_ARG; - ref_get(&sampler->ref); - return RES_OK; -} - -res_T -s3d_sampler_ref_put(struct s3d_sampler* sampler) -{ - if(!sampler) return RES_BAD_ARG; - ref_put(&sampler->ref, sampler_release); - return RES_OK; -} - -res_T -s3d_sampler_begin_sampling(struct s3d_sampler* sampler) -{ - if(!sampler) return RES_BAD_ARG; - return sampler_sync(sampler); -} - -res_T -s3d_sampler_end_sampling(struct s3d_sampler* sampler) -{ - if(!sampler) return RES_BAD_ARG; - if(!sampler->is_sync) return RES_BAD_OP; - sampler_clear(sampler); - return RES_OK; -} - -res_T -s3d_sampler_get - (struct s3d_sampler* sampler, - const float u, - const float v, - const float w, - struct s3d_primitive* primitive, /* sampled primitive */ - float uv[2]) -{ - const float* cdf_start = NULL; - const float* cdf_stop = NULL; - const float* cdf_found = NULL; - double sqrt_v; - struct mesh* mesh = NULL; - size_t imesh; - size_t imesh2tri; - res_T res = RES_OK; - - if(!sampler || !primitive || !uv) { - res = RES_BAD_ARG; - goto error; - } - - /* Expecting canonic numbers */ - if(u < 0.f || u >= 1.f || v < 0.f || v >= 1.f || w < 0.f || w >= 1.f) { - res = RES_BAD_ARG; - goto error; - } - - if(!sampler->is_sync) { - res = RES_BAD_OP; - goto error; - } - - /* Find which mesh is sampled */ - if(darray_float_size_get(&sampler->meshes_cdf) == 1) { - imesh = 0; - } else { - cdf_start = darray_float_cdata_get(&sampler->meshes_cdf); - cdf_stop = cdf_start + darray_float_size_get(&sampler->meshes_cdf); - cdf_found = std::lower_bound(cdf_start, cdf_stop, u); - ASSERT(cdf_found != cdf_stop); - imesh = cdf_found - cdf_start; - } - - imesh2tri = darray_size_t_data_get(&sampler->mesh2triangle)[imesh]; - mesh = darray_mesh_data_get(&sampler->cached_meshes)[imesh]; - - /* Find which triangle is sampled into the mesh */ - cdf_start = darray_float_cdata_get(&sampler->triangles_cdf) + imesh2tri; - cdf_stop = cdf_start + mesh_get_ntris(mesh); - cdf_found = std::lower_bound(cdf_start, cdf_stop, u); - ASSERT(cdf_found != cdf_stop); - - /* Setup the sampled primitive */ - primitive->mesh__ = mesh; - primitive->inst__ = sampler->cached_instance; - primitive->prim_id = (unsigned)(cdf_found - cdf_start); - primitive->geom_id = mesh->id; - primitive->inst_id = sampler->cached_instance - ? sampler->cached_instance->id - : S3D_INVALID_ID; - - /* Compute the sampled barycentric coordinate of the sampled primitive */ - sqrt_v = sqrt(v); - uv[0] = (float)(1.0 - sqrt_v); - uv[1] = (float)(w * sqrt_v); - -exit: - return res; -error: - goto exit; -} -#endif diff --git a/src/s3d_scene.c b/src/s3d_scene.c @@ -881,3 +881,153 @@ error: goto exit; } +res_T +s3d_scene_primitives_count(struct s3d_scene* scn, size_t* prims_count) +{ + struct list_node* node; + struct s3d_shape* shape; + struct geometry** pgeom; + struct geometry* geom; + size_t inst_count; + res_T res = RES_OK; + + if(!scn || !prims_count) { + res = RES_BAD_ARG; + goto error; + } + if(!scn->session_mask) { + res = RES_BAD_OP; + goto error; + } + + *prims_count = 0; + LIST_FOR_EACH(node, &scn->shapes) { + shape = CONTAINER_OF(node, struct s3d_shape, scene_attachment); + pgeom = htable_geom_find(&scn->cached_geoms, &shape); + ASSERT(pgeom != NULL); + geom = *pgeom; + + if(!geom->is_enabled) continue; + + switch(geom->type) { + case GEOM_MESH: + *prims_count += mesh_get_ntris(geom->data.mesh); + break; + case GEOM_INSTANCE: + S3D(scene_primitives_count(geom->data.instance->scene, &inst_count)); + *prims_count += inst_count; + break; + default: FATAL("Unreachable code\n"); break; + } + } + +exit: + return res; +error: + goto exit; +} + +res_T +s3d_scene_compute_area(struct s3d_scene* scn, float* area) +{ + res_T res = RES_OK; + + if(!scn || !area) { + res = RES_BAD_ARG; + goto error; + } + if(!scn->session_mask) { + res = RES_BAD_OP; + goto error; + } + + if((scn->session_mask & S3D_SAMPLE) != 0) { + /* Retrieve the overall scene area from the scene cumulative distribution + * function. Note that the CDF stores the cumulateive triangle area + * multiplied by 2; the real scene area is thus the CDF upper bound / 2 */ + size_t len = darray_fltui_size_get(&scn->cdf); + if(!len) { + *area = 0.f; + } else { + *area = darray_fltui_cdata_get(&scn->cdf)[len - 1].flt * 0.5f; + } + } else { + struct list_node* node; + struct s3d_shape* shape; + struct geometry** pgeom; + struct geometry* geom; + float inst_area; + + *area = 0.f; + LIST_FOR_EACH(node, &scn->shapes) { + shape = CONTAINER_OF(node, struct s3d_shape, scene_attachment); + pgeom = htable_geom_find(&scn->cached_geoms, &shape); + ASSERT(pgeom != NULL); + geom = *pgeom; + + if(!geom->is_enabled) continue; + + switch(geom->type) { + case GEOM_MESH: + *area += mesh_compute_area(geom->data.mesh); + break; + case GEOM_INSTANCE: + /* TODO take into account the instance scale factor */ + S3D(scene_compute_area(geom->data.instance->scene, &inst_area)); + *area += inst_area; + break; + default: FATAL("Unreachable code\n"); break; + } + } + } + +exit: + return res; +error: + goto exit; +} + +res_T +s3d_scene_compute_volume(struct s3d_scene* scn, float* volume) +{ + struct list_node* node; + struct s3d_shape* shape; + struct geometry** pgeom; + struct geometry* geom; + res_T res = RES_OK; + + if(!scn || !volume) { + res = RES_BAD_ARG; + goto error; + } + if(!scn->session_mask) { + res = RES_BAD_OP; + goto error; + } + + *volume = 0.f; + LIST_FOR_EACH(node, &scn->shapes) { + shape = CONTAINER_OF(node, struct s3d_shape, scene_attachment); + pgeom = htable_geom_find(&scn->cached_geoms, &shape); + ASSERT(pgeom != NULL); + geom = *pgeom; + + if(!geom->is_enabled) continue; + + switch(geom->type) { + case GEOM_MESH: + *volume += mesh_compute_volume(geom->data.mesh, geom->flip_surface); + break; + case GEOM_INSTANCE: + *volume += instance_compute_volume(geom->data.instance, geom->flip_surface); + break; + default: FATAL("Unreachable code\n"); break; + } + } + +exit: + return res; +error: + goto exit; + +} diff --git a/src/s3d_shape.c b/src/s3d_shape.c @@ -200,58 +200,6 @@ s3d_shape_flip_surface(struct s3d_shape* shape) } res_T -s3d_shape_primitives_count(struct s3d_shape* shape, size_t* nprims) -{ - if(!shape || !nprims) return RES_BAD_ARG; - switch(shape->type) { - case GEOM_MESH: - *nprims = mesh_get_ntris(shape->data.mesh); - break; - case GEOM_INSTANCE: - *nprims = instance_get_ntris(shape->data.instance); - break; - default: FATAL("Unreachable code\n"); break; - } - return RES_OK; -} - -res_T -s3d_shape_compute_area(struct s3d_shape* shape, float* area) -{ - if(!shape || !area) - return RES_BAD_ARG; - switch(shape->type) { - case GEOM_MESH: - *area = mesh_compute_area(shape->data.mesh); - break; - case GEOM_INSTANCE: - *area = instance_compute_area(shape->data.instance); - break; - default: FATAL("Unreachable code\n"); break; - } - return RES_OK; -} - -res_T -s3d_shape_compute_volume(struct s3d_shape* shape, float* volume) -{ - if(!shape || !volume) - return RES_BAD_ARG; - switch(shape->type) { - case GEOM_MESH: - *volume = mesh_compute_volume - (shape->data.mesh, shape->flip_surface); - break; - case GEOM_INSTANCE: - *volume = instance_compute_volume - (shape->data.instance, shape->flip_surface); - break; - default: FATAL("Unreachable code \n"); break; - } - return RES_OK; -} - -res_T s3d_instance_set_position (struct s3d_shape* shape, const float position[3]) { diff --git a/src/test_s3d_scene.c b/src/test_s3d_scene.c @@ -32,6 +32,57 @@ #include "s3d.h" #include "test_s3d_utils.h" +#include "test_s3d_cbox.h" + +#include <rsys/math.h> + +static const float cube_verts[] = { + 5.f, 5.f, 5.f, + 6.f, 5.f, 5.f, + 5.f, 6.f, 5.f, + 6.f, 6.f, 5.f, + 5.f, 5.f, 6.f, + 6.f, 5.f, 6.f, + 5.f, 6.f, 6.f, + 6.f, 6.f, 6.f +}; +static const unsigned cube_nverts= sizeof(cube_verts) / sizeof(float[3]); + +/* Front faces are CW. The normals point into the cube */ +static const unsigned cube_ids[] = { + 0, 2, 1, 1, 2, 3, /* Front */ + 0, 4, 2, 2, 4, 6, /* Left */ + 4, 5, 6, 6, 5, 7, /* Back */ + 3, 7, 1, 1, 7, 5, /* Right */ + 2, 6, 3, 3, 6, 7, /* Top */ + 0, 1, 4, 4, 1, 5 /* Bottom */ +}; +static const unsigned cube_ntris = sizeof(cube_ids) / sizeof(unsigned[3]); + +static void +cube_get_ids(const unsigned itri, unsigned ids[3], void* data) +{ + const unsigned id = itri * 3; + CHECK(data, NULL); + NCHECK(ids, NULL); + CHECK(itri < cube_ntris, 1); + ids[0] = cube_ids[id + 0]; + ids[1] = cube_ids[id + 1]; + ids[2] = cube_ids[id + 2]; +} + +static void +cube_get_pos(const unsigned ivert, float pos[3], void* data) +{ + const unsigned i = ivert*3; + (void)data; + CHECK(data, NULL); + NCHECK(pos, NULL); + CHECK(ivert < cube_nverts, 1); + pos[0] = cube_verts[i + 0]; + pos[1] = cube_verts[i + 1]; + pos[2] = cube_verts[i + 2]; +} int main(int argc, char** argv) @@ -41,9 +92,15 @@ main(int argc, char** argv) struct s3d_scene* scn; struct s3d_scene* scn2; struct s3d_scene* scn3; + struct s3d_vertex_data attribs[2]; struct s3d_shape* shapes[4]; const size_t nshapes = sizeof(shapes)/sizeof(struct s3d_shape*); + void* data = (void*)&cbox_walls_desc; + const unsigned cbox_ntris = sizeof(cbox_walls_ids) / sizeof(unsigned[3]); + const unsigned cbox_nverts = sizeof(cbox_walls) / sizeof(float[3]); size_t i; + size_t nprims; + float area, volume; unsigned id; int mask; (void)argc, (void)argv; @@ -124,8 +181,24 @@ main(int argc, char** argv) CHECK(s3d_scene_attach_shape(scn2, shapes[1]), RES_OK); CHECK(s3d_scene_attach_shape(scn2, shapes[2]), RES_OK); CHECK(s3d_scene_begin_session(scn2, S3D_SAMPLE|S3D_TRACE), RES_OK); + + CHECK(s3d_scene_compute_area(NULL, NULL), RES_BAD_ARG); + CHECK(s3d_scene_compute_area(scn2, NULL), RES_BAD_ARG); + CHECK(s3d_scene_compute_area(NULL, &area), RES_BAD_ARG); + CHECK(s3d_scene_compute_area(scn2, &area), RES_OK); + CHECK(area, 0.f); + + CHECK(s3d_scene_primitives_count(NULL, NULL), RES_BAD_ARG); + CHECK(s3d_scene_primitives_count(scn2, NULL), RES_BAD_ARG); + CHECK(s3d_scene_primitives_count(NULL, &nprims), RES_BAD_ARG); + CHECK(s3d_scene_primitives_count(scn2, &nprims), RES_OK); + CHECK(nprims, 0); + CHECK(s3d_scene_end_session(scn2), RES_OK); + CHECK(s3d_scene_compute_area(scn2, &area), RES_BAD_OP); + CHECK(s3d_scene_primitives_count(scn2, &nprims), RES_BAD_OP); + CHECK(s3d_scene_instantiate(scn2, shapes + 3), RES_OK); CHECK(s3d_scene_attach_shape(scn3, shapes[3]), RES_OK); CHECK(s3d_scene_begin_session(scn3, S3D_SAMPLE|S3D_TRACE), RES_BAD_ARG); @@ -145,6 +218,68 @@ main(int argc, char** argv) CHECK(s3d_scene_ref_put(scn2), RES_OK); CHECK(s3d_scene_ref_put(scn3), RES_OK); + CHECK(s3d_scene_create(dev, &scn), RES_OK); + CHECK(s3d_scene_create(dev, &scn2), RES_OK); + + attribs[0].type = S3D_FLOAT3; + attribs[0].usage = S3D_POSITION; + attribs[0].get = cbox_get_position; + attribs[1] = S3D_VERTEX_DATA_NULL; + CHECK(s3d_shape_create_mesh(dev, shapes + 0), RES_OK); + CHECK(s3d_mesh_setup_indexed_vertices + (shapes[0], cbox_ntris, cbox_get_ids, cbox_nverts, attribs, data), RES_OK); + CHECK(s3d_scene_attach_shape(scn, shapes[0]), RES_OK); + + CHECK(s3d_scene_begin_session(scn, S3D_TRACE), RES_OK); + CHECK(s3d_scene_compute_area(scn, &area), RES_OK); + CHECK(eq_epsf(area, 1532296.f, 1.e-6f), 1); + CHECK(s3d_scene_primitives_count(scn, &nprims), RES_OK); + CHECK(nprims, 10); + CHECK(s3d_scene_end_session(scn), RES_OK); + + CHECK(s3d_scene_begin_session(scn, S3D_TRACE), RES_OK); + CHECK(s3d_scene_compute_area(scn, &area), RES_OK); + CHECK(eq_epsf(area, 1532296.f, 1.e-6f), 1); + CHECK(s3d_scene_primitives_count(scn, &nprims), RES_OK); + CHECK(nprims, 10); + CHECK(s3d_scene_end_session(scn), RES_OK); + + CHECK(s3d_scene_instantiate(scn, shapes + 1), RES_OK); + CHECK(s3d_scene_attach_shape(scn2, shapes[1]), RES_OK); + + CHECK(s3d_scene_begin_session(scn2, S3D_TRACE), RES_OK); + CHECK(s3d_scene_compute_area(scn2, &area), RES_OK); + CHECK(eq_epsf(area, 1532296.f, 1.e-6f), 1); + CHECK(s3d_scene_primitives_count(scn, &nprims), RES_OK); + CHECK(nprims, 10); + CHECK(s3d_scene_end_session(scn2), RES_OK); + + CHECK(s3d_scene_begin_session(scn2, S3D_SAMPLE), RES_OK); + CHECK(s3d_scene_compute_area(scn2, &area), RES_OK); + CHECK(eq_epsf(area, 1532296.f, 1.e-6f), 1); + CHECK(s3d_scene_primitives_count(scn, &nprims), RES_OK); + CHECK(nprims, 10); + CHECK(s3d_scene_end_session(scn2), RES_OK); + + attribs[0].type = S3D_FLOAT3; + attribs[0].usage = S3D_POSITION; + attribs[0].get = cube_get_pos; + attribs[1] = S3D_VERTEX_DATA_NULL; + CHECK(s3d_mesh_setup_indexed_vertices + (shapes[0], cube_ntris, cube_get_ids, cube_nverts, attribs, NULL), RES_OK); + + CHECK(s3d_scene_begin_session(scn, S3D_TRACE), RES_OK); + CHECK(s3d_scene_compute_area(scn, &area), RES_OK); + CHECK(eq_epsf(area, 6.f, 1.e-6f), 1); + CHECK(s3d_scene_compute_volume(scn, &volume), RES_OK); + CHECK(eq_epsf(volume, 1.f, 1.e-6f), 1); + CHECK(s3d_scene_end_session(scn), RES_OK); + + CHECK(s3d_scene_ref_put(scn), RES_OK); + CHECK(s3d_scene_ref_put(scn2), RES_OK); + CHECK(s3d_shape_ref_put(shapes[0]), RES_OK); + CHECK(s3d_shape_ref_put(shapes[1]), RES_OK); + CHECK(s3d_device_ref_put(dev), RES_OK);; check_memory_allocator(&allocator); diff --git a/src/test_s3d_shape.c b/src/test_s3d_shape.c @@ -36,54 +36,6 @@ #include <rsys/math.h> -static const float cube_verts[] = { - 5.f, 5.f, 5.f, - 6.f, 5.f, 5.f, - 5.f, 6.f, 5.f, - 6.f, 6.f, 5.f, - 5.f, 5.f, 6.f, - 6.f, 5.f, 6.f, - 5.f, 6.f, 6.f, - 6.f, 6.f, 6.f -}; -static const unsigned cube_nverts= sizeof(cube_verts) / sizeof(float[3]); - -/* Front faces are CW. The normals point into the cube */ -static const unsigned cube_ids[] = { - 0, 2, 1, 1, 2, 3, /* Front */ - 0, 4, 2, 2, 4, 6, /* Left */ - 4, 5, 6, 6, 5, 7, /* Back */ - 3, 7, 1, 1, 7, 5, /* Right */ - 2, 6, 3, 3, 6, 7, /* Top */ - 0, 1, 4, 4, 1, 5 /* Bottom */ -}; -static const unsigned cube_ntris = sizeof(cube_ids) / sizeof(unsigned[3]); - -static void -cube_get_ids(const unsigned itri, unsigned ids[3], void* data) -{ - const unsigned id = itri * 3; - CHECK(data, NULL); - NCHECK(ids, NULL); - CHECK(itri < cube_ntris, 1); - ids[0] = cube_ids[id + 0]; - ids[1] = cube_ids[id + 1]; - ids[2] = cube_ids[id + 2]; -} - -static void -cube_get_pos(const unsigned ivert, float pos[3], void* data) -{ - const unsigned i = ivert*3; - (void)data; - CHECK(data, NULL); - NCHECK(pos, NULL); - CHECK(ivert < cube_nverts, 1); - pos[0] = cube_verts[i + 0]; - pos[1] = cube_verts[i + 1]; - pos[2] = cube_verts[i + 2]; -} - int main(int argc, char** argv) { @@ -93,10 +45,7 @@ main(int argc, char** argv) struct s3d_shape* inst; struct s3d_scene* scn; struct s3d_vertex_data attribs[4]; - size_t nprims; float pos[3]; - float area; - float volume; const unsigned cbox_ntris = sizeof(cbox_walls_ids) / sizeof(unsigned[3]); const unsigned cbox_nverts = sizeof(cbox_walls) / sizeof(float[3]); unsigned id; @@ -126,20 +75,8 @@ main(int argc, char** argv) CHECK(s3d_shape_is_attached(shape, &c), RES_OK); CHECK(c, 0); - CHECK(s3d_shape_primitives_count(NULL, NULL), RES_BAD_ARG); - CHECK(s3d_shape_primitives_count(shape, NULL), RES_BAD_ARG); - CHECK(s3d_shape_primitives_count(NULL, &nprims), RES_BAD_ARG); - CHECK(s3d_shape_primitives_count(shape, &nprims), RES_OK); - CHECK(nprims, 0); - - CHECK(s3d_shape_compute_area(NULL, NULL), RES_BAD_ARG); - CHECK(s3d_shape_compute_area(shape, NULL), RES_BAD_ARG); - CHECK(s3d_shape_compute_area(NULL, &area), RES_BAD_ARG); - CHECK(s3d_shape_compute_area(shape, &area), RES_OK); - CHECK(area, 0.f); - - 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, NULL), RES_BAD_ARG); + CHECK(s3d_scene_attach_shape(scn, NULL), RES_BAD_ARG); CHECK(s3d_scene_attach_shape(NULL, shape), RES_BAD_ARG); CHECK(s3d_scene_attach_shape(scn, shape), RES_OK); CHECK(s3d_shape_is_attached(shape, &c), RES_OK); @@ -221,11 +158,6 @@ main(int argc, char** argv) CHECK(s3d_mesh_setup_indexed_vertices (shape, cbox_ntris, cbox_get_ids, cbox_nverts, attribs, data), RES_OK); - CHECK(s3d_shape_primitives_count(shape, &nprims), RES_OK); - CHECK(nprims, 10); - CHECK(s3d_shape_compute_area(shape, &area), RES_OK); - CHECK(eq_epsf(area, 1532296.f, 1.e-6f), 1); - attribs[0] = S3D_VERTEX_DATA_NULL; CHECK(s3d_mesh_setup_indexed_vertices (shape, cbox_ntris, cbox_get_ids, cbox_nverts, attribs, data), RES_BAD_ARG); @@ -325,20 +257,6 @@ main(int argc, char** argv) CHECK(s3d_shape_ref_put(inst), RES_OK); CHECK(s3d_scene_ref_put(scn), RES_OK); - attribs[0].type = S3D_FLOAT3; - attribs[0].usage = S3D_POSITION; - attribs[0].get = cube_get_pos; - attribs[1] = S3D_VERTEX_DATA_NULL; - CHECK(s3d_shape_create_mesh(dev, &shape), RES_OK); - CHECK(s3d_mesh_setup_indexed_vertices - (shape, cube_ntris, cube_get_ids, cube_nverts, attribs, NULL), RES_OK); - CHECK(s3d_shape_primitives_count(shape, &nprims), RES_OK); - CHECK(nprims, 12); - CHECK(s3d_shape_compute_area(shape, &area), RES_OK); - CHECK(eq_epsf(area, 6.f, 1.e-6f), 1); - CHECK(s3d_shape_compute_volume(shape, &volume), RES_OK); - CHECK(eq_epsf(volume, 1.f, 1.e-6f), 1); - CHECK(s3d_shape_ref_put(shape), RES_OK); CHECK(s3d_device_ref_put(dev), RES_OK);; check_memory_allocator(&allocator); diff --git a/src/test_s3d_trace_ray.c b/src/test_s3d_trace_ray.c @@ -184,8 +184,6 @@ main(int argc, char** argv) CHECK(s3d_shape_get_id(tall_block, &tall_block_id), RES_OK); CHECK(s3d_mesh_setup_indexed_vertices (tall_block, ntris, cbox_get_ids, nverts, attribs, &desc), RES_OK); - CHECK(s3d_shape_primitives_count(tall_block, &nprims), RES_OK); - CHECK(nprims, 10); CHECK(s3d_scene_attach_shape(scn, tall_block), RES_OK); /* Update the inst vertices */ @@ -199,14 +197,14 @@ main(int argc, char** argv) CHECK(s3d_shape_get_id(short_block, &short_block_id), RES_OK); CHECK(s3d_mesh_setup_indexed_vertices (short_block, ntris, cbox_get_ids, nverts, attribs, &desc), RES_OK); - CHECK(s3d_shape_primitives_count(short_block, &nprims), RES_OK); - CHECK(nprims, 10); CHECK(s3d_scene_attach_shape(scn, short_block), RES_OK); /* Instantiate the scene */ CHECK(s3d_scene_instantiate(scn, &inst), RES_OK); - CHECK(s3d_shape_primitives_count(inst, &nprims), RES_OK); + CHECK(s3d_scene_begin_session(scn, S3D_SAMPLE), RES_OK); + CHECK(s3d_scene_primitives_count(scn, &nprims), RES_OK); CHECK(nprims, 20); + CHECK(s3d_scene_end_session(scn), RES_OK); CHECK(s3d_shape_get_id(inst, &inst_id), RES_OK); /* Create the CBox walls */ @@ -218,11 +216,7 @@ main(int argc, char** argv) CHECK(s3d_shape_get_id(walls, &walls_id), RES_OK); CHECK(s3d_mesh_setup_indexed_vertices (walls, ntris, cbox_get_ids, nverts, attribs, &desc), RES_OK); - CHECK(s3d_shape_primitives_count(walls, &nprims), RES_OK); - CHECK(nprims, 10); CHECK(s3d_scene_attach_shape(scn, walls), RES_OK); - CHECK(s3d_shape_primitives_count(inst, &nprims), RES_OK); - CHECK(nprims, 30); /* Check that the ids are all different */ NCHECK(walls_id, short_block_id); @@ -258,6 +252,8 @@ main(int argc, char** argv) CHECK(s3d_scene_attach_shape(scn, tall_block), RES_OK); CHECK(s3d_scene_begin_session(scn2, S3D_TRACE), RES_OK); + CHECK(s3d_scene_primitives_count(scn2, &nprims), RES_OK); + CHECK(nprims, 30); camera_init(&cam); FOR_EACH(iy, 0, IMG_HEIGHT) {