X-Git-Url: https://harrygodden.com/git/?a=blobdiff_plain;f=bvh.h;h=945d3cbbe1c6f8d4963e28bac9fc93f61056fa62;hb=e311bbe2fa903a7e2a922f202f389b799193195d;hp=9fdbe985040f51e0cadfd0a183b921240c82a036;hpb=47941822dae18a018c985847b052e70214a3ccc6;p=carveJwlIkooP6JGAAIwe30JlM.git diff --git a/bvh.h b/bvh.h index 9fdbe98..945d3cb 100644 --- a/bvh.h +++ b/bvh.h @@ -4,7 +4,10 @@ #ifndef BVH_H #define BVH_H -#include "common.h" + +#include "vg/vg_mem.h" +#include "vg/vg_m.h" +#include "vg/vg_lines.h" /* * Usage: @@ -18,36 +21,43 @@ * system: the system we created above which will deal with the data * * call bh_create( bh_tree *bh, u32 item_count ) - * VG_STATIC int bh_ray( bh_tree *bh, u32 inode, v3f co, v3f dir, ray_hit *hit ) - * VG_STATIC int bh_select( bh_tree *bh, boxf box, u32 *buffer, int len ) + * static int bh_ray( bh_tree *bh, u32 inode, v3f co, v3f dir, ray_hit *hit ) + * static int bh_select( bh_tree *bh, boxf box, u32 *buffer, int len ) */ typedef struct bh_node bh_node; typedef struct bh_tree bh_tree; typedef struct bh_system bh_system; -struct bh_tree -{ +typedef struct ray_hit ray_hit; +struct ray_hit{ + float dist; + u32 *tri; + v3f pos, normal; +}; + +struct bh_tree{ u32 node_count; bh_system *system; void *user; + u32 max_per_leaf; struct bh_node { boxf bbx; /* if il is 0, this is a leaf */ - u32 il, count; - union{ u32 ir, start; }; + int il, count; + union{ int ir, start; }; } nodes[]; }; -struct bh_system -{ +struct bh_system{ void (*expand_bound)( void *user, boxf bound, u32 item_index ); float (*item_centroid)( void *user, u32 item_index, int axis ); + void (*item_closest)( void *user, u32 item_index, v3f point, v3f closest ); void (*item_swap)( void *user, u32 ia, u32 ib ); /* @@ -61,22 +71,22 @@ struct bh_system int (*cast_ray)( void *user, u32 index, v3f co, v3f dir, ray_hit *hit ); }; -VG_STATIC void bh_update_bounds( bh_tree *bh, u32 inode ) -{ +static void bh_update_bounds( bh_tree *bh, u32 inode ){ bh_node *node = &bh->nodes[ inode ]; box_init_inf( node->bbx ); - for( u32 i=0; icount; i++ ) - { + for( u32 i=0; icount; i++ ){ u32 idx = node->start+i; bh->system->expand_bound( bh->user, node->bbx, idx ); } } -VG_STATIC void bh_subdivide( bh_tree *bh, u32 inode ) -{ +static void bh_subdivide( bh_tree *bh, u32 inode ){ bh_node *node = &bh->nodes[ inode ]; + if( node->count <= bh->max_per_leaf ) + return; + v3f extent; v3_sub( node->bbx[1], node->bbx[0], extent ); @@ -85,7 +95,6 @@ VG_STATIC void bh_subdivide( bh_tree *bh, u32 inode ) if( extent[2] > extent[axis] ) axis = 2; float split = node->bbx[0][axis] + extent[axis]*0.5f; - float avg = 0.0; for( u32 t=0; tcount; t++ ) { @@ -93,18 +102,16 @@ VG_STATIC void bh_subdivide( bh_tree *bh, u32 inode ) avg += bh->system->item_centroid( bh->user, idx, axis ); } avg /= (float)node->count; - split = avg; + i32 i = node->start, j = i + node->count-1; - while( i <= j ) - { + while( i <= j ){ if( bh->system->item_centroid( bh->user, i, axis ) < split ) i ++; - else - { + else{ bh->system->item_swap( bh->user, i, j ); j --; } @@ -134,13 +141,17 @@ VG_STATIC void bh_subdivide( bh_tree *bh, u32 inode ) bh_subdivide( bh, ir ); } -VG_STATIC bh_tree *bh_create( void *lin_alloc, bh_system *system, - void *user, u32 item_count ) -{ - u32 totsize = sizeof(bh_tree) + sizeof(bh_node)*(item_count*2-1); - bh_tree *bh = vg_linear_alloc( lin_alloc, totsize ); +static bh_tree *bh_create( void *lin_alloc, bh_system *system, + void *user, u32 item_count, u32 max_per_leaf ){ + assert( max_per_leaf > 0 ); + + u32 alloc_count = VG_MAX( 1, item_count ); + + u32 totsize = sizeof(bh_tree) + sizeof(bh_node)*(alloc_count*2-1); + bh_tree *bh = vg_linear_alloc( lin_alloc, vg_align8(totsize) ); bh->system = system; bh->user = user; + bh->max_per_leaf = max_per_leaf; bh_node *root = &bh->nodes[0]; bh->node_count = 1; @@ -151,141 +162,233 @@ VG_STATIC bh_tree *bh_create( void *lin_alloc, bh_system *system, root->start = 0; bh_update_bounds( bh, 0 ); - bh_subdivide( bh, 0 ); - totsize = sizeof(bh_tree) + sizeof(bh_node) * bh->node_count; + if( item_count > 2 ) + bh_subdivide( bh, 0 ); + + totsize = vg_align8(sizeof(bh_tree) + sizeof(bh_node) * bh->node_count); bh = vg_linear_resize( lin_alloc, bh, totsize ); - vg_success( "BVH done, size: %u/%u\n", bh->node_count, (item_count*2-1) ); + vg_success( "BVH done, size: %u/%u\n", bh->node_count, (alloc_count*2-1) ); return bh; } -VG_STATIC void bh_debug_node( bh_tree *bh, u32 inode, v3f pos, u32 colour ) -{ +/* + * Draw items in this leaf node. + * *item_debug() must be set! + */ +static void bh_debug_leaf( bh_tree *bh, bh_node *node ){ + vg_line_boxf( node->bbx, 0xff00ff00 ); + + if( bh->system->item_debug ){ + for( u32 i=0; icount; i++ ){ + u32 idx = node->start+i; + bh->system->item_debug( bh->user, idx ); + } + } +} + +/* + * Trace the bh tree all the way down to the leaf nodes where pos is inside + */ +static void bh_debug_trace( bh_tree *bh, u32 inode, v3f pos, u32 colour ){ bh_node *node = &bh->nodes[ inode ]; if( (pos[0] >= node->bbx[0][0] && pos[0] <= node->bbx[1][0]) && (pos[2] >= node->bbx[0][2] && pos[2] <= node->bbx[1][2]) ) { - if( !node->count ) - { + if( !node->count ){ vg_line_boxf( node->bbx, colour ); - bh_debug_node( bh, node->il, pos, colour ); - bh_debug_node( bh, node->ir, pos, colour ); + bh_debug_trace( bh, node->il, pos, colour ); + bh_debug_trace( bh, node->ir, pos, colour ); } - else - { - vg_line_boxf( node->bbx, 0xff00ff00 ); - + else{ if( bh->system->item_debug ) - { - for( u32 i=0; icount; i++ ) - { - u32 idx = node->start+i; - bh->system->item_debug( bh->user, idx ); - } - } + bh_debug_leaf( bh, node ); } } } -VG_STATIC int bh_ray( bh_tree *bh, v3f co, v3f dir, ray_hit *hit ) -{ - int count = 0; - u32 stack[100]; - u32 depth = 2; +typedef struct bh_iter bh_iter; +struct bh_iter{ + struct { + i32 id, depth; + } + stack[64]; + + enum bh_query_type{ + k_bh_query_box, + k_bh_query_ray, + k_bh_query_range + } + query; + + union{ + struct{ + boxf box; + } + box; + + struct{ + v3f co, inv_dir; + f32 max_dist; + } + ray; + + struct { + v3f co; + f32 dist_sqr; + } + range; + }; + + i32 depth, i; +}; + +static void bh_iter_init_generic( i32 root, bh_iter *it ){ + it->stack[0].id = root; + it->stack[0].depth = 0; + it->depth = 0; + it->i = 0; +} + +static void bh_iter_init_box( i32 root, bh_iter *it, boxf box ){ + bh_iter_init_generic( root, it ); + it->query = k_bh_query_box; + + box_copy( box, it->box.box ); +} - stack[0] = 0; - stack[1] = bh->nodes[0].il; - stack[2] = bh->nodes[0].ir; +static void bh_iter_init_ray( i32 root, bh_iter *it, v3f co, + v3f dir, f32 max_dist ){ + bh_iter_init_generic( root, it ); + it->query = k_bh_query_ray; - while(depth) - { - bh_node *inode = &bh->nodes[ stack[depth] ]; - if( ray_aabb( inode->bbx, co, dir, hit->dist ) ) - { - if( inode->count ) - { - for( u32 i=0; icount; i++ ) - { - u32 idx = inode->start+i; - - if( bh->system->cast_ray ) - count += bh->system->cast_ray( bh->user, idx, co, dir, hit ); - else - count ++; - } + v3_div( (v3f){1.0f,1.0f,1.0f}, dir, it->ray.inv_dir ); + v3_copy( co, it->ray.co ); + it->ray.max_dist = max_dist; +} - depth --; +static void bh_iter_init_range( i32 root, bh_iter *it, v3f co, f32 range ){ + bh_iter_init_generic( root, it ); + it->query = k_bh_query_range; + + v3_copy( co, it->range.co ); + it->range.dist_sqr = range*range; +} + +/* NOTE: does not compute anything beyond the leaf level. element level tests + * should be implemented by the users code. + * + * this is like a 'broad phase only' deal. + */ +static i32 bh_next( bh_tree *bh, bh_iter *it, i32 *em ){ + while( it->depth >= 0 ){ + bh_node *inode = &bh->nodes[ it->stack[it->depth].id ]; + + /* Only process overlapping nodes */ + i32 q = 0; + + if( it->i ) /* already checked */ + q = 1; + else{ + if( it->query == k_bh_query_box ) + q = box_overlap( inode->bbx, it->box.box ); + else if( it->query == k_bh_query_ray ) + q = ray_aabb1( inode->bbx, it->ray.co, + it->ray.inv_dir, it->ray.max_dist ); + else { + v3f nearest; + closest_point_aabb( it->range.co, inode->bbx, nearest ); + + if( v3_dist2( nearest, it->range.co ) <= it->range.dist_sqr ) + q = 1; } - else - { - if( depth+1 >= vg_list_size(stack) ) - { - vg_error( "Maximum stack reached!\n" ); - return count; - } + } - stack[depth] = inode->il; - stack[depth+1] = inode->ir; - depth ++; + if( !q ){ + it->depth --; + continue; + } + + if( inode->count ){ + if( it->i < inode->count ){ + *em = inode->start+it->i; + it->i ++; + return 1; + } + else{ + it->depth --; + it->i = 0; } } - else - { - depth --; + else{ + if( it->depth+1 >= vg_list_size(it->stack) ){ + vg_error( "Maximum stack reached!\n" ); + return 0; + } + + it->stack[it->depth ].id = inode->il; + it->stack[it->depth+1].id = inode->ir; + it->depth ++; + it->i = 0; } } - return count; + return 0; } -VG_STATIC int bh_select( bh_tree *bh, boxf box, u32 *buffer, int len ) +static int bh_closest_point( bh_tree *bh, v3f pos, + v3f closest, float max_dist ) { - int count = 0; - u32 stack[100]; - u32 depth = 2; + if( bh->node_count < 2 ) + return -1; - stack[0] = 0; - stack[1] = bh->nodes[0].il; - stack[2] = bh->nodes[0].ir; - - while(depth) - { - bh_node *inode = &bh->nodes[ stack[depth] ]; - if( box_overlap( inode->bbx, box ) ) - { - if( inode->count ) - { - if( count + inode->count >= len ) - return count; + max_dist = max_dist*max_dist; + + int queue[ 128 ], + depth = 0, + best_item = -1; + + queue[0] = 0; + + while( depth >= 0 ){ + bh_node *inode = &bh->nodes[ queue[depth] ]; + + v3f p1; + closest_point_aabb( pos, inode->bbx, p1 ); - for( u32 i=0; icount; i++ ) - buffer[ count ++ ] = inode->start+i; + /* branch into node if its closer than current best */ + float node_dist = v3_dist2( pos, p1 ); + if( node_dist < max_dist ){ + if( inode->count ){ + for( int i=0; icount; i++ ){ + v3f p2; + bh->system->item_closest( bh->user, inode->start+i, pos, p2 ); + + float item_dist = v3_dist2( pos, p2 ); + if( item_dist < max_dist ){ + max_dist = item_dist; + v3_copy( p2, closest ); + best_item = inode->start+i; + } + } depth --; } - else - { - if( depth+1 >= vg_list_size(stack) ) - { - vg_error( "Maximum stack reached!\n" ); - return count; - } + else{ + queue[depth] = inode->il; + queue[depth+1] = inode->ir; - stack[depth] = inode->il; - stack[depth+1] = inode->ir; depth ++; } } else - { depth --; - } } - return count; + return best_item; } #endif /* BVH_H */