star-vx

Structuring voxels for ray-tracing
git clone git://git.meso-star.fr/star-vx.git
Log | Files | Refs | README | LICENSE

commit d01a4ad76131dd4e236fb569823dae267dcd27cc
parent 985c112d6b0876f0f8f6f32b24060a64d7979b50
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Wed,  4 Apr 2018 16:21:35 +0200

Add several use cases in the trace_ray test

Diffstat:
Mcmake/CMakeLists.txt | 6+++---
Msrc/svx.h | 2++
Msrc/svx_octree.c | 6+++---
Msrc/svx_octree_trace_ray.c | 23+++++++++++++++--------
Msrc/test_svx_octree_trace_ray.c | 155+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 178 insertions(+), 14 deletions(-)

diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt @@ -78,17 +78,17 @@ if(NOT NO_TEST) add_executable(${_name} ${SVX_SOURCE_DIR}/${_name}.c ${SVX_SOURCE_DIR}/test_svx_utils.h) - target_link_libraries(${_name} svx RSys) + target_link_libraries(${_name} svx RSys ${ARGN}) endfunction() function(new_test _name) - build_test(${_name}) + build_test(${_name} ${ARGN}) add_test(${_name} ${_name}) endfunction() new_test(test_svx_device) new_test(test_svx_octree) - new_test(test_svx_octree_trace_ray) + new_test(test_svx_octree_trace_ray m) endif() ################################################################################ diff --git a/src/svx.h b/src/svx.h @@ -55,6 +55,8 @@ struct svx_voxel { NULL, SIZE_MAX, SIZE_MAX, 0 } static const struct svx_voxel SVX_VOXEL_NULL = SVX_VOXEL_NULL__; +#define SVX_VOXEL_EQ(V0, V1) ((V0)->id == (V1)->id) + #define SVX_VOXEL_NONE(Voxel) ((Voxel)->id == SVX_VOXEL_NULL.id) /* Descriptor of a voxel */ diff --git a/src/svx_octree.c b/src/svx_octree.c @@ -568,9 +568,9 @@ svx_octree_create oct->oclow[0] = lower[0]; oct->oclow[1] = lower[1]; oct->oclow[2] = lower[2]; - oct->ocupp[0] = (double)oct->definition * vox_sz[0]; - oct->ocupp[1] = (double)oct->definition * vox_sz[1]; - oct->ocupp[2] = (double)oct->definition * vox_sz[2]; + oct->ocupp[0] = oct->oclow[0] + (double)oct->definition * vox_sz[0]; + oct->ocupp[1] = oct->oclow[1] + (double)oct->definition * vox_sz[1]; + oct->ocupp[2] = oct->oclow[2] + (double)oct->definition * vox_sz[2]; oct->ocsize[0] = oct->ocupp[0] - oct->oclow[0]; oct->ocsize[1] = oct->ocupp[1] - oct->oclow[1]; oct->ocsize[2] = oct->ocupp[2] - oct->oclow[2]; diff --git a/src/svx_octree_trace_ray.c b/src/svx_octree_trace_ray.c @@ -148,8 +148,8 @@ setup_ray /* Mirror rays with position directions */ ray->octant_mask = 0; if(dir[0] > 0) { ray->octant_mask ^= 4; ray->org[0] = 3.0 - ray->org[0]; } - if(dir[1] > 0) { ray->octant_mask ^= 2; ray->org[0] = 3.0 - ray->org[1]; } - if(dir[2] > 0) { ray->octant_mask ^= 1; ray->org[0] = 3.0 - ray->org[2]; } + if(dir[1] > 0) { ray->octant_mask ^= 2; ray->org[1] = 3.0 - ray->org[1]; } + if(dir[2] > 0) { ray->octant_mask ^= 1; ray->org[2] = 3.0 - ray->org[2]; } /* Save the world space ray origin */ ray->orgws[0] = org[0]; @@ -218,7 +218,7 @@ trace_ray /* Octree traversal */ scale_max = scale + 1; - while(scale < scale_max) { + while(scale < scale_max && t_min < t_max) { const struct octree_xnode* node = octree_buffer_get_node(&oct->buffer, inode); double t_corner[3]; double t_max_corner; @@ -242,21 +242,24 @@ trace_ray /* If the current voxel is a leaf or if a challenge function is set, * check the current hit */ if(is_leaf || challenge) { - const double dst = t_min < ray->range[0] ? t_max_child : t_min; + struct svx_hit hit_tmp; + const double dst = t_min <= ray->range[0] ? t_max_child : t_min; const size_t depth = SCALE_MAX - scale; const struct octree_index iattr = octree_buffer_get_child_attr_index (&oct->buffer, inode, (int)ichild_adjusted); setup_hit(oct, iattr, dst, corner, scale_exp2, depth, is_leaf, - ray->octant_mask, hit); + ray->octant_mask, &hit_tmp); - if(is_leaf || challenge(hit, context)) { + if(is_leaf || challenge(&hit_tmp, context)) { go_deeper = 0; /* Stop the traversal if no filter is defined or if the filter * function returns 0 */ if(!filter /* By default, i.e. with no filter, stop the traversal */ - || !filter(hit, ray->orgws, ray->dirws, ray->range, context)) + || !filter(&hit_tmp, ray->orgws, ray->dirws, ray->range, context)) { + *hit = hit_tmp; break; + } } } @@ -282,7 +285,11 @@ trace_ray stack[scale].t_max = t_max_parent; stack[scale].inode = inode; - /* Define the id and the lower left corner of the first child */ + /* Get the node index of the traversed child */ + inode = octree_buffer_get_child_node_index + (&oct->buffer, inode, (int)ichild_adjusted); + + /* Define the id and the lower left corner of the first grand child */ ichild = 0; if(t_center[0] > t_min) { ichild ^= 4; corner[0] += scale_exp2_child; } if(t_center[1] > t_min) { ichild ^= 2; corner[1] += scale_exp2_child; } diff --git a/src/test_svx_octree_trace_ray.c b/src/test_svx_octree_trace_ray.c @@ -13,11 +13,23 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#define _POSIX_C_SOURCE 200112L /* nextafter function */ + #include "svx.h" #include "test_svx_utils.h" +#include <math.h> + +#include <rsys/double2.h> +#include <rsys/double3.h> #include <rsys/math.h> +struct ray { + double org[3]; + double dir[3]; + double range[3]; +}; + struct scene { double origin[3]; double vxsz[3]; @@ -119,17 +131,83 @@ write_scalars fprintf(stream, "%d\n", *(char*)leaf->data); } +static int +hit_filter + (const struct svx_hit* hit, + const double ray_org[3], + const double ray_dir[3], + const double ray_range[3], + void* context) +{ + const struct ray* ray = context; + + CHK(hit && ray_org && ray_dir && ray_range && context); + CHK(d3_eq(ray->org, ray_org)); + CHK(d3_eq(ray->dir, ray_dir)); + CHK(d2_eq(ray->range, ray_range)); + CHK(!SVX_HIT_NONE(hit)); + + return *((char*)hit->voxel.data) == 0; +} + +static int +hit_filter2 + (const struct svx_hit* hit, + const double ray_org[3], + const double ray_dir[3], + const double ray_range[3], + void* context) +{ + const struct svx_voxel* voxel = context; + + CHK(hit && ray_org && ray_dir && ray_range && context); + CHK(!SVX_HIT_NONE(hit)); + return SVX_VOXEL_EQ(&hit->voxel, voxel) + || *((char*)hit->voxel.data) == 0; +} + +static int +hit_filter3 + (const struct svx_hit* hit, + const double ray_org[3], + const double ray_dir[3], + const double ray_range[3], + void* context) +{ + int* accum = context; + CHK(hit && ray_org && ray_dir && ray_range && context); + CHK(!SVX_HIT_NONE(hit)); + *accum += *(char*)hit->voxel.data; + return 1; +} + +static int +hit_challenge(const struct svx_hit* hit, void* context) +{ + (void)context; + CHK(hit); + CHK(!SVX_HIT_NONE(hit)); + return 1; +} + int main(int argc, char** argv) { struct scene scn; + struct ray r; struct svx_device* dev = NULL; struct svx_octree* oct = NULL; + struct svx_octree_desc octree_desc = SVX_OCTREE_DESC_NULL; struct svx_voxel_desc voxel_desc = SVX_VOXEL_DESC_NULL; + struct svx_hit hit = SVX_HIT_NULL; + struct svx_hit hit2 = SVX_HIT_NULL; const double lower[3] = {-1,-1,-1}; const double upper[3] = { 1, 1, 1}; const size_t def[3] = {32, 32, 32}; double scnsz[3]; + double vxsz; + double dst; + int accum; (void)argc, (void)argv; CHK(svx_device_create(NULL, NULL, 1, &dev) == RES_OK); @@ -159,6 +237,83 @@ main(int argc, char** argv) dump_data(stdout, oct, CHAR, 1, write_scalars); + #define RT svx_octree_trace_ray + d3(r.org, -5,-5, 0); + d3(r.dir, 0, 1, 0); + d2(r.range, 0, INF); + CHK(RT(oct, r.org, r.dir, r.range, NULL, NULL, NULL, &hit) == RES_OK); + CHK(SVX_HIT_NONE(&hit)); + + r.org[0] = 0; + CHK(RT(oct, r.org, r.dir, r.range, NULL, NULL, NULL, &hit) == RES_OK); + CHK(!SVX_HIT_NONE(&hit)); + CHK(eq_eps(hit.distance, hit.voxel.lower[1] - r.org[1], 1.e-6)); + CHK(hit.voxel.is_leaf); + CHK(*(char*)hit.voxel.data == 0); + + /* Use challenge functor to intersect voxels that are not leaves */ + CHK(svx_octree_get_desc(oct, &octree_desc) == RES_OK); + CHK(RT(oct, r.org, r.dir, r.range, hit_challenge, NULL, NULL, &hit2) == RES_OK); + CHK(!SVX_HIT_NONE(&hit2)); + CHK(!SVX_VOXEL_EQ(&hit.voxel, &hit2.voxel)); + CHK(hit2.voxel.is_leaf == 0); + CHK(*(char*)hit2.voxel.data == 1); + CHK(eq_eps(hit.distance, hit2.distance, 1.e-6)); + + /* Use filter function to discard leaves with a value == 0 */ + CHK(RT(oct, r.org, r.dir, r.range, NULL, hit_filter, &r, &hit) == RES_OK); + CHK(!SVX_HIT_NONE(&hit)); + CHK(eq_eps(hit.distance, hit.voxel.lower[1] - r.org[1], 1.e-6)); + CHK(hit.voxel.is_leaf); + CHK(*(char*)hit.voxel.data == 1); + + /* Use th ray range to vvoid the intersection closest voxel */ + r.range[1] = nextafter(hit.distance, -1); + CHK(RT(oct, r.org, r.dir, r.range, NULL, hit_filter, &r, &hit) == RES_OK); + CHK(SVX_HIT_NONE(&hit)); + + r.range[1] = INF; + CHK(RT(oct, r.org, r.dir, r.range, NULL, hit_filter, &r, &hit) == RES_OK); + CHK(!SVX_HIT_NONE(&hit)); + CHK(*(char*)hit.voxel.data == 1); + CHK(hit.voxel.is_leaf); + + /* Use the ray range to discard the closest voxel */ + dst = hit.voxel.upper[1] - r.org[1]; + r.range[0] = nextafter(dst, DBL_MAX); + CHK(RT(oct, r.org, r.dir, r.range, NULL, hit_filter, &r, &hit2) == RES_OK); + CHK(!SVX_HIT_NONE(&hit2)); + CHK(!SVX_VOXEL_EQ(&hit.voxel, &hit2.voxel)); + vxsz = hit2.voxel.upper[1] - hit2.voxel.lower[1]; + CHK(eq_eps(hit2.distance, dst + vxsz, 1.e-6)); + CHK(*(char*)hit.voxel.data == 1); + CHK(hit.voxel.is_leaf); + + /* Adjust the ray range to hit the interior of the closest voxel */ + r.range[0] = nextafter(hit.distance, DBL_MAX); + CHK(RT(oct, r.org, r.dir, r.range, NULL, hit_filter, &r, &hit2) == RES_OK); + CHK(!SVX_HIT_NONE(&hit2)); + CHK(SVX_VOXEL_EQ(&hit.voxel, &hit2.voxel)); + CHK(eq_eps(hit2.distance, dst, 1.e-6)); + CHK(*(char*)hit.voxel.data == 1); + CHK(hit.voxel.is_leaf); + + /* Discard the closest voxel with the filter function */ + r.range[0] = 0; + CHK(RT(oct, r.org, r.dir, r.range, NULL, hit_filter2, &hit.voxel, &hit2) == RES_OK); + CHK(eq_eps(hit2.distance, hit2.voxel.lower[1] - r.org[1], 1.e-6)); + CHK(!SVX_HIT_NONE(&hit2)); + CHK(!SVX_VOXEL_EQ(&hit.voxel, &hit2.voxel)); + CHK(eq_eps(hit2.distance, dst, 1.e-6)); + CHK(*(char*)hit.voxel.data == 1); + CHK(hit.voxel.is_leaf); + + /* Use the filter functor to accumulate the leaves */ + accum = 0; + CHK(RT(oct, r.org, r.dir, r.range, NULL, hit_filter3, &accum, &hit) == RES_OK); + CHK(SVX_HIT_NONE(&hit)); + CHK(accum != 0); + CHK(svx_octree_ref_put(oct) == RES_OK); CHK(svx_device_ref_put(dev) == RES_OK); CHK(mem_allocated_size() == 0);