add skybox editor
[carveJwlIkooP6JGAAIwe30JlM.git] / bvh.h
diff --git a/bvh.h b/bvh.h
index 41a4108c908ff345a06978c39c313b06a06bc27a..b04c3de2b95f38342259a85d54386854ce295488 100644 (file)
--- a/bvh.h
+++ b/bvh.h
@@ -4,8 +4,10 @@
 
 #ifndef BVH_H
 #define BVH_H
-#include "common.h"
-#include "distq.h"
+
+#include "vg/vg_mem.h"
+#include "vg/vg_m.h"
+#include "vg/vg_lines.h"
 
 /*
  * Usage:
  *   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;
@@ -46,8 +54,8 @@ struct bh_tree
    nodes[];
 };
 
-struct bh_system
-{
+struct bh_system{
+   u32 system_type;
    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 );
@@ -64,20 +72,24 @@ 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 float scene_bh_centroid( void *user, u32 item_index, int axis );
+static void scene_bh_swap( void *user, u32 ia, u32 ib );
+static void scene_bh_expand_bound( void *user, boxf bound, u32 item_index );
+
+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; i<node->count; i++ )
-   {
+   for( u32 i=0; i<node->count; i++ ){
       u32 idx = node->start+i;
-      bh->system->expand_bound( bh->user, node->bbx, idx );
+      if( bh->system->system_type == 0x1 )
+         scene_bh_expand_bound( bh->user, node->bbx, idx );
+      else
+         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 )
@@ -91,27 +103,37 @@ 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; t<node->count; t++ )
-   {
+   for( u32 t=0; t<node->count; t++ ){
       u32 idx = node->start+t;
-      avg += bh->system->item_centroid( bh->user, idx, axis );
+
+      if( bh->system->system_type == 0x1 )
+         avg += scene_bh_centroid( bh->user, idx, axis );
+      else 
+         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 )
-   {
-      if( bh->system->item_centroid( bh->user, i, axis ) < split )
-         i ++;
+   while( i <= j ){
+      f32 centroid;
+      
+      if( bh->system->system_type == 0x1 )
+         centroid = scene_bh_centroid( bh->user, i, axis );
       else
-      {
-         bh->system->item_swap( bh->user, i, j );
+         centroid = bh->system->item_centroid( bh->user, i, axis );
+
+      if( centroid < split )
+         i ++;
+      else{
+         if( bh->system->system_type == 0x1 )
+            scene_bh_swap( bh->user, i, j );
+         else
+            bh->system->item_swap( bh->user, i, j );
          j --;
       }
    }
@@ -140,21 +162,13 @@ 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 max_per_leaf )
-{
+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 );
 
-   if( item_count == 0 )
-   {
-      bh_tree *bh = vg_linear_alloc( lin_alloc, sizeof(bh_tree) );
-      bh->node_count = 0;
-      bh->system = system;
-      bh->user = user;
-      return bh;
-   }
+   u32 alloc_count = VG_MAX( 1, item_count );
 
-   u32 totsize = sizeof(bh_tree) + sizeof(bh_node)*(item_count*2-1);
+   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;
@@ -169,165 +183,184 @@ 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; i<node->count; 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; i<node->count; 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 )
-{
-   if( bh->node_count < 2 )
-      return 0;
-
-   int count = 0;
-   u32 stack[100];
-   u32 depth = 2;
-
-   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( ray_aabb( inode->bbx, co, dir, hit->dist ) )
-      {
-         if( inode->count )
-         {
-            for( u32 i=0; i<inode->count; i++ )
-            {
-               u32 idx = inode->start+i;
-
-               if( bh->system->cast_ray )
-                  count += bh->system->cast_ray( bh->user, idx, co, dir, hit );
-               else
-                  count ++;
-            }
+typedef struct bh_iter bh_iter;
+struct bh_iter{
+   struct {
+      i32 id, depth;
+   }
+   stack[64];
 
-            depth --;
-         }
-         else
-         {
-            if( depth+1 >= vg_list_size(stack) )
-            {
-               vg_error( "Maximum stack reached!\n" );
-               return count;
-            }
+   enum bh_query_type{
+      k_bh_query_box,
+      k_bh_query_ray,
+      k_bh_query_range
+   }
+   query;
 
-            stack[depth] = inode->il;
-            stack[depth+1] = inode->ir;
-            depth ++;
-         }
-      }
-      else
-      {
-         depth --;
+   union{
+      struct{
+         boxf box;
       }
-   }
+      box;
 
-   return count;
-}
+      struct{
+         v3f co, inv_dir;
+         f32 max_dist;
+      }
+      ray;
 
-typedef struct bh_iter bh_iter;
-struct bh_iter
-{
-   struct 
-   {
-      int id, depth;
-   }
-   stack[64];
+      struct {
+         v3f co;
+         f32 dist_sqr;
+      }
+      range;
+   };
 
-   int depth, i;
+   i32 depth, i;
 };
 
-VG_STATIC void bh_iter_init( int root, bh_iter *it )
-{
+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;
 }
 
-VG_STATIC int bh_next( bh_tree *bh, bh_iter *it, boxf box, int *em )
-{
-   while( it->depth >= 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 );
+}
+
+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;
+   
+   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;
+}
+
+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 ];
       
-      if( box_overlap( inode->bbx, box ) )
-      {
-         if( inode->count )
-         {
-            if( it->i < inode->count )
-            {
-               *em = inode->start+it->i;
-               it->i ++;
-               return 1;
-            }
-            else
-            {
-               it->depth --;
-               it->i = 0;
-            }
+      /* 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( it->depth+1 >= vg_list_size(it->stack) )
-            {
-               vg_error( "Maximum stack reached!\n" );
-               return 0;
-            }
+      }
+
+      if( !q ){
+         it->depth --;
+         continue;
+      }
 
-            it->stack[it->depth  ].id = inode->il;
-            it->stack[it->depth+1].id = inode->ir;
-            it->depth ++;
+      if( inode->count ){
+         if( it->i < inode->count ){
+            *em = inode->start+it->i;
+            it->i ++;
+            return 1;
+         }
+         else{
+            it->depth --;
             it->i = 0;
          }
       }
-      else
-      {
-         it->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 0;
 }
 
-VG_STATIC int bh_closest_point( bh_tree *bh, v3f pos, 
+static int bh_closest_point( bh_tree *bh, v3f pos, 
                                 v3f closest, float max_dist )
 {
    if( bh->node_count < 2 )
@@ -341,8 +374,7 @@ VG_STATIC int bh_closest_point( bh_tree *bh, v3f pos,
 
    queue[0] = 0;
 
-   while( depth >= 0 )
-   {
+   while( depth >= 0 ){
       bh_node *inode = &bh->nodes[ queue[depth] ];
 
       v3f p1;
@@ -350,18 +382,14 @@ VG_STATIC int bh_closest_point( bh_tree *bh, v3f pos,
 
       /* 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; i<inode->count; i++ )
-            {
+      if( node_dist < max_dist ){
+         if( inode->count ){
+            for( int i=0; i<inode->count; 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 )
-               {
+               if( item_dist < max_dist ){
                   max_dist = item_dist;
                   v3_copy( p2, closest );
                   best_item = inode->start+i;
@@ -370,8 +398,7 @@ VG_STATIC int bh_closest_point( bh_tree *bh, v3f pos,
 
             depth --;
          }
-         else
-         {
+         else{
             queue[depth] = inode->il;
             queue[depth+1] = inode->ir;