77#ifndef PICO_BVH_UDATA_TYPE
78 #define PICO_BVH_UDATA_TYPE uint64_t
99#define BVH_NULL_ID (-1)
204#ifdef PICO_BVH_IMPLEMENTATION
240 #define PICO_BVH_ASSERT(expr) ((void)0)
242 #ifndef PICO_BVH_ASSERT
244 #define PICO_BVH_ASSERT(expr) (assert(expr))
248#if !defined(PICO_BVH_CALLOC) || \
249 !defined(PICO_BVH_REALLOC) || \
250 !defined(PICO_BVH_FREE)
252 #define PICO_BVH_CALLOC(num, size) (calloc(num, size))
253 #define PICO_BVH_REALLOC(ptr, size) (realloc(ptr, size))
254 #define PICO_BVH_FREE(ptr) (free(ptr))
257#ifndef PICO_BVH_MEMSET
259 #define PICO_BVH_MEMSET memset
262#ifndef PICO_BVH_MEMCPY
264 #define PICO_BVH_MEMCPY memcpy
267#ifndef PICO_BVH_STACK_SIZE
268 #define PICO_BVH_STACK_SIZE 1024
271#define BVH_IS_LEAF(n) ((n)->child[0] == BVH_NULL_ID)
272#define BVH_INITIAL_CAPACITY 64
273#define PICO_BVH_EPSILON 1e-9f
303 float inherited_cost;
308 bvh_heap_entry_t* data;
317static inline float bvh_aabb_perimeter(
bvh_aabb_t a);
320static void bvh_grow(
bvh_t* t);
321static int bvh_alloc_node(
bvh_t* t);
322static void bvh_free_node(
bvh_t* t,
int id);
323static void bvh_refit(
bvh_t* t,
int id);
324static void bvh_rotate(
bvh_t* t,
int a_id);
325static void bvh_refit_and_rotate(
bvh_t* t,
int start);
326static void bvh_heap_push(bvh_min_heap_t* h, bvh_heap_entry_t e);
327static bvh_heap_entry_t bvh_heap_pop(bvh_min_heap_t* h);
330static void bvh_walk_rec(
const bvh_t* t,
int id,
int depth,
bvh_walk_cb cb,
void* ctx);
331static float bvh_get_cost_rec(
const bvh_t* t,
int id);
340 t->capacity = BVH_INITIAL_CAPACITY;
341 t->nodes = (bvh_node_t*)PICO_BVH_CALLOC((
size_t)t->capacity,
sizeof(bvh_node_t));
343 PICO_BVH_ASSERT(t->nodes);
349 for (
int i = 0; i < t->capacity - 1; ++i)
351 t->nodes[i].child[0] = i + 1;
367 PICO_BVH_FREE(t->nodes);
375 int leaf_id = bvh_alloc_node(t);
376 bvh_node_t* leaf = &t->nodes[leaf_id];
377 leaf->aabb = padding > 0.f ? bvh_aabb_pad(aabb, padding) : aabb;
391 int sib_id = bvh_best_sibling(t, leaf->aabb);
394 int new_id = bvh_alloc_node(t);
395 bvh_node_t* new_node = &t->nodes[new_id];
396 int old_parent = t->nodes[sib_id].parent;
398 new_node->parent = old_parent;
399 new_node->child[0] = sib_id;
400 new_node->child[1] = leaf_id;
401 new_node->height = 0;
403 t->nodes[sib_id].parent = new_id;
404 t->nodes[leaf_id].parent = new_id;
412 bvh_node_t* op = &t->nodes[old_parent];
414 if (op->child[0] == sib_id)
416 op->child[0] = new_id;
420 op->child[1] = new_id;
424 bvh_refit_and_rotate(t, new_id);
433 PICO_BVH_ASSERT(leaf_id >= 0 && leaf_id < t->capacity);
434 PICO_BVH_ASSERT(BVH_IS_LEAF(&t->nodes[leaf_id]));
436 int parent_id = t->nodes[leaf_id].parent;
437 bvh_free_node(t, leaf_id);
448 bvh_node_t* parent = &t->nodes[parent_id];
449 int sibling = parent->child[0] == leaf_id
450 ? parent->child[1] : parent->child[0];
452 int grandparent = parent->parent;
453 bvh_free_node(t, parent_id);
455 t->nodes[sibling].parent = grandparent;
463 bvh_node_t* gp = &t->nodes[grandparent];
465 if (gp->child[0] == parent_id)
467 gp->child[0] = sibling;
471 gp->child[1] = sibling;
474 bvh_refit_and_rotate(t, grandparent);
482 PICO_BVH_ASSERT(BVH_IS_LEAF(&t->nodes[leaf_id]));
484 bvh_aabb_t padded = padding > 0.f ? bvh_aabb_pad(new_aabb, padding) : new_aabb;
487 if (bvh_aabb_contains(t->nodes[leaf_id].aabb, new_aabb))
490 bvh_aabb_t big = bvh_aabb_pad(new_aabb, padding * 4.f);
492 if (bvh_aabb_contains(big, t->nodes[leaf_id].aabb))
504 if (t->free_list != leaf_id)
506 int prev = t->free_list;
508 while (t->nodes[prev].child[0] != leaf_id)
510 prev = t->nodes[prev].child[0];
513 t->nodes[prev].child[0] = t->nodes[leaf_id].child[0];
514 t->nodes[leaf_id].child[0] = t->free_list;
515 t->free_list = leaf_id;
520 PICO_BVH_ASSERT(new_id == leaf_id);
537 int stack[PICO_BVH_STACK_SIZE];
539 stack[top++] = t->root;
543 int id = stack[--top];
549 const bvh_node_t* n = &t->nodes[id];
551 if (!bvh_aabb_overlaps(n->aabb, query))
558 if (!cb(
id, n->udata, ctx))
565 PICO_BVH_ASSERT(top + 2 <= PICO_BVH_STACK_SIZE);
566 stack[top++] = n->child[0];
567 stack[top++] = n->child[1];
586 fabsf(dir.
x) > PICO_BVH_EPSILON ? 1.f / dir.
x : (dir.
x >= 0.f ? FLT_MAX : -FLT_MAX),
587 fabsf(dir.
y) > PICO_BVH_EPSILON ? 1.f / dir.
y : (dir.
y >= 0.f ? FLT_MAX : -FLT_MAX)
590 int stack[PICO_BVH_STACK_SIZE];
592 stack[top++] = t->root;
596 int id = stack[--top];
602 const bvh_node_t* n = &t->nodes[id];
604 if (!bvh_ray_aabb(origin, inv_dir, n->aabb, max_t))
611 if (!cb(
id, n->udata, ctx))
618 PICO_BVH_ASSERT(top + 2 <= PICO_BVH_STACK_SIZE);
619 stack[top++] = n->child[0];
620 stack[top++] = n->child[1];
629 PICO_BVH_ASSERT(BVH_IS_LEAF(&t->nodes[leaf_id]));
630 return t->nodes[leaf_id].udata;
635 return t->nodes[leaf_id].aabb;
644 bvh_walk_rec(t, t->root, 0, cb, ctx);
651 return bvh_get_cost_rec(t, t->root);
658 return (
bvh_aabb_t){ {x, y}, {x + w, y + h} };
676static inline float bvh_aabb_perimeter(
bvh_aabb_t a)
695static void bvh_grow(
bvh_t* t)
697 int old_cap = t->capacity;
698 int new_cap = old_cap * 2;
699 t->nodes = (bvh_node_t*)PICO_BVH_REALLOC(t->nodes, (
size_t)new_cap *
sizeof(bvh_node_t));
701 PICO_BVH_ASSERT(t->nodes);
703 PICO_BVH_MEMSET(t->nodes + old_cap, 0, (
size_t)(new_cap - old_cap) *
sizeof(bvh_node_t));
706 for (
int i = old_cap; i < new_cap - 1; ++i)
708 t->nodes[i].child[0] = i + 1;
711 t->nodes[new_cap - 1].child[0] = t->free_list;
712 t->free_list = old_cap;
713 t->capacity = new_cap;
716static int bvh_alloc_node(
bvh_t* t)
723 int id = t->free_list;
724 t->free_list = t->nodes[id].child[0];
725 bvh_node_t* n = &t->nodes[id];
735static void bvh_free_node(
bvh_t* t,
int id)
737 PICO_BVH_ASSERT(
id >= 0 && id < t->capacity);
738 t->nodes[id].allocated =
false;
739 t->nodes[id].child[0] = t->free_list;
745static void bvh_refit(
bvh_t* t,
int id)
747 bvh_node_t* n = &t->nodes[id];
748 n->aabb = bvh_aabb_union(t->nodes[n->child[0]].aabb,
749 t->nodes[n->child[1]].aabb);
750 int h0 = t->nodes[n->child[0]].height;
751 int h1 = t->nodes[n->child[1]].height;
752 n->height = 1 + (h0 > h1 ? h0 : h1);
793static void bvh_rotate(
bvh_t* t,
int a_id)
795 bvh_node_t* A = &t->nodes[a_id];
802 int b_id = A->child[0];
803 int c_id = A->child[1];
804 bvh_node_t* B = &t->nodes[b_id];
805 bvh_node_t* C = &t->nodes[c_id];
807 float base_cost = bvh_aabb_perimeter(A->aabb);
810 float costs[4] = { FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX };
814 costs[0] = bvh_aabb_perimeter(bvh_aabb_union(C->aabb,
815 t->nodes[B->child[1]].aabb));
816 costs[1] = bvh_aabb_perimeter(bvh_aabb_union(C->aabb,
817 t->nodes[B->child[0]].aabb));
822 costs[2] = bvh_aabb_perimeter(bvh_aabb_union(B->aabb,
823 t->nodes[C->child[1]].aabb));
824 costs[3] = bvh_aabb_perimeter(bvh_aabb_union(B->aabb,
825 t->nodes[C->child[0]].aabb));
830 float best_cost = base_cost;
832 for (
int i = 0; i < 4; ++i)
834 if (costs[i] < best_cost)
836 best_cost = costs[i];
852 t->nodes[c_id].parent = b_id;
854 t->nodes[x].parent = a_id;
864 t->nodes[c_id].parent = b_id;
866 t->nodes[x].parent = a_id;
876 t->nodes[b_id].parent = c_id;
878 t->nodes[x].parent = a_id;
888 t->nodes[b_id].parent = c_id;
890 t->nodes[x].parent = a_id;
899static void bvh_refit_and_rotate(
bvh_t* t,
int start)
904 if (!BVH_IS_LEAF(&t->nodes[
id]))
910 id = t->nodes[id].parent;
925static void bvh_heap_push(bvh_min_heap_t* h, bvh_heap_entry_t entry)
927 if (h->size == h->cap)
929 h->cap = h->cap ? h->cap * 2 : 16;
930 h->data = (bvh_heap_entry_t*)PICO_BVH_REALLOC(h->data,
931 (
size_t)h->cap *
sizeof(bvh_heap_entry_t));
933 PICO_BVH_ASSERT(h->data);
936 PICO_BVH_ASSERT(h->size < h->cap);
943 int parent = (i - 1) / 2;
945 if (h->data[parent].inherited_cost <= entry.inherited_cost)
950 h->data[i] = h->data[parent];
957static bvh_heap_entry_t bvh_heap_pop(bvh_min_heap_t* h)
959 PICO_BVH_ASSERT(h->size > 0);
961 bvh_heap_entry_t top = h->data[0];
962 bvh_heap_entry_t last = h->data[--h->size];
973 int left = i * 2 + 1;
980 int right = left + 1;
984 && h->data[right].inherited_cost < h->data[left].inherited_cost)
989 if (h->data[child].inherited_cost >= last.inherited_cost)
994 h->data[i] = h->data[child];
1004 float new_cost = bvh_aabb_perimeter(new_aabb);
1006 bvh_min_heap_t heap = {0};
1007 bvh_heap_push(&heap, (bvh_heap_entry_t){ t->root, 0.f });
1009 int best_id = t->root;
1010 float best_cost = FLT_MAX;
1012 while (heap.size > 0)
1014 bvh_heap_entry_t entry = bvh_heap_pop(&heap);
1016 if (entry.inherited_cost >= best_cost)
1021 bvh_node_t* node = &t->nodes[entry.id];
1022 bvh_aabb_t combined = bvh_aabb_union(node->aabb, new_aabb);
1023 float direct_cost = bvh_aabb_perimeter(combined);
1024 float total_cost = direct_cost + entry.inherited_cost;
1026 if (total_cost < best_cost)
1028 best_cost = total_cost;
1032 if (!BVH_IS_LEAF(node))
1035 float inherited_cost = entry.inherited_cost + direct_cost
1036 - bvh_aabb_perimeter(node->aabb);
1038 float lower_bound = inherited_cost + new_cost;
1040 if (lower_bound < best_cost)
1042 bvh_heap_push(&heap, (bvh_heap_entry_t){ node->child[0], inherited_cost });
1043 bvh_heap_push(&heap, (bvh_heap_entry_t){ node->child[1], inherited_cost });
1048 PICO_BVH_FREE(heap.data);
1059 float tx1 = (aabb.
min.
x - origin.
x) * inv_dir.
x;
1060 float tx2 = (aabb.
max.
x - origin.
x) * inv_dir.
x;
1061 float tmin = tx1 < tx2 ? tx1 : tx2;
1062 float tmax = tx1 > tx2 ? tx1 : tx2;
1064 float ty1 = (aabb.
min.
y - origin.
y) * inv_dir.
y;
1065 float ty2 = (aabb.
max.
y - origin.
y) * inv_dir.
y;
1066 float tymin = ty1 < ty2 ? ty1 : ty2;
1067 float tymax = ty1 > ty2 ? ty1 : ty2;
1069 tmin = tmin > tymin ? tmin : tymin;
1070 tmax = tmax < tymax ? tmax : tymax;
1072 return tmax >= 0.f && tmin <= tmax && tmin <= max_t;
1077static void bvh_walk_rec(
const bvh_t* t,
int id,
int depth,
bvh_walk_cb cb,
void* ctx)
1084 const bvh_node_t* n = &t->nodes[id];
1085 cb(n->aabb, depth, BVH_IS_LEAF(n), n->udata, ctx);
1087 if (!BVH_IS_LEAF(n))
1089 bvh_walk_rec(t, n->child[0], depth + 1, cb, ctx);
1090 bvh_walk_rec(t, n->child[1], depth + 1, cb, ctx);
1096static float bvh_get_cost_rec(
const bvh_t* t,
int id)
1103 const bvh_node_t* n = &t->nodes[id];
1104 float c = bvh_aabb_perimeter(n->aabb);
1106 if (!BVH_IS_LEAF(n))
1108 c += bvh_get_cost_rec(t, n->child[0]) + bvh_get_cost_rec(t, n->child[1]);
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:115
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:109
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.
void bvh_query_ray(const bvh_t *tree, bvh_vec2_t origin, bvh_vec2_t dir, float max_t, bvh_query_cb cb, void *ctx)
Queries the tree against a ray.
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:104
#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