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:
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 */
+