-
- m4x3f mboard;
- v3_copy( player.rb.to_world[0], mboard[0] );
- v3_copy( player.rb.to_world[2], mboard[1] );
- v3_copy( player.rb.to_world[1], mboard[2] );
- m4x3_mulv( player.rb.to_world, (v3f){ 0.0f, 0.3f, 0.0f }, mboard[3] );
-
- debug_capsule( mboard, k_board_length*2.0f, k_board_radius, 0xff0000ff );
-
- boxf region = {{ -k_board_radius, -k_board_length, -k_board_radius },
- { k_board_radius, k_board_length, k_board_radius }};
- m4x3_transform_aabb( mboard, region );
-
- u32 geo[256];
- v3f tri[3];
- int len = bh_select( &world.geo.bhtris, region, geo, 256 );
-
- v3f poles[2];
- m4x3_mulv(mboard, (v3f){0.0f,-k_board_length+k_board_radius,0.0f}, poles[0]);
- m4x3_mulv(mboard, (v3f){0.0f, k_board_length-k_board_radius,0.0f}, poles[1]);
-
- struct contact manifold[12];
- int manifold_count = 0;
-
- v3f surface_avg = {0.0f, 0.0f, 0.0f};
-
- for( int i=0; i<len; i++ )
- {
- u32 *ptri = &world.geo.indices[ geo[i]*3 ];
-
- for( int j=0; j<3; j++ )
- v3_copy( world.geo.verts[ptri[j]].co, tri[j] );
-
- vg_line(tri[0],tri[1],0xff00ff00 );
- vg_line(tri[1],tri[2],0xff00ff00 );
- vg_line(tri[2],tri[0],0xff00ff00 );
-
- v3f co, norm;
- float p;
-
- for( int j=0; j<2; j++ )
- {
- if( sphere_vs_triangle( poles[j], k_board_radius, tri,co,norm,&p) )
- {
- if(manifold_count >= vg_list_size(manifold))
- {
- vg_error("Manifold overflow!\n");
- break;
- }
-
- v3f p1;
- v3_muladds( poles[j], norm, p, p1 );
- vg_line( poles[j], p1, 0xffffffff );
-
- struct contact *ct = &manifold[manifold_count ++];
- v3_copy( co, ct->co );
- v3_copy( norm, ct->n );
-
- ct->bias = -0.2f*k_rb_rate*vg_minf(0.0f,-p+k_board_allowance);
- ct->norm_impulse = 0.0f;
-
- v3_add( norm, surface_avg, surface_avg );
- }
- }
- }
-
- if( !manifold_count )
- {
- player_start_air();
- }
- else
- {
- v3_normalize( surface_avg );
-
- if( v3_dot( player.rb.v, surface_avg ) > 0.5f )
- {
- player_start_air();
- }
- else
- player.in_air = 0;
- }
-
- for( int j=0; j<5; j++ )
- {
- for( int i=0; i<manifold_count; i++ )
- {
- struct contact *ct = &manifold[i];
-
- v3f dv, delta;
- v3_sub( ct->co, player.rb.co, delta );
- v3_cross( player.rb.w, delta, dv );
- v3_add( player.rb.v, dv, dv );
-
- float vn = -v3_dot( dv, ct->n );
- vn += ct->bias;
-
- float temp = ct->norm_impulse;
- ct->norm_impulse = vg_maxf( temp + vn, 0.0f );
- vn = ct->norm_impulse - temp;
-
- v3f impulse;
- v3_muls( ct->n, vn, impulse );
-
- if( fabsf(v3_dot( impulse, player.rb.forward )) > 10.0f ||
- fabsf(v3_dot( impulse, player.rb.up )) > 50.0f )
- {
- player.is_dead = 1;
- character_ragdoll_copypose( &player.mdl, player.rb.v );
- return;
- }
-
- v3_add( impulse, player.rb.v, player.rb.v );
- v3_cross( delta, impulse, impulse );
-
- /*
- * W Impulses are limited to the Y and X axises, we don't really want
- * roll angular velocities being included.
- *
- * Can also tweak the resistance of each axis here by scaling the wx,wy
- * components.
- */
-
- float wy = v3_dot( player.rb.up, impulse ),
- wx = v3_dot( player.rb.right, impulse )*1.5f;
-
- v3_muladds( player.rb.w, player.rb.up, wy, player.rb.w );
- v3_muladds( player.rb.w, player.rb.right, wx, player.rb.w );
- }
- }
-
- if( !player.in_air )
- {
- v3f axis;
- float angle = v3_dot( player.rb.up, surface_avg );
- v3_cross( player.rb.up, surface_avg, axis );
-
- float cz = v3_dot( player.rb.forward, axis );
- v3_muls( player.rb.forward, cz, axis );
-
- if( angle < 0.999f )
- {
- v4f correction;
- q_axis_angle( correction, axis, acosf(angle)*0.3f );
- q_mul( correction, player.rb.q, player.rb.q );
- }
-
- v3_muladds( player.rb.v, player.rb.up,
- -k_downforce*ktimestep, player.rb.v );
- player_physics_control();
- }
- else
- {
- player_physics_control_air();
- }
-}
-
-static void player_do_motion(void)
-{
- float horizontal = vg_get_axis("horizontal"),
- vertical = vg_get_axis("vertical");
-
- player_physics();
-
- /* Integrate velocity */
- v3f prevco;
- v3_copy( player.rb.co, prevco );
-
- apply_gravity( player.rb.v, ktimestep );
- v3_muladds( player.rb.co, player.rb.v, ktimestep, player.rb.co );
-
- /* Real angular velocity integration */
- v3_lerp( player.rb.w, (v3f){0.0f,0.0f,0.0f}, 0.125f, player.rb.w );
- if( v3_length2( player.rb.w ) > 0.0f )
- {
- v4f rotation;
- v3f axis;
- v3_copy( player.rb.w, axis );
-
- float mag = v3_length( axis );
- v3_divs( axis, mag, axis );
- q_axis_angle( rotation, axis, mag*k_rb_delta );
- q_mul( rotation, player.rb.q, player.rb.q );
- }
-
- /* Faux angular velocity */
- v4f rotate;
-
- static float siY = 0.0f;
- float lerpq = player.in_air? 0.04f: 0.3f;
- siY = vg_lerpf( siY, player.iY, lerpq );
-
- q_axis_angle( rotate, player.rb.up, siY );
- q_mul( rotate, player.rb.q, player.rb.q );
- player.iY = 0.0f;
-
- /*
- * Gate intersection, by tracing a line over the gate planes
- */
- for( int i=0; i<world.gate_count; i++ )
- {
- teleport_gate *gate = &world.gates[i];
-
- if( gate_intersect( gate, player.rb.co, prevco ) )
- {
- m4x3_mulv( gate->transport, player.rb.co, player.rb.co );
- m3x3_mulv( gate->transport, player.rb.v, player.rb.v );
- m3x3_mulv( gate->transport, player.vl, player.vl );
- m3x3_mulv( gate->transport, player.v_last, player.v_last );
- m3x3_mulv( gate->transport, player.m, player.m );
- m3x3_mulv( gate->transport, player.bob, player.bob );
-
- v4f transport_rotation;
- m3x3_q( gate->transport, transport_rotation );
- q_mul( transport_rotation, player.rb.q, player.rb.q );
-
- break;
- }
- }
-
- rb_update_transform( &player.rb );
-}
-
-/*
- * Walkgrid implementation,
- * loosely based of cmuratoris youtube video 'Killing the Walkmonster'
- */
-
-#define WALKGRID_SIZE 16
-struct walkgrid
-{
- struct grid_sample
- {
- enum sample_type
- {
- k_sample_type_air, /* Nothing was hit. */
- k_sample_type_invalid, /* The point is invalid, but there is a sample
- underneath that can be used */
- k_sample_type_valid, /* This point is good */
- }
- type;
-
- v3f clip[2];
- v3f pos;
-
- enum traverse_state
- {
- k_traverse_none = 0x00,
- k_traverse_h = 0x01,
- k_traverse_v = 0x02
- }
- state;
- }
- samples[WALKGRID_SIZE][WALKGRID_SIZE];
-
- boxf region;
-
- float move; /* Current amount of movement we have left to apply */
- v2f dir; /* The movement delta */
- v2i cell_id;/* Current cell */
- v2f pos; /* Local position (in cell) */
- float h;
-};
-
-static int player_walkgrid_tri_walkable( u32 tri[3] )
-{
- return tri[0] > world.sm_geo_std_oob.vertex_count;
-}
-
-/*
- * Get a sample at this pole location, will return 1 if the sample is valid,
- * and pos will be updated to be the intersection location.
- */
-static void player_walkgrid_samplepole( struct grid_sample *s )
-{
- boxf region = {{ s->pos[0] -0.01f, s->pos[1] - 4.0f, s->pos[2] -0.01f},
- { s->pos[0] +0.01f, s->pos[1] + 4.0f, s->pos[2] +0.01f}};
-
- u32 geo[256];
- v3f tri[3];
- int len = bh_select( &world.geo.bhtris, region, geo, 256 );
-
- const float k_minworld_y = -2000.0f;
-
- float walk_height = k_minworld_y,
- block_height = k_minworld_y;
-
- s->type = k_sample_type_air;
-
- for( int i=0; i<len; i++ )
- {
- u32 *ptri = &world.geo.indices[ geo[i]*3 ];
-
- for( int j=0; j<3; j++ )
- v3_copy( world.geo.verts[ptri[j]].co, tri[j] );
-
- v3f vdown = {0.0f,-1.0f,0.0f};
- v3f sample_from;
- v3_copy( s->pos, sample_from );
- sample_from[1] = region[1][1];
-
- float dist;
- if( ray_tri( tri, sample_from, vdown, &dist ))
- {
- v3f p0;
- v3_muladds( sample_from, vdown, dist, p0 );
-
- if( player_walkgrid_tri_walkable(ptri) )
- {
- if( p0[1] > walk_height )
- {
- walk_height = p0[1];
- }
- }
- else
- {
- if( p0[1] > block_height )
- block_height = p0[1];
- }
- }
- }
-
- s->pos[1] = walk_height;
-
- if( walk_height > k_minworld_y )
- if( block_height > walk_height )
- s->type = k_sample_type_invalid;
- else
- s->type = k_sample_type_valid;
- else
- s->type = k_sample_type_air;
-}
-
-float const k_gridscale = 0.5f;
-
-enum eclipdir
-{
- k_eclipdir_h = 0,
- k_eclipdir_v = 1
-};
-
-static void player_walkgrid_clip_blocker( struct grid_sample *sa,
- struct grid_sample *sb,
- struct grid_sample *st,
- enum eclipdir dir )
-{
- v3f clipdir, pos;
- int valid_a = sa->type == k_sample_type_valid,
- valid_b = sb->type == k_sample_type_valid;
- struct grid_sample *target = valid_a? sa: sb,
- *other = valid_a? sb: sa;
- v3_copy( target->pos, pos );
- v3_sub( other->pos, target->pos, clipdir );
-
- boxf cell_region;
- v3_muladds( pos, (v3f){1.0f,1.0f,1.0f}, -k_gridscale*2.1f, cell_region[0]);
- v3_muladds( pos, (v3f){1.0f,1.0f,1.0f}, k_gridscale*2.1f, cell_region[1]);
-
- u32 geo[256];
- v3f tri[3];
- int len = bh_select( &world.geo.bhtris, cell_region, geo, 256 );
-
- float start_time = v3_length( clipdir ),
- min_time = start_time;
- v3_normalize( clipdir );
- v3_muls( clipdir, 0.0001f, st->clip[dir] );
-
- for( int i=0; i<len; i++ )
- {
- u32 *ptri = &world.geo.indices[ geo[i]*3 ];
- for( int j=0; j<3; j++ )
- v3_copy( world.geo.verts[ptri[j]].co, tri[j] );
-
- if( player_walkgrid_tri_walkable(ptri) )
- continue;
-
- float dist;
- if(ray_tri( tri, pos, clipdir, &dist ))
- {
- if( dist > 0.0f && dist < min_time )
- {
- min_time = dist;
- sb->type = k_sample_type_air;
- }
- }
- }
-
- if( !(min_time < start_time) )
- min_time = 0.5f * k_gridscale;
-
- min_time = vg_clampf( min_time/k_gridscale, 0.01f, 0.99f );
-
- v3_muls( clipdir, min_time, st->clip[dir] );
-
- v3f p0;
- v3_muladds( target->pos, st->clip[dir], k_gridscale, p0 );
-}
-
-static void player_walkgrid_clip_edge( struct grid_sample *sa,
- struct grid_sample *sb,
- struct grid_sample *st, /* data store */
- enum eclipdir dir )
-{
- v3f clipdir = { 0.0f, 0.0f, 0.0f }, pos;
- int valid_a = sa->type == k_sample_type_valid,
- valid_b = sb->type == k_sample_type_valid;
-
- struct grid_sample *target = valid_a? sa: sb,
- *other = valid_a? sb: sa;
-
- v3_sub( other->pos, target->pos, clipdir );
- clipdir[1] = 0.0f;
-
- v3_copy( target->pos, pos );
-
- boxf cell_region;
- v3_muladds( pos, (v3f){1.0f,1.0f,1.0f}, -k_gridscale*1.1f, cell_region[0]);
- v3_muladds( pos, (v3f){1.0f,1.0f,1.0f}, k_gridscale*1.1f, cell_region[1]);
-
- u32 geo[256];
- int len = bh_select( &world.geo.bhtris, cell_region, geo, 256 );
-
- float max_dist = 0.0f;
- v3f tri[3];
- v3f perp;
- v3_cross( clipdir,(v3f){0.0f,1.0f,0.0f},perp );
- v3_muls( clipdir, 0.001f, st->clip[dir] );
-
- for( int i=0; i<len; i++ )
- {
- u32 *ptri = &world.geo.indices[ geo[i]*3 ];
- for( int j=0; j<3; j++ )
- v3_copy( world.geo.verts[ptri[j]].co, tri[j] );
-
- if( !player_walkgrid_tri_walkable(ptri) )
- continue;
-
- for( int k=0; k<3; k++ )
- {
- int ia = k,
- ib = (k+1)%3;
-
- v3f v0, v1;
- v3_sub( tri[ia], pos, v0 );
- v3_sub( tri[ib], pos, v1 );
-
- if( (clipdir[2]*v0[0] - clipdir[0]*v0[2]) *
- (clipdir[2]*v1[0] - clipdir[0]*v1[2]) < 0.0f )
- {
- float da = v3_dot(v0,perp),
- db = v3_dot(v1,perp),
- d = da-db,
- qa = da/d;
-
- v3f p0;
- v3_muls( v1, qa, p0 );
- v3_muladds( p0, v0, 1.0f-qa, p0 );
-
- float h = v3_dot(p0,clipdir)/v3_dot(clipdir,clipdir);
-
- if( h >= max_dist && h <= 1.0f )
- {
- max_dist = h;
- float l = 1.0f/v3_length(clipdir);
- v3_muls( p0, l, st->clip[dir] );
- }
- }
- }
- }
-}
-
-static const struct conf
-{
- struct confedge
- {
- /* i: sample index
- * d: data index
- * a: axis index
- * o: the 'other' point to do a A/B test with
- * if its -1, all AB is done.
- */
- int i0, i1,
- d0, d1,
- a0, a1,
- o0, o1;
- }
- edges[2];
- int edge_count;
-}
-k_walkgrid_configs[16] = {
- {{},0},
- {{{ 3,3, 3,0, 1,0, -1,-1 }}, 1},
- {{{ 2,2, 1,3, 0,1, -1,-1 }}, 1},
- {{{ 2,3, 1,0, 0,0, 3,-1 }}, 1},
-
- {{{ 1,1, 0,1, 1,0, -1,-1 }}, 1},
- {{{ 3,3, 3,0, 1,0, -1,-1 },
- { 1,1, 0,1, 1,0, -1,-1 }}, 2},
- {{{ 1,2, 0,3, 1,1, 2,-1 }}, 1},
- {{{ 1,3, 0,0, 1,0, 2, 2 }}, 1},
-
- {{{ 0,0, 0,0, 0,1, -1,-1 }}, 1},
- {{{ 3,0, 3,0, 1,1, 0,-1 }}, 1},
- {{{ 2,2, 1,3, 0,1, -1,-1 },
- { 0,0, 0,0, 0,1, -1,-1 }}, 2},
- {{{ 2,0, 1,0, 0,1, 3, 3 }}, 1},
-
- {{{ 0,1, 0,1, 0,0, 1,-1 }}, 1},
- {{{ 3,1, 3,1, 1,0, 0, 0 }}, 1},
- {{{ 0,2, 0,3, 0,1, 1, 1 }}, 1},
- {{},0},
-};
-
-/*
- * Get a buffer of edges from cell location
- */
-static const struct conf *player_walkgrid_conf( struct walkgrid *wg,
- v2i cell,
- struct grid_sample *corners[4] )
-{
- corners[0] = &wg->samples[cell[1] ][cell[0] ];
- corners[1] = &wg->samples[cell[1]+1][cell[0] ];
- corners[2] = &wg->samples[cell[1]+1][cell[0]+1];
- corners[3] = &wg->samples[cell[1] ][cell[0]+1];
-
- u32 vd0 = corners[0]->type == k_sample_type_valid,
- vd1 = corners[1]->type == k_sample_type_valid,
- vd2 = corners[2]->type == k_sample_type_valid,
- vd3 = corners[3]->type == k_sample_type_valid,
- config = (vd0<<3) | (vd1<<2) | (vd2<<1) | vd3;
-
- return &k_walkgrid_configs[ config ];
-}
-
-static void player_walkgrid_floor(v3f pos)
-{
- v3_muls( pos, 1.0f/k_gridscale, pos );
- v3_floor( pos, pos );
- v3_muls( pos, k_gridscale, pos );
-}
-
-/*
- * Computes the barycentric coordinate of location on a triangle (vertical),
- * then sets the Y position to the interpolation of the three points
- */
-static void player_walkgrid_stand_tri( v3f a, v3f b, v3f c, v3f pos )
-{
- v3f v0,v1,v2;
- v3_sub( b, a, v0 );
- v3_sub( c, a, v1 );
- v3_sub( pos, a, v2 );
-
- float d = v0[0]*v1[2] - v1[0]*v0[2],
- v = (v2[0]*v1[2] - v1[0]*v2[2]) / d,
- w = (v0[0]*v2[2] - v2[0]*v0[2]) / d,
- u = 1.0f - v - w;
-
- vg_line( pos, a, 0xffff0000 );
- vg_line( pos, b, 0xff00ff00 );
- vg_line( pos, c, 0xff0000ff );
- pos[1] = u*a[1] + v*b[1] + w*c[1];
-}
-
-/*
- * Get the minimum time value of pos+dir until a cell edge
- *
- * t[0] -> t[3] are the individual time values
- * t[5] & t[6] are the maximum axis values
- * t[6] is the minimum value
- *
- */
-static void player_walkgrid_min_cell( float t[7], v2f pos, v2f dir )
-{
- v2f frac = { 1.0f/dir[0], 1.0f/dir[1] };
-
- t[0] = 999.9f;
- t[1] = 999.9f;
- t[2] = 999.9f;
- t[3] = 999.9f;
-
- if( fabsf(dir[0]) > 0.0001f )
- {
- t[0] = (0.0f-pos[0]) * frac[0];
- t[1] = (1.0f-pos[0]) * frac[0];
- }
- if( fabsf(dir[1]) > 0.0001f )
- {
- t[2] = (0.0f-pos[1]) * frac[1];
- t[3] = (1.0f-pos[1]) * frac[1];
- }
-
- t[4] = vg_maxf(t[0],t[1]);
- t[5] = vg_maxf(t[2],t[3]);
- t[6] = vg_minf(t[4],t[5]);
-}
-
-static void player_walkgrid_iter(struct walkgrid *wg, int iter)
-{