#define SDL_MAIN_HANDLED
+#define VG_MAX_CONTACTS 2048
+
#include "vg/vg.h"
#include "vg/vg_camera.h"
+#include "vg/vg_rigidbody.h"
+#include "vg/vg_rigidbody_collision.h"
+#include "vg/vg_profiler.h"
+#include "vg/vg_bvh.h"
+
+#define SHAPE_MAX 256
+static rigidbody shapes[SHAPE_MAX];
+static rb_capsule shapes_inf[SHAPE_MAX];
+static v4f shapes_colour[SHAPE_MAX];
+static boxf shapes_bbx[SHAPE_MAX];
+
+static boxf floor_box = {{-6.999,-2.001,-6.999},{6.999,-0.999,6.999}};
+
+static rigidbody racket;
+
+static f32 k_iterations = 8.0f,
+ k_view_x = 0.0f,
+ k_view_y = 0.2f,
+ k_view_z = 10.0f,
+ k_shapes = 32.0f,
+ k_racket_d = 8.0f;
+
+static rb_capsule racket_ca = { .h = 4.3f, .r = 0.6f },
+ racket_cb = { .h = 5.5f, .r = 0.9f };
+static m4x3f racket_a_mdl, racket_b_mdl;
+static m3x3f racket_I;
+
+static v3f k_racket_init_w;
+static i32 k_demo = 0;
+static i32 k_gyro = 0;
+static i32 k_prof_normalize = 0;
+static i32 k_bbx = 1;
+static i32 k_spacial = 0;
+
+static struct vg_profile prof_refit = { .name = "Refit" },
+ prof_broad = { .name = "Broad phase",
+ .mode = k_profile_mode_accum },
+ prof_narrow = { .name = "Narrow phase",
+ .mode = k_profile_mode_accum },
+ prof_solve = { .name = "Solver" };
+
+static void shape_bvh_expand_bound( void *user, boxf bound, u32 item_index ){
+ box_concat( bound, shapes_bbx[item_index] );
+}
+
+static f32 shape_bvh_centroid( void *user, u32 item_index, int axis ){
+ f32 x = shapes_bbx[item_index][0][axis] + shapes_bbx[item_index][1][axis];
+ return x*0.5f;
+}
+
+static void shape_bvh_closest( void *user, u32 item_index,
+ v3f point, v3f closest ){
+ closest_point_aabb( point, shapes_bbx[item_index], closest );
+}
+
+static void shape_bvh_swap( void *user, u32 ia, u32 ib ){
+ rigidbody temp = shapes[ib];
+ shapes[ib] = shapes[ia];
+ shapes[ia] = temp;
+
+ rb_capsule cb = shapes_inf[ib];
+ shapes_inf[ib] = shapes_inf[ia];
+ shapes_inf[ia] = cb;
+
+ v4f colourb;
+ v4_copy( shapes_colour[ib], colourb );
+ v4_copy( shapes_colour[ia], shapes_colour[ib] );
+ v4_copy( colourb, shapes_colour[ia] );
+
+ boxf boxb;
+ box_copy( shapes_bbx[ib], boxb );
+ box_copy( shapes_bbx[ia], shapes_bbx[ib] );
+ box_copy( boxb, shapes_bbx[ia] );
+}
+
+static bh_system shape_bvh = {
+ .expand_bound = shape_bvh_expand_bound,
+ .item_centroid = shape_bvh_centroid,
+ .item_closest = shape_bvh_closest,
+ .item_swap = shape_bvh_swap
+};
+
+static bh_tree *shape_bvh_tree = NULL;
int main( int argc, char *argv[] ){
vg_mem.use_libc_malloc = 0;
vg_audio.dsp_enabled = 0;
}
+static void init_random(void){
+ for( u32 i=0; i<(u32)k_shapes; i ++ ){
+ f32 h = vg_randf64( &vg.rand ) * 2.0f + 1.3f,
+ r = vg_randf64( &vg.rand ) * 0.5f + 0.125f,
+ pv = vg_capsule_volume( r, h ),
+ k_density = 8.0f,
+ pm = pv * k_density;
+
+ shapes_inf[i].r = r;
+ shapes_inf[i].h = h;
+
+ m3x3f pI;
+ vg_capsule_inertia( r, h, pm, pI );
+ m3x3_inv( pI, shapes[i].iI );
+ shapes[i].inv_mass = 1.0f / pm;
+
+ v3f dir;
+ vg_rand_dir( &vg.rand, dir );
+ q_axis_angle( shapes[i].q, dir, vg_randf64(&vg.rand)*VG_TAUf );
+ vg_rand_sphere( &vg.rand, shapes[i].co );
+ v3_muladds( (v3f){0,4,0}, shapes[i].co, 4.0f, shapes[i].co );
+ v3_zero( shapes[i].v );
+ v3_zero( shapes[i].w );
+ shapes_colour[i][0] = vg_randf64(&vg.rand);
+ shapes_colour[i][1] = vg_randf64(&vg.rand);
+ shapes_colour[i][2] = vg_randf64(&vg.rand);
+ shapes_colour[i][3] = 1.0f;
+ rb_update_matrices( &shapes[i] );
+ }
+}
+
+static void init_racket(void){
+ f32 ma = vg_capsule_volume( racket_ca.r, racket_ca.h ) * k_racket_d,
+ mb = vg_capsule_volume( racket_cb.r, racket_cb.h ) * k_racket_d,
+ mt = ma+mb;
+ m3x3f aI, bI;
+
+ /* tensor for A */
+ vg_capsule_inertia( racket_ca.r, racket_ca.h, ma, aI );
+ m4x3_identity( racket_a_mdl );
+ racket_a_mdl[3][1] = -racket_ca.h*0.5f*(mb/mt);
+ vg_translate_inertia( aI, ma, racket_a_mdl[3] );
+
+ /* tensor for B */
+ vg_capsule_inertia( racket_cb.r, racket_cb.h, mb, bI );
+
+ v4f q;
+ q_axis_angle( q, (v4f){1,0,0}, VG_TAUf*0.25f );
+
+ m4x3_identity( racket_b_mdl );
+ q_m3x3( q, racket_b_mdl );
+ vg_rotate_inertia( bI, racket_b_mdl );
+ racket_b_mdl[3][1] = racket_ca.h*0.5f*(ma/mt);
+ vg_translate_inertia( bI, mb, racket_b_mdl[3] );
+
+ m3x3_add( aI, bI, racket_I );
+ m3x3_inv( racket_I, racket.iI );
+ racket.inv_mass = 1.0f/(mb+ma);
+}
+
+static void reset_racket(void){
+ q_identity( racket.q );
+ v3_zero( racket.co );
+ v3_copy( k_racket_init_w, racket.w );
+ v3_zero( racket.v );
+ rb_update_matrices( &racket );
+}
+
static void vg_load(void){
vg_bake_shaders();
+ init_random();
+ shape_bvh_tree = bh_create( NULL, &shape_bvh, NULL, SHAPE_MAX, 1 );
+ init_racket();
+ reset_racket();
}
static void vg_pre_update(void){
+ vg_console.cheats = 1;
+ vg_lines.render = 1;
+}
+
+static void demo0_refit(void){
+ if( k_spacial == 0 ) return;
+
+ for( u32 i=0; i<(u32)k_shapes; i ++ ){
+ f32 h = shapes_inf[i].h,
+ r = shapes_inf[i].r;
+
+ rigidbody *rb = &shapes[i];
+
+ v3f p0, p1;
+ v3_muladds( rb->to_world[3], rb->to_world[1], -h*0.5f+r, p0 );
+ v3_muladds( rb->to_world[3], rb->to_world[1], h*0.5f-r, p1 );
+
+ v3f *bbx = shapes_bbx[i];
+ v3_minv( p0, p1, bbx[0] );
+ v3_maxv( p0, p1, bbx[1] );
+ v3_muladds( bbx[0], (v3f){-1,-1,-1}, r, bbx[0] );
+ v3_muladds( bbx[1], (v3f){ 1, 1, 1}, r, bbx[1] );
+ }
+
+ if( k_spacial == 1 ) return;
+
+ if( k_spacial == 2 )
+ bh_rebuild( shape_bvh_tree, (u32)k_shapes );
+}
+
+static void demo0(void){
+ vg_profile_begin( &prof_refit );
+ demo0_refit();
+ vg_profile_end( &prof_refit );
+
+ static rigidbody _null,
+ _mover;
+ rb_solver_reset();
+
+ f32 t = vg.time * 0.1f * VG_TAUf;
+ v3f sphere_pos = { sinf(t)*2.0f, -1, cosf(t)*2.0f };
+ _mover.v[0] = (sinf(t+vg.time_fixed_delta)-sinf(t))*2.0f;
+ _mover.v[2] = (cosf(t+vg.time_fixed_delta)-cosf(t))*2.0f;
+
+ for( u32 i=0; i<(u32)k_shapes; i ++ ){
+ rigidbody *rbi = &shapes[i];
+ rb_capsule *infi = &shapes_inf[i];
+ v3f *bbxi = shapes_bbx[i];
+
+ if( rb_global_has_space() ){
+ rb_ct *buf = rb_global_buffer();
+ m4x3f mtx;
+ m4x3_identity( mtx );
+ u32 l = rb_capsule__box( rbi->to_world, infi,
+ mtx, mtx, floor_box, buf );
+
+ for( u32 k=0; k<l; k ++ ){
+ buf[k].rba = rbi;
+ buf[k].rbb = &_null;
+ }
+
+ rb_contact_count += l;
+ }
+ else break;
+
+ if( rb_global_has_space() ){
+ rb_ct *buf = rb_global_buffer();
+ u32 l = rb_capsule__sphere( rbi->to_world, infi, sphere_pos, 1, buf );
+
+ for( u32 k=0; k<l; k ++ ){
+ buf[k].rba = rbi;
+ buf[k].rbb = &_mover;
+ }
+
+ rb_contact_count += l;
+ }
+
+ if( k_spacial == 2 ){
+ bh_iter it;
+ bh_iter_init_box( 0, &it, bbxi );
+ i32 idx;
+
+ while(1){
+ vg_profile_begin( &prof_broad );
+ if( !bh_next( shape_bvh_tree, &it, &idx ) ){
+ vg_profile_end( &prof_broad );
+ break;
+ }
+ vg_profile_end( &prof_broad );
+
+ if( idx <= i ) continue;
+
+ vg_profile_begin( &prof_narrow );
+
+ rigidbody *rbj = &shapes[idx];
+ v3f *bbxj = shapes_bbx[idx];
+ rb_capsule *infj = &shapes_inf[idx];
+
+ if( rb_global_has_space() ){
+ rb_ct *buf = rb_global_buffer();
+ u32 l = rb_capsule__capsule( rbi->to_world, infi,
+ rbj->to_world, infj, buf );
+
+ for( u32 k=0; k<l; k ++ ){
+ buf[k].rba = rbi;
+ buf[k].rbb = rbj;
+ }
+
+ rb_contact_count += l;
+ }
+
+ vg_profile_end( &prof_narrow );
+ }
+ }
+ else {
+ if( i == ((u32)k_shapes)-1 ){
+ break;
+ }
+ for( u32 j=i+1; j<(u32)k_shapes; j ++ ){
+ rigidbody *rbj = &shapes[j];
+ v3f *bbxj = shapes_bbx[j];
+ rb_capsule *infj = &shapes_inf[j];
+
+ if( k_spacial == 1 ){
+ vg_profile_begin( &prof_broad );
+ if( !box_overlap( bbxi, bbxj ) ){
+ vg_profile_end( &prof_broad );
+ continue;
+ }
+ }
+
+ vg_profile_begin( &prof_narrow );
+ if( rb_global_has_space() ){
+ rb_ct *buf = rb_global_buffer();
+ u32 l = rb_capsule__capsule( rbi->to_world, infi,
+ rbj->to_world, infj, buf );
+
+ for( u32 k=0; k<l; k ++ ){
+ buf[k].rba = rbi;
+ buf[k].rbb = rbj;
+ }
+
+ rb_contact_count += l;
+ }
+ vg_profile_end( &prof_narrow );
+ }
+ }
+ }
+
+ vg_profile_increment( &prof_broad );
+ vg_profile_increment( &prof_narrow );
+
+ vg_profile_begin( &prof_solve );
+ rb_presolve_contacts( rb_contact_buffer, rb_contact_count );
+ for( u32 i=0; i<(u32)k_iterations; i ++ )
+ rb_solve_contacts( rb_contact_buffer, rb_contact_count );
+
+ for( u32 i=0; i<(u32)k_shapes; i ++ ){
+ rigidbody *rbi = &shapes[i];
+ if( k_gyro ){
+ m3x3f I;
+ m3x3_inv( rbi->iI, I );
+ rb_solve_gyroscopic( rbi, I, vg.time_fixed_delta );
+ }
+ rb_iter( rbi );
+ rb_update_matrices( rbi );
+ }
+ vg_profile_end( &prof_solve );
+}
+
+static void demo1(void){
+ vg_profile_begin( &prof_refit );
+ vg_profile_end( &prof_refit );
+
+ vg_profile_increment( &prof_broad );
+ vg_profile_increment( &prof_narrow );
+
+ vg_profile_begin( &prof_solve );
+
+ v3_muladds( racket.v, (v3f){0,9.8f,0}, vg.time_fixed_delta, racket.v );
+ if( k_gyro ) rb_solve_gyroscopic( &racket, racket_I, vg.time_fixed_delta );
+ rb_iter( &racket );
+ rb_update_matrices( &racket );
+
+ vg_profile_end( &prof_solve );
}
static void vg_fixed_update(void){
+ if( k_demo == 0 ) demo0();
+ else if( k_demo == 1 ) demo1();
}
static void vg_post_update(void){
+ if( vg_getkey( SDLK_8 ) )
+ init_random();
}
static void vg_framebuffer_resize( int w, int h ){
vg_line( (v3f){ 0.0f, 0.0f, 0.0f }, (v3f){ 0.0f, 0.0f, 1.0f }, 0xff0000ff );
}
+static void render0(void){
+ f32 t = vg.time * 0.1f * VG_TAUf;
+ m4x3f mdl;
+ m4x3_identity( mdl );
+ vg_rb_view_box( mdl, floor_box, (v4f){0.8f,0.8f,0.8f,1} );
+
+ mdl[3][0] = sinf(t)*2.0f;
+ mdl[3][1] = -1;
+ mdl[3][2] = cosf(t)*2.0f;
+ vg_rb_view_sphere( mdl, 1, (v4f){0,1,0,1} );
+
+ for( u32 i=0; i<(u32)k_shapes; i ++ ){
+ rigidbody *rbi = &shapes[i];
+ rb_capsule *infi = &shapes_inf[i];
+ f32 *coli = shapes_colour[i];
+
+ v4f q;
+ v3f co;
+ rb_extrapolate( rbi, co, q );
+ q_m3x3( q, mdl );
+ v3_copy( co, mdl[3] );
+ vg_rb_view_capsule( mdl, infi->r, infi->h, coli );
+ }
+
+ if( k_spacial && k_bbx ){
+ for( u32 i=0; i<(u32)k_shapes; i ++ ){
+ vg_line_boxf( shapes_bbx[i], VG__RED );
+ }
+
+ if( k_spacial == 2 ){
+ bh_debug_trace( shape_bvh_tree, 0, (v3f){0,0,0}, VG__GREEN );
+ }
+ }
+}
+
+static void render1(void){
+ m4x3f mdl, mmdl;
+ v4f q;
+ rb_extrapolate( &racket, mdl[3], q );
+ q_m3x3( q, mdl );
+
+ m4x3_mul( mdl, racket_a_mdl, mmdl );
+ vg_rb_view_capsule( mmdl, racket_ca.r, racket_ca.h, (v4f){1,0,0,1} );
+
+ m4x3_mul( mdl, racket_b_mdl, mmdl );
+ vg_rb_view_capsule( mmdl, racket_cb.r, racket_cb.h, (v4f){0,1,0,1} );
+}
+
static void vg_render(void){
glBindFramebuffer( GL_FRAMEBUFFER, 0 );
glViewport( 0,0, vg.window_x, vg.window_y );
glClearColor( 0.05f, 0.05f, 0.05f, 1.0f );
glClear( GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT );
- f32 x = -((f32)vg.mouse_pos[0] / (f32)vg.window_x) * VG_TAUf,
- y = (((f32)vg.mouse_pos[1] / (f32)vg.window_y) - 0.5f)*VG_PIf;
-
- f32 t = vg.time * 0.1f * VG_TAUf;
-
vg_camera cam = {
- .angles = { -x, y, 0 },
+ .angles = { -k_view_x, k_view_y, 0 },
.nearz = 0.01f,
.farz = 500.0f,
.fov = 90.0f,
- .pos = { sinf(x)*10.0f*cosf(y),
- sinf(y)*10.0f,
- cosf(x)*10.0f*cosf(y) },
+ .pos = { sinf(k_view_x)*k_view_z*cosf(k_view_y),
+ sinf(k_view_y)*k_view_z,
+ cosf(k_view_x)*k_view_z*cosf(k_view_y) },
};
vg_camera_update_transform( &cam );
vg_camera_finalize( &cam );
m4x4_copy( cam.mtx.pv, vg.pv );
- m4x3f mdl;
- m4x3_identity( mdl );
-
vg_rb_view_bind();
- vg_rb_view_capsule( mdl, (sinf(t*0.2f)*0.5f+0.55f)*2.0f,
- (sinf(t*0.33f)*0.5f+0.55f)*1.0f,
- (v4f){1,0,0,1} );
- vg_rb_view_box( mdl, (boxf){{-4.999,-2.001,-4.999},{4.999,-0.999,4.999}},
- (v4f){0.8f,0.8f,0.8f,1} );
-
- mdl[3][0] = sinf(t)*2.0f;
- mdl[3][2] = cosf(t)*2.0f;
- vg_rb_view_sphere( mdl, 1, (v4f){0,1,0,1} );
+ if( k_demo == 0 ) render0();
+ else if( k_demo == 1 ) render1();
draw_origin_axis();
vg_lines_drawall();
glDisable(GL_DEPTH_TEST);
}
+struct ui_enum_opt spacial_mode_ui_enum[] = {
+ { 0, "None" },
+ { 1, "BBX" },
+ { 2, "BVH - Full rebuild" },
+ { 3, "BVH - Temporal fitting" }
+};
+
+static void gui0( ui_rect panel ){
+ ui_slider( panel, "Shapes", 2, vg_list_size(shapes), &k_shapes, "%.0f" );
+
+ if( ui_button( panel, "randomize" ) == k_ui_button_click ){
+ init_random();
+ }
+}
+
+static void gui1( ui_rect panel ){
+ ui_rect l, r;
+ ui_standard_widget( panel, l, 1 );
+ ui_split_ratio( l, k_ui_axis_v, 0.5f, 4, l, r );
+ ui_slider( l, "aH", 1.0f, 10.0f, &racket_ca.h, "%.1f" );
+ ui_slider( r, "aR", 0.1f, 10.0f, &racket_ca.r, "%.1f" );
+
+ ui_standard_widget( panel, l, 1 );
+ ui_split_ratio( l, k_ui_axis_v, 0.5f, 4, l, r );
+ ui_slider( l, "bH", 1.0f, 10.0f, &racket_cb.h, "%.1f" );
+ ui_slider( r, "bR", 0.1f, 10.0f, &racket_cb.r, "%.1f" );
+
+ for( u32 i=0; i<3; i ++ ){
+ ui_rect v0,v1,v2;
+ ui_standard_widget( panel, v0, 1 );
+ ui_split_ratio( v0, k_ui_axis_v, 2.0f/3.0f, 4, v0, v2 );
+ ui_split_ratio( v0, k_ui_axis_v, 1.0f/2.0f, 4, v0, v1 );
+
+ char buf[16];
+ snprintf( buf, sizeof(buf), "%.3f", racket_I[i][0] );
+ ui_text( v0, buf, 1, k_ui_align_middle_center, 0 );
+ snprintf( buf, sizeof(buf), "%.3f", racket_I[i][1] );
+ ui_text( v1, buf, 1, k_ui_align_middle_center, 0 );
+ snprintf( buf, sizeof(buf), "%.3f", racket_I[i][2] );
+ ui_text( v2, buf, 1, k_ui_align_middle_center, 0 );
+ }
+
+ init_racket();
+
+ ui_info( panel, "init conditions" );
+ ui_rect v0,v1,v2;
+ ui_standard_widget( panel, v0, 1 );
+ ui_split_ratio( v0, k_ui_axis_v, 2.0f/3.0f, 4, v0, v2 );
+ ui_split_ratio( v0, k_ui_axis_v, 1.0f/2.0f, 4, v0, v1 );
+ ui_slider( v0, "X", 0.01f, 30.0f, k_racket_init_w+0, "%.1f" );
+ ui_slider( v1, "Y", 0.01f, 30.0f, k_racket_init_w+1, "%.1f" );
+ ui_slider( v2, "Z", 0.01f, 30.0f, k_racket_init_w+2, "%.1f" );
+
+ if( ui_button( panel, "init" ) == k_ui_button_click ){
+ reset_racket();
+ }
+}
+
static void vg_gui(void){
vg_ui.wants_mouse = 1;
+ ui_rect panel = { vg.window_x-300, 0, 300, vg.window_y };
+ ui_rect_pad( panel, (ui_px[2]){ 8, 8 } );
+
+ ui_rect box;
+ ui_split( panel, k_ui_axis_h, VG_PROFILE_SAMPLE_COUNT*2 +4, 8, box, panel );
+ vg_profile_drawn( (struct vg_profile *[]){ &prof_refit,
+ &prof_broad,
+ &prof_narrow,
+ &prof_solve }, 4,
+ vg.time_fixed_delta*1000.0, box, 0, k_prof_normalize );
+
+ ui_split( panel, k_ui_axis_h, 14*2+8, 4, box, panel );
+ ui_checkbox( panel, "Normalize", &k_prof_normalize );
+
+ ui_slider( panel, "Iterations", 1.0f, 20.0f, &k_iterations, "%.0f" );
+ ui_enum( panel, "Spacial Type", spacial_mode_ui_enum,
+ vg_list_size(spacial_mode_ui_enum), &k_spacial );
+
+ static f32 rate = 60.0f;
+ ui_slider( panel, "Fixed timestep", 10, 200, &rate, "%.1f" );
+ vg.time_fixed_delta = 1.0f/rate;
+
+ ui_checkbox( panel, "Show BBX", &k_bbx );
+ ui_checkbox( panel, "Gyroscopic Term", &k_gyro );
+
+ ui_tabs( panel, panel,
+ (const char *[]){ "collision", "racket" }, 2, &k_demo );
+
+ if( k_demo == 0 ) gui0( panel );
+ else if( k_demo == 1 ) gui1( panel );
+
+ ui_rect viewport = { 0,0, vg.window_x-300, vg.window_y };
+
+ if( ui_inside_rect( viewport, vg_ui.mouse ) && ui_clicking(UI_MOUSE_LEFT) ){
+ k_view_x += -((f32)vg.mouse_delta[0] / (f32)vg.window_x) * VG_TAUf,
+ k_view_y += ((f32)vg.mouse_delta[1] / (f32)vg.window_y) * VG_PIf;
+ }
}
time_fixed_extrapolate,
time_frame_delta;
+ f32 time_fixed_delta;
+
u64 time_hp, time_hp_last, time_spinning;
int fixed_iterations;
vg_rand rand;
}
-static vg = { .time_rate = 1.0 };
+static vg = { .time_rate = 1.0, .time_fixed_delta = VG_TIMESTEP_FIXED };
const char *vg_get_basepath(void){
return vg.base_path;
}
/* Diagnostic */
static struct vg_profile vg_prof_update = {.name="update()"},
- vg_prof_render = {.name="render()"},
- vg_prof_swap = {.name="swap"};
+ vg_prof_render = {.name="render()"},
+ vg_prof_swap = {.name="swap"};
static void vg_checkgl( const char *src_info )
{
#endif
vg_loader_step( vg_profiler_init, NULL );
- vg_async_call( async_internal_complete, NULL, 0 );
-
/* client */
vg_load();
+ vg_async_call( async_internal_complete, NULL, 0 );
+
+
vg_success( "Client loaded in %fs\n", vg.time_real );
}
vg_lines.enabled = vg_lines.render;
vg.time_fixed_accumulator += vg.time_delta;
- while( vg.time_fixed_accumulator >= VG_TIMESTEP_FIXED ){
+ while( vg.time_fixed_accumulator >= vg.time_fixed_delta ){
vg_fixed_update();
vg_lines.enabled = 0;
- vg.time_fixed_accumulator -= VG_TIMESTEP_FIXED;
+ vg.time_fixed_accumulator -= vg.time_fixed_delta;
vg.fixed_iterations ++;
if( vg.fixed_iterations == 8 ){
}
}
vg_lines.enabled = vg_lines.render;
- vg.time_fixed_extrapolate = vg.time_fixed_accumulator / VG_TIMESTEP_FIXED;
+ vg.time_fixed_extrapolate = vg.time_fixed_accumulator / vg.time_fixed_delta;
vg.engine_stage = k_engine_stage_update;
vg_post_update();
vg.engine_stage = k_engine_stage_rendering;
vg_render();
+ vg_profile_end( &vg_prof_render );
+
/* ui */
vg.engine_stage = k_engine_stage_ui;
{
vg.time_fixed_extrapolate, vg.time_frame_delta,
vg.time_spinning );
- ui_text( (ui_rect){258, 4+24+12+12,900,900},perf,1,0,k_ui_align_left);
+ ui_text( (ui_rect){258,4,900,900},perf,1,0,k_ui_align_left);
}
ui_postrender();
}
-
- vg_profile_end( &vg_prof_render );
}
static void aaaaaaaaaaaaaaaaa( ui_rect r ){
(struct vg_profile *[]){
&vg_prof_update,&vg_prof_render,&vg_prof_swap}, 3,
(1.0f/(f32)frame_target)*1500.0f,
- r, 0, 1
+ r, 1, 0
);
ui_fill( (ui_rect){ r[0], r[1] + (r[3]*2)/3, r[2], 1 }, ui_colour(k_ui_fg) );
&vg_prof_audio_mix,
&vg_prof_audio_dsp}, 3,
budget, (ui_rect){ 4, VG_PROFILE_SAMPLE_COUNT*2 + 8,
- 512, 0 }, 3, 0 );
+ 512, 0 }, 0, 0 );
char perf[128];
--- /dev/null
+#pragma once
+#include "vg_mem.h"
+#include "vg_m.h"
+#include "vg_lines.h"
+
+/*
+ * Usage:
+ *
+ * create a bh_system with functions filled out for expand, centroid, and swap.
+ * optionally include item_debug and cast_ray functions if needed, otherwise,
+ * set them to null
+ *
+ * create a bh_tree struct with:
+ * user: a pointer back the base of the data you are ordering
+ * system: the system we created above which will deal with the data
+ *
+ * call bh_create( bh_tree *bh, u32 item_count )
+ * 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;
+
+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 */
+ int il, count;
+ union{ int ir, start; };
+ }
+ nodes[];
+};
+
+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 );
+
+ /*
+ * Optional:
+ * item_debug - draw this item quickly usually with lines
+ * cast_ray - shoot a ray against the object, if this is not set,
+ * raycasts will simply return the hit on the bvh node
+ */
+
+ void (*item_debug)( void *user, u32 item_index );
+ int (*cast_ray)( void *user, u32 index, v3f co, v3f dir, ray_hit *hit );
+};
+
+#define BVH_FIXED_MODE
+#ifdef BVH_FIXED_MODE
+static f32 shape_bvh_centroid( void *user, u32 item_index, int axis );
+static void shape_bvh_swap( void *user, u32 ia, u32 ib );
+static void shape_bvh_expand_bound( void *user, boxf bound, u32 item_index );
+#endif
+
+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++ ){
+ u32 idx = node->start+i;
+#ifdef BVH_FIXED_MODE
+ shape_bvh_expand_bound( bh->user, node->bbx, idx );
+#else
+ bh->system->expand_bound( bh->user, node->bbx, idx );
+#endif
+ }
+}
+
+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 );
+
+ 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; t<node->count; t++ ){
+ u32 idx = node->start+t;
+#ifdef BVH_FIXED_MODE
+ avg += shape_bvh_centroid( bh->user, idx, axis );
+#else
+ avg += bh->system->item_centroid( bh->user, idx, axis );
+#endif
+ }
+ avg /= (float)node->count;
+ split = avg;
+
+
+ i32 i = node->start,
+ j = i + node->count-1;
+
+ while( i <= j ){
+#ifdef BVH_FIXED_MODE
+ f32 centroid = shape_bvh_centroid( bh->user, i, axis );
+#else
+ f32 centroid = bh->system->item_centroid( bh->user, i, axis );
+#endif
+
+ if( centroid < split )
+ i ++;
+ else{
+#ifdef BVH_FIXED_MODE
+ shape_bvh_swap( bh->user, i, j );
+#else
+ bh->system->item_swap( bh->user, i, j );
+#endif
+ 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 );
+}
+
+static 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 );
+}
+
+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 = 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!
+ */
+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 ){
+ 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 );
+ }
+ }
+}
+
+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 );
+}
+
+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 ];
+
+ /* 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;
+}
+
+static 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; 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 ){
+ 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;
+}
GLuint tex_glyphs, vao, vbo, ebo, tex_bg;
v2f bg_inverse_ratio;
- ui_px mouse[2], mouse_click[2];
+ ui_px mouse[2], mouse_delta[2], mouse_click[2];
u32 mouse_state[2];
u32 ignore_input_frames;
int wants_mouse;
int x, y;
vg_ui.mouse_state[1] = vg_ui.mouse_state[0];
vg_ui.mouse_state[0] = SDL_GetMouseState( &x, &y );
+ vg_ui.mouse_delta[0] = x-vg_ui.mouse[0];
+ vg_ui.mouse_delta[1] = y-vg_ui.mouse[1];
vg_ui.mouse[0] = x;
vg_ui.mouse[1] = y;
}
static void vg_profile_drawn( struct vg_profile **profiles, u32 count,
- float budget, ui_rect panel, u32 colour_offset,
- int dir )
+ f64 budget, ui_rect panel,
+ int dir, i32 normalize )
{
if( panel[2] == 0 )
panel[2] = 256;
if( panel[3] == 0 )
panel[3] = VG_PROFILE_SAMPLE_COUNT * 2;
- f32 sh = (f32)panel[3^dir] / (f32)VG_PROFILE_SAMPLE_COUNT,
+ f64 sh = (f32)panel[3^dir] / (f32)VG_PROFILE_SAMPLE_COUNT,
sw = (f32)panel[2^dir];
ui_fill( panel, 0xa0000000 );
assert( count <= 8 );
f64 avgs[8];
- int ptrs[8];
-
- for( int i=0; i<count; i++ ){
-#if 0
- ptrs[i] = profiles[i]->buffer_current;
-#else
- ptrs[i] = 0;
-#endif
- avgs[i] = 0.0f;
+ u32 colours[8];
+ for( u32 i=0; i<count; i ++ ){
+ avgs[i] = 0.0;
+ colours[i] = ui_colour( k_ui_red + ((i*3)&0xe) );
}
- u32 colours[] = { 0xff0000ff, 0xff00ff00, 0xff00ffff, 0xffff0000,
- 0xffff00ff, 0xffffff00 };
-
f64 rate_mul = 1000.0 / (f64)SDL_GetPerformanceFrequency();
- for( int i=0; i<VG_PROFILE_SAMPLE_COUNT-1; i++ ){
+ for( i32 i=0; i<VG_PROFILE_SAMPLE_COUNT; i++ ){
f64 total = 0.0;
- for( int j=0; j<count; j++ ){
-#if 0
- ptrs[j] --;
-
- if( ptrs[j] < 0 )
- ptrs[j] = VG_PROFILE_SAMPLE_COUNT-1;
-#else
- ptrs[j] ++;
-#endif
+ if( normalize ){
+ budget = 0.0;
+ for( u32 j=0; j<count; j++ )
+ budget += (f64)profiles[j]->samples[i] * rate_mul;
+ }
- f64 sample = (f64)profiles[j]->samples[ptrs[j]] * rate_mul,
- px = (total / (budget)) * sw,
- wx = (sample / (budget)) * sw;
+ for( int j=0; j<count; j++ ){
+ f64 sample = (f64)profiles[j]->samples[i] * rate_mul,
+ px = (total / budget) * sw,
+ wx = (sample / budget) * sw;
ui_rect block;
block[0^dir] = panel[0^dir] + px;
block[1^dir] = panel[1^dir] + (f32)i*sh;
- block[2^dir] = wx;
+ block[2^dir] = VG_MAX( 1, wx-1 );
block[3^dir] = ceilf(sh)-1;
-
- u32 colour = colours[ (j+colour_offset) % vg_list_size(colours) ];
- ui_fill( block, colour );
+ ui_fill( block, colours[j] );
total += sample;
avgs[j] += sample;
avgs[i] * (1.0f/(VG_PROFILE_SAMPLE_COUNT-1)),
profiles[i]->name );
- ui_text( (ui_rect){ panel[0] + panel[2] + 4,
- panel[1] + i * 14, 0, 0 },
- infbuf,
- 1,
- k_ui_align_left, 0 );
+ ui_text( (ui_rect){ panel[0] + 4,
+ panel[1] + panel[3] + 4 + i*14,
+ panel[2]-8, 14 },
+ infbuf, 1, k_ui_align_left, 0 );
}
}
*/
static const float
- k_rb_rate = (1.0/VG_TIMESTEP_FIXED),
- k_rb_delta = (1.0/k_rb_rate),
+ //k_rb_rate = (1.0/VG_TIMESTEP_FIXED),
+ //k_rb_delta = (1.0/k_rb_rate),
k_friction = 0.4f,
k_damp_linear = 0.1f, /* scale velocity 1/(1+x) */
k_damp_angular = 0.1f, /* scale angular 1/(1+x) */
*/
static void rb_extrapolate( rigidbody *rb, v3f co, v4f q ){
float substep = vg.time_fixed_extrapolate;
- v3_muladds( rb->co, rb->v, k_rb_delta*substep, co );
+ v3_muladds( rb->co, rb->v, vg.time_fixed_delta*substep, co );
if( v3_length2( rb->w ) > 0.0f ){
v4f rotation;
float mag = v3_length( axis );
v3_divs( axis, mag, axis );
- q_axis_angle( rotation, axis, mag*k_rb_delta*substep );
+ q_axis_angle( rotation, axis, mag*vg.time_fixed_delta*substep );
q_mul( rotation, rb->q, q );
q_normalize( q );
}
}
v3f gravity = { 0.0f, -9.8f, 0.0f };
- v3_muladds( rb->v, gravity, k_rb_delta, rb->v );
+ v3_muladds( rb->v, gravity, vg.time_fixed_delta, rb->v );
/* intergrate velocity */
- v3_muladds( rb->co, rb->v, k_rb_delta, rb->co );
+ v3_muladds( rb->co, rb->v, vg.time_fixed_delta, rb->co );
+#if 0
v3_lerp( rb->w, (v3f){0.0f,0.0f,0.0f}, 0.0025f, rb->w );
+#endif
/* inegrate inertia */
if( v3_length2( rb->w ) > 0.0f ){
float mag = v3_length( axis );
v3_divs( axis, mag, axis );
- q_axis_angle( rotation, axis, mag*k_rb_delta );
+ q_axis_angle( rotation, axis, mag*vg.time_fixed_delta );
q_mul( rotation, rb->q, rb->q );
q_normalize( rb->q );
}
}
+/*
+ * based on: https://box2d.org/files/ErinCatto_NumericalMethods_GDC2015.pdf,
+ * page 76.
+ */
+static void rb_solve_gyroscopic( rigidbody *rb, m3x3f I, f32 h ){
+ /* convert to body coordinates */
+ v3f w_local;
+ m3x3_mulv( rb->to_local, rb->w, w_local );
+
+ /* Residual vector */
+ v3f f, v0;
+ m3x3_mulv( I, w_local, v0 );
+ v3_cross( w_local, v0, f );
+ v3_muls( f, h, f );
+
+ /* Jacobian */
+ m3x3f iJ, J, skew_w_local, skew_v0, m0;
+ m3x3_skew_symetric( skew_w_local, w_local );
+ m3x3_mul( skew_w_local, I, m0 );
+
+ m3x3_skew_symetric( skew_v0, v0 );
+ m3x3_sub( m0, skew_v0, J );
+ m3x3_scalef( J, h );
+ m3x3_add( I, J, J );
+
+ /* Single Newton-Raphson update */
+ v3f w1;
+ m3x3_inv( J, iJ );
+ m3x3_mulv( iJ, f, w1 );
+ v3_sub( w_local, w1, rb->w );
+
+ m3x3_mulv( rb->to_world, rb->w, rb->w );
+}
+
/*
* Creates relative contact velocity vector
*/
float depth = v3_dot( plane, ra->co ) - plane[3],
lambda = vg_clampf( -depth, 0.0f, 1.0f ) * amt;
- v3_muladds( ra->v, plane, lambda * k_rb_delta, ra->v );
+ v3_muladds( ra->v, plane, lambda * vg.time_fixed_delta, ra->v );
if( depth < 0.0f )
- v3_muls( ra->v, 1.0f-(drag*k_rb_delta), ra->v );
+ v3_muls( ra->v, 1.0f-(drag*vg.time_fixed_delta), ra->v );
}
/* apply a spring&dampener force to match ra(worldspace) on rigidbody, to
#pragma once
#include "vg_rigidbody.h"
+#ifndef VG_MAX_CONTACTS
+#define VG_MAX_CONTACTS 256
+#endif
+
typedef struct rb_ct rb_ct;
static struct rb_ct{
rigidbody *rba, *rbb;
enum contact_type type;
}
-rb_contact_buffer[256];
+rb_contact_buffer[VG_MAX_CONTACTS];
static int rb_contact_count = 0;
/*
* Debugging
*/
+#if 0
if( count == 2 )
vg_line( buf[0].co, buf[1].co, 0xff0000ff );
+#endif
return count;
}
-#if 0
-static int rb_capsule_sphere( rb_object *obja, rb_object *objb, rb_ct *buf ){
- rigidbody *rba = &obja->rb, *rbb = &objb->rb;
- f32 h = obja->inf.capsule.h,
- ra = obja->inf.capsule.r,
- rb = objb->inf.sphere.r;
+static int rb_capsule__sphere( m4x3f mtxA, rb_capsule *ca,
+ v3f coB, f32 rb, rb_ct *buf ){
+ f32 ha = ca->h,
+ ra = ca->r,
+ r = ra + rb;
v3f p0, p1;
- v3_muladds( rba->co, rba->to_world[1], -h*0.5f+ra, p0 );
- v3_muladds( rba->co, rba->to_world[1], h*0.5f-ra, p1 );
+ v3_muladds( mtxA[3], mtxA[1], -ha*0.5f+ra, p0 );
+ v3_muladds( mtxA[3], mtxA[1], ha*0.5f-ra, p1 );
v3f c, delta;
- closest_point_segment( p0, p1, rbb->co, c );
- v3_sub( c, rbb->co, delta );
-
- f32 d2 = v3_length2(delta),
- r = ra + rb;
+ closest_point_segment( p0, p1, coB, c );
+ v3_sub( c, coB, delta );
+ f32 d2 = v3_length2(delta);
if( d2 < r*r ){
f32 d = sqrtf(d2);
v3f p0, p1;
v3_muladds( c, ct->n, -ra, p0 );
- v3_muladds( rbb->co, ct->n, rb, p1 );
+ v3_muladds( coB, ct->n, rb, p1 );
v3_add( p0, p1, ct->co );
v3_muls( ct->co, 0.5f, ct->co );
-
- ct->rba = rba;
- ct->rbb = rbb;
ct->type = k_contact_type_default;
-
return 1;
}
-
- return 0;
+ else return 0;
}
-#endif
static int rb_capsule__capsule( m4x3f mtxA, rb_capsule *ca,
m4x3f mtxB, rb_capsule *cb, rb_ct *buf ){
return rb_capsule__manifold_done( mtxA, ca, &manifold, buf );
}
-#if 0
-static int rb_sphere_box( rb_object *obja, rb_object *objb, rb_ct *buf ){
- v3f co, delta;
- rigidbody *rba = &obja->rb, *rbb = &objb->rb;
+/*
+ * Generates up to two contacts; optimised for the most stable manifold
+ */
+static int rb_capsule__box( m4x3f mtxA, rb_capsule *ca,
+ m4x3f mtxB, m4x3f mtxB_inverse, boxf box,
+ rb_ct *buf ){
+ f32 h = ca->h, r = ca->r;
- closest_point_obb( rba->co, rbb->bbx, rbb->to_world, rbb->to_local, co );
- v3_sub( rba->co, co, delta );
+ /*
+ * Solving this in symetric local space of the cube saves us some time and a
+ * couple branches when it comes to the quad stage.
+ */
+ v3f centroid;
+ v3_add( box[0], box[1], centroid );
+ v3_muls( centroid, 0.5f, centroid );
- f32 d2 = v3_length2(delta),
- r = obja->inf.sphere.radius;
+ boxf bbx;
+ v3_sub( box[0], centroid, bbx[0] );
+ v3_sub( box[1], centroid, bbx[1] );
+
+ v3f pc, p0w, p1w, p0, p1;
+ v3_muladds( mtxA[3], mtxA[1], -h*0.5f+r, p0w );
+ v3_muladds( mtxA[3], mtxA[1], h*0.5f-r, p1w );
+
+ m4x3_mulv( mtxB_inverse, p0w, p0 );
+ m4x3_mulv( mtxB_inverse, p1w, p1 );
+ v3_sub( p0, centroid, p0 );
+ v3_sub( p1, centroid, p1 );
+ v3_add( p0, p1, pc );
+ v3_muls( pc, 0.5f, pc );
+
+ /*
+ * Finding an appropriate quad to collide lines with
+ */
+ v3f region;
+ v3_div( pc, bbx[1], region );
+
+ v3f quad[4];
+ if( (fabsf(region[0]) > fabsf(region[1])) &&
+ (fabsf(region[0]) > fabsf(region[2])) )
+ {
+ f32 px = vg_signf(region[0]) * bbx[1][0];
+ v3_copy( (v3f){ px, bbx[0][1], bbx[0][2] }, quad[0] );
+ v3_copy( (v3f){ px, bbx[1][1], bbx[0][2] }, quad[1] );
+ v3_copy( (v3f){ px, bbx[1][1], bbx[1][2] }, quad[2] );
+ v3_copy( (v3f){ px, bbx[0][1], bbx[1][2] }, quad[3] );
+ }
+ else if( fabsf(region[1]) > fabsf(region[2]) )
+ {
+ f32 py = vg_signf(region[1]) * bbx[1][1];
+ v3_copy( (v3f){ bbx[0][0], py, bbx[0][2] }, quad[0] );
+ v3_copy( (v3f){ bbx[1][0], py, bbx[0][2] }, quad[1] );
+ v3_copy( (v3f){ bbx[1][0], py, bbx[1][2] }, quad[2] );
+ v3_copy( (v3f){ bbx[0][0], py, bbx[1][2] }, quad[3] );
+ }
+ else
+ {
+ f32 pz = vg_signf(region[2]) * bbx[1][2];
+ v3_copy( (v3f){ bbx[0][0], bbx[0][1], pz }, quad[0] );
+ v3_copy( (v3f){ bbx[1][0], bbx[0][1], pz }, quad[1] );
+ v3_copy( (v3f){ bbx[1][0], bbx[1][1], pz }, quad[2] );
+ v3_copy( (v3f){ bbx[0][0], bbx[1][1], pz }, quad[3] );
+ }
- if( d2 <= r*r ){
+ capsule_manifold manifold;
+ rb_capsule_manifold_init( &manifold );
+
+ v3f c0, c1;
+ closest_point_aabb( p0, bbx, c0 );
+ closest_point_aabb( p1, bbx, c1 );
+
+ v3f d0, d1, da;
+ v3_sub( c0, p0, d0 );
+ v3_sub( c1, p1, d1 );
+ v3_sub( p1, p0, da );
+
+ /* TODO: ? */
+ v3_normalize(d0);
+ v3_normalize(d1);
+ v3_normalize(da);
+
+ if( v3_dot( da, d0 ) <= 0.01f )
+ rb_capsule_manifold( p0, c0, 0.0f, r, &manifold );
+
+ if( v3_dot( da, d1 ) >= -0.01f )
+ rb_capsule_manifold( p1, c1, 1.0f, r, &manifold );
+
+ for( i32 i=0; i<4; i++ ){
+ i32 i0 = i,
+ i1 = (i+1)%4;
+
+ v3f ca, cb;
+ f32 ta, tb;
+ closest_segment_segment( p0, p1, quad[i0], quad[i1], &ta, &tb, ca, cb );
+ rb_capsule_manifold( ca, cb, ta, r, &manifold );
+ }
+
+ /*
+ * Create final contacts based on line manifold
+ */
+ m3x3_mulv( mtxB, manifold.d0, manifold.d0 );
+ m3x3_mulv( mtxB, manifold.d1, manifold.d1 );
+ return rb_capsule__manifold_done( mtxA, ca, &manifold, buf );
+}
+
+static int rb_sphere__box( v3f coA, f32 ra,
+ m4x3f mtxB, m4x3f mtxB_inverse, boxf box,
+ rb_ct *buf ){
+ v3f co, delta;
+ closest_point_obb( coA, box, mtxB, mtxB_inverse, co );
+ v3_sub( coA, co, delta );
+
+ f32 d2 = v3_length2(delta);
+
+ if( d2 <= ra*ra ){
f32 d;
rb_ct *ct = buf;
if( d2 <= 0.0001f ){
- v3_sub( rba->co, rbb->co, delta );
+ v3f e, coB;
+ v3_sub( box[1], box[0], e );
+ v3_muls( e, 0.5f, e );
+ v3_add( box[0], e, coB );
+ v3_sub( coA, coB, delta );
/*
* some extra testing is required to find the best axis to push the
* object back outside the box. Since there isnt a clear seperating
* vector already, especially on really high aspect boxes.
*/
- f32 lx = v3_dot( rbb->to_world[0], delta ),
- ly = v3_dot( rbb->to_world[1], delta ),
- lz = v3_dot( rbb->to_world[2], delta ),
- px = rbb->bbx[1][0] - fabsf(lx),
- py = rbb->bbx[1][1] - fabsf(ly),
- pz = rbb->bbx[1][2] - fabsf(lz);
-
- if( px < py && px < pz )
- v3_muls( rbb->to_world[0], vg_signf(lx), ct->n );
- else if( py < pz )
- v3_muls( rbb->to_world[1], vg_signf(ly), ct->n );
- else
- v3_muls( rbb->to_world[2], vg_signf(lz), ct->n );
-
- v3_muladds( rba->co, ct->n, -r, ct->co );
- ct->p = r;
+ f32 lx = v3_dot( mtxB[0], delta ),
+ ly = v3_dot( mtxB[1], delta ),
+ lz = v3_dot( mtxB[2], delta ),
+ px = e[0] - fabsf(lx),
+ py = e[1] - fabsf(ly),
+ pz = e[2] - fabsf(lz);
+
+ if( px < py && px < pz ) v3_muls( mtxB[0], vg_signf(lx), ct->n );
+ else if( py < pz ) v3_muls( mtxB[1], vg_signf(ly), ct->n );
+ else v3_muls( mtxB[2], vg_signf(lz), ct->n );
+
+ v3_muladds( coA, ct->n, -ra, ct->co );
+ ct->p = ra;
}
else{
d = sqrtf(d2);
v3_muls( delta, 1.0f/d, ct->n );
- ct->p = r-d;
+ ct->p = ra-d;
v3_copy( co, ct->co );
}
- ct->rba = rba;
- ct->rbb = rbb;
ct->type = k_contact_type_default;
return 1;
}
-
- return 0;
+ else return 0;
}
-#endif
-#if 0
-static int rb_sphere_sphere( rb_object *obja, rb_object *objb, rb_ct *buf ){
- rigidbody *rba = &obja->rb, *rbb = &objb->rb;
+static int rb_sphere__sphere( v3f coA, f32 ra,
+ v3f coB, f32 rb, rb_ct *buf ){
v3f delta;
- v3_sub( rba->co, rbb->co, delta );
+ v3_sub( coA, coB, delta );
f32 d2 = v3_length2(delta),
- r = obja->inf.sphere.radius + objb->inf.sphere.radius;
+ r = ra+rb;
if( d2 < r*r ){
f32 d = sqrtf(d2);
v3_muls( delta, 1.0f/d, ct->n );
v3f p0, p1;
- v3_muladds( rba->co, ct->n,-obja->inf.sphere.radius, p0 );
- v3_muladds( rbb->co, ct->n, objb->inf.sphere.radius, p1 );
+ v3_muladds( coA, ct->n,-ra, p0 );
+ v3_muladds( coB, ct->n, rb, p1 );
v3_add( p0, p1, ct->co );
v3_muls( ct->co, 0.5f, ct->co );
ct->type = k_contact_type_default;
ct->p = r-d;
- ct->rba = rba;
- ct->rbb = rbb;
return 1;
}
-
- return 0;
+ else return 0;
}
-#endif
static int rb_sphere__triangle( m4x3f mtxA, f32 r,
v3f tri[3], rb_ct *buf ){
return rb_contact_buffer + rb_contact_count;
}
-static void rb_prepare_contact( rb_ct *ct, float timestep ){
- ct->bias = -k_phys_baumgarte * (timestep*3600.0f)
+static void rb_prepare_contact( rb_ct *ct ){
+ ct->bias = -k_phys_baumgarte * (vg.time_fixed_delta*3600.0f)
* vg_minf( 0.0f, -ct->p+k_penetration_slop );
v3_tangent_basis( ct->n, ct->t[0], ct->t[1] );
static void rb_presolve_contacts( rb_ct *buffer, int len ){
for( int i=0; i<len; i++ ){
rb_ct *ct = &buffer[i];
- rb_prepare_contact( ct, k_rb_delta );
+ rb_prepare_contact( ct );
v3f ra, rb, raCn, rbCn, raCt, rbCt;
v3_sub( ct->co, ct->rba->co, ra );
ct->tangent_mass[j] += v3_dot( rbCt, rbCtI );
ct->tangent_mass[j] = 1.0f/ct->tangent_mass[j];
}
-
- rb_debug_contact( ct );
}
}
m4x3_identity( mmdl1 );
m3x3_scalef( mmdl0, r );
m3x3_scalef( mmdl1, r );
- mmdl0[3][1] = -h-r;
- mmdl1[3][1] = h+r;
+ mmdl0[3][1] = -h*0.5f+r;
+ mmdl1[3][1] = h*0.5f-r;
m4x3_mul( mdl, mmdl0, mmdl0 );
m4x3_mul( mdl, mmdl1, mmdl1 );
void (*link)(void);
int compiled;
}
- * shaders[32];
+ * shaders[48];
u32 count;
}
static vg_shaders;