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:
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;
+}
+