77#ifndef PICO_BVH_UDATA_TYPE
78 #define PICO_BVH_UDATA_TYPE uint64_t
99#define BVH_NULL_ID (-1)
253#ifdef PICO_BVH_IMPLEMENTATION
289 #define PICO_BVH_ASSERT(expr) ((void)0)
291 #ifndef PICO_BVH_ASSERT
293 #define PICO_BVH_ASSERT(expr) (assert(expr))
297#if !defined(PICO_BVH_CALLOC) || \
298 !defined(PICO_BVH_REALLOC) || \
299 !defined(PICO_BVH_FREE)
301 #define PICO_BVH_CALLOC(num, size) (calloc(num, size))
302 #define PICO_BVH_REALLOC(ptr, size) (realloc(ptr, size))
303 #define PICO_BVH_FREE(ptr) (free(ptr))
306#ifndef PICO_BVH_MEMSET
308 #define PICO_BVH_MEMSET memset
311#ifndef PICO_BVH_MEMCPY
313 #define PICO_BVH_MEMCPY memcpy
316#ifndef PICO_BVH_STACK_SIZE
317 #define PICO_BVH_STACK_SIZE 1024
320#define BVH_IS_LEAF(n) ((n)->child[0] == BVH_NULL_ID)
321#define BVH_INITIAL_CAPACITY 64
322#define PICO_BVH_EPSILON 1e-9f
352 float inherited_cost;
357 bvh_heap_entry_t* data;
366static inline float bvh_aabb_perimeter(
bvh_aabb_t a);
369static void bvh_grow(
bvh_t* t);
370static int bvh_alloc_node(
bvh_t* t);
371static void bvh_free_node(
bvh_t* t,
int id);
372static void bvh_refit(
bvh_t* t,
int id);
373static void bvh_rotate(
bvh_t* t,
int a_id);
374static void bvh_refit_and_rotate(
bvh_t* t,
int start);
375static void bvh_heap_push(bvh_min_heap_t* h, bvh_heap_entry_t e);
376static bvh_heap_entry_t bvh_heap_pop(bvh_min_heap_t* h);
379static void bvh_walk_rec(
const bvh_t* t,
int id,
int depth,
bvh_walk_cb cb,
void* ctx);
380static float bvh_get_cost_rec(
const bvh_t* t,
int id);
389 t->capacity = BVH_INITIAL_CAPACITY;
390 t->nodes = (bvh_node_t*)PICO_BVH_CALLOC((
size_t)t->capacity,
sizeof(bvh_node_t));
392 PICO_BVH_ASSERT(t->nodes);
398 for (
int i = 0; i < t->capacity - 1; ++i)
400 t->nodes[i].child[0] = i + 1;
416 PICO_BVH_FREE(t->nodes);
424 int leaf_id = bvh_alloc_node(t);
425 bvh_node_t* leaf = &t->nodes[leaf_id];
426 leaf->aabb = padding > 0.f ? bvh_aabb_pad(aabb, padding) : aabb;
440 int sib_id = bvh_best_sibling(t, leaf->aabb);
443 int new_id = bvh_alloc_node(t);
444 bvh_node_t* new_node = &t->nodes[new_id];
445 int old_parent = t->nodes[sib_id].parent;
447 new_node->parent = old_parent;
448 new_node->child[0] = sib_id;
449 new_node->child[1] = leaf_id;
450 new_node->height = 0;
452 t->nodes[sib_id].parent = new_id;
453 t->nodes[leaf_id].parent = new_id;
461 bvh_node_t* op = &t->nodes[old_parent];
463 if (op->child[0] == sib_id)
465 op->child[0] = new_id;
469 op->child[1] = new_id;
473 bvh_refit_and_rotate(t, new_id);
482 PICO_BVH_ASSERT(leaf_id >= 0 && leaf_id < t->capacity);
483 PICO_BVH_ASSERT(BVH_IS_LEAF(&t->nodes[leaf_id]));
485 int parent_id = t->nodes[leaf_id].parent;
486 bvh_free_node(t, leaf_id);
497 bvh_node_t* parent = &t->nodes[parent_id];
498 int sibling = parent->child[0] == leaf_id
499 ? parent->child[1] : parent->child[0];
501 int grandparent = parent->parent;
502 bvh_free_node(t, parent_id);
504 t->nodes[sibling].parent = grandparent;
512 bvh_node_t* gp = &t->nodes[grandparent];
514 if (gp->child[0] == parent_id)
516 gp->child[0] = sibling;
520 gp->child[1] = sibling;
523 bvh_refit_and_rotate(t, grandparent);
531 PICO_BVH_ASSERT(BVH_IS_LEAF(&t->nodes[leaf_id]));
533 bvh_aabb_t padded = padding > 0.f ? bvh_aabb_pad(new_aabb, padding) : new_aabb;
536 if (bvh_aabb_contains(t->nodes[leaf_id].aabb, new_aabb))
539 bvh_aabb_t big = bvh_aabb_pad(new_aabb, padding * 4.f);
541 if (bvh_aabb_contains(big, t->nodes[leaf_id].aabb))
553 if (t->free_list != leaf_id)
555 int prev = t->free_list;
557 while (t->nodes[prev].child[0] != leaf_id)
559 prev = t->nodes[prev].child[0];
562 t->nodes[prev].child[0] = t->nodes[leaf_id].child[0];
563 t->nodes[leaf_id].child[0] = t->free_list;
564 t->free_list = leaf_id;
569 PICO_BVH_ASSERT(new_id == leaf_id);
586 int stack[PICO_BVH_STACK_SIZE];
588 stack[top++] = t->root;
592 int id = stack[--top];
598 const bvh_node_t* n = &t->nodes[id];
600 if (!bvh_aabb_overlaps(n->aabb, query))
607 if (!cb(
id, n->udata, ctx))
614 PICO_BVH_ASSERT(top + 2 <= PICO_BVH_STACK_SIZE);
615 stack[top++] = n->child[0];
616 stack[top++] = n->child[1];
635 fabsf(dir.
x) > PICO_BVH_EPSILON ? 1.f / dir.
x : (dir.
x >= 0.f ? FLT_MAX : -FLT_MAX),
636 fabsf(dir.
y) > PICO_BVH_EPSILON ? 1.f / dir.
y : (dir.
y >= 0.f ? FLT_MAX : -FLT_MAX)
639 int stack[PICO_BVH_STACK_SIZE];
641 stack[top++] = t->root;
645 int id = stack[--top];
651 const bvh_node_t* n = &t->nodes[id];
653 if (!bvh_ray_aabb(origin, inv_dir, n->aabb, t_max))
660 if (!cb(
id, n->udata, ctx))
667 PICO_BVH_ASSERT(top + 2 <= PICO_BVH_STACK_SIZE);
668 stack[top++] = n->child[0];
669 stack[top++] = n->child[1];
678 PICO_BVH_ASSERT(BVH_IS_LEAF(&t->nodes[leaf_id]));
679 return t->nodes[leaf_id].udata;
684 return t->nodes[leaf_id].aabb;
693 bvh_walk_rec(t, t->root, 0, cb, ctx);
700 return bvh_get_cost_rec(t, t->root);
707 return (
bvh_aabb_t){ {x, y}, {x + w, y + h} };
725static inline float bvh_aabb_perimeter(
bvh_aabb_t a)
744static void bvh_grow(
bvh_t* t)
746 int old_cap = t->capacity;
747 int new_cap = old_cap * 2;
748 t->nodes = (bvh_node_t*)PICO_BVH_REALLOC(t->nodes, (
size_t)new_cap *
sizeof(bvh_node_t));
750 PICO_BVH_ASSERT(t->nodes);
752 PICO_BVH_MEMSET(t->nodes + old_cap, 0, (
size_t)(new_cap - old_cap) *
sizeof(bvh_node_t));
755 for (
int i = old_cap; i < new_cap - 1; ++i)
757 t->nodes[i].child[0] = i + 1;
760 t->nodes[new_cap - 1].child[0] = t->free_list;
761 t->free_list = old_cap;
762 t->capacity = new_cap;
765static int bvh_alloc_node(
bvh_t* t)
772 int id = t->free_list;
773 t->free_list = t->nodes[id].child[0];
774 bvh_node_t* n = &t->nodes[id];
784static void bvh_free_node(
bvh_t* t,
int id)
786 PICO_BVH_ASSERT(
id >= 0 && id < t->capacity);
787 t->nodes[id].allocated =
false;
788 t->nodes[id].child[0] = t->free_list;
794static void bvh_refit(
bvh_t* t,
int id)
796 bvh_node_t* n = &t->nodes[id];
797 n->aabb = bvh_aabb_union(t->nodes[n->child[0]].aabb,
798 t->nodes[n->child[1]].aabb);
799 int h0 = t->nodes[n->child[0]].height;
800 int h1 = t->nodes[n->child[1]].height;
801 n->height = 1 + (h0 > h1 ? h0 : h1);
842static void bvh_rotate(
bvh_t* t,
int a_id)
844 bvh_node_t* A = &t->nodes[a_id];
851 int b_id = A->child[0];
852 int c_id = A->child[1];
853 bvh_node_t* B = &t->nodes[b_id];
854 bvh_node_t* C = &t->nodes[c_id];
856 float base_cost = bvh_aabb_perimeter(A->aabb);
859 float costs[4] = { FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX };
863 costs[0] = bvh_aabb_perimeter(bvh_aabb_union(C->aabb,
864 t->nodes[B->child[1]].aabb));
865 costs[1] = bvh_aabb_perimeter(bvh_aabb_union(C->aabb,
866 t->nodes[B->child[0]].aabb));
871 costs[2] = bvh_aabb_perimeter(bvh_aabb_union(B->aabb,
872 t->nodes[C->child[1]].aabb));
873 costs[3] = bvh_aabb_perimeter(bvh_aabb_union(B->aabb,
874 t->nodes[C->child[0]].aabb));
879 float best_cost = base_cost;
881 for (
int i = 0; i < 4; ++i)
883 if (costs[i] < best_cost)
885 best_cost = costs[i];
901 t->nodes[c_id].parent = b_id;
903 t->nodes[x].parent = a_id;
913 t->nodes[c_id].parent = b_id;
915 t->nodes[x].parent = a_id;
925 t->nodes[b_id].parent = c_id;
927 t->nodes[x].parent = a_id;
937 t->nodes[b_id].parent = c_id;
939 t->nodes[x].parent = a_id;
948static void bvh_refit_and_rotate(
bvh_t* t,
int start)
953 if (!BVH_IS_LEAF(&t->nodes[
id]))
959 id = t->nodes[id].parent;
974static void bvh_heap_push(bvh_min_heap_t* h, bvh_heap_entry_t entry)
976 if (h->size == h->cap)
978 h->cap = h->cap ? h->cap * 2 : 16;
979 h->data = (bvh_heap_entry_t*)PICO_BVH_REALLOC(h->data,
980 (
size_t)h->cap *
sizeof(bvh_heap_entry_t));
982 PICO_BVH_ASSERT(h->data);
985 PICO_BVH_ASSERT(h->size < h->cap);
992 int parent = (i - 1) / 2;
994 if (h->data[parent].inherited_cost <= entry.inherited_cost)
999 h->data[i] = h->data[parent];
1006static bvh_heap_entry_t bvh_heap_pop(bvh_min_heap_t* h)
1008 PICO_BVH_ASSERT(h->size > 0);
1010 bvh_heap_entry_t top = h->data[0];
1011 bvh_heap_entry_t last = h->data[--h->size];
1022 int left = i * 2 + 1;
1024 if (left >= h->size)
1029 int right = left + 1;
1033 && h->data[right].inherited_cost < h->data[left].inherited_cost)
1038 if (h->data[child].inherited_cost >= last.inherited_cost)
1043 h->data[i] = h->data[child];
1053 float new_cost = bvh_aabb_perimeter(new_aabb);
1055 bvh_min_heap_t heap = {0};
1056 bvh_heap_push(&heap, (bvh_heap_entry_t){ t->root, 0.f });
1058 int best_id = t->root;
1059 float best_cost = FLT_MAX;
1061 while (heap.size > 0)
1063 bvh_heap_entry_t entry = bvh_heap_pop(&heap);
1065 if (entry.inherited_cost >= best_cost)
1070 bvh_node_t* node = &t->nodes[entry.id];
1071 bvh_aabb_t combined = bvh_aabb_union(node->aabb, new_aabb);
1072 float direct_cost = bvh_aabb_perimeter(combined);
1073 float total_cost = direct_cost + entry.inherited_cost;
1075 if (total_cost < best_cost)
1077 best_cost = total_cost;
1081 if (!BVH_IS_LEAF(node))
1084 float inherited_cost = entry.inherited_cost + direct_cost
1085 - bvh_aabb_perimeter(node->aabb);
1087 float lower_bound = inherited_cost + new_cost;
1089 if (lower_bound < best_cost)
1091 bvh_heap_push(&heap, (bvh_heap_entry_t){ node->child[0], inherited_cost });
1092 bvh_heap_push(&heap, (bvh_heap_entry_t){ node->child[1], inherited_cost });
1097 PICO_BVH_FREE(heap.data);
1107 float tx1 = (aabb.
min.
x - origin.
x) * inv_dir.
x;
1108 float tx2 = (aabb.
max.
x - origin.
x) * inv_dir.
x;
1109 float tmin = tx1 < tx2 ? tx1 : tx2;
1110 float tmax = tx1 > tx2 ? tx1 : tx2;
1112 float ty1 = (aabb.
min.
y - origin.
y) * inv_dir.
y;
1113 float ty2 = (aabb.
max.
y - origin.
y) * inv_dir.
y;
1114 float tymin = ty1 < ty2 ? ty1 : ty2;
1115 float tymax = ty1 > ty2 ? ty1 : ty2;
1117 tmin = tmin > tymin ? tmin : tymin;
1118 tmax = tmax < tymax ? tmax : tymax;
1120 return tmax >= 0.f && tmin <= tmax && tmin <= t_max;
1125static void bvh_walk_rec(
const bvh_t* t,
int id,
int depth,
bvh_walk_cb cb,
void* ctx)
1132 const bvh_node_t* n = &t->nodes[id];
1133 cb(n->aabb, depth, BVH_IS_LEAF(n), n->udata, ctx);
1135 if (!BVH_IS_LEAF(n))
1137 bvh_walk_rec(t, n->child[0], depth + 1, cb, ctx);
1138 bvh_walk_rec(t, n->child[1], depth + 1, cb, ctx);
1144static float bvh_get_cost_rec(
const bvh_t* t,
int id)
1151 const bvh_node_t* n = &t->nodes[id];
1152 float c = bvh_aabb_perimeter(n->aabb);
1154 if (!BVH_IS_LEAF(n))
1156 c += bvh_get_cost_rec(t, n->child[0]) + bvh_get_cost_rec(t, n->child[1]);
void bvh_query_ray(const bvh_t *tree, bvh_vec2_t origin, bvh_vec2_t dir, float t_max, bvh_query_cb cb, void *ctx)
Queries the tree against a ray.
void bvh_destroy(bvh_t *tree)
Destroys and deallocates a BVH instance.
void bvh_query_aabb(const bvh_t *tree, bvh_aabb_t query, bvh_query_cb cb, void *ctx)
Queries the tree against an AABB.
bvh_udata_t bvh_get_udata(const bvh_t *tree, int leaf_id)
Returns the user data from the specified leaf node.
bool bvh_move(bvh_t *tree, int leaf_id, bvh_aabb_t new_aabb, float padding)
Update a leaf's AABB.
float bvh_get_cost(const bvh_t *tree)
Total surface-area cost (lower = better balanced).
int bvh_get_leaf_count(const bvh_t *tree)
Returns number of leaves in the tree.
struct bvh_t bvh_t
BVH instance.
Definition pico_bvh.h:124
bvh_aabb_t bvh_make_aabb(float x, float y, float w, float h)
Constructs an AABB from a position and dimensions.
bvh_aabb_t bvh_get_padded_aabb(const bvh_t *tree, int leaf_id)
Returns the enlarged bounds from the specified leaf node.
void bvh_remove(bvh_t *tree, int leaf_id)
Remove a leaf.
#define BVH_NULL_ID
Definition pico_bvh.h:99
void(* bvh_walk_cb)(bvh_aabb_t aabb, int depth, bool is_leaf, bvh_udata_t udata, void *ctx)
Called for every node during bvh_walk(); depth=0 at root.
Definition pico_bvh.h:118
PICO_BVH_UDATA_TYPE bvh_udata_t
Definition pico_bvh.h:81
void bvh_walk(const bvh_t *tree, bvh_walk_cb cb, void *ctx)
Depth-first walk over every node (internal + leaf).
bvh_t * bvh_create(void)
Allocates and initializes a BVH instances.
int bvh_insert(bvh_t *tree, bvh_aabb_t aabb, float padding, bvh_udata_t udata)
Inserts a new leaf.
bool(* bvh_query_cb)(int leaf_id, bvh_udata_t udata, void *ctx)
Return false to stop traversal early.
Definition pico_bvh.h:108
#define PICO_BVH_UDATA_TYPE
Definition pico_bvh.h:78
bvh_vec2_t max
Definition pico_bvh.h:94
bvh_vec2_t min
Definition pico_bvh.h:93
float x
Definition pico_bvh.h:87
float y
Definition pico_bvh.h:88