star-vx

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

commit e52d051a5a3c9b5be7bb07258ce265af6e4c8dd4
parent 8d06f72773e62c5e259e5087b4ed0fb59bc94a35
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Fri,  9 Feb 2018 13:10:06 +0100

Draft of new memory layout and build algorithm of the octree

Diffstat:
Msrc/htvox_c.h | 10++++++++++
Msrc/htvox_octree_buffer.h | 85++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------
Msrc/htvox_scene.c | 442+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
Msrc/test_htvox_scene.c | 2+-
4 files changed, 328 insertions(+), 211 deletions(-)

diff --git a/src/htvox_c.h b/src/htvox_c.h @@ -44,6 +44,16 @@ morton3D_decode_u21(const uint64_t u64) return (uint32_t)tmp; } +/* Count the number of bits set to 1 */ +static FINLINE int +popcount(const uint8_t x) +{ + int n = x - ((x >> 1) & 0x55); + n = (n & 0x33) + ((n >> 2) & 0x33); + n = (n + (n >> 4)) & 0x0f; + return (n * 0x0101) >> 8; +} + static INLINE uint64_t morton_xyz_encode_u21(const uint32_t xyz[3]) { diff --git a/src/htvox_octree_buffer.h b/src/htvox_octree_buffer.h @@ -16,6 +16,7 @@ #ifndef HTVOX_OCTREE_BUFFER_H #define HTVOX_OCTREE_BUFFER_H +#include "htvox_c.h" #include <rsys/dynamic_array.h> /* @@ -33,24 +34,22 @@ * aforementioned indexing procedure of the node children. */ -#define OCTREE_XNODE_FLAG_FAR_INDEX 0x80000000u -#define OCTREE_XNODE_FLAG_LEAF 0x40000000u -#define OCTREE_XNODE_FLAG_EMPTY 0x20000000u -#define OCTREE_XNODE_MAX_CHILDREN_OFFSET (BIT(29)-1) +#define OCTREE_XNODE_FLAG_FAR_INDEX (1u<<15) +#define OCTREE_XNODE_MAX_CHILDREN_OFFSET (OCTREE_XNODE_FLAG_FAR_INDEX-1) #define OCTREE_XNODE_MASK OCTREE_XNODE_MAX_CHILDREN_OFFSET struct octree_xnode { - /* Relative offset to retrieve the node children. If the - * OCTREE_XNODE_FLAG_FAR_INDEX bit is not set, the node children are stored - * `offset' nodes *before* the node. If OCTREE_XNODE_FLAG_FAR_INDEX is set, - * the index toward the children are stored N bytes *after* the node with N - * defined as : N = (offset & OCTREE_XNODE_MASK)*sizeof(struct octree_xnode). - * Finally if the OCTREE_XNODE_EMPTY bit is set, then the node does not - * contain anything and thus has no child. */ - uint32_t offset; + /* Relative offset to retrieve the children. If the + * OCTREE_XNODE_FLAG_FAR_INDEX bit is not set, the children are stored in the + * same page at the position `offset & OCTREE_XNODE_MASK'. If + * OCTREE_XNODE_FLAG_FAR_INDEX is set, `offset & OCTREE_XNODE_MASK' reference + * an octree_index toward the node children */ + uint16_t node_offset; + uint16_t leaf_offset; + uint8_t is_valid; /* Mask defining if the children are valid */ + uint8_t is_leaf; /* Mask defining if the children are leafs */ + uint16_t dummy__; /* Ensure that the size of the node is 8 bytes */ }; -#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 @@ -169,22 +168,64 @@ octree_buffer_get_child_index { struct octree_index child_id = OCTREE_INDEX_NULL; struct octree_xnode* node = NULL; - uint32_t node_offset; + uint16_t offset; + const int ichild_flag = BIT(ichild); + int ichild_off; + uint8_t mask; + ASSERT(ichild >= 0 && ichild < 8 && buf); node = octree_buffer_get_node(buf, id); - if(node->offset & OCTREE_XNODE_FLAG_EMPTY) return OCTREE_INDEX_NULL; - node_offset = (node->offset & OCTREE_XNODE_MASK); + mask = (uint8_t)(node->is_valid & ~node->is_leaf); + ASSERT(mask & ichild_flag); + + /* Compute the child offset from the first child node */ + ichild_off = popcount((uint8_t)((ichild_flag-1) & (int)mask)); - if(!(node->offset & OCTREE_XNODE_FLAG_FAR_INDEX)) { + offset = node->node_offset & OCTREE_XNODE_MASK; + if(!(node->node_offset & OCTREE_XNODE_FLAG_FAR_INDEX)) { child_id.ipage = id.ipage; - child_id.inode = (uint16_t)(id.inode - node_offset + (uint32_t)ichild); + child_id.inode = (uint16_t)(offset + ichild_off); } else { - child_id = *(struct octree_index*) - ((char*)node + node_offset*sizeof(struct octree_xnode)); - child_id.inode = (uint16_t)(child_id.inode + ichild); + char* mem = darray_page_data_get(&buf->node_pages)[id.ipage]; + child_id = *(struct octree_index*)(mem+offset*(sizeof(struct octree_xnode))); + child_id.inode = (uint16_t)(child_id.inode + ichild_off); } return child_id; } +static FINLINE struct octree_index +octree_buffer_get_leaf_index + (struct octree_buffer* buf, + const struct octree_index id, + const int ileaf) /* In [0, 7] */ +{ + struct octree_index leaf_id = OCTREE_INDEX_NULL; + struct octree_xnode* node = NULL; + uint16_t offset; + const int ileaf_flag = BIT(ileaf); + int ileaf_off; + uint8_t mask; + + ASSERT(ileaf >= 0 && ileaf < 8 && buf); + + node = octree_buffer_get_node(buf, id); + mask = node->is_valid & node->is_leaf; + ASSERT(mask & ileaf_flag); + + /* Compute the leaf offset from the first leaf of the node */ + ileaf_off = popcount((uint8_t)((ileaf_flag-1) & (int)mask)); + + offset = node->leaf_offset & OCTREE_XNODE_MASK; + if(!(node->leaf_offset & OCTREE_XNODE_FLAG_FAR_INDEX)) { + leaf_id.ipage = id.ipage; + leaf_id.inode = (uint16_t)(offset + ileaf_off); + } else { + char* mem = darray_page_data_get(&buf->node_pages)[id.ipage]; + leaf_id = *(struct octree_index*)(mem+offset*(sizeof(struct octree_xnode))); + leaf_id.inode = (uint16_t)(leaf_id.inode + ileaf_off); + } + return leaf_id; +} + #endif /* HTVOX_OCTREE_BUFFER_H */ diff --git a/src/htvox_scene.c b/src/htvox_scene.c @@ -25,28 +25,32 @@ struct voxel { uint64_t mcode; /* Morton code of the voxel */ - double data; /* Data of the voxel. -DBL_MAX == empty voxels */ + double data; /* Data of the voxel */ }; -#define VOXEL_EMPTY_DATA -DBL_MAX struct octree_node { - struct octree_index ichildren; /* Index of the 1st child */ - uint8_t is_leaf; /* Define if the node is a leaf or not */ - double data; /* Data of the node */ + struct octree_index ichild_node; /* Index of the 1st child node */ + struct octree_index ichild_leaf; /* Index of the 1st child leaf */ + uint8_t is_valid; /* Mask defining whether the children are valid or not */ + uint8_t is_leaf; /* Mask defining whether the children are leaves or not */ + double data[8]; /* Data of the leaves */ }; /* Stacked children of an octree node */ struct stack { struct octree_node nodes[8]; /* List of registered children nodes */ - int len; /* Number of registered nodes in [0, 8[ */ + uint8_t mask; /* Mask of valid children nodes (0 = empty) */ }; struct octree_builder { struct stack stacks[OCTREE_DEPTH_MAX]; struct octree_buffer* buffer; + int (*merge)(const double min_val, const double max_val, void* ctx); void* context; /* Client side data sent to the merge callback */ + int octree_depth; + uint64_t mcode; /* Morton code of the last registered voxels */ size_t nvoxels; /* Number of voxels, i.e. leaves, */ }; @@ -59,19 +63,22 @@ static void check_octree(struct octree_buffer* buf, const struct octree_index root) { const struct octree_xnode* node; + int ichild; 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); + FOR_EACH(ichild, 0, 8) { + const int ichild_flag = BIT(ichild); + if((node->is_valid & ichild_flag) == 0) continue; + + if(node->is_leaf & ichild_flag) { + struct octree_index leaf; + leaf = octree_buffer_get_leaf_index(buf, root, ichild); + ASSERT(octree_buffer_get_leaf(buf, leaf) != NULL); + } else { + struct octree_index child; + child = octree_buffer_get_child_index(buf, root, ichild); + check_octree(buf, child); } } } @@ -82,11 +89,16 @@ stack_clear(struct stack* stack) { int inode; FOR_EACH(inode, 0, 8) { + int ileaf; stack->nodes[inode].is_leaf = 0; - stack->nodes[inode].data = 0; - stack->nodes[inode].ichildren = OCTREE_INDEX_NULL; + stack->nodes[inode].is_valid = 0; + stack->nodes[inode].ichild_node = OCTREE_INDEX_NULL; + stack->nodes[inode].ichild_leaf = OCTREE_INDEX_NULL; + FOR_EACH(ileaf, 0, 8) { + stack->nodes[inode].data[ileaf] = 0; + } } - stack->len = 0; + stack->mask = 0; } /* Build a parent octree node from the registered stack nodes */ @@ -97,40 +109,40 @@ stack_setup_node int (*merge_func)(const double min_val, const double max_val, void* ctx), void* context) { - double data_min = DBL_MAX; - double data_max =-DBL_MAX; - int challenge_leaves_merging = 1; int ichild; - ASSERT(stack && node && stack->len == 8/*The octree is not sparse*/); - ASSERT(merge_func); + ASSERT(stack && node && merge_func); - node->ichildren = OCTREE_INDEX_NULL; + node->ichild_node = OCTREE_INDEX_NULL; + node->ichild_leaf = OCTREE_INDEX_NULL; + node->is_valid = stack->mask; + node->is_leaf = 0; - /* Find the minimum and maximum of the children data */ - FOR_EACH(ichild, 0, 8) { - if(!stack->nodes[ichild].is_leaf) { /* Could merge only leaf nodes */ - challenge_leaves_merging = 0; - break; - } - data_min = MMIN(stack->nodes[ichild].data, data_min); - data_max = MMAX(stack->nodes[ichild].data, data_max); - } + if(stack->mask == 0) return; /* Empty stack */ - if(!challenge_leaves_merging) { - node->is_leaf = 0; - node->data = 0; - } else { - int merge = 0; - if(data_min == VOXEL_EMPTY_DATA || data_max == VOXEL_EMPTY_DATA) { - merge = data_min == data_max; - } else { - merge = merge_func(data_min, data_max, context); + /* Try to merge the child's leaves */ + FOR_EACH(ichild, 0, 8) { + double data_min = DBL_MAX; + double data_max =-DBL_MAX; + struct octree_node* child = stack->nodes + ichild; + int ileaf; + + /* Only child "full of leaves" can be merged */ + if(child->is_leaf != 0xFF) continue; + + /* Find the minimum and maximum of the leaves */ + FOR_EACH(ileaf, 0, 8) { + data_min = MMIN(child->data[ileaf], data_min); + data_max = MMAX(child->data[ileaf], data_max); } - if(merge) { - node->is_leaf = 1; - node->data = data_max; - /* Notify that the stacked nodes does not exist anymore: they are merged */ - stack->len = 0; + /* Challenge the merge function */ + if(merge_func(data_min, data_max, context)) { + const uint8_t ichild_flag = (uint8_t)BIT(ichild); + node->data[ichild] = data_max; + node->is_leaf |= ichild_flag; + + /* The node does not exist anymore in the stack since it became a leaf + * for its parent node */ + stack->mask ^= ichild_flag; } } } @@ -143,77 +155,104 @@ stack_write size_t* nvoxels) /* Number of written voxels */ { struct octree_index nodes_id = OCTREE_INDEX_NULL; + struct octree_node* node = NULL; size_t nvoxs = 0; int inode; res_T res = RES_OK; ASSERT(stack && buffer && out_index && nvoxels); - /* No registered nodes <=> nodes were merged in an higher level */ - if(!stack->len) goto exit; + /* No registered nodes, this means that the nodes were merged in an higher + * level */ + if(!stack->mask) goto exit; + + /* Write the leaves */ + FOR_EACH(inode, 0, 8) { + size_t nleaves; + double* leaves; + size_t nvoxs_node; + int ileaf; + + if((stack->mask & BIT(inode)) == 0) continue; /* Empty node */ - ASSERT(stack->len == 8/*legacy stack*/ || stack->len == 1/*root stack*/); + node = stack->nodes + inode; + if(!node->is_leaf) continue; /* No leaf */ + + nleaves = (size_t)popcount(node->is_leaf); + + res = octree_buffer_alloc_leaves(buffer, nleaves, &node->ichild_leaf); + if(res != RES_OK) goto error; + + leaves = octree_buffer_get_leaf(buffer, node->ichild_leaf); + nvoxs_node = 0; + FOR_EACH(ileaf, 0, 8) { + if(node->is_leaf & BIT(ileaf)) leaves[nvoxs_node++] = node->data[ileaf]; + } + ASSERT(nvoxs_node == nleaves); + nvoxs += nleaves; + } do { struct octree_xnode* xnodes = NULL; + const size_t nnodes = (size_t)popcount(stack->mask); + size_t ixnode = 0; /* Alloc the octree nodes */ - res = octree_buffer_alloc_nodes(buffer, (size_t)stack->len, &nodes_id); + res = octree_buffer_alloc_nodes(buffer, (size_t)nnodes, &nodes_id); if(res != RES_OK) goto error; xnodes = octree_buffer_get_node(buffer, nodes_id); - nvoxs = 0; - FOR_EACH(inode, 0, stack->len) { - struct octree_node* node = stack->nodes + inode; - size_t offset = SIZE_MAX; - - if(node->data == VOXEL_EMPTY_DATA) { - xnodes[inode].offset = OCTREE_XNODE_FLAG_EMPTY; - } else { - if(node->is_leaf) { - double* leaf; - - /* Alloc the leaf node */ - res = octree_buffer_alloc_leaves(buffer, 1, &node->ichildren); - if(res != RES_OK) goto error; + FOR_EACH(inode, 0, 8) { + uint16_t leaf_offset = UINT16_MAX; + uint16_t node_offset = UINT16_MAX; - /* Setup the leaf data */ - leaf = octree_buffer_get_leaf(buffer, node->ichildren); - *leaf = node->data; + if((stack->mask & BIT(inode)) == 0) continue; /* Empty node */ + node = stack->nodes + inode; - nvoxs += 1; + /* Setup the offset toward the children */ + if(node->is_valid & ~node->is_leaf) { + if(node->ichild_node.ipage == nodes_id.ipage) { + node_offset = node->ichild_node.inode; } + /* Node offset is too high. Allocate a far index */ + if(node_offset > OCTREE_XNODE_MAX_CHILDREN_OFFSET) { + struct octree_index index; + /* Not enough memory in the current page. The far index cannot be + * stored in the same page of 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; - /* Define the offset toward the children of the current node */ - if(node->ichildren.ipage == nodes_id.ipage) { - offset = (nodes_id.inode + (size_t)inode) - node->ichildren.inode; + *octree_buffer_get_far_index(buffer, index) = node->ichild_node; + node_offset = OCTREE_XNODE_FLAG_FAR_INDEX | index.inode; } + } - /* Offset is too high. Allocate a far index */ - if(offset > OCTREE_XNODE_MAX_CHILDREN_OFFSET) { + /* Setup the offset toward the leaves */ + if(node->is_leaf) { + if(node->ichild_leaf.ipage == nodes_id.ipage) { + leaf_offset = node->ichild_leaf.inode; + } + /* Leaf offset is too high. Allocate a far index */ + if(leaf_offset > OCTREE_XNODE_FLAG_FAR_INDEX) { struct octree_index index; - struct octree_index* far_id; - /* Not enough memory in the current page. The far index cannot be - * stored relatively to its associated node. Stop the write process and - * rewrite the whole stacked nodes in a new page. */ + * stored in the same page of 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; - } - far_id = octree_buffer_get_far_index(buffer, index); - - *far_id = node->ichildren; - offset = (index.inode - (nodes_id.inode + (size_t)inode)); - offset = offset | OCTREE_XNODE_FLAG_FAR_INDEX; - } + if(res != RES_OK) break; - if(node->is_leaf) { - offset = offset | OCTREE_XNODE_FLAG_LEAF; + *octree_buffer_get_far_index(buffer, index) = node->ichild_leaf; + leaf_offset = OCTREE_XNODE_FLAG_FAR_INDEX | index.inode; } - xnodes[inode].offset = (uint32_t)offset; } + + xnodes[ixnode].node_offset = node_offset; + xnodes[ixnode].leaf_offset = leaf_offset; + xnodes[ixnode].is_valid = node->is_valid; + xnodes[ixnode].is_leaf = node->is_leaf; + ++ixnode; } - } while(inode < stack->len); + } while(inode < 8); exit: @@ -239,7 +278,7 @@ octree_builder_init memset(bldr, 0, sizeof(struct octree_builder)); /* Compute the maximum depth of the octree */ - bldr->octree_depth = log2i((int)definition) + 1; + bldr->octree_depth = log2i((int)definition); if(bldr->octree_depth > OCTREE_DEPTH_MAX) { res = RES_MEM_ERR; goto error; @@ -268,6 +307,9 @@ octree_builder_add_voxel const struct voxel* vox) { uint64_t mcode_xor; + int inode; + int ichild; + uint8_t ichild_flag; res_T res = RES_OK; ASSERT(bldr && vox && vox->mcode >= bldr->mcode); @@ -279,46 +321,57 @@ octree_builder_add_voxel if(mcode_xor >= 8) { size_t ilvl; + inode = (bldr->mcode >> 3) & 7; + bldr->stacks[0].mask |= (uint8_t)BIT(inode); + /* Flush the stack of the octree level that does not contain the next - * voxel. The last octree level is actually the root node. It contains all - * the voxels and can be skipped */ - FOR_EACH(ilvl, 0, bldr->octree_depth-1/*The last level contain all voxels*/) { + * voxel. The last octree level is actually the level that contains the + * root node while the penultimate describes the root node itself. These 2 + * levels contain all the voxels and can be skipped */ + FOR_EACH(ilvl, 0, bldr->octree_depth-2/*The 2 last leaves contain all voxels*/) { struct octree_node* stack_node; size_t nvoxels; uint64_t mcode_max_lvl; /* Compute the maximum morton code value for the current octree level */ - mcode_max_lvl = 8lu/*#children*/ * (1lu<<(3*ilvl))/*#voxels per children*/; + mcode_max_lvl = 8lu/*#children*/ * (1lu<<(3*(ilvl+1)))/*#voxels per children*/; if(mcode_xor < mcode_max_lvl) break; - ASSERT(bldr->stacks[ilvl].len == 8); /* The octree is not sparse */ + + /* Retrieve the node index of the next level */ + inode = (bldr->mcode >> (3*(ilvl+2))) & 7; /* The next voxel is not in the ilvl^th stack. Setup the parent node of the * nodes registered into the stack */ - stack_node = &bldr->stacks[ilvl+1].nodes[bldr->stacks[ilvl+1].len]; + stack_node = &bldr->stacks[ilvl+1].nodes[inode]; stack_setup_node (&bldr->stacks[ilvl], stack_node, bldr->merge, bldr->context); - bldr->stacks[ilvl+1].len += 1; - ASSERT(bldr->stacks[ilvl+1].len <= 8); + bldr->stacks[ilvl+1].mask |= (uint8_t)BIT(inode); /* Write the nodes of the stack of the current octree level into the buf */ res = stack_write - (&bldr->stacks[ilvl], bldr->buffer, &stack_node->ichildren, &nvoxels); + (&bldr->stacks[ilvl], bldr->buffer, &stack_node->ichild_node, &nvoxels); if(res != RES_OK) goto error; - bldr->nvoxels += nvoxels; + bldr->nvoxels += nvoxels; /* TODO remove this */ /* Reset the current stack */ stack_clear(&bldr->stacks[ilvl]); } } + /* Retrieve the index of the current voxel and of its parent */ + ichild = vox->mcode & 7; + inode = (vox->mcode >> 3) & 7; + ichild_flag = (uint8_t)BIT(ichild); + /* Register the voxel */ + bldr->stacks[0].nodes[inode].data[ichild] = vox->data; + bldr->stacks[0].nodes[inode].is_valid |= ichild_flag; + bldr->stacks[0].nodes[inode].is_leaf |= ichild_flag; + + /* Update morton code of the last registered voxel */ bldr->mcode = vox->mcode; - bldr->stacks[0].nodes[bldr->stacks[0].len].data = vox->data; - bldr->stacks[0].nodes[bldr->stacks[0].len].ichildren = OCTREE_INDEX_NULL; - bldr->stacks[0].nodes[bldr->stacks[0].len].is_leaf = 1; - bldr->stacks[0].len += 1; exit: return res; @@ -332,39 +385,43 @@ octree_builder_finalize struct octree_index* root_id) { size_t nvoxels; + size_t inode; int ilvl; res_T res = RES_OK; ASSERT(bldr); + inode = (bldr->mcode >> 3) & 7; + bldr->stacks[0].mask |= (uint8_t)BIT(inode); + /* Flush the stacked nodes */ FOR_EACH(ilvl, 0, bldr->octree_depth-1) { 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]; + if(bldr->stacks[ilvl].mask == 0) continue; + + /* Retrieve the node index of the next level */ + inode = (bldr->mcode >> (3*(ilvl+2))) & 7; /* Setup the parent node of the nodes registered into the current stack */ + parent_node = &bldr->stacks[ilvl+1].nodes[inode]; /* Fetch the parent node */ stack_setup_node (&bldr->stacks[ilvl], parent_node, bldr->merge, bldr->context); - bldr->stacks[ilvl+1].len += 1; + bldr->stacks[ilvl+1].mask |= (uint8_t)BIT(inode); /* Write the stacked nodes of the current level */ res = stack_write - (&bldr->stacks[ilvl], bldr->buffer, &parent_node->ichildren, &nvoxels); + (&bldr->stacks[ilvl], bldr->buffer, &parent_node->ichild_node, &nvoxels); if(res != RES_OK) goto error; - bldr->nvoxels += nvoxels; + bldr->nvoxels += nvoxels; /* TODO remove this */ } - ASSERT(bldr->stacks[bldr->octree_depth-1].len == 1); /* Write the root node */ ilvl = bldr->octree_depth-1; /* Root level */ res = stack_write(&bldr->stacks[ilvl], bldr->buffer, root_id, &nvoxels); if(res != RES_OK) goto error; - bldr->nvoxels += nvoxels; + bldr->nvoxels += nvoxels; /* TODO remove this */ exit: return res; @@ -401,7 +458,6 @@ htvox_scene_create { struct htvox_scene* scn = NULL; double vox_sz[3]; /* World space size of a voxel */ - double vox_sz_max; /* Max voxel size */ struct octree_builder bldr; uint64_t mcode_max; uint64_t mcode; @@ -443,7 +499,7 @@ htvox_scene_create scn->dev = dev; /* Compute the octree definition */ - scn->definition = MMAX(nvoxels[0], 1); + scn->definition = MMAX(nvoxels[0], 2); scn->definition = MMAX(nvoxels[1], scn->definition); scn->definition = MMAX(nvoxels[2], scn->definition); scn->definition = round_up_pow2(scn->definition); @@ -456,6 +512,7 @@ htvox_scene_create mcode_max = scn->definition * scn->definition * scn->definition; FOR_EACH(mcode, 0, mcode_max) { struct voxel vox; + size_t xyz[3]; uint32_t ui3[3]; morton_xyz_decode_u21(mcode, ui3); @@ -463,16 +520,14 @@ htvox_scene_create /* Out of bound voxels */ if(ui3[0] >= nvoxels[0] || ui3[1] >= nvoxels[1] - || ui3[2] >= nvoxels[2]) { - vox.data = VOXEL_EMPTY_DATA; - } else { - /* Retrieve the voxel data from the caller */ - size_t xyz[3]; - xyz[0] = (size_t)ui3[0]; - xyz[1] = (size_t)ui3[1]; - xyz[2] = (size_t)ui3[2]; - get(xyz, &vox.data, context); - } + || ui3[2] >= nvoxels[2]) + continue; + + /* Retrieve the voxel data from the caller */ + xyz[0] = (size_t)ui3[0]; + xyz[1] = (size_t)ui3[1]; + xyz[2] = (size_t)ui3[2]; + get(xyz, &vox.data, context); vox.mcode = mcode; /* Register the voxel against the octree */ @@ -567,7 +622,6 @@ htvox_scene_for_each_voxel double upp[3]; } stack[OCTREE_DEPTH_MAX*8]; int istack; - size_t ivoxel = 0; if(!scn || !func) return RES_BAD_ARG; @@ -583,30 +637,18 @@ htvox_scene_for_each_voxel do { const struct stack_entry entry = stack[--istack]; struct octree_xnode* node; + int ichild; node = octree_buffer_get_node(&scn->buffer, entry.inode); - if(OCTREE_XNODE_IS_LEAF(node)) { - double val; - struct octree_index ileaf; - - ileaf = octree_buffer_get_child_index(&scn->buffer, entry.inode, 0); - val = *octree_buffer_get_leaf(&scn->buffer, ileaf); - - ASSERT(entry.upp[0] <= scn->upper[0]); - ASSERT(entry.upp[1] <= scn->upper[1]); - ASSERT(entry.upp[2] <= scn->upper[2]); - ASSERT(entry.low[0] >= scn->lower[0]); - ASSERT(entry.low[1] >= scn->lower[1]); - ASSERT(entry.low[2] >= scn->lower[2]); - - func(val, ivoxel, entry.low, entry.upp, ctx); - ++ivoxel; - - } else if(!OCTREE_XNODE_IS_EMPTY(node)) { + FOR_EACH(ichild, 0, 8) { + const uint8_t ichild_flag = (uint8_t)BIT(ichild); double half_sz[3]; /* Half size of the current node */ double mid[3]; /* Middle point of the current node */ - int i; + double upp[3]; + double low[3]; + + if((node->is_valid & ichild_flag) == 0) continue; /* Empty node */ half_sz[0] = (entry.upp[0] - entry.low[0])*0.5; half_sz[1] = (entry.upp[1] - entry.low[1])*0.5; @@ -614,17 +656,42 @@ htvox_scene_for_each_voxel mid[0] = entry.low[0] + half_sz[0]; mid[1] = entry.low[1] + half_sz[1]; mid[2] = entry.low[2] + half_sz[2]; - - /* Push the children */ - FOR_EACH(i, 0, 8) { + low[0] = ichild&4 ? mid[0] : entry.low[0]; + low[1] = ichild&2 ? mid[1] : entry.low[1]; + low[2] = ichild&1 ? mid[2] : entry.low[2]; + upp[0] = low[0] + half_sz[0]; + upp[1] = low[1] + half_sz[1]; + upp[2] = low[2] + half_sz[2]; + + if(node->is_leaf & ichild_flag) { + struct octree_index ileaf; + size_t ivoxel; + double val; + + ileaf = octree_buffer_get_leaf_index(&scn->buffer, entry.inode, ichild); + val = *octree_buffer_get_leaf(&scn->buffer, ileaf); + + ASSERT(upp[0] <= scn->upper[0]); + ASSERT(upp[1] <= scn->upper[1]); + ASSERT(upp[2] <= scn->upper[2]); + ASSERT(low[0] >= scn->lower[0]); + ASSERT(low[1] >= scn->lower[1]); + ASSERT(low[2] >= scn->lower[2]); + + ivoxel = + ileaf.ipage * scn->buffer.pagesize / sizeof(double) + ileaf.inode; + func(val, ivoxel, low, upp, ctx); + } else { struct stack_entry* top = stack + istack; - top->inode = octree_buffer_get_child_index(&scn->buffer, entry.inode, i); - top->low[0] = i&4 ? mid[0] : entry.low[0]; - top->low[1] = i&2 ? mid[1] : entry.low[1]; - top->low[2] = i&1 ? mid[2] : entry.low[2]; - top->upp[0] = top->low[0] + half_sz[0]; - top->upp[1] = top->low[1] + half_sz[1]; - top->upp[2] = top->low[2] + half_sz[2]; + + top->inode = octree_buffer_get_child_index + (&scn->buffer, entry.inode, ichild); + top->low[0] = low[0]; + top->low[1] = low[1]; + top->low[2] = low[2]; + top->upp[0] = upp[0]; + top->upp[1] = upp[1]; + top->upp[2] = upp[2]; ++istack; } } @@ -667,40 +734,39 @@ htvox_scene_at scale_exp2 = 0.5; inode = scn->root; - do { + *voxel = HTVOX_VOXEL_NULL; + for(;;) { struct octree_xnode* node = octree_buffer_get_node(&scn->buffer, inode); - - if(OCTREE_XNODE_IS_LEAF(node)) { /* Reach a leaf */ - const size_t page_nvoxs = scn->buffer.pagesize/sizeof(double); - struct octree_index ileaf; - - ileaf = octree_buffer_get_child_index(&scn->buffer, inode, 0); - voxel->data = *octree_buffer_get_leaf(&scn->buffer, ileaf); - voxel->id = ileaf.ipage * page_nvoxs + ileaf.inode; - - } else { - double mid[3]; - int ichild = 0; - - ASSERT(!OCTREE_XNODE_IS_EMPTY(node)); - - /* Compute the middle point of the node */ - mid[0] = low[0] + scale_exp2; - mid[1] = low[1] + scale_exp2; - mid[2] = low[2] + scale_exp2; - - /* Define the child of the current node into which pos `lies' and compute - * its lower left corner */ - if(pos[0] > mid[0]) { ichild |= 4; low[0] = mid[0]; } - if(pos[1] > mid[1]) { ichild |= 2; low[1] = mid[1]; } - if(pos[2] > mid[2]) { ichild |= 1; low[2] = mid[2]; } - - /* Fetch the child node */ + int ichild; + uint8_t ichild_flag; + double mid[3]; + + /* Compute the middle point of the node */ + mid[0] = low[0] + scale_exp2; + mid[1] = low[1] + scale_exp2; + mid[2] = low[2] + scale_exp2; + + /* Define the child of the current node into which pos `lies' and compute + * its lower left corner */ + ichild = 0; + if(pos[0] > mid[0]) { ichild |= 4; low[0] = mid[0]; } + if(pos[1] > mid[1]) { ichild |= 2; low[1] = mid[1]; } + if(pos[2] > mid[2]) { ichild |= 1; low[2] = mid[2]; } + + ichild_flag = (uint8_t)BIT(ichild); + if((node->is_valid & ichild_flag) == 0) { /* Empty node */ + break; + } else if(node->is_leaf & ichild_flag) { /* Leaf node */ + inode = octree_buffer_get_leaf_index(&scn->buffer, inode, ichild); + voxel->data = *octree_buffer_get_leaf(&scn->buffer, inode); + voxel->id = + inode.ipage * scn->buffer.pagesize / sizeof(double) + inode.inode; + break; + } else { /* Child node */ inode = octree_buffer_get_child_index(&scn->buffer, inode, ichild); scale_exp2 *= 0.5; } - } while(HTVOX_VOXEL_NONE(voxel)); - + } return RES_OK; } diff --git a/src/test_htvox_scene.c b/src/test_htvox_scene.c @@ -302,7 +302,7 @@ main(int argc, char** argv) CHK(htvox_scene_ref_put(scn) == RES_OK); - nvxls[0] = nvxls[1] = nvxls[2] = 8; + nvxls[0] = nvxls[1] = nvxls[2] = 128; CHK(NEW_SCN(dev, low, upp, nvxls, get, merge_level0, ptr, &scn) == RES_OK); CHK(htvox_scene_get_voxels_count(scn, &nvoxels) == RES_OK); CHK(nvoxels == nvxls[0]*nvxls[1]*nvxls[2] / 8);