star-vx

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

commit 695d9c868c1efa18fa18b1aa35dd7ecf574533cf
parent e11f21b907a6d11677627f5a4671dd923366f181
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Mon,  5 Feb 2018 11:35:56 +0100

Add a basic test on the scene creation

Diffstat:
Mcmake/CMakeLists.txt | 1+
Msrc/htvox.h | 6++++++
Msrc/htvox_octree_buffer.h | 10++++++----
Msrc/htvox_scene.c | 87++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
Msrc/htvox_scene.h | 2++
Asrc/test_htvox_scene.c | 114+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
6 files changed, 206 insertions(+), 14 deletions(-)

diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt @@ -86,6 +86,7 @@ if(NOT NO_TEST) endfunction() new_test(test_htvox_device) + new_test(test_htvox_scene) endif() ################################################################################ diff --git a/src/htvox.h b/src/htvox.h @@ -97,6 +97,12 @@ htvox_scene_ref_put (struct htvox_scene* scn); HTVOX_API res_T +htvox_scene_get_aabb + (const struct htvox_scene* scn, + double lower[3], + double upper[3]); + +HTVOX_API res_T htvox_scene_trace_ray (struct htvox_scene* scn, const double ray_origin[3], diff --git a/src/htvox_octree_buffer.h b/src/htvox_octree_buffer.h @@ -35,8 +35,8 @@ #define OCTREE_XNODE_FLAG_FAR_INDEX 0x80000000u #define OCTREE_XNODE_FLAG_LEAF 0x40000000u -#define OCTREE_XNODE_EMPTY 0xC0000000u -#define OCTREE_XNODE_MAX_CHILDREN_OFFSET (BIT(30)-1) +#define OCTREE_XNODE_FLAG_EMPTY 0x20000000u +#define OCTREE_XNODE_MAX_CHILDREN_OFFSET (BIT(29)-1) #define OCTREE_XNODE_MASK OCTREE_XNODE_MAX_CHILDREN_OFFSET struct octree_xnode { @@ -49,6 +49,8 @@ struct octree_xnode { * contain anything and thus has no child. */ uint32_t offset; }; +#define OCTREE_XNODE_IS_LEAF(N) (((N)->offset & OCTREE_XNODE_FLAG_LEAF)!=0) +#define OCTREE_XNODE_IS_EMPTY(N) (((N)->offset & OCTREE_XNODE_FLAG_EMPTY)!=0) #define OCTREE_INDEX_IPAGE_MAX UINT32_MAX #define OCTREE_INDEX_INODE_MAX UINT16_MAX @@ -171,7 +173,7 @@ octree_buffer_get_child_index ASSERT(ichild >= 0 && ichild < 8 && buf); node = octree_buffer_get_node(buf, id); - if(node->offset == OCTREE_XNODE_EMPTY) return OCTREE_INDEX_NULL; + if(node->offset & OCTREE_XNODE_FLAG_EMPTY) return OCTREE_INDEX_NULL; node_offset = (node->offset & OCTREE_XNODE_MASK); if(!(node->offset & OCTREE_XNODE_FLAG_FAR_INDEX)) { @@ -179,7 +181,7 @@ octree_buffer_get_child_index child_id.inode = (uint16_t)(id.inode - node_offset + (uint32_t)ichild); } else { child_id = *(struct octree_index*) - ((char*)node + node_offset*sizeof(struct octree_index)); + ((char*)node + node_offset*sizeof(struct octree_xnode)); child_id.inode = (uint16_t)(child_id.inode + ichild); } return child_id; diff --git a/src/htvox_scene.c b/src/htvox_scene.c @@ -18,6 +18,7 @@ #include "htvox_device.h" #include "htvox_scene.h" +#include <rsys/double3.h> #include <rsys/mem_allocator.h> #define OCTREE_DEPTH_MAX 16 /* Maximum depth of an octree */ @@ -52,6 +53,29 @@ struct octree_builder { /******************************************************************************* * Helper function ******************************************************************************/ +#ifndef NDEBUG +static void +check_octree(struct octree_buffer* buf, const struct octree_index root) +{ + const struct octree_xnode* node; + ASSERT(buf); + + node = octree_buffer_get_node(buf, root); + if(OCTREE_XNODE_IS_EMPTY(node)) { + /* Do nothing */ + } else if(OCTREE_XNODE_IS_LEAF(node)) { + struct octree_index ichild = octree_buffer_get_child_index(buf, root, 0); + ASSERT(octree_buffer_get_leaf(buf, ichild) != NULL); + } else { + int i; + FOR_EACH(i, 0, 8) { /* The octree is not sparse */ + struct octree_index ichild = octree_buffer_get_child_index(buf, root, i); + check_octree(buf, ichild); + } + } +} +#endif + static INLINE void stack_clear(struct stack* stack) { @@ -139,7 +163,7 @@ stack_write size_t offset = SIZE_MAX; if(node->data == VOXEL_EMPTY_DATA) { - xnodes[inode].offset = OCTREE_XNODE_EMPTY; + xnodes[inode].offset |= OCTREE_XNODE_FLAG_EMPTY; } else { if(node->is_leaf) { double* leaf; @@ -168,7 +192,10 @@ stack_write * stored relatively to its associated node. Stop the write process and * rewrite the whole stacked nodes in a new page. */ res = octree_buffer_alloc_far_index(buffer, &index); - if(res != RES_OK) break; + if(res != RES_OK) { + printf("Hop\n"); + break; + } far_id = octree_buffer_get_far_index(buffer, index); *far_id = node->ichildren; @@ -252,9 +279,10 @@ octree_builder_add_voxel(struct octree_builder* bldr, const struct voxel* vox) uint64_t mcode_max_lvl; /* Compute the maximum morton code value for the current octree level */ - mcode_max_lvl = 8lu/*#children*/ * (1lu<<(3*(ilvl+1)))/*#voxels per children*/; + mcode_max_lvl = 8lu/*#children*/ * (1lu<<(3*ilvl))/*#voxels per children*/; if(mcode_xor < mcode_max_lvl) break; + ASSERT(bldr->stacks[ilvl].len == 8); /* The octree is not sparse */ /* The next voxel is not in the ilvl^th stack. Setup the parent node of the * nodes registered into the stack */ @@ -265,7 +293,6 @@ octree_builder_add_voxel(struct octree_builder* bldr, const struct voxel* vox) ASSERT(bldr->stacks[ilvl+1].len <= 8); /* Write the nodes of the stack of the current octree level into the buf */ - ASSERT(bldr->stacks[ilvl].len == 8); /* The octree is not sparse */ res = stack_write(&bldr->stacks[ilvl], bldr->buffer, &stack_node->ichildren); if(res != RES_OK) goto error; @@ -293,17 +320,38 @@ octree_builder_finalize struct octree_index* root_id) { int ilvl; + res_T res = RES_OK; ASSERT(bldr); -#ifndef NDEBUG - /* Since the octree is not sparse, all the levels should be cleaned up excepted - * the root level that should contain the root node */ + + /* Flush the stacked nodes */ FOR_EACH(ilvl, 0, bldr->octree_depth-1) { - ASSERT(bldr->stacks[ilvl].len == 0); + struct octree_node* parent_node; + ASSERT(bldr->stacks[ilvl+0].len == 8); + ASSERT(bldr->stacks[ilvl+1].len <= 7); + + /* Fetch the parent node */ + parent_node = &bldr->stacks[ilvl+1].nodes[bldr->stacks[ilvl+1].len]; + + /* Setup the parent node of the nodes registered into the current stack */ + stack_setup_node + (&bldr->stacks[ilvl], parent_node, bldr->merge, bldr->context); + bldr->stacks[ilvl+1].len += 1; + + /* Write the stacked nodes of the current level */ + res = stack_write(&bldr->stacks[ilvl], bldr->buffer, &parent_node->ichildren); + if(res != RES_OK) goto error; } ASSERT(bldr->stacks[bldr->octree_depth-1].len == 1); -#endif + + /* Write the root node */ ilvl = bldr->octree_depth-1; /* Root level */ - return stack_write(&bldr->stacks[ilvl], bldr->buffer, root_id); + res = stack_write(&bldr->stacks[ilvl], bldr->buffer, root_id); + if(res != RES_OK) goto error; + +exit: + return res; +error: + goto exit; } static void @@ -313,6 +361,7 @@ scene_release(ref_T* ref) struct htvox_device* dev; ASSERT(ref); scn = CONTAINER_OF(ref, struct htvox_scene, ref); + octree_buffer_release(&scn->buffer); dev = scn->dev; MEM_RM(dev->allocator, scn); HTVOX(device_ref_put(dev)); @@ -430,6 +479,13 @@ htvox_scene_create res = octree_builder_finalize(&bldr, &scn->root); if(res != RES_OK) goto error; +#ifndef NDEBUG + check_octree(&scn->buffer, scn->root); +#endif + + d3_set(scn->lower, lower); + d3_set(scn->upper, upper); + exit: if(out_scn) *out_scn = scn; return res; @@ -457,3 +513,14 @@ htvox_scene_ref_put(struct htvox_scene* scn) return RES_OK; } +res_T +htvox_scene_get_aabb + (const struct htvox_scene* scn, + double lower[3], + double upper[3]) +{ + if(!scn || !lower || !upper) return RES_BAD_ARG; + d3_set(lower, scn->lower); + d3_set(upper, scn->upper); + return RES_OK; +} diff --git a/src/htvox_scene.h b/src/htvox_scene.h @@ -23,6 +23,8 @@ struct htvox_scene { double vox_scale[3]; /* Scale factor of the octree */ size_t definition; /* Definition of the octree */ + double lower[3], upper[3]; /* World space AABB */ + struct octree_buffer buffer; struct octree_index root; /* Index toward the root node of the octree */ diff --git a/src/test_htvox_scene.c b/src/test_htvox_scene.c @@ -0,0 +1,114 @@ +/* Copyright (C) CNRS 2018 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include "htvox.h" +#include "htvox_c.h" /* For morton_xyz_encode_u21 */ +#include "test_htvox_utils.h" + +#include <rsys/double3.h> + +static int +merge(const double min_val, const double max_val, void* ctx) +{ + CHK((intptr_t)ctx == 0xDECAFBAD); + return (max_val - min_val) < 0.5; +} + +static void +get(const size_t xyz[3], double* val, void* ctx) +{ + uint32_t ui3[3]; + uint64_t mcode; + CHK(xyz != NULL); + CHK(val != NULL); + CHK((intptr_t)ctx == 0xDECAFBAD); + + ui3[0] = (uint32_t)xyz[0]; + ui3[1] = (uint32_t)xyz[1]; + ui3[2] = (uint32_t)xyz[2]; + + mcode = morton_xyz_encode_u21(ui3); + *val = (double)(mcode % 8) / (double)4; +} + +int +main(int argc, char** argv) +{ + struct htvox_device* dev = NULL; + struct htvox_scene* scn = NULL; + struct mem_allocator allocator; + double low[3]; + double upp[3]; + size_t nvxls[3]; + void* ctx = (void*)0xDECAFBAD; + (void)argc, (void)argv; + + CHK(mem_init_proxy_allocator(&allocator, &mem_default_allocator) == RES_OK); + + CHK(htvox_device_create(NULL, &allocator, 1, &dev) == RES_OK); + + d3_splat(low, 0.0); + d3_splat(upp, 1.0); + nvxls[0] = nvxls[1] = nvxls[2] = 10; + + #define NEW_SCN htvox_scene_create + + CHK(NEW_SCN(dev, low, upp, nvxls, get, merge, ctx, &scn) == RES_OK); + + CHK(htvox_scene_ref_get(NULL) == RES_BAD_ARG); + CHK(htvox_scene_ref_get(scn) == RES_OK); + CHK(htvox_scene_ref_put(NULL) == RES_BAD_ARG); + CHK(htvox_scene_ref_put(scn) == RES_OK); + CHK(htvox_scene_ref_put(scn) == RES_OK); + + upp[0] = low[0]; + CHK(NEW_SCN(dev, low, upp, nvxls, get, merge, ctx, &scn) == RES_BAD_ARG); + upp[0] = 1.0; + + nvxls[2] = 0; + CHK(NEW_SCN(dev, low, upp, nvxls, get, merge, ctx, &scn) == RES_BAD_ARG); + nvxls[2] = 10; + + CHK(NEW_SCN(NULL, low, upp, nvxls, get, merge, ctx, &scn) == RES_BAD_ARG); + CHK(NEW_SCN(dev, NULL, upp, nvxls, get, merge, ctx, &scn) == RES_BAD_ARG); + CHK(NEW_SCN(dev, low, NULL, nvxls, get, merge, ctx, &scn) == RES_BAD_ARG); + CHK(NEW_SCN(dev, low, upp, NULL, get, merge, ctx, &scn) == RES_BAD_ARG); + CHK(NEW_SCN(dev, low, upp, nvxls, NULL, merge, ctx, &scn) == RES_BAD_ARG); + CHK(NEW_SCN(dev, low, upp, nvxls, get, NULL, ctx, &scn) == RES_BAD_ARG); + CHK(NEW_SCN(dev, low, upp, nvxls, get, merge, ctx, NULL) == RES_BAD_ARG); + + CHK(NEW_SCN(dev, low, upp, nvxls, get, merge, ctx, &scn) == RES_OK); + + #undef NEW_SCN + + d3_splat(low, DBL_MAX); + d3_splat(upp,-DBL_MAX); + CHK(htvox_scene_get_aabb(scn, low, upp) == RES_OK); + CHK(low[0] == 0 && low[1] == 0 && low[2] == 0); + CHK(upp[0] == 1 && upp[1] == 1 && upp[2] == 1); + + CHK(htvox_scene_get_aabb(NULL, low, upp) == RES_BAD_ARG); + CHK(htvox_scene_get_aabb(scn, NULL, upp) == RES_BAD_ARG); + CHK(htvox_scene_get_aabb(scn, low, NULL) == RES_BAD_ARG); + + CHK(htvox_device_ref_put(dev) == RES_OK); + CHK(htvox_scene_ref_put(scn) == RES_OK); + + check_memory_allocator(&allocator); + mem_shutdown_proxy_allocator(&allocator); + CHK(mem_allocated_size() == 0); + return 0; +} +