From a868a2958dd7ea138b02543ef81cc78abf8622c7 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Fri, 15 Jan 2010 14:24:13 -0500 Subject: Rename kD splay trees to Bounding Volume Splay Trees. --- bench/libdimension/Makefile.am | 10 +- bench/libdimension/bvst.c | 176 ++++++++++++++ bench/libdimension/kD_splay_tree.c | 176 -------------- libdimension/Makefile.am | 4 +- libdimension/bvst.c | 462 ++++++++++++++++++++++++++++++++++++ libdimension/bvst.h | 65 ++++++ libdimension/dimension_impl.h | 2 +- libdimension/kD_splay_tree.c | 464 ------------------------------------- libdimension/kD_splay_tree.h | 66 ------ libdimension/raytrace.c | 37 ++- tests/libdimension/Makefile.am | 6 +- tests/libdimension/bvst.c | 72 ++++++ tests/libdimension/kD_splay_tree.c | 72 ------ 13 files changed, 803 insertions(+), 809 deletions(-) create mode 100644 bench/libdimension/bvst.c delete mode 100644 bench/libdimension/kD_splay_tree.c create mode 100644 libdimension/bvst.c create mode 100644 libdimension/bvst.h delete mode 100644 libdimension/kD_splay_tree.c delete mode 100644 libdimension/kD_splay_tree.h create mode 100644 tests/libdimension/bvst.c delete mode 100644 tests/libdimension/kD_splay_tree.c diff --git a/bench/libdimension/Makefile.am b/bench/libdimension/Makefile.am index 8b124bc..2e23fde 100644 --- a/bench/libdimension/Makefile.am +++ b/bench/libdimension/Makefile.am @@ -19,7 +19,7 @@ INCLUDES = -I$(top_srcdir)/libdimension -EXTRA_PROGRAMS = bench-array bench-geometry bench-kD_splay_tree +EXTRA_PROGRAMS = bench-array bench-geometry bench-bvst bench_array_SOURCES = array.c bench_array_LDADD = -lsandglass $(top_builddir)/libdimension/libdimension.la @@ -27,12 +27,12 @@ bench_array_LDADD = -lsandglass $(top_builddir)/libdimension/libdimension.la bench_geometry_SOURCES = geometry.c bench_geometry_LDADD = -lsandglass $(top_builddir)/libdimension/libdimension.la -bench_kD_splay_tree_SOURCES = kD_splay_tree.c -bench_kD_splay_tree_LDADD = -lsandglass $(top_builddir)/libdimension/libdimension.la +bench_bvst_SOURCES = bvst.c +bench_bvst_LDADD = -lsandglass $(top_builddir)/libdimension/libdimension.la -bench: bench-array$(EXEEXT) bench-geometry$(EXEEXT) bench-kD_splay_tree$(EXEEXT) +bench: bench-array$(EXEEXT) bench-geometry$(EXEEXT) bench-bvst$(EXEEXT) ./bench-array$(EXEEXT) ./bench-geometry$(EXEEXT) - ./bench-kD_splay_tree$(EXEEXT) + ./bench-bvst$(EXEEXT) .PHONY: bench diff --git a/bench/libdimension/bvst.c b/bench/libdimension/bvst.c new file mode 100644 index 0000000..3d363ca --- /dev/null +++ b/bench/libdimension/bvst.c @@ -0,0 +1,176 @@ +/************************************************************************* + * Copyright (C) 2009 Tavian Barnes * + * * + * This file is part of The Dimension Test Suite. * + * * + * The Dimension Test Suite 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. * + * * + * The Dimension Test Suite 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 . * + *************************************************************************/ + +#include "../../libdimension/dimension_impl.h" +#include +#include + +dmnsn_intersection * +dmnsn_fake_intersection_fn(const dmnsn_object *object, dmnsn_line line) +{ + dmnsn_intersection *intersection = dmnsn_new_intersection(); + intersection->t = (object->min.z - line.x0.z)/line.n.z; + intersection->texture = object->texture; + return intersection; +} + +void +dmnsn_randomize_bounding_box(dmnsn_object *object) +{ + double rand1, rand2; + + rand1 = 2.0*((double)rand())/RAND_MAX - 1.0; + rand2 = 2.0*((double)rand())/RAND_MAX - 1.0; + if (rand1 < rand2) { + object->min.x = rand1; + object->max.x = rand2; + } else { + object->max.x = rand1; + object->min.x = rand2; + } + + rand1 = 2.0*((double)rand())/RAND_MAX - 1.0; + rand2 = 2.0*((double)rand())/RAND_MAX - 1.0; + if (rand1 < rand2) { + object->min.y = rand1; + object->max.y = rand2; + } else { + object->max.y = rand1; + object->min.y = rand2; + } + + rand1 = 2.0*((double)rand())/RAND_MAX - 1.0; + rand2 = 2.0*((double)rand())/RAND_MAX - 1.0; + if (rand1 < rand2) { + object->min.z = rand1; + object->max.z = rand2; + } else { + object->max.z = rand1; + object->min.z = rand2; + } +} + +dmnsn_bvst_node * +dmnsn_bvst_deepest_recursive(dmnsn_bvst_node *node, + unsigned int depth, unsigned int *deepest) +{ + dmnsn_bvst_node *left = NULL, *right = NULL; + + if (node->contains) { + left = dmnsn_bvst_deepest_recursive(node->contains, depth + 1, deepest); + } + if (node->container) { + right = dmnsn_bvst_deepest_recursive(node->container, + depth + 1, deepest); + } + + if (right) { + return right; + } else if (left) { + return left; + } else if (depth >= *deepest) { + *deepest = depth; + return node; + } else { + return NULL; + } +} + +dmnsn_bvst_node * +dmnsn_bvst_deepest(dmnsn_bvst *tree) +{ + unsigned int deepest = 0; + return dmnsn_bvst_deepest_recursive(tree->root, 0, &deepest); +} + +int +main() +{ + dmnsn_bvst *tree; + dmnsn_bvst_node *node; + dmnsn_intersection *intersection; + dmnsn_line ray; + const unsigned int nobjects = 128; + dmnsn_object *objects[nobjects]; + unsigned int i; + long grains; + + sandglass_t sandglass; + sandglass_attributes_t attr = { SANDGLASS_MONOTONIC, SANDGLASS_CPUTIME }; + + if (sandglass_create(&sandglass, &attr, &attr) != 0) { + perror("sandglass_create()"); + return EXIT_FAILURE; + } + + tree = dmnsn_new_bvst(); + + for (i = 0; i < nobjects; ++i) { + objects[i] = dmnsn_new_object(); + if (!objects[i]) { + fprintf(stderr, "--- Couldn't allocate object! ---\n"); + return EXIT_FAILURE; + } + + /* Generate a bounding box in (-1, -1, -1), (1, 1, 1) */ + dmnsn_randomize_bounding_box(objects[i]); + objects[i]->intersection_fn = &dmnsn_fake_intersection_fn; + } + + /* dmnsn_bvst_insert() */ + + grains = 0; + for (i = 0; i < nobjects; ++i) { + sandglass_bench_noprecache(&sandglass, + dmnsn_bvst_insert(tree, objects[i])); + sandglass.grains += grains; + grains = sandglass.grains; + } + printf("dmnsn_bvst_insert(): %ld\n", sandglass.grains/nobjects); + + /* dmnsn_bvst_search() */ + + ray.x0 = dmnsn_new_vector(0.0, 0.0, -2.0); + ray.n = dmnsn_new_vector(0.0, 0.0, 1.0); + + dmnsn_delete_intersection((*objects[0]->intersection_fn)(objects[0], ray)); + sandglass_bench_noprecache(&sandglass, { + intersection = dmnsn_bvst_search(tree, ray); + }); + dmnsn_delete_intersection(intersection); + printf("dmnsn_bvst_search(): %ld\n", sandglass.grains); + + /* dmnsn_bvst_splay() */ + grains = 0; + for (i = 0; i < nobjects; ++i) { + node = dmnsn_bvst_deepest(tree); + sandglass_bench_noprecache(&sandglass, dmnsn_bvst_splay(tree, node)); + sandglass.grains += grains; + grains = sandglass.grains; + } + printf("dmnsn_bvst_splay(): %ld\n", sandglass.grains/nobjects); + + /* Cleanup */ + dmnsn_delete_bvst(tree); + for (i = 0; i < nobjects; ++i) { + dmnsn_delete_object(objects[i]); + } + + return EXIT_SUCCESS; +} diff --git a/bench/libdimension/kD_splay_tree.c b/bench/libdimension/kD_splay_tree.c deleted file mode 100644 index fffab69..0000000 --- a/bench/libdimension/kD_splay_tree.c +++ /dev/null @@ -1,176 +0,0 @@ -/************************************************************************* - * Copyright (C) 2009 Tavian Barnes * - * * - * This file is part of The Dimension Test Suite. * - * * - * The Dimension Test Suite 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. * - * * - * The Dimension Test Suite 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 . * - *************************************************************************/ - -#include "../../libdimension/dimension_impl.h" -#include -#include - -dmnsn_intersection * -dmnsn_fake_intersection_fn(const dmnsn_object *object, dmnsn_line line) -{ - dmnsn_intersection *intersection = dmnsn_new_intersection(); - intersection->t = (object->min.z - line.x0.z)/line.n.z; - intersection->texture = object->texture; - return intersection; -} - -void -dmnsn_randomize_bounding_box(dmnsn_object *object) -{ - double rand1, rand2; - - rand1 = 2.0*((double)rand())/RAND_MAX - 1.0; - rand2 = 2.0*((double)rand())/RAND_MAX - 1.0; - if (rand1 < rand2) { - object->min.x = rand1; - object->max.x = rand2; - } else { - object->max.x = rand1; - object->min.x = rand2; - } - - rand1 = 2.0*((double)rand())/RAND_MAX - 1.0; - rand2 = 2.0*((double)rand())/RAND_MAX - 1.0; - if (rand1 < rand2) { - object->min.y = rand1; - object->max.y = rand2; - } else { - object->max.y = rand1; - object->min.y = rand2; - } - - rand1 = 2.0*((double)rand())/RAND_MAX - 1.0; - rand2 = 2.0*((double)rand())/RAND_MAX - 1.0; - if (rand1 < rand2) { - object->min.z = rand1; - object->max.z = rand2; - } else { - object->max.z = rand1; - object->min.z = rand2; - } -} - -dmnsn_kD_splay_node * -dmnsn_kD_splay_deepest_recursive(dmnsn_kD_splay_node *node, - unsigned int depth, unsigned int *deepest) -{ - dmnsn_kD_splay_node *left = NULL, *right = NULL; - - if (node->contains) { - left = dmnsn_kD_splay_deepest_recursive(node->contains, depth + 1, deepest); - } - if (node->container) { - right = dmnsn_kD_splay_deepest_recursive(node->container, - depth + 1, deepest); - } - - if (right) { - return right; - } else if (left) { - return left; - } else if (depth >= *deepest) { - *deepest = depth; - return node; - } else { - return NULL; - } -} - -dmnsn_kD_splay_node * -dmnsn_kD_splay_deepest(dmnsn_kD_splay_tree *tree) -{ - unsigned int deepest = 0; - return dmnsn_kD_splay_deepest_recursive(tree->root, 0, &deepest); -} - -int -main() -{ - dmnsn_kD_splay_tree *tree; - dmnsn_kD_splay_node *node; - dmnsn_intersection *intersection; - dmnsn_line ray; - const unsigned int nobjects = 128; - dmnsn_object *objects[nobjects]; - unsigned int i; - long grains; - - sandglass_t sandglass; - sandglass_attributes_t attr = { SANDGLASS_MONOTONIC, SANDGLASS_CPUTIME }; - - if (sandglass_create(&sandglass, &attr, &attr) != 0) { - perror("sandglass_create()"); - return EXIT_FAILURE; - } - - tree = dmnsn_new_kD_splay_tree(); - - for (i = 0; i < nobjects; ++i) { - objects[i] = dmnsn_new_object(); - if (!objects[i]) { - fprintf(stderr, "--- Couldn't allocate object! ---\n"); - return EXIT_FAILURE; - } - - /* Generate a bounding box in (-1, -1, -1), (1, 1, 1) */ - dmnsn_randomize_bounding_box(objects[i]); - objects[i]->intersection_fn = &dmnsn_fake_intersection_fn; - } - - /* dmnsn_kD_splay_insert() */ - - grains = 0; - for (i = 0; i < nobjects; ++i) { - sandglass_bench_noprecache(&sandglass, - dmnsn_kD_splay_insert(tree, objects[i])); - sandglass.grains += grains; - grains = sandglass.grains; - } - printf("dmnsn_kD_splay_insert(): %ld\n", sandglass.grains/nobjects); - - /* dmnsn_kD_splay_search() */ - - ray.x0 = dmnsn_new_vector(0.0, 0.0, -2.0); - ray.n = dmnsn_new_vector(0.0, 0.0, 1.0); - - dmnsn_delete_intersection((*objects[0]->intersection_fn)(objects[0], ray)); - sandglass_bench_noprecache(&sandglass, { - intersection = dmnsn_kD_splay_search(tree, ray); - }); - dmnsn_delete_intersection(intersection); - printf("dmnsn_kD_splay_search(): %ld\n", sandglass.grains); - - /* dmnsn_kD_splay() */ - grains = 0; - for (i = 0; i < nobjects; ++i) { - node = dmnsn_kD_splay_deepest(tree); - sandglass_bench_noprecache(&sandglass, dmnsn_kD_splay(tree, node)); - sandglass.grains += grains; - grains = sandglass.grains; - } - printf("dmnsn_kD_splay(): %ld\n", sandglass.grains/nobjects); - - /* Cleanup */ - dmnsn_delete_kD_splay_tree(tree); - for (i = 0; i < nobjects; ++i) { - dmnsn_delete_object(objects[i]); - } - - return EXIT_SUCCESS; -} diff --git a/libdimension/Makefile.am b/libdimension/Makefile.am index 0efa05c..b3f6813 100644 --- a/libdimension/Makefile.am +++ b/libdimension/Makefile.am @@ -42,6 +42,8 @@ lib_LTLIBRARIES = libdimension.la libdimension_la_SOURCES = $(nobase_include_HEADERS) \ ambient.c \ + bvst.c \ + bvst.h \ camera.c \ canvas.c \ color.c \ @@ -53,8 +55,6 @@ libdimension_la_SOURCES = $(nobase_include_HEADERS) \ geometry.c \ gl.c \ inlines.c \ - kD_splay_tree.c \ - kD_splay_tree.h \ light.c \ object.c \ perspective.c \ diff --git a/libdimension/bvst.c b/libdimension/bvst.c new file mode 100644 index 0000000..a73b4b2 --- /dev/null +++ b/libdimension/bvst.c @@ -0,0 +1,462 @@ +/************************************************************************* + * Copyright (C) 2009 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +#include "dimension_impl.h" +#include + +static dmnsn_bvst_node *dmnsn_new_bvst_node(); +static void dmnsn_delete_bvst_node(dmnsn_bvst_node *node); + +/* Return an empty tree */ +dmnsn_bvst * +dmnsn_new_bvst() +{ + dmnsn_bvst *tree = malloc(sizeof(dmnsn_bvst)); + if (tree) { + tree->root = NULL; + } else { + dmnsn_error(DMNSN_SEVERITY_HIGH, "kD splay tree allocation failed."); + } + return tree; +} + +/* Recursively copy the nodes of a kD splay tree */ +static dmnsn_bvst_node * +dmnsn_bvst_copy_recursive(dmnsn_bvst_node *root) +{ + dmnsn_bvst_node *node = dmnsn_new_bvst_node(); + *node = *root; + if (node->contains) { + node->contains = dmnsn_bvst_copy_recursive(node->contains); + node->contains->parent = node; + } + if (node->container) { + node->container = dmnsn_bvst_copy_recursive(node->container); + node->container->parent = node; + } + return node; +} + +/* Copy a kD splay tree */ +dmnsn_bvst * +dmnsn_bvst_copy(dmnsn_bvst *tree) +{ + dmnsn_bvst *copy = dmnsn_new_bvst(); + if (tree->root) + copy->root = dmnsn_bvst_copy_recursive(tree->root); + else + copy->root = NULL; + return copy; +} + +/* Recursively free a kD splay tree */ +void +dmnsn_delete_bvst_recursive(dmnsn_bvst_node *node) +{ + if (node) { + dmnsn_delete_bvst_recursive(node->contains); + dmnsn_delete_bvst_recursive(node->container); + dmnsn_delete_bvst_node(node); + } +} + +/* Free a kD splay tree */ +void +dmnsn_delete_bvst(dmnsn_bvst *tree) +{ + if (tree) { + dmnsn_delete_bvst_recursive(tree->root); + free(tree); + } +} + +/* Return whether node1 contains node2 */ +static int dmnsn_box_contains(dmnsn_vector p, + dmnsn_vector min, dmnsn_vector max); +/* Expand node to contain the bounding box from min to max */ +static void dmnsn_bvst_node_swallow(dmnsn_bvst_node *node, + dmnsn_vector min, dmnsn_vector max); + +/* Insert an object into the tree */ +void +dmnsn_bvst_insert(dmnsn_bvst *tree, dmnsn_object *object) +{ + dmnsn_bvst_node *node = dmnsn_new_bvst_node(), *parent = tree->root; + + /* Store the inverse of the transformation matrix */ + object->trans_inv = dmnsn_matrix_inverse(object->trans); + + node->contains = NULL; + node->container = NULL; + node->parent = NULL; + node->object = object; + + /* Calculate the new bounding box by finding the minimum coordinate of the + transformed corners of the object's original bounding box */ + + node->min = dmnsn_matrix_vector_mul(object->trans, object->min); + node->max = node->min; + + dmnsn_vector corner; + corner = dmnsn_new_vector(object->min.x, object->min.y, object->max.z); + corner = dmnsn_matrix_vector_mul(object->trans, corner); + dmnsn_bvst_node_swallow(node, corner, corner); + + corner = dmnsn_new_vector(object->min.x, object->max.y, object->min.z); + corner = dmnsn_matrix_vector_mul(object->trans, corner); + dmnsn_bvst_node_swallow(node, corner, corner); + + corner = dmnsn_new_vector(object->min.x, object->max.y, object->max.z); + corner = dmnsn_matrix_vector_mul(object->trans, corner); + dmnsn_bvst_node_swallow(node, corner, corner); + + corner = dmnsn_new_vector(object->max.x, object->min.y, object->min.z); + corner = dmnsn_matrix_vector_mul(object->trans, corner); + dmnsn_bvst_node_swallow(node, corner, corner); + + corner = dmnsn_new_vector(object->max.x, object->min.y, object->max.z); + corner = dmnsn_matrix_vector_mul(object->trans, corner); + dmnsn_bvst_node_swallow(node, corner, corner); + + corner = dmnsn_new_vector(object->max.x, object->max.y, object->min.z); + corner = dmnsn_matrix_vector_mul(object->trans, corner); + dmnsn_bvst_node_swallow(node, corner, corner); + + corner = dmnsn_new_vector(object->max.x, object->max.y, object->max.z); + corner = dmnsn_matrix_vector_mul(object->trans, corner); + dmnsn_bvst_node_swallow(node, corner, corner); + + /* Now insert the node */ + + while (parent) { + if (dmnsn_box_contains(node->min, parent->min, parent->max) + && dmnsn_box_contains(node->max, parent->min, parent->max)) { + /* parent fully contains node */ + if (parent->contains) + parent = parent->contains; + else { + /* We found our parent; insert node into the tree */ + parent->contains = node; + node->parent = parent; + break; + } + } else { + /* Expand node's bounding box to fully contain parent's if it doesn't + already */ + dmnsn_bvst_node_swallow(node, parent->min, parent->max); + /* node now fully contains parent */ + if (parent->container) + parent = parent->container; + else { + /* We found our parent; insert node into the tree */ + parent->container = node; + node->parent = parent; + break; + } + } + } + + dmnsn_bvst_splay(tree, node); +} + +/* Return whether p is within the axis-aligned box with corners min and max */ +static int +dmnsn_box_contains(dmnsn_vector p, dmnsn_vector min, dmnsn_vector max) +{ + return (p.x >= min.x && p.y >= min.y && p.z >= min.z) + && (p.x <= max.x && p.y <= max.y && p.z <= max.z); +} + +/* Expand node to contain the bounding box from min to max */ +static void +dmnsn_bvst_node_swallow(dmnsn_bvst_node *node, + dmnsn_vector min, dmnsn_vector max) +{ + if (node->min.x > min.x) node->min.x = min.x; + if (node->min.y > min.y) node->min.y = min.y; + if (node->min.z > min.z) node->min.z = min.z; + + if (node->max.x < max.x) node->max.x = max.x; + if (node->max.y < max.y) node->max.y = max.y; + if (node->max.z < max.z) node->max.z = max.z; +} + +/* Tree rotations */ +static void dmnsn_bvst_rotate(dmnsn_bvst_node *node); + +/* Splay a node: move it to the root via tree rotations */ +void +dmnsn_bvst_splay(dmnsn_bvst *tree, dmnsn_bvst_node *node) +{ + while (node->parent) { + if (!node->parent->parent) { + /* Zig step - we are a child of the root node */ + dmnsn_bvst_rotate(node); + break; + } else if ((node == node->parent->contains + && node->parent == node->parent->parent->contains) + || (node == node->parent->container + && node->parent == node->parent->parent->container)) { + /* Zig-zig step - we are a child on the same side as our parent */ + dmnsn_bvst_rotate(node->parent); + dmnsn_bvst_rotate(node); + } else { + /* Zig-zag step - we are a child on a different side than our parent is */ + dmnsn_bvst_rotate(node); + dmnsn_bvst_rotate(node); + } + } + tree->root = node; +} + +/* Rotate a tree on the edge connecting node and node->parent */ +static void +dmnsn_bvst_rotate(dmnsn_bvst_node *node) +{ + dmnsn_bvst_node *P, *Q, *B; + if (node == node->parent->contains) { + /* We are a left child; perform a right rotation: + * + * Q P + * / \ / \ + * P C ---> A Q + * / \ / \ + * A B B C + */ + Q = node->parent; + P = node; + /* A = node->contains; */ + B = node->container; + /* C = node->parent->container; */ + + /* First fix up the parents */ + if (Q->parent) { + if (Q->parent->contains == Q) + Q->parent->contains = P; + else + Q->parent->container = P; + } + P->parent = Q->parent; + Q->parent = P; + if (B) B->parent = Q; + + /* Then the children */ + P->container = Q; + Q->contains = B; + } else { + /* We are a right child; perform a left rotation: + * + * P Q + * / \ / \ + * A Q ---> P C + * / \ / \ + * B C A B + */ + P = node->parent; + Q = node; + /* A = node->parent->contains; */ + B = node->contains; + /* C = node->container; */ + + /* First fix up the parents */ + if (P->parent) { + if (P->parent->contains == P) + P->parent->contains = Q; + else + P->parent->container = Q; + } + Q->parent = P->parent; + P->parent = Q; + if (B) B->parent = P; + + /* Then the children */ + Q->contains = P; + P->container = B; + } +} + +typedef struct { + dmnsn_bvst_node *node; + dmnsn_intersection *intersection; +} dmnsn_bvst_search_result; + +static dmnsn_bvst_search_result +dmnsn_bvst_search_recursive(dmnsn_bvst_node *node, dmnsn_line ray, double t); + +dmnsn_intersection * +dmnsn_bvst_search(dmnsn_bvst *tree, dmnsn_line ray) +{ + dmnsn_bvst_search_result result + = dmnsn_bvst_search_recursive(tree->root, ray, -1.0); + + if (result.node) + dmnsn_bvst_splay(tree, result.node); + + return result.intersection; +} + +static int dmnsn_ray_box_intersection(dmnsn_line ray, dmnsn_vector min, + dmnsn_vector max, double t); + +static dmnsn_bvst_search_result +dmnsn_bvst_search_recursive(dmnsn_bvst_node *node, dmnsn_line ray, double t) +{ + dmnsn_line ray_trans; + dmnsn_bvst_search_result result = { NULL, NULL }, result_temp; + + if (!node) + return result; + + /* Go down the right subtree first because the closest object is more likely + to lie in the larger bounding boxes */ + result_temp = dmnsn_bvst_search_recursive(node->container, ray, t); + if (result_temp.node && (t < 0.0 || result_temp.intersection->t < t)) { + result = result_temp; + t = result.intersection->t; + } else { + dmnsn_delete_intersection(result_temp.intersection); + } + + if (dmnsn_box_contains(ray.x0, node->min, node->max) + || dmnsn_ray_box_intersection(ray, node->min, node->max, t)) + { + /* Transform the ray according to the object */ + ray_trans = dmnsn_matrix_line_mul(node->object->trans_inv, ray); + + if (dmnsn_box_contains(ray_trans.x0, node->object->min, node->object->max) + || dmnsn_ray_box_intersection(ray_trans, node->object->min, + node->object->max, t)) + { + result_temp.intersection = + (*node->object->intersection_fn)(node->object, ray_trans); + + if (result_temp.intersection + && (t < 0.0 || result_temp.intersection->t < t)) { + dmnsn_delete_intersection(result.intersection); + result.node = node; + result.intersection = result_temp.intersection; + t = result.intersection->t; + + /* Transform the intersection back to the observer's view */ + result.intersection->ray = ray; + result.intersection->normal = dmnsn_vector_normalize( + dmnsn_vector_sub( + dmnsn_matrix_vector_mul( + node->object->trans, + result.intersection->normal + ), + dmnsn_matrix_vector_mul( + node->object->trans, + dmnsn_zero + ) + ) + ); + } else { + dmnsn_delete_intersection(result_temp.intersection); + } + } + + /* Go down the left subtree */ + result_temp = dmnsn_bvst_search_recursive(node->contains, ray, t); + if (result_temp.node && (t < 0.0 || result_temp.intersection->t < t)) { + dmnsn_delete_intersection(result.intersection); + result = result_temp; + t = result.intersection->t; + } else { + dmnsn_delete_intersection(result_temp.intersection); + } + } + + return result; +} + +static int +dmnsn_ray_box_intersection(dmnsn_line line, dmnsn_vector min, dmnsn_vector max, + double t) +{ + double t_temp; + dmnsn_vector p; + + if (line.n.x != 0.0) { + /* x == min.x */ + t_temp = (min.x - line.x0.x)/line.n.x; + p = dmnsn_line_point(line, t_temp); + if (p.y >= min.y && p.y <= max.y && p.z >= min.z && p.z <= max.z + && t_temp >= 0.0 && (t < 0.0 || t_temp < t)) + return 1; + + /* x == max.x */ + t_temp = (max.x - line.x0.x)/line.n.x; + p = dmnsn_line_point(line, t_temp); + if (p.y >= min.y && p.y <= max.y && p.z >= min.z && p.z <= max.z + && t_temp >= 0.0 && (t < 0.0 || t_temp < t)) + return 1; + } + + if (line.n.y != 0.0) { + /* y == -1.0 */ + t_temp = (min.y - line.x0.y)/line.n.y; + p = dmnsn_line_point(line, t_temp); + if (p.x >= min.x && p.x <= max.x && p.z >= min.z && p.z <= max.z + && t_temp >= 0.0 && (t < 0.0 || t_temp < t)) + return 1; + + /* y == 1.0 */ + t_temp = (max.y - line.x0.y)/line.n.y; + p = dmnsn_line_point(line, t_temp); + if (p.x >= min.x && p.x <= max.x && p.z >= min.z && p.z <= max.z + && t_temp >= 0.0 && (t < 0.0 || t_temp < t)) + return 1; + } + + if (line.n.z != 0.0) { + /* z == -1.0 */ + t_temp = (min.z - line.x0.z)/line.n.z; + p = dmnsn_line_point(line, t_temp); + if (p.x >= min.x && p.x <= max.x && p.y >= min.y && p.y <= max.y + && t_temp >= 0.0 && (t < 0.0 || t_temp < t)) + return 1; + + /* z == 1.0 */ + t_temp = (max.z - line.x0.z)/line.n.z; + p = dmnsn_line_point(line, t_temp); + if (p.x >= min.x && p.x <= max.x && p.y >= min.y && p.y <= max.y + && t_temp >= 0.0 && (t < 0.0 || t_temp < t)) + return 1; + } + + return 0; +} + +static dmnsn_bvst_node * +dmnsn_new_bvst_node() +{ + dmnsn_bvst_node *node = malloc(sizeof(dmnsn_bvst_node)); + if (!node) { + dmnsn_error(DMNSN_SEVERITY_HIGH, "kD splay tree node allocation failed."); + } + return node; +} + +static void +dmnsn_delete_bvst_node(dmnsn_bvst_node *node) +{ + free(node); +} diff --git a/libdimension/bvst.h b/libdimension/bvst.h new file mode 100644 index 0000000..57b71b8 --- /dev/null +++ b/libdimension/bvst.h @@ -0,0 +1,65 @@ +/************************************************************************* + * Copyright (C) 2009 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/* + * Bounding Volume Splay Trees for storing object bounding boxes. + * Each node's bounding box entirely contains the bounding boxes of the nodes + * to its left, and is entirely contained by the bounding boxes of the nodes to + * its right. Splay trees are used for the implementation, to bring commonly- + * used objects (the most recent object which a ray has hit) near the root of + * the tree for fast access. Object's bounding boxes are expanded as needed + * when inserted into the tree: if they intersect an existing bounding box, they + * are expanded to contain it. + */ + +#ifndef DIMENSION_IMPL_BVST_H +#define DIMENSION_IMPL_BVST_H + +typedef struct dmnsn_bvst dmnsn_bvst; +typedef struct dmnsn_bvst_node dmnsn_bvst_node; + +struct dmnsn_bvst { + dmnsn_bvst_node *root; +}; + +struct dmnsn_bvst_node { + /* Tree children */ + dmnsn_bvst_node *contains, *container; + + /* Parent node for easy backtracking */ + dmnsn_bvst_node *parent; + + /* Bounding box corners */ + dmnsn_vector min, max; + + /* Node payload */ + dmnsn_object *object; +}; + +dmnsn_bvst *dmnsn_new_bvst(); +dmnsn_bvst *dmnsn_bvst_copy(dmnsn_bvst *tree); +void dmnsn_delete_bvst(dmnsn_bvst *tree); + +void dmnsn_bvst_insert(dmnsn_bvst *tree, dmnsn_object *object); +void dmnsn_bvst_splay(dmnsn_bvst *tree, dmnsn_bvst_node *node); + +dmnsn_intersection *dmnsn_bvst_search(dmnsn_bvst *tree, dmnsn_line ray); + +#endif /* DIMENSION_IMPL_BVST_H */ diff --git a/libdimension/dimension_impl.h b/libdimension/dimension_impl.h index 216302a..880aee7 100644 --- a/libdimension/dimension_impl.h +++ b/libdimension/dimension_impl.h @@ -22,6 +22,6 @@ #define DIMENSION_IMPL_H #include "dimension.h" -#include "kD_splay_tree.h" +#include "bvst.h" #endif /* DIMENSION_IMPL_H */ diff --git a/libdimension/kD_splay_tree.c b/libdimension/kD_splay_tree.c deleted file mode 100644 index b6be801..0000000 --- a/libdimension/kD_splay_tree.c +++ /dev/null @@ -1,464 +0,0 @@ -/************************************************************************* - * Copyright (C) 2009 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library 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 * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -#include "dimension_impl.h" -#include - -static dmnsn_kD_splay_node *dmnsn_new_kD_splay_node(); -static void dmnsn_delete_kD_splay_node(dmnsn_kD_splay_node *node); - -/* Return an empty tree */ -dmnsn_kD_splay_tree * -dmnsn_new_kD_splay_tree() -{ - dmnsn_kD_splay_tree *tree = malloc(sizeof(dmnsn_kD_splay_tree)); - if (tree) { - tree->root = NULL; - } else { - dmnsn_error(DMNSN_SEVERITY_HIGH, "kD splay tree allocation failed."); - } - return tree; -} - -/* Recursively copy the nodes of a kD splay tree */ -static dmnsn_kD_splay_node * -dmnsn_kD_splay_copy_recursive(dmnsn_kD_splay_node *root) -{ - dmnsn_kD_splay_node *node = dmnsn_new_kD_splay_node(); - *node = *root; - if (node->contains) { - node->contains = dmnsn_kD_splay_copy_recursive(node->contains); - node->contains->parent = node; - } - if (node->container) { - node->container = dmnsn_kD_splay_copy_recursive(node->container); - node->container->parent = node; - } - return node; -} - -/* Copy a kD splay tree */ -dmnsn_kD_splay_tree * -dmnsn_kD_splay_copy(dmnsn_kD_splay_tree *tree) -{ - dmnsn_kD_splay_tree *copy = dmnsn_new_kD_splay_tree(); - if (tree->root) - copy->root = dmnsn_kD_splay_copy_recursive(tree->root); - else - copy->root = NULL; - return copy; -} - -/* Recursively free a kD splay tree */ -void -dmnsn_delete_kD_splay_tree_recursive(dmnsn_kD_splay_node *node) -{ - if (node) { - dmnsn_delete_kD_splay_tree_recursive(node->contains); - dmnsn_delete_kD_splay_tree_recursive(node->container); - dmnsn_delete_kD_splay_node(node); - } -} - -/* Free a kD splay tree */ -void -dmnsn_delete_kD_splay_tree(dmnsn_kD_splay_tree *tree) -{ - if (tree) { - dmnsn_delete_kD_splay_tree_recursive(tree->root); - free(tree); - } -} - -/* Return whether node1 contains node2 */ -static int dmnsn_box_contains(dmnsn_vector p, - dmnsn_vector min, dmnsn_vector max); -/* Expand node to contain the bounding box from min to max */ -static void dmnsn_kD_splay_node_swallow(dmnsn_kD_splay_node *node, - dmnsn_vector min, dmnsn_vector max); - -/* Insert an object into the tree */ -void -dmnsn_kD_splay_insert(dmnsn_kD_splay_tree *tree, dmnsn_object *object) -{ - dmnsn_kD_splay_node *node = dmnsn_new_kD_splay_node(), *parent = tree->root; - - /* Store the inverse of the transformation matrix */ - object->trans_inv = dmnsn_matrix_inverse(object->trans); - - node->contains = NULL; - node->container = NULL; - node->parent = NULL; - node->object = object; - - /* Calculate the new bounding box by finding the minimum coordinate of the - transformed corners of the object's original bounding box */ - - node->min = dmnsn_matrix_vector_mul(object->trans, object->min); - node->max = node->min; - - dmnsn_vector corner; - corner = dmnsn_new_vector(object->min.x, object->min.y, object->max.z); - corner = dmnsn_matrix_vector_mul(object->trans, corner); - dmnsn_kD_splay_node_swallow(node, corner, corner); - - corner = dmnsn_new_vector(object->min.x, object->max.y, object->min.z); - corner = dmnsn_matrix_vector_mul(object->trans, corner); - dmnsn_kD_splay_node_swallow(node, corner, corner); - - corner = dmnsn_new_vector(object->min.x, object->max.y, object->max.z); - corner = dmnsn_matrix_vector_mul(object->trans, corner); - dmnsn_kD_splay_node_swallow(node, corner, corner); - - corner = dmnsn_new_vector(object->max.x, object->min.y, object->min.z); - corner = dmnsn_matrix_vector_mul(object->trans, corner); - dmnsn_kD_splay_node_swallow(node, corner, corner); - - corner = dmnsn_new_vector(object->max.x, object->min.y, object->max.z); - corner = dmnsn_matrix_vector_mul(object->trans, corner); - dmnsn_kD_splay_node_swallow(node, corner, corner); - - corner = dmnsn_new_vector(object->max.x, object->max.y, object->min.z); - corner = dmnsn_matrix_vector_mul(object->trans, corner); - dmnsn_kD_splay_node_swallow(node, corner, corner); - - corner = dmnsn_new_vector(object->max.x, object->max.y, object->max.z); - corner = dmnsn_matrix_vector_mul(object->trans, corner); - dmnsn_kD_splay_node_swallow(node, corner, corner); - - /* Now insert the node */ - - while (parent) { - if (dmnsn_box_contains(node->min, parent->min, parent->max) - && dmnsn_box_contains(node->max, parent->min, parent->max)) { - /* parent fully contains node */ - if (parent->contains) - parent = parent->contains; - else { - /* We found our parent; insert node into the tree */ - parent->contains = node; - node->parent = parent; - break; - } - } else { - /* Expand node's bounding box to fully contain parent's if it doesn't - already */ - dmnsn_kD_splay_node_swallow(node, parent->min, parent->max); - /* node now fully contains parent */ - if (parent->container) - parent = parent->container; - else { - /* We found our parent; insert node into the tree */ - parent->container = node; - node->parent = parent; - break; - } - } - } - - dmnsn_kD_splay(tree, node); -} - -/* Return whether p is within the axis-aligned box with corners min and max */ -static int -dmnsn_box_contains(dmnsn_vector p, dmnsn_vector min, dmnsn_vector max) -{ - return (p.x >= min.x && p.y >= min.y && p.z >= min.z) - && (p.x <= max.x && p.y <= max.y && p.z <= max.z); -} - -/* Expand node to contain the bounding box from min to max */ -static void -dmnsn_kD_splay_node_swallow(dmnsn_kD_splay_node *node, - dmnsn_vector min, dmnsn_vector max) -{ - if (node->min.x > min.x) node->min.x = min.x; - if (node->min.y > min.y) node->min.y = min.y; - if (node->min.z > min.z) node->min.z = min.z; - - if (node->max.x < max.x) node->max.x = max.x; - if (node->max.y < max.y) node->max.y = max.y; - if (node->max.z < max.z) node->max.z = max.z; -} - -/* Tree rotations */ -static void dmnsn_kD_splay_rotate(dmnsn_kD_splay_node *node); - -/* Splay a node: move it to the root via tree rotations */ -void -dmnsn_kD_splay(dmnsn_kD_splay_tree *tree, dmnsn_kD_splay_node *node) -{ - while (node->parent) { - if (!node->parent->parent) { - /* Zig step - we are a child of the root node */ - dmnsn_kD_splay_rotate(node); - break; - } else if ((node == node->parent->contains - && node->parent == node->parent->parent->contains) - || (node == node->parent->container - && node->parent == node->parent->parent->container)) { - /* Zig-zig step - we are a child on the same side as our parent */ - dmnsn_kD_splay_rotate(node->parent); - dmnsn_kD_splay_rotate(node); - } else { - /* Zig-zag step - we are a child on a different side than our parent is */ - dmnsn_kD_splay_rotate(node); - dmnsn_kD_splay_rotate(node); - } - } - tree->root = node; -} - -/* Rotate a tree on the edge connecting node and node->parent */ -static void -dmnsn_kD_splay_rotate(dmnsn_kD_splay_node *node) -{ - dmnsn_kD_splay_node *P, *Q, *B; - if (node == node->parent->contains) { - /* We are a left child; perform a right rotation: - * - * Q P - * / \ / \ - * P C ---> A Q - * / \ / \ - * A B B C - */ - Q = node->parent; - P = node; - /* A = node->contains; */ - B = node->container; - /* C = node->parent->container; */ - - /* First fix up the parents */ - if (Q->parent) { - if (Q->parent->contains == Q) - Q->parent->contains = P; - else - Q->parent->container = P; - } - P->parent = Q->parent; - Q->parent = P; - if (B) B->parent = Q; - - /* Then the children */ - P->container = Q; - Q->contains = B; - } else { - /* We are a right child; perform a left rotation: - * - * P Q - * / \ / \ - * A Q ---> P C - * / \ / \ - * B C A B - */ - P = node->parent; - Q = node; - /* A = node->parent->contains; */ - B = node->contains; - /* C = node->container; */ - - /* First fix up the parents */ - if (P->parent) { - if (P->parent->contains == P) - P->parent->contains = Q; - else - P->parent->container = Q; - } - Q->parent = P->parent; - P->parent = Q; - if (B) B->parent = P; - - /* Then the children */ - Q->contains = P; - P->container = B; - } -} - -typedef struct { - dmnsn_kD_splay_node *node; - dmnsn_intersection *intersection; -} dmnsn_kD_splay_search_result; - -static dmnsn_kD_splay_search_result -dmnsn_kD_splay_search_recursive(dmnsn_kD_splay_node *node, dmnsn_line ray, - double t); - -dmnsn_intersection * -dmnsn_kD_splay_search(dmnsn_kD_splay_tree *tree, dmnsn_line ray) -{ - dmnsn_kD_splay_search_result result - = dmnsn_kD_splay_search_recursive(tree->root, ray, -1.0); - - if (result.node) - dmnsn_kD_splay(tree, result.node); - - return result.intersection; -} - -static int dmnsn_ray_box_intersection(dmnsn_line ray, dmnsn_vector min, - dmnsn_vector max, double t); - -static dmnsn_kD_splay_search_result -dmnsn_kD_splay_search_recursive(dmnsn_kD_splay_node *node, dmnsn_line ray, - double t) -{ - dmnsn_line ray_trans; - dmnsn_kD_splay_search_result result = { NULL, NULL }, result_temp; - - if (!node) - return result; - - /* Go down the right subtree first because the closest object is more likely - to lie in the larger bounding boxes */ - result_temp = dmnsn_kD_splay_search_recursive(node->container, ray, t); - if (result_temp.node && (t < 0.0 || result_temp.intersection->t < t)) { - result = result_temp; - t = result.intersection->t; - } else { - dmnsn_delete_intersection(result_temp.intersection); - } - - if (dmnsn_box_contains(ray.x0, node->min, node->max) - || dmnsn_ray_box_intersection(ray, node->min, node->max, t)) - { - /* Transform the ray according to the object */ - ray_trans = dmnsn_matrix_line_mul(node->object->trans_inv, ray); - - if (dmnsn_box_contains(ray_trans.x0, node->object->min, node->object->max) - || dmnsn_ray_box_intersection(ray_trans, node->object->min, - node->object->max, t)) - { - result_temp.intersection = - (*node->object->intersection_fn)(node->object, ray_trans); - - if (result_temp.intersection - && (t < 0.0 || result_temp.intersection->t < t)) { - dmnsn_delete_intersection(result.intersection); - result.node = node; - result.intersection = result_temp.intersection; - t = result.intersection->t; - - /* Transform the intersection back to the observer's view */ - result.intersection->ray = ray; - result.intersection->normal = dmnsn_vector_normalize( - dmnsn_vector_sub( - dmnsn_matrix_vector_mul( - node->object->trans, - result.intersection->normal - ), - dmnsn_matrix_vector_mul( - node->object->trans, - dmnsn_zero - ) - ) - ); - } else { - dmnsn_delete_intersection(result_temp.intersection); - } - } - - /* Go down the left subtree */ - result_temp = dmnsn_kD_splay_search_recursive(node->contains, ray, t); - if (result_temp.node && (t < 0.0 || result_temp.intersection->t < t)) { - dmnsn_delete_intersection(result.intersection); - result = result_temp; - t = result.intersection->t; - } else { - dmnsn_delete_intersection(result_temp.intersection); - } - } - - return result; -} - -static int -dmnsn_ray_box_intersection(dmnsn_line line, dmnsn_vector min, dmnsn_vector max, - double t) -{ - double t_temp; - dmnsn_vector p; - - if (line.n.x != 0.0) { - /* x == min.x */ - t_temp = (min.x - line.x0.x)/line.n.x; - p = dmnsn_line_point(line, t_temp); - if (p.y >= min.y && p.y <= max.y && p.z >= min.z && p.z <= max.z - && t_temp >= 0.0 && (t < 0.0 || t_temp < t)) - return 1; - - /* x == max.x */ - t_temp = (max.x - line.x0.x)/line.n.x; - p = dmnsn_line_point(line, t_temp); - if (p.y >= min.y && p.y <= max.y && p.z >= min.z && p.z <= max.z - && t_temp >= 0.0 && (t < 0.0 || t_temp < t)) - return 1; - } - - if (line.n.y != 0.0) { - /* y == -1.0 */ - t_temp = (min.y - line.x0.y)/line.n.y; - p = dmnsn_line_point(line, t_temp); - if (p.x >= min.x && p.x <= max.x && p.z >= min.z && p.z <= max.z - && t_temp >= 0.0 && (t < 0.0 || t_temp < t)) - return 1; - - /* y == 1.0 */ - t_temp = (max.y - line.x0.y)/line.n.y; - p = dmnsn_line_point(line, t_temp); - if (p.x >= min.x && p.x <= max.x && p.z >= min.z && p.z <= max.z - && t_temp >= 0.0 && (t < 0.0 || t_temp < t)) - return 1; - } - - if (line.n.z != 0.0) { - /* z == -1.0 */ - t_temp = (min.z - line.x0.z)/line.n.z; - p = dmnsn_line_point(line, t_temp); - if (p.x >= min.x && p.x <= max.x && p.y >= min.y && p.y <= max.y - && t_temp >= 0.0 && (t < 0.0 || t_temp < t)) - return 1; - - /* z == 1.0 */ - t_temp = (max.z - line.x0.z)/line.n.z; - p = dmnsn_line_point(line, t_temp); - if (p.x >= min.x && p.x <= max.x && p.y >= min.y && p.y <= max.y - && t_temp >= 0.0 && (t < 0.0 || t_temp < t)) - return 1; - } - - return 0; -} - -static dmnsn_kD_splay_node * -dmnsn_new_kD_splay_node() -{ - dmnsn_kD_splay_node *node = malloc(sizeof(dmnsn_kD_splay_node)); - if (!node) { - dmnsn_error(DMNSN_SEVERITY_HIGH, "kD splay tree node allocation failed."); - } - return node; -} - -static void -dmnsn_delete_kD_splay_node(dmnsn_kD_splay_node *node) -{ - free(node); -} diff --git a/libdimension/kD_splay_tree.h b/libdimension/kD_splay_tree.h deleted file mode 100644 index eac47d8..0000000 --- a/libdimension/kD_splay_tree.h +++ /dev/null @@ -1,66 +0,0 @@ -/************************************************************************* - * Copyright (C) 2009 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library 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 * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/* - * k-dimensional (in this case, k == 3) trees for storing object bounding boxes. - * Each node's bounding box entirely contains the bounding boxes of the nodes - * to its left, and is entirely contained by the bounding boxes of the nodes to - * its right. Splay trees are used for the implementation, to bring commonly- - * used objects (the most recent object which a ray has hit) near the root of - * the tree for fast access. Object's bounding boxes are expanded as needed - * when inserted into the tree: if they intersect an existing bounding box, they - * are expanded to contain it. - */ - -#ifndef DIMENSION_IMPL_KD_SPLAY_TREE_H -#define DIMENSION_IMPL_KD_SPLAY_TREE_H - -typedef struct dmnsn_kD_splay_tree dmnsn_kD_splay_tree; -typedef struct dmnsn_kD_splay_node dmnsn_kD_splay_node; - -struct dmnsn_kD_splay_tree { - dmnsn_kD_splay_node *root; -}; - -struct dmnsn_kD_splay_node { - /* Tree children */ - dmnsn_kD_splay_node *contains, *container; - - /* Parent node for easy backtracking */ - dmnsn_kD_splay_node *parent; - - /* Bounding box corners */ - dmnsn_vector min, max; - - /* Node payload */ - dmnsn_object *object; -}; - -dmnsn_kD_splay_tree *dmnsn_new_kD_splay_tree(); -dmnsn_kD_splay_tree *dmnsn_kD_splay_copy(dmnsn_kD_splay_tree *tree); -void dmnsn_delete_kD_splay_tree(dmnsn_kD_splay_tree *tree); - -void dmnsn_kD_splay_insert(dmnsn_kD_splay_tree *tree, dmnsn_object *object); -void dmnsn_kD_splay(dmnsn_kD_splay_tree *tree, dmnsn_kD_splay_node *node); - -dmnsn_intersection *dmnsn_kD_splay_search(dmnsn_kD_splay_tree *tree, - dmnsn_line ray); - -#endif /* DIMENSION_IMPL_KD_SPLAY_TREE_H */ diff --git a/libdimension/raytrace.c b/libdimension/raytrace.c index a76a2f5..1e03b91 100644 --- a/libdimension/raytrace.c +++ b/libdimension/raytrace.c @@ -30,7 +30,7 @@ typedef struct { dmnsn_progress *progress; dmnsn_scene *scene; - dmnsn_kD_splay_tree *kD_splay_tree; + dmnsn_bvst *bvst; /* For multithreading */ unsigned int index, threads; @@ -63,19 +63,19 @@ dmnsn_raytrace_scene_async(dmnsn_scene *scene) return NULL; } - payload->progress = progress; - payload->scene = scene; - payload->kD_splay_tree = dmnsn_new_kD_splay_tree(); + payload->progress = progress; + payload->scene = scene; + payload->bvst = dmnsn_new_bvst(); for (i = 0; i < dmnsn_array_size(payload->scene->objects); ++i) { dmnsn_array_get(payload->scene->objects, i, &object); - dmnsn_kD_splay_insert(payload->kD_splay_tree, object); + dmnsn_bvst_insert(payload->bvst, object); } if (pthread_create(&progress->thread, NULL, &dmnsn_raytrace_scene_thread, payload) != 0) { - dmnsn_delete_kD_splay_tree(payload->kD_splay_tree); + dmnsn_delete_bvst(payload->bvst); free(payload); dmnsn_delete_progress(progress); return NULL; @@ -145,8 +145,7 @@ dmnsn_raytrace_scene_multithread(dmnsn_raytrace_payload *payload) payloads[i].index = i; payloads[i].threads = nthreads; if (i > 0) { - payloads[i].kD_splay_tree = - dmnsn_kD_splay_copy(payloads[0].kD_splay_tree); + payloads[i].bvst = dmnsn_bvst_copy(payloads[0].bvst); } } @@ -164,7 +163,7 @@ dmnsn_raytrace_scene_multithread(dmnsn_raytrace_payload *payload) } else { /* Only free on a successful join - otherwise we might free a pointer out from under a running thread */ - dmnsn_delete_kD_splay_tree(payloads[j].kD_splay_tree); + dmnsn_delete_bvst(payloads[j].bvst); free(ptr); } } @@ -182,7 +181,7 @@ dmnsn_raytrace_scene_multithread(dmnsn_raytrace_payload *payload) if (retval == 0) { retval = *(int *)ptr; } - dmnsn_delete_kD_splay_tree(payloads[i].kD_splay_tree); + dmnsn_delete_bvst(payloads[i].bvst); free(ptr); } } @@ -195,7 +194,7 @@ dmnsn_raytrace_scene_multithread(dmnsn_raytrace_payload *payload) /* Actual raytracing implementation */ static int dmnsn_raytrace_scene_impl(dmnsn_progress *progress, dmnsn_scene *scene, - dmnsn_kD_splay_tree *kD_splay_tree, + dmnsn_bvst *bvst, unsigned int index, unsigned int threads); /* Multi-threading thread callback */ @@ -206,7 +205,7 @@ dmnsn_raytrace_scene_multithread_thread(void *ptr) int *retval = malloc(sizeof(int)); if (retval) { *retval = dmnsn_raytrace_scene_impl(payload->progress, payload->scene, - payload->kD_splay_tree, + payload->bvst, payload->index, payload->threads); } return retval; @@ -219,7 +218,7 @@ dmnsn_raytrace_scene_multithread_thread(void *ptr) typedef struct dmnsn_raytrace_state { const dmnsn_scene *scene; const dmnsn_intersection *intersection; - dmnsn_kD_splay_tree *kD_splay_tree; + dmnsn_bvst *bvst; unsigned int level; dmnsn_vector r; @@ -238,12 +237,12 @@ static dmnsn_color dmnsn_raytrace_shoot(dmnsn_raytrace_state *state, /* Actually raytrace a scene */ static int dmnsn_raytrace_scene_impl(dmnsn_progress *progress, dmnsn_scene *scene, - dmnsn_kD_splay_tree *kD_splay_tree, + dmnsn_bvst *bvst, unsigned int index, unsigned int threads) { dmnsn_raytrace_state state = { .scene = scene, - .kD_splay_tree = kD_splay_tree + .bvst = bvst }; unsigned int width = scene->canvas->x; @@ -339,10 +338,8 @@ dmnsn_raytrace_light_ray(const dmnsn_raytrace_state *state, unsigned int level = state->level; while (level) { - dmnsn_intersection *shadow_caster = dmnsn_kD_splay_search( - state->kD_splay_tree, - shadow_ray - ); + dmnsn_intersection *shadow_caster + = dmnsn_bvst_search(state->bvst, shadow_ray); if (!shadow_caster || shadow_caster->t > 1.0) { dmnsn_delete_intersection(shadow_caster); @@ -468,7 +465,7 @@ dmnsn_raytrace_shoot(dmnsn_raytrace_state *state, dmnsn_line ray) --state->level; dmnsn_intersection *intersection - = dmnsn_kD_splay_search(state->kD_splay_tree, ray); + = dmnsn_bvst_search(state->bvst, ray); dmnsn_color color = state->scene->background; if (intersection) { diff --git a/tests/libdimension/Makefile.am b/tests/libdimension/Makefile.am index 7b238da..886eb42 100644 --- a/tests/libdimension/Makefile.am +++ b/tests/libdimension/Makefile.am @@ -22,7 +22,7 @@ INCLUDES = -I$(top_srcdir)/libdimension check_LTLIBRARIES = libdimension-tests.la check_PROGRAMS = error-test \ warning-test \ - kD_splay_tree-test \ + bvst-test \ png-test \ gl-test TESTS = $(check_PROGRAMS) @@ -45,8 +45,8 @@ error_test_LDADD = libdimension-tests.la warning_test_SOURCES = warning.c warning_test_LDADD = libdimension-tests.la -kD_splay_tree_test_SOURCES = kD_splay_tree.c -kD_splay_tree_test_LDADD = libdimension-tests.la +bvst_test_SOURCES = bvst.c +bvst_test_LDADD = libdimension-tests.la png_test_SOURCES = png.c png_test_LDADD = libdimension-tests.la diff --git a/tests/libdimension/bvst.c b/tests/libdimension/bvst.c new file mode 100644 index 0000000..36dce04 --- /dev/null +++ b/tests/libdimension/bvst.c @@ -0,0 +1,72 @@ +/************************************************************************* + * Copyright (C) 2009 Tavian Barnes * + * * + * This file is part of The Dimension Test Suite. * + * * + * The Dimension Test Suite 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. * + * * + * The Dimension Test Suite 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 . * + *************************************************************************/ + +/* + * Basic tests of our Bounding Volume Splay Tree framework + */ + +#include "../../libdimension/dimension_impl.h" +#include + +int +main() +{ + dmnsn_bvst *tree; + dmnsn_object *obj1, *obj2, *obj3; + + obj1 = dmnsn_new_object(); + obj2 = dmnsn_new_object(); + obj3 = dmnsn_new_object(); + + obj1->min = dmnsn_new_vector(0.0, 0.0, 0.0); + obj1->max = dmnsn_new_vector(1.0, 1.0, 1.0); + + obj2->min = dmnsn_new_vector(-2.0, -2.0, -2.0); + obj2->max = dmnsn_new_vector(1.0, 1.0, 1.0); + + obj3->min = dmnsn_new_vector(-1.0, -1.0, -1.0); + obj3->max = dmnsn_new_vector(0.0, 0.0, 0.0); + + tree = dmnsn_new_bvst(); + + dmnsn_bvst_insert(tree, obj1); + if (tree->root->object != obj1) { + fprintf(stderr, "Wrong BVST built.\n"); + return EXIT_FAILURE; + } + + dmnsn_bvst_insert(tree, obj2); + if (tree->root->object != obj2 || tree->root->contains->object != obj1) { + fprintf(stderr, "Wrong BVST built.\n"); + return EXIT_FAILURE; + } + + dmnsn_bvst_insert(tree, obj3); + if (tree->root->object != obj3 || tree->root->contains->object != obj1 + || tree->root->container->object != obj2) { + fprintf(stderr, "Wrong BVST built.\n"); + return EXIT_FAILURE; + } + + dmnsn_delete_bvst(tree); + dmnsn_delete_object(obj3); + dmnsn_delete_object(obj2); + dmnsn_delete_object(obj1); + return EXIT_SUCCESS; +} diff --git a/tests/libdimension/kD_splay_tree.c b/tests/libdimension/kD_splay_tree.c deleted file mode 100644 index ea53550..0000000 --- a/tests/libdimension/kD_splay_tree.c +++ /dev/null @@ -1,72 +0,0 @@ -/************************************************************************* - * Copyright (C) 2009 Tavian Barnes * - * * - * This file is part of The Dimension Test Suite. * - * * - * The Dimension Test Suite 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. * - * * - * The Dimension Test Suite 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 . * - *************************************************************************/ - -/* - * Basic tests of our fancy k-D splay tree framework - */ - -#include "../../libdimension/dimension_impl.h" -#include - -int -main() -{ - dmnsn_kD_splay_tree *tree; - dmnsn_object *obj1, *obj2, *obj3; - - obj1 = dmnsn_new_object(); - obj2 = dmnsn_new_object(); - obj3 = dmnsn_new_object(); - - obj1->min = dmnsn_new_vector(0.0, 0.0, 0.0); - obj1->max = dmnsn_new_vector(1.0, 1.0, 1.0); - - obj2->min = dmnsn_new_vector(-2.0, -2.0, -2.0); - obj2->max = dmnsn_new_vector(1.0, 1.0, 1.0); - - obj3->min = dmnsn_new_vector(-1.0, -1.0, -1.0); - obj3->max = dmnsn_new_vector(0.0, 0.0, 0.0); - - tree = dmnsn_new_kD_splay_tree(); - - dmnsn_kD_splay_insert(tree, obj1); - if (tree->root->object != obj1) { - fprintf(stderr, "Wrong kD splay tree built.\n"); - return EXIT_FAILURE; - } - - dmnsn_kD_splay_insert(tree, obj2); - if (tree->root->object != obj2 || tree->root->contains->object != obj1) { - fprintf(stderr, "Wrong kD splay tree built.\n"); - return EXIT_FAILURE; - } - - dmnsn_kD_splay_insert(tree, obj3); - if (tree->root->object != obj3 || tree->root->contains->object != obj1 - || tree->root->container->object != obj2) { - fprintf(stderr, "Wrong kD splay tree built.\n"); - return EXIT_FAILURE; - } - - dmnsn_delete_kD_splay_tree(tree); - dmnsn_delete_object(obj3); - dmnsn_delete_object(obj2); - dmnsn_delete_object(obj1); - return EXIT_SUCCESS; -} -- cgit v1.2.3