X-Git-Url: https://harrygodden.com/git/?p=vg.git;a=blobdiff_plain;f=vg_bvh.c;fp=vg_bvh.c;h=bf1ffc47bf2f5ad9ae6cf03051f16edf23381efa;hp=0000000000000000000000000000000000000000;hb=3b14f3dcd5bf9dd3c85144f2123d667bfa4bb63f;hpb=fce86711735b15bff37de0f70716808410fcf269 diff --git a/vg_bvh.c b/vg_bvh.c new file mode 100644 index 0000000..bf1ffc4 --- /dev/null +++ b/vg_bvh.c @@ -0,0 +1,301 @@ +#include "vg_bvh.h" +#include "vg_mem.h" +#include "vg_m.h" +#include "vg_lines.h" +#include "vg_log.h" + +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++ ){ + u32 idx = node->start+i; + bh->system->expand_bound( bh->user, node->bbx, idx ); + } +} + +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 ); + + int axis = 0; + if( extent[1] > extent[0] ) axis = 1; + 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++ ){ + u32 idx = node->start+t; + 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 ){ + f32 centroid = bh->system->item_centroid( bh->user, i, axis ); + + if( centroid < split ) + i ++; + else{ + bh->system->item_swap( bh->user, i, j ); + j --; + } + } + + u32 left_count = i - node->start; + if( left_count == 0 || left_count == node->count ) return; + + u32 il = bh->node_count ++, + ir = bh->node_count ++; + + bh_node *lnode = &bh->nodes[il], + *rnode = &bh->nodes[ir]; + + lnode->start = node->start; + lnode->count = left_count; + rnode->start = i; + rnode->count = node->count - left_count; + + node->il = il; + node->ir = ir; + node->count = 0; + + bh_update_bounds( bh, il ); + bh_update_bounds( bh, ir ); + bh_subdivide( bh, il ); + bh_subdivide( bh, ir ); +} + +void bh_rebuild( bh_tree *bh, u32 item_count ) +{ + bh_node *root = &bh->nodes[0]; + bh->node_count = 1; + + root->il = 0; + root->ir = 0; + root->count = item_count; + root->start = 0; + + bh_update_bounds( bh, 0 ); + + if( item_count > 2 ) + bh_subdivide( bh, 0 ); +} + +bh_tree *bh_create( void *lin_alloc, bh_system *system, + void *user, u32 item_count, u32 max_per_leaf ) +{ + u32 alloc_count = VG_MAX( 1, item_count ); + + u32 totsize = sizeof(bh_tree) + sizeof(bh_node)*(alloc_count*2-1); + bh_tree *bh = lin_alloc? vg_linear_alloc( lin_alloc, vg_align8(totsize) ): + malloc( totsize ); + bh->system = system; + bh->user = user; + bh->max_per_leaf = max_per_leaf; + bh_rebuild( bh, item_count ); + + if( lin_alloc ){ + 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, (alloc_count*2-1) ); + return bh; +} + +/* + * Draw items in this leaf node. + * *item_debug() must be set! + */ +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 + */ +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 ){ + vg_line_boxf( node->bbx, colour ); + + bh_debug_trace( bh, node->il, pos, colour ); + bh_debug_trace( bh, node->ir, pos, colour ); + } + else{ + if( bh->system->item_debug ) + bh_debug_leaf( bh, node ); + } + } +} + +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; +} + +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 ); +} + +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; + + 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; +} + +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. + */ +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; + } + } + + 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{ + 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 0; +} + +int bh_closest_point( bh_tree *bh, v3f pos, v3f closest, float max_dist ) +{ + if( bh->node_count < 2 ) + return -1; + + 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 ); + + /* 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{ + queue[depth] = inode->il; + queue[depth+1] = inode->ir; + + depth ++; + } + } + else + depth --; + } + + return best_item; +}