star-vx

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

commit c9547d0f09ee927cd75266ed4be104fa9de5f4f9
parent 4921220dd28c71da5d3b265005c09e599f6c99ca
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Thu,  1 Feb 2018 16:20:46 +0100

Draft of the octree build. It does not compile yet

Diffstat:
Mcmake/CMakeLists.txt | 6++++--
Msrc/htvox.h | 7+------
Asrc/htvox_c.h | 65+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/htvox_device.c | 29+++++++++++++++++++++++++++++
Msrc/htvox_device.h | 10++++++++++
Asrc/htvox_octree.h | 183+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/htvox_scene.c | 428++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Asrc/htvox_scene.h | 32++++++++++++++++++++++++++++++++
8 files changed, 750 insertions(+), 10 deletions(-)

diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt @@ -41,9 +41,11 @@ set(VERSION_PATCH 0) set(VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}) set(HTVOX_FILES_SRC - htvox_device.c) + htvox_device.c + htvox_scene.c) set(SSOL_FILES_INC - htvox_device.h) + htvox_device.h + htvox_scene.h) set(SSOL_FILES_INC_API htvox.h) diff --git a/src/htvox.h b/src/htvox.h @@ -36,10 +36,6 @@ #define HTVOX(Func) htvox_ ## Func #endif -enum htvox_type { - HTVOX_DOUBLE -}; - struct htvox_voxel { double data; /* Data of the voxel */ size_t id; /* Indentifier of the voxel */ @@ -83,11 +79,10 @@ htvox_device_ref_put ******************************************************************************/ HTVOX_API res_T htvox_scene_create - (struct htvox_device, + (struct htvox_device* dev, const double lower[3], /* Lower bound of the scene */ const double upper[3], /* Upper bound of the scene */ const size_t nvoxels[3], /* # voxels along the 3 axis */ - const enum htvox_type type, /* Per voxel data type */ void (*get)(const size_t xyz[3], double* value, void* ctx), void* context, /* Client data send as the last param of the `get' callback */ struct htvox_scene** scn); diff --git a/src/htvox_c.h b/src/htvox_c.h @@ -0,0 +1,65 @@ +/* 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/>. */ + +#ifndef HTVOX_C_H +#define HTVOX_C_H + +#include <rsys/rsys.h> + +static INLINE uint64_t +morton3D_encode_u21(const uint32_t u21) +{ + uint64_t u64 = u21 & ((1<<21) - 1); + ASSERT(u21 <= ((1 << 21) - 1)); + u64 = (u64 | (u64 << 32)) & 0xFFFF00000000FFFF; + u64 = (u64 | (u64 << 16)) & 0x00FF0000FF0000FF; + u64 = (u64 | (u64 << 8)) & 0xF00F00F00F00F00F; + u64 = (u64 | (u64 << 4)) & 0x30C30C30C30C30C3; + u64 = (u64 | (u64 << 2)) & 0x9249249249249249; + return u64; +} + +static INLINE uint32_t +morton3D_decode_u21(const uint64_t u64) +{ + uint64_t tmp = (u64 & 0x9249249249249249); + tmp = (tmp | (tmp >> 2)) & 0x30C30C30C30C30C3; + tmp = (tmp | (tmp >> 4)) & 0xF00F00F00F00F00F; + tmp = (tmp | (tmp >> 8)) & 0x00FF0000FF0000FF; + tmp = (tmp | (tmp >> 16)) & 0xFFFF00000000FFFF; + tmp = (tmp | (tmp >> 32)) & 0x00000000FFFFFFFF; + ASSERT(tmp <= ((1<<21)-1)); + return (uint32_t)tmp; +} + +static INLINE uint64_t +morton_xyz_encode_u21(const uint32_t xyz[3]) +{ + return (morton3D_encode_u21(xyz[0]) << 2) + | (morton3D_encode_u21(xyz[1]) << 1) + | (morton3D_encode_u21(xyz[2]) << 0); +} + +static INLINE void +morton_xyz_decode_u21(const uint64_t code, uint32_t xyz[3]) +{ + ASSERT(xyz && code < ((1ul << 63)-1)); + xyz[0] = (uint32_t)morton3D_decode_u21(code >> 2); + xyz[1] = (uint32_t)morton3D_decode_u21(code >> 1); + xyz[2] = (uint32_t)morton3D_decode_u21(code >> 0); +} + +#endif /* HTVOX_C_H */ + diff --git a/src/htvox_device.c b/src/htvox_device.c @@ -23,6 +23,21 @@ * Helper functions ******************************************************************************/ static void +log_msg + (const struct htvox_device* dev, + const enum log_type stream, + const char* msg, + va_list vargs) +{ + ASSERT(dev && msg); + if(dev->verbose) { + res_T res; (void)res; + res = logger_vprint(dev->logger, stream, msg, vargs); + ASSERT(res == RES_OK); + } +} + +static void device_release(ref_T* ref) { struct htvox_device* dev; @@ -93,3 +108,17 @@ htvox_device_ref_put(struct htvox_device* dev) return RES_OK; } +/******************************************************************************* + * Local function + ******************************************************************************/ +void +log_err(const struct htvox_device* dev, const char* msg, ...) +{ + va_list vargs_list; + ASSERT(dev && msg); + + va_start(vargs_list, msg); + log_msg(dev, LOG_ERROR, msg, vargs_list); + va_end(vargs_list); +} + diff --git a/src/htvox_device.h b/src/htvox_device.h @@ -25,5 +25,15 @@ struct htvox_device { ref_T ref; }; +extern LOCAL_SYM void +log_err + (const struct htvox_device* dev, + const char* fmt, + ...) +#ifdef COMPILER_GCC + __attribute((format(printf, 2, 3))) +#endif + ; + #endif /* HTVOX_DEVICE_H */ diff --git a/src/htvox_octree.h b/src/htvox_octree.h @@ -0,0 +1,183 @@ +/* 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/>. */ + +#ifndef HTVOX_OCTREE_H +#define HTVOX_OCTREE_H + +#include <rsys/dynamic_array.h> + +/* + * Incore buffer of octree data partitioned in fixed sized memory pages. + * + * The nodes are stored in pages whose memory size is defined at the init of + * the buffer. The node children are indexed relatively to their parent node + * excepted if the relative offset is too high regarding the encoding precision + * of the offset, or if the children are stored in another page. In such cases, + * the node relatively references an 'octree_index', allocated in the same + * page, that defines the absolute position of the children in the buffer. + * + * Leaf attributes are stored in a separate buffer and are indexed either + * relatively, or absolutely by the leaf node storing, following the same + * aforementioned indexing procedure of the node children. + */ +#define OCTREE_XNODE_FLAG_FAR_INDEX_FLAG BIT(31) +#define OCTREE_XNODE_FLAG_LEAF_FLAG BIT(30) +#define OCTREE_XNODE_MAX_CHILDREN_OFFSET (BIT(30)-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) */ + uint32_t offset; +}; + +struct octree_index { + uint32_t ipage; /* Identifier of the page */ + uint16_t inode; /* Identifier of the node in the page */ + uint16_t dummy__; /* Padding to ensure the octree index is 8 bytes lenght */ +}; +STATIC_ASSERT(sizeof(struct octree_index) == 8, + Unexpected_sizeof_octree_index); +#define OCTREE_INDEX_NULL__ {UINT32_MAX, UINT16_MAX, UINT16_MAX} +static const struct octree_index OCTREE_INDEX_NULL = OCTREE_INDEX_NULL__; +#define OCTREE_INDEX_EQ(A, B) ((A)->inode==(B)->inode && (A)->ipage==(B)->ipage) + +/* Define the dynamic array of pages */ +#define DARRAY_NAME page +#define DARRAY_DATA char* +#include <rsys/dynamic_array.h> + +struct octree_buffer { + size_t pagesize; /* Memory page size in bytes */ + + /* Number of per page nodes. */ + size_t page_nnodes; + + struct darray_page node_pages; /* List of pages storing nodes */ + struct darray_page leaf_pages; /* List of pages storing leaves */ + struct octree_index node_head; /* Index of the next valid node */ + struct octree_index leaf_head; /* Index of the next valid leaf */ + + struct mem_allocator* allocator; +}; + +extern LOCAL_SYM void +octree_buffer_init + (struct mem_allocator* allocator, + struct octree_buffer* buf); + +extern LOCAL_SYM void +octree_buffer_release + (struct octree_buffer* buf); + +extern LOCAL_SYM res_T +octree_buffer_alloc_nodes + (struct octree_buffer* buf, + const size_t nnodes, + struct octree_index* first_node); /* Index toward the 1st allocated node */ + +extern LOCAL_SYM res_T +octree_buffer_alloc_leaves + (struct octree_buffer* buf, + const size_t nleaves, + struct octree_index* first_leaf); /* Index toward the 1st allocated leaf */ + +/* Allocate a octree_index in the current buffer page. Return RES_MEM_ERR if + * the node index cannot be allocated in the current page. In this case one + * have to alloc new nodes */ +extern LOCAL_SYM res_T +octree_buffer_alloc_far_index + (struct octree_buffer* buf, + struct octree_index* id); /* Index toward the allocated far index */ + +extern LOCAL_SYM void +octree_buffer_clear + (struct octree_buffer* buf); + +static FINLINE int +octree_buffer_is_empty(const struct octree_buffer* buf) +{ + ASSERT(buf); + return darray_page_size_get(buf->node_pages) == 0; +} + +static FINLINE struct octree_xnode* +octree_buffer_get_node + (struct octree_buffer* buf, + const struct octree_index id) +{ + char* mem; + ASSERT(buf && id.inode < buf->pagesize/sizeof(struct octree_node)); + ASSERT(id.ipage < darray_page_size_get(&buf->node_pages)); + mem = darray_page_data_get(&buf->node_pages)[id.ipage]; + mem += id.inode * sizeof(struct octree_xnode); + return (struct octree_xnode*)mem; +} + +static FINLINE struct octree_index* +octree_buffer_get_far_index + (struct octree_buffer* buf, + const struct octree_index id) +{ + char* mem; + ASSERT(buf && id.inode < buf->pagesize/sizeof(struct octree_node)); + ASSERT(id.ipage < darray_page_size_get(&buf->node_pages)); + mem = darray_node_page_data_get(&buf->node_pages)[id.ipage]; + mem += id.inode * sizeof(struct octree_xnode); + return (struct octree_index*)mem; +} + +static FINLINE double* +octree_buffer_get_leaf + (struct octree_buffer* buf, + const struct octree_index id) +{ + char* mem; + ASSERT(buf && id.inode < buf->pagesize/sizeof(double)); + ASSERT(id.ipage < darray_page_size_get(&buf->leaf_pages)); + mem = darray_page_data_get(&buf->leaf_pages)[id.ipage]; + mem += id.inode * sizeof(double); + return (double*)mem; +} + +static FINLINE struct octree_index +octree_byffer_get_child_index + (struct octree_buffer* buf, + const struct octree_index id, + const int ichild) /* in [0, 7] */ +{ + struct octree_index child_id = OCTREE_INDEX_NULL; + struct octree_xnode* node = NULL; + uint32_t node_offset; + ASSERT(ichild >= 0 && ichild < 8 && buf); + + node = octree_buffer_get_node(buf, id); + node_offset = (node->offset & OCTREE_XNODE_MASK); + + if(!(node->offset & OCTREE_XNODE_FLAG_FAR_INDEX)) { + child_id.ipage = id.ipage; + child_id.inode = id.inode - node_offset + ichild; + } else { + child_id = *(struct octree_index*) + ((char*)node + node_offset*sizeof(struct octree_index)); + child_id.inode = child_id.node_offset + ichild; + } + return child_id; +} + +#endif /* HTVOX_OCTREE_H */ diff --git a/src/htvox_scene.c b/src/htvox_scene.c @@ -13,10 +13,434 @@ * 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 "htvx.h" +#include "htvox.h" +#include "htvox_c.h" +#include "htvox_device.h" +#include "htvox_scene.h" + +#include <rsys/mem_allocator.h> + +#define OCTREE_DEPTH_MAX 16 /* Maximum depth of an octree */ + +struct voxel { + uint64_t mcode; /* Morton code of the voxel */ + double data; /* Data of the voxel */ +}; + +struct octree_index { + uint32_t ipage; /* Identifier of the page */ + uint16_t inode; /* Identifier of the node in the page */ + uint16_t dummy__; /* Padding to ensure the octree index is 8 bytes lenght */ +}; +STATIC_ASSERT(sizeof(struct octree_index) == 8, + Unexpected_sizeof_octree_index); + +#define OCTREE_INDEX_NULL__ {UINT32_MAX, UINT16_MAX, UINT16_MAX} +static const struct octree_index OCTREE_INDEX_NULL = OCTREE_INDEX_NULL__; + +#define OCTREE_INDEX_EQ(A, B) ((A)->inode==(B)->inode && (A)->ipage==(B)->ipage) + +#define OCTREE_XNODE_FLAG_FAR_INDEX_FLAG BIT(31) +#define OCTREE_XNODE_FLAG_LEAF_FLAG BIT(30) +#define OCTREE_XNODE_MAX_CHILDREN_OFFSET (BIT(30)-1) +#define OCTREE_XNODE_MASK OCTREE_XNODE_MAX_CHILDREN_OFFSET + +struct octree_xnode { + uint32_t offset; +}; + +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 */ +}; + +/* Per octree node stack of children */ +struct stack { + struct octree_node nodes[8]; /* List of registered children nodes */ + int len; /* Number of registered nodes in [0, 8[ */ +}; + +struct octree_builder { + struct stack stacks[OCTREE_DEPTH_MAX]; + int octree_depth; + uint64_t mcode; /* Morton code of the last registered voxels */ +}; + +/******************************************************************************* + * Helper function + ******************************************************************************/ +static INLINE void +stack_clear(struct stack* stack) +{ + int inode; + FOR_EACH(inode, 0, 8) { + stack->nodes[inode].is_leaf = 0; + stack->nodes[inode].data = 0; + stack->ichildren = OCTREE_INDEX_NULL; + } + stack->len = 0; +} + +/* Build a parent octree node from the registered stack nodes */ +static INLINE void +stack_setup_node(struct stack* stack, struct octree_node* node) +{ + double data_min = DBL_MAX; + double data_max =-DBL_MAX; + int challenge_leaves_merging = 1; + int ichild; + ASSERT(stack && stack->mask != 0 && node); + + node->ichildren = OCTREE_INDEX_NULL; + + /* Find the minimum and maximum data of the children */ + FOR_EACH(ichild, 0, 8) { + if(!stach->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(challenge_leaves_merging && merge(data_min, data_max)) { + node->is_leaf = 1; + node->data = data_max; + /* Notify that the stacked nodes does not exist anymore: they are merged */ + stack->len = 0; + } else { + node->is_leaf = 0; + node->data = 0; + } +} + +static res_T +stack_write + (struct stack* stack, /* Node to write */ + struct octree_buffer* buffer, /* Buffer where nodes are written */ + struct octree_index* out_index) /* Index of the first written node */ +{ + struct octree_index nodes_id; + int inode; + res_T res = RES_OK; + ASSERT(stack && buffer && out_index); + + /* No registered nodes <=> nodes were merged in an higher level */ + if(!stack->len) goto exit; + + do { + struct octree_xnode* xnodes = NULL; + + /* Alloc the 8 octree nodes */ + res = octree_buffer_alloc_nodes(buffer, 8, &nodes_id); + if(res != RES_OK) goto error; + xnodes = octree_buffer_get_node(buffer, nodes_id); + + FOR_EACH(inode, 0, stack->len) { + const struct octree_node* node = stack->nodes + inode; + size_t offset = SIZE_MAX; + + if(node->is_leaf) { + double* leaf; + + /* Alloc the leaf node */ + ASSERT(OCTREE_INDEX_EQ(node->ichildren, OCTREE_INDEX_NULL)); + res = octree_buffer_alloc_leaf(buffer, 1, &node->ichildren); + if(res != RES_OK) goto error; + + /* Setup the leaf data */ + leaf = octree_buffer_get_leaf(buffer, &node->ichildren); + *leaf = node->data; + } + + /* Define the offset toward the children of the current node */ + if(node->ichildren.page == nodes_id.page) { + offset = (node_id.inode + (size_t)inode) - node->ichildren.inode; + } + + /* Offset is too high. Allocate a far index */ + if(offset > OCTREE_XNODE_MAX_CHILDREN_OFFSET) { + 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. */ + 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 - (node_id.inode + (size_t)inode)); + offset = offset | OCTREE_XNODE_FLAG_FAR_INDEX; + } + + if(node->is_leaf) { + offset |= OCTREE_XNODE_FLAG_LEAF; + } + xnodes[inode].offset = offset; + } + } while(inode < stack->len); + + /* Return the index toward the first writen nodes */ + *out_index = nodes_id; + +exit: + return res; +error: + goto exit; +} + +static INLINE + +static res_T +octree_builder_init + (struct octree_builder* bldr, + const size_t definition) +{ + int ilvl; + res_T res = RES_OK; + ASSERT(bldr && IS_POW2(definition)); + memset(bldr, 0, sizeof(struct octree_builder)); + + /* Compute the maximum depth of the octree */ + bldr->octree_depth = log2i((int)definition) + 1; + if(bldr->octree_depth > OCTREE_DEPTH_MAX) { + res = RES_MEM_ERR; + goto error; + } + + /* Init the per octree level stack */ + FOR_EACH(ilvl, 0, bldr->octree_depth) { + stack_clear(&bldr->stacks[ilvl]); + } + +exit: + return res; +error: + goto exit; +} + +static res_T +octree_builder_add_voxel(struct octree_builder* bldr, const struct voxel* vox) +{ + uint64_t mcode_xor; + ASSERT(bldr && vox && vox->mcode >= bldr->mcode); + + /* Define if the bits in [4 .. 63] of the previous and the next Morton + * codes are the same */ + mcode_xor = bldr->mcode ^ vox->mcode; + + /* The next voxel is not in the current node */ + if(mcode_xor >= 8) { + /* 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*/) { + struct octree_node* stack_node; + 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*/; + + if(mcode_xor < mcode_max_lvl) break; + + /* 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_setup_node(&bldr->stacks[ilvl], stack_node); + bldr->stacks[ilvl+1].len += 1; + ASSERT(bldr->stacks[ilvl+1].len <= 8); + + /* Write the nodes of the stack of the current octree level into the octree */ + ASSERT(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; + + /* Reset the current stack */ + stack_clear(&bldr->stacks[ilvl]); + } + } + + /* Register the voxel */ + bldr->mcode = vox->mcode; + bldr->stack[0].nodes[bldr->stack[0].len].data = vox->data; + bldr->stack[0].nodes[bldr->stack[0].len].children = OCTREE_INDEX_NULL; + bldr->stack[0].nodes[bldr->stack[0].len].is_leaf = 1; + bldr->stack[0].len += 1; + +exit: + return res; +error: + goto exit; +} + +static INLINE res_T +octree_builder_finalize + (struct octree_builder* bldr, + struct octree_index* root_id) +{ + int ilvl; + 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 */ + FOR_EACH(ilvl, 0, bldr->octree_depth-1) { + ASSERT(bldr->stacks[ilvl].len == 0); + } + ASSERT(bldr->stacks[bldr->octree_depth-1].len == 1); +#endif + ilvl = bldr->octree_depth-1; /* Root level */ + return stack_write(&bldr->stacks[ilvl], bldr->buffer, root_id); +} + +static void +scene_release(ref_T* ref) +{ + struct htvox_scene* scn; + struct htvox_device* dev; + ASSERT(ref); + scn = CONTAINER_OF(ref, struct htvox_scene, ref); + dev = scn->dev; + MEM_RM(dev->allocator, scn); + HTVOX(device_ref_put(dev)); +} /******************************************************************************* * Exported functions ******************************************************************************/ res_T -htvx_scene_c +htvox_scene_create + (struct htvox_device* dev, + const double lower[3], /* Lower bound of the scene */ + const double upper[3], /* Upper bound of the scene */ + const size_t nvoxels[3], /* # voxels along the 3 axis */ + void (*get)(const size_t xyz[3], double* value, void* ctx), + void* context, /* Client data send as the last param of the `get' callback */ + struct htvox_scene** out_scn) +{ + struct htvox_scene* scn = NULL; + double sz[3]; /* Size of the scene AABB */ + double vox_sz; /* World space size of a voxel */ + double vox_sz_max; /* Max voxel size */ + double vox_scale[3]; /* Scale factor of the voxel size */ + struct stack stacks[OCTREE_DEPTH_MAX]; + uint64_t mcode_max; + uint64_t imcode; + int octree_depth; + res_T res = RES_OK; + + if(!dev || !lower || !upper || !nvoxels || !get || !out_scn) { + res = RES_BAD_ARG; + goto error; + } + if(lower[0] >= upper[0] + || lower[1] >= upper[1] + || lower[2] >= upper[2]) { + log_err(dev, + "%s: the scene AABB is degenerated.\n" + "\tlower={%g, %g, %g}, upper={%g, %g, %g}.\n", + FUNC_NAME, SPLIT3(lower), SPLIT3(upper)); + res = RES_BAD_ARG; + goto error; + } + if(!nvoxels[0] || !nvoxels[1] || !nvoxels[2]) { + log_err(dev, + "%s: the number of voxels along each axis cannot be null.\n" + "\t#voxels XYZ = {%lu, %lu, %lu}.\n", + FUNC_NAME, + (unsigned long)nvoxels[0], + (unsigned long)nvoxels[1], + (unsigned long)nvoxels[2]); + res = RES_BAD_ARG; + goto error; + } + scn = MEM_CALLOC(dev->allocator, 1, sizeof(struct htvox_scene)); + if(!scn) { + res = RES_MEM_ERR; + goto error; + } + ref_init(&scn->ref); + HTVOX(device_ref_get(dev)); + scn->dev = dev; + + /* Compute the voxel size in world space */ + vox_sz[0] = (upper[0] - lower[0]) / (double)nvoxels[0]; + vox_sz[1] = (upper[1] - lower[1]) / (double)nvoxels[1]; + vox_sz[2] = (upper[2] - lower[2]) / (double)nvoxels[2]; + vox_sz_max = MMAX(MMAX(vox_sz[0], vox_sz[1]), vox_sz[2]); + + /* Compute the scale factor of the voxels to transform them in cube */ + scn->vox_scale[0] = 1.0; + scn->vox_scale[1] = 1.0; + scn->vox_scale[2] = 1.0; + if(vox_sz[0] != vox_sz_max) scn->vox_scale[0] = vox_sz_max / vox_sz[0]; + if(vox_sz[1] != vox_sz_max) scn->vox_scale[1] = vox_sz_max / vox_sz[1]; + if(vox_sz[2] != vox_sz_max) scn->vox_scale[2] = vox_sz_max / vox_sz[2]; + + /* Compute the octree definition */ + scn->definition = MAX(nvoxels[0], 1); + scn->definition = MAX(nvoxels[1], scn->definition); + scn->definition = MAX(nvoxels[2], scn->definition); + scn->definition = round_up_pow2(scn->definition); + + /* Intialize the octree builder */ + res = octree_builder_init(&bldr, scn->definition); + if(res != RES_OK) goto error; + + mcode_max = scn->definition * scn->definition * scn->definition; + FOR_EACH(mcode, 0, mcode_max) { + struct voxel vox: + uint32_t ui3[3]; + const size_t xyz[3]; + + morton_xyz_decode_u21(mcode, ui3); + + /* Out of bound voxels */ + if(ui3[0] > nvoxels[0]) continue; + if(ui3[1] > nvoxels[1]) continue; + if(ui3[2] > nvoxels[2]) continue; + xyz[0] = (size_t)ui3[0]; + xyz[1] = (size_t)ui3[1]; + xyz[2] = (size_t)ui3[2]; + + /* Retrieve the voxel data from the caller */ + get(xyz, &vox.data, context); + vox.mcode = mcode; + + /* Register the voxel against the octree */ + res = octree_builder_add_voxel(&bldr, &vox); + if(res != RES_OK) goto error; + } + + res = octree_builder_finalize(&bldr, &scn->root); + if(res != RES_OK) goto error; + +exit: + if(out_scn) *out_scn = scn; + return res; +error: + if(scn) { + HTVOX(scene_ref_put(scn)); + scn = NULL; + } + goto exit; +} + +res_T +htvox_scene_ref_get(struct htvox_scene* scn) +{ + if(!scn) return RES_BAD_ARG; + ref_get(&scn->ref); + return RES_OK; +} + +res_T +htvox_scene_ref_put(struct htvox_scene* scn) +{ + if(!scn) return RES_BAD_ARG; + ref_put(&scn->ref, scene_release); + return RES_OK; +} + diff --git a/src/htvox_scene.h b/src/htvox_scene.h @@ -0,0 +1,32 @@ +/* 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/>. */ + +#ifndef HTVOX_SCENE_H +#define HTVOX_SCENE_H + +#include <rsys/ref_count.h> + +struct htvox_scene { + double vox_scale[3]; /* Scale factor of the octree */ + double definition; /* Definition of the octree */ + + struct octree_index root; /* Index toward the root node of the octree */ + + struct htvox_device* dev; + ref_T ref; +}; + +#endif /* HTVOX_SCENE_H */ +