From: hgn Date: Wed, 15 Feb 2023 01:54:44 +0000 (+0000) Subject: better? grinds X-Git-Url: https://harrygodden.com/git/?a=commitdiff_plain;h=0a5ec40708e7d128511cac04f84d85055e6fc164;p=carveJwlIkooP6JGAAIwe30JlM.git better? grinds --- diff --git a/common.h b/common.h index f9bcafa..82b0808 100644 --- a/common.h +++ b/common.h @@ -129,22 +129,28 @@ VG_STATIC float k_mmdecay = 12.0f, k_spring_angular = 1.0f, - k_spring_force = 15.0f, + k_spring_force = 130.0f, k_spring_dampener = 5.0f, k_grind_spring = 100.0f, - k_grind_dampener = 6.0f, + k_grind_aligment = 10.0f, + k_grind_dampener = 40.0f, + k_board_spring = 100.0f, k_board_dampener = 40.0f, k_manul_spring = 200.0f, k_manul_dampener = 30.0f, k_board_interia = 8.0f, + k_grind_decayxy = 30.0f, + k_grind_axel_min_vel = 3.0f, + k_grind_axel_max_angle = 0.97f, /* cosine(|a|) */ + k_grind_max_friction = 3.0f, + k_board_length = 0.45f, k_board_width = 0.13f, k_board_end_radius = 0.1f, - k_board_radius = 0.07f; - + k_board_radius = 0.14f; /* 0.07 */ VG_STATIC float k_walkspeed = 10.0f, @@ -183,9 +189,14 @@ VG_STATIC void common_var_temp(void) VG_VAR_F32( k_grind_dampener ); VG_VAR_F32( k_grind_spring ); + VG_VAR_F32( k_grind_aligment ); VG_VAR_F32( k_board_spring ); VG_VAR_F32( k_board_dampener ); VG_VAR_F32( k_board_interia ); + VG_VAR_F32( k_grind_decayxy ); + VG_VAR_F32( k_grind_axel_min_vel ); + VG_VAR_F32( k_grind_axel_max_angle ); + VG_VAR_F32( k_grind_max_friction ); VG_VAR_F32( k_walkspeed ); VG_VAR_F32( k_stopspeed ); diff --git a/maps_src/mp_gridmap.mdl b/maps_src/mp_gridmap.mdl index d7ae80c..9240f93 100644 Binary files a/maps_src/mp_gridmap.mdl and b/maps_src/mp_gridmap.mdl differ diff --git a/player.c b/player.c index d86c8c8..6534523 100644 --- a/player.c +++ b/player.c @@ -230,7 +230,10 @@ VG_STATIC void player__pre_render( player_instance *player ) skeleton_apply_pose( sk, res.pose, k_anim_apply_deffered_only ); skeleton_apply_inverses( sk ); skeleton_apply_transform( sk, transform ); + +#if 0 skeleton_debug( sk ); +#endif } if( _player_post_animate[ player->subsystem ] ) diff --git a/player_skate.c b/player_skate.c index 7088cf1..01e3d7a 100644 --- a/player_skate.c +++ b/player_skate.c @@ -44,8 +44,6 @@ VG_STATIC int skate_collide_smooth( player_instance *player, man[i].rbb = NULL; } - return len; - rb_manifold_filter_coplanar( man, len, 0.05f ); if( len > 1 ) @@ -149,30 +147,29 @@ VG_STATIC int skate_grind_collide( player_instance *player, rb_ct *contact ) return 0; } -VG_STATIC int skate_grind_scansq( player_instance *player, v3f pos, - v3f result_co, v3f result_dir, v3f result_n ) +struct grind_info +{ + v3f co, dir, n; +}; + +VG_STATIC int skate_grind_scansq( player_instance *player, + v3f pos, v3f dir, float r, + struct grind_info *inf ) { v4f plane; - v3_copy( player->rb.v, plane ); + v3_copy( dir, plane ); v3_normalize( plane ); plane[3] = v3_dot( plane, pos ); boxf box; - float r = 0.3f; v3_add( pos, (v3f){ r, r, r }, box[1] ); v3_sub( pos, (v3f){ r, r, r }, box[0] ); -#if 0 - vg_line_boxf( box, VG__BLUE ); -#endif + vg_line_boxf( box, VG__BLACK ); m4x3f mtx; m3x3_copy( player->rb.to_world, mtx ); v3_copy( pos, mtx[3] ); - -#if 0 - debug_sphere( mtx, r, VG__CYAN ); -#endif bh_iter it; bh_iter_init( 0, &it ); @@ -186,7 +183,6 @@ VG_STATIC int skate_grind_scansq( player_instance *player, v3f pos, centroid; } samples[48]; - int sample_count = 0; v2f support_min, @@ -250,11 +246,14 @@ VG_STATIC int skate_grind_scansq( player_instance *player, v3f pos, if( sample_count < 2 ) return 0; - v3f average_position, + v3f average_direction, average_normal; - v3_zero( average_position ); + v2f min_co, max_co; + v2_fill( min_co, INFINITY ); + v2_fill( max_co, -INFINITY ); + v3_zero( average_direction ); v3_zero( average_normal ); @@ -288,11 +287,8 @@ VG_STATIC int skate_grind_scansq( player_instance *player, v3f pos, v3_dot( v0, sj->normal3 ) <= 0.0f ) continue; - v3f p0; - v3_muls( support_axis, sj->co[0], p0 ); - p0[1] += sj->co[1]; - - v3_add( average_position, p0, average_position ); + v2_minv( sj->co, min_co, min_co ); + v2_maxv( sj->co, max_co, max_co ); v3f n0, n1, dir; v3_copy( si->normal3, n0 ); @@ -321,25 +317,292 @@ VG_STATIC int skate_grind_scansq( player_instance *player, v3f pos, return 0; float div = 1.0f/(float)passed_samples; - v3_muls( average_position, div, average_position ); v3_normalize( average_direction ); v3_normalize( average_normal ); + + v2f average_coord; + v2_add( min_co, max_co, average_coord ); + v2_muls( average_coord, 0.5f, average_coord ); + + v3_muls( support_axis, average_coord[0], inf->co ); + inf->co[1] += average_coord[1]; + v3_add( pos, inf->co, inf->co ); + v3_copy( average_normal, inf->n ); + v3_copy( average_direction, inf->dir ); + + vg_line_pt3( inf->co, 0.02f, VG__GREEN ); + vg_line_arrow( inf->co, average_direction, 0.3f, VG__GREEN ); + vg_line_arrow( inf->co, inf->n, 0.2f, VG__CYAN ); + + return passed_samples; +} + +#if 0 +static inline void skate_grind_coordv2i( v2f co, v2i d ) +{ + const float k_inv_res = 1.0f/0.01f; + d[0] = floorf( co[0] * k_inv_res ); + d[1] = floorf( co[1] * k_inv_res ); +} + +static inline u32 skate_grind_hashv2i( v2i d ) +{ + return (d[0] * 92837111) ^ (d[1] * 689287499); +} + +static inline u32 skate_grind_hashv2f( v2f co ) +{ + v2i d; + skate_grind_coordv2i( co, d ); + return skate_grind_hashv2i( d ); +} + +VG_STATIC int skate_grind_scansq( player_instance *player, v3f pos, + v3f result_co, v3f result_dir, v3f result_n ) +{ + v4f plane; + v3_copy( player->rb.v, plane ); + v3_normalize( plane ); + plane[3] = v3_dot( plane, pos ); + + boxf box; + float r = k_board_length; + v3_add( pos, (v3f){ r, r, r }, box[1] ); + v3_sub( pos, (v3f){ r, r, r }, box[0] ); + + vg_line_boxf( box, VG__BLACK ); + + m4x3f mtx; + m3x3_copy( player->rb.to_world, mtx ); + v3_copy( pos, mtx[3] ); + + bh_iter it; + bh_iter_init( 0, &it ); + int idx; + + struct grind_sample + { + v2f co; + v2f normal; + v3f normal3, + centroid; + } + samples[48]; + + int sample_count = 0; + + v2f support_min, + support_max; + + v3f support_axis; + v3_cross( plane, (v3f){0.0f,1.0f,0.0f}, support_axis ); + v3_normalize( support_axis ); + + while( bh_next( world.geo_bh, &it, box, &idx ) ) + { + u32 *ptri = &world.scene_geo->arrindices[ idx*3 ]; + v3f tri[3]; + + for( int j=0; j<3; j++ ) + v3_copy( world.scene_geo->arrvertices[ptri[j]].co, tri[j] ); + + for( int j=0; j<3; j++ ) + { + int i0 = j, + i1 = (j+1) % 3; + + struct grind_sample *sample = &samples[ sample_count ]; + v3f co; + + if( plane_segment( plane, tri[i0], tri[i1], co ) ) + { + v3f d; + v3_sub( co, pos, d ); + if( v3_length2( d ) > r*r ) + continue; + + v3f va, vb, normal; + v3_sub( tri[1], tri[0], va ); + v3_sub( tri[2], tri[0], vb ); + v3_cross( va, vb, normal ); + + sample->normal[0] = v3_dot( support_axis, normal ); + sample->normal[1] = normal[1]; + sample->co[0] = v3_dot( support_axis, d ); + sample->co[1] = d[1]; + + v3_copy( normal, sample->normal3 ); /* normalize later + if we want to us it */ + + v3_muls( tri[0], 1.0f/3.0f, sample->centroid ); + v3_muladds( sample->centroid, tri[1], 1.0f/3.0f, sample->centroid ); + v3_muladds( sample->centroid, tri[2], 1.0f/3.0f, sample->centroid ); + + v2_normalize( sample->normal ); + sample_count ++; + + if( sample_count == vg_list_size( samples ) ) + { + break; + } + } + } + } + + if( sample_count < 2 ) + return 0; + + + + /* spacial hashing */ + + const int k_hashmap_size = 128; + u32 hashmap[k_hashmap_size+1]; + u32 entries[48]; + + for( int i=0; ico, start ); + + v2i offsets[] = { {-1,-1},{ 0,-1},{ 1,-1}, + {-1, 0},{ 0, 0},{ 1, 0}, + {-1, 1},{ 0, 1},{ 1, 1} }; + + for( int j=0; jco, sj->co ) >= (0.01f*0.01f) ) + continue; + + /* not sharp angle */ + if( v2_dot( si->normal, sj->normal ) >= 0.7f ) + continue; + + /* not convex */ + v3f v0; + v3_sub( sj->centroid, si->centroid, v0 ); + if( v3_dot( v0, si->normal3 ) >= 0.0f || + v3_dot( v0, sj->normal3 ) <= 0.0f ) + continue; + + v2_minv( sj->co, min_co, min_co ); + v2_maxv( sj->co, max_co, max_co ); + + v3f n0, n1, dir; + v3_copy( si->normal3, n0 ); + v3_copy( sj->normal3, n1 ); + v3_cross( n0, n1, dir ); + v3_normalize( dir ); + + /* make sure the directions all face a common hemisphere */ + v3_muls( dir, vg_signf(v3_dot(dir,plane)), dir ); + v3_add( average_direction, dir, average_direction ); + + if( si->normal3[1] > sj->normal3[1] ) + v3_add( si->normal3, average_normal, average_normal ); + else + v3_add( sj->normal3, average_normal, average_normal ); + + passed_samples ++; + } + } + } + + if( !passed_samples ) + return 0; + + if( (v3_length2( average_direction ) <= 0.001f) || + (v3_length2( average_normal ) <= 0.001f ) ) + return 0; + + float div = 1.0f/(float)passed_samples; + v3_normalize( average_direction ); + v3_normalize( average_normal ); + + v2f average_coord; + v2_add( min_co, max_co, average_coord ); + v2_muls( average_coord, 0.5f, average_coord ); + + + v3_muls( support_axis, average_coord[0], result_co ); + result_co[1] += average_coord[1]; + v3_add( pos, result_co, result_co ); - v3_add( pos, average_position, average_position ); - vg_line_pt3( average_position, 0.02f, VG__GREEN ); +#if 0 + vg_line_pt3( result_co, 0.02f, VG__GREEN ); v3f p0, p1; - v3_muladds( average_position, average_direction, 0.35f, p0 ); - v3_muladds( average_position, average_direction, -0.35f, p1 ); + v3_muladds( result_co, average_direction, 0.35f, p0 ); + v3_muladds( result_co, average_direction, -0.35f, p1 ); vg_line( p0, p1, VG__PINK ); +#endif - v3_copy( average_position, result_co ); v3_copy( average_normal, result_n ); v3_copy( average_direction, result_dir ); return passed_samples; } +#endif + /* * * Prediction system @@ -563,6 +826,7 @@ void player__approximate_best_trajectory( player_instance *player ) * ------------------------------------------------ */ +#if 0 VG_STATIC void skate_apply_grind_model( player_instance *player, rb_ct *manifold, int len ) { @@ -639,6 +903,7 @@ VG_STATIC void skate_apply_grind_model( player_instance *player, #endif } } +#endif /* * Air control, no real physics @@ -647,9 +912,6 @@ VG_STATIC void skate_apply_air_model( player_instance *player ) { struct player_skate *s = &player->_skate; - if( s->state.activity != k_skate_activity_air ) - return; - if( s->state.activity_prev != k_skate_activity_air ) player__approximate_best_trajectory( player ); @@ -725,6 +987,7 @@ VG_STATIC void skate_apply_air_model( player_instance *player ) limiter *= limiter; limiter = 1.0f-limiter; +#if 0 if( fabsf(angle) < 0.9999f ) { v4f correction; @@ -732,21 +995,18 @@ VG_STATIC void skate_apply_air_model( player_instance *player ) acosf(angle)*(1.0f-limiter)*2.0f*VG_TIMESTEP_FIXED ); q_mul( correction, player->rb.q, player->rb.q ); } +#endif } v2f steer = { player->input_js1h->axis.value, player->input_js1v->axis.value }; v2_normalize_clamp( steer ); -#if 0 - s->state.steery -= steer[0] * k_steer_air * VG_TIMESTEP_FIXED; - s->state.steerx += steer[1] * s->state.reverse * k_steer_air - * limiter * k_rb_delta; -#endif s->land_dist = time_to_impact; v3_copy( target_normal, s->land_normal ); } +#if 0 VG_STATIC void skate_get_board_points( player_instance *player, v3f front, v3f back ) { @@ -756,7 +1016,9 @@ VG_STATIC void skate_get_board_points( player_instance *player, m4x3_mulv( player->rb.to_world, pos_front, front ); m4x3_mulv( player->rb.to_world, pos_back, back ); } +#endif +#if 0 /* * Casts and pushes a sphere-spring model into the world */ @@ -806,6 +1068,7 @@ VG_STATIC int skate_simulate_spring( player_instance *player, return 0; } } +#endif /* @@ -818,16 +1081,9 @@ VG_STATIC void skate_apply_interface_model( player_instance *player, { struct player_skate *s = &player->_skate; - if( !((s->state.activity == k_skate_activity_ground) || - (s->state.activity == k_skate_activity_air )) ) - return; - - if( s->state.activity == k_skate_activity_air ) - s->debug_normal_pressure = 0.0f; - else - s->debug_normal_pressure = v3_dot( player->rb.to_world[1], player->rb.v ); - /* springs */ + +#if 0 v3f spring0, spring1; skate_get_board_points( player, spring1, spring0 ); @@ -909,6 +1165,7 @@ VG_STATIC void skate_apply_interface_model( player_instance *player, #endif } } +#endif } VG_STATIC int player_skate_trick_input( player_instance *player ); @@ -971,8 +1228,7 @@ VG_STATIC void skate_apply_trick_model( player_instance *player ) } else { - if( (s->state.lift_frames == 0) - && (v3_length2(s->state.trick_vel) >= 0.0001f ) && + if( (v3_length2(s->state.trick_vel) >= 0.0001f ) && s->state.trick_time > 0.2f) { player__dead_transition( player ); @@ -1005,6 +1261,39 @@ VG_STATIC void skate_apply_grab_model( player_instance *player ) s->state.grabbing = vg_lerpf( s->state.grabbing, grabt, 8.4f*k_rb_delta ); } +VG_STATIC void skate_apply_steering_model( player_instance *player ) +{ + struct player_skate *s = &player->_skate; + + /* Steering */ + float input = player->input_js1h->axis.value, + grab = player->input_grab->axis.value, + steer = input * (1.0f-(s->state.jump_charge+grab)*0.4f), + steer_scaled = vg_signf(steer) * powf(steer,2.0f) * k_steer_ground; + + v3f steer_axis; + v3_muls( player->rb.to_world[1], -vg_signf( steer_scaled ), steer_axis ); + + float rate = 26.0f; + + if( s->state.activity == k_skate_activity_air ) + { + rate = 6.0f * fabsf(steer_scaled); + } + + else if( s->state.activity >= k_skate_activity_grind_any ) + { + rate *= fabsf(steer_scaled); + } + + float current = v3_dot( player->rb.to_world[1], player->rb.w ), + addspeed = (steer_scaled * -1.0f) - current, + maxaccel = rate * k_rb_delta, + accel = vg_clampf( addspeed, -maxaccel, maxaccel ); + + v3_muladds( player->rb.w, player->rb.to_world[1], accel, player->rb.w ); +} + /* * Computes friction and surface interface model */ @@ -1012,9 +1301,6 @@ VG_STATIC void skate_apply_friction_model( player_instance *player ) { struct player_skate *s = &player->_skate; - if( s->state.activity != k_skate_activity_ground ) - return; - /* * Computing localized friction forces for controlling the character * Friction across X is significantly more than Z @@ -1062,41 +1348,20 @@ VG_STATIC void skate_apply_friction_model( player_instance *player ) /* Send back to velocity */ m3x3_mulv( player->rb.to_world, vel, player->rb.v ); - - /* Steering */ - float input = player->input_js1h->axis.value, - grab = player->input_grab->axis.value, - steer = input * (1.0f-(s->state.jump_charge+grab)*0.4f), - steer_scaled = vg_signf(steer) * powf(steer,2.0f) * k_steer_ground; +} - v3f steer_axis; - v3_muls( player->rb.to_world[1], -vg_signf( steer_scaled ), steer_axis ); - - float current = v3_dot( player->rb.to_world[1], player->rb.w ), - addspeed = (steer_scaled * -1.0f) - current, - maxaccel = 26.0f * k_rb_delta, - accel = vg_clampf( addspeed, -maxaccel, maxaccel ); - - v3_muladds( player->rb.w, player->rb.to_world[1], accel, player->rb.w ); - - -#if 0 - player_accelerate( player->rb.w, steer_axis, - fabsf(steer_scaled) * 1.0f, 30.0f ); - - //s->state.steery -= steer_scaled * k_rb_delta; -#endif -} - -VG_STATIC void skate_apply_jump_model( player_instance *player ) -{ - struct player_skate *s = &player->_skate; - int charging_jump_prev = s->state.charging_jump; - s->state.charging_jump = player->input_jump->button.value; +VG_STATIC void skate_apply_jump_model( player_instance *player ) +{ + struct player_skate *s = &player->_skate; + int charging_jump_prev = s->state.charging_jump; + s->state.charging_jump = player->input_jump->button.value; /* Cannot charge this in air */ - if( s->state.activity != k_skate_activity_ground ) + if( s->state.activity == k_skate_activity_air ) + { s->state.charging_jump = 0; + return; + } if( s->state.charging_jump ) { @@ -1107,14 +1372,11 @@ VG_STATIC void skate_apply_jump_model( player_instance *player ) } else { - s->state.jump_charge -= k_jump_charge_speed * VG_TIMESTEP_FIXED; + s->state.jump_charge -= k_jump_charge_speed * k_rb_delta; } s->state.jump_charge = vg_clampf( s->state.jump_charge, 0.0f, 1.0f ); - if( s->state.activity == k_skate_activity_air ) - return; - /* player let go after charging past 0.2: trigger jump */ if( (!s->state.charging_jump) && (s->state.jump_charge > 0.2f) ) { @@ -1135,17 +1397,19 @@ VG_STATIC void skate_apply_jump_model( player_instance *player ) v3_muladds( player->rb.v, jumpdir, force, player->rb.v ); s->state.jump_charge = 0.0f; s->state.jump_time = vg.time; + s->state.activity = k_skate_activity_air; v2f steer = { player->input_js1h->axis.value, player->input_js1v->axis.value }; v2_normalize_clamp( steer ); + #if 0 float maxspin = k_steer_air * k_rb_delta * k_spin_boost; s->state.steery_s = -steer[0] * maxspin; s->state.steerx = s->state.steerx_s; -#endif s->state.lift_frames ++; +#endif /* FIXME audio events */ #if 0 @@ -1236,131 +1500,28 @@ VG_STATIC void skate_apply_cog_model( player_instance *player ) v3_muladds( s->state.cog, s->state.cog_v, k_rb_delta, s->state.cog ); } -VG_STATIC void skate_collision_response( player_instance *player, - rb_ct *manifold, int len ) -{ - struct player_skate *s = &player->_skate; - - for( int j=0; j<10; j++ ) - { - for( int i=0; ico, player->rb.co, delta ); - v3_cross( player->rb.w, delta, rv ); - v3_add( player->rb.v, rv, rv ); - - v3f raCn; - v3_cross( delta, ct->n, raCn ); - - float normal_mass = 1.0f / (1.0f + v3_dot(raCn,raCn)); - float vn = v3_dot( rv, ct->n ); - float lambda = normal_mass * ( -vn + ct->bias ); - - float temp = ct->norm_impulse; - ct->norm_impulse = vg_maxf( temp + lambda, 0.0f ); - lambda = ct->norm_impulse - temp; - - v3f impulse; - v3_muls( ct->n, lambda, impulse ); - - if( fabsf(v3_dot( impulse, player->rb.to_world[2] )) > 10.0f || - fabsf(v3_dot( impulse, player->rb.to_world[1] )) > 50.0f ) - { - player__dead_transition( player ); - 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.to_world[1], impulse ) * 1.0f, - wx = v3_dot( player->rb.to_world[0], impulse ) * 1.0f, - wz = v3_dot( player->rb.to_world[2], impulse ) * 1.0f; - - v3_muladds( player->rb.w, player->rb.to_world[1], wy, player->rb.w ); - v3_muladds( player->rb.w, player->rb.to_world[0], wx, player->rb.w ); - v3_muladds( player->rb.w, player->rb.to_world[2], wz, player->rb.w ); - - - v3_cross( player->rb.w, delta, rv ); - v3_add( player->rb.v, rv, rv ); - vn = v3_dot( rv, ct->n ); - } - } -} VG_STATIC void skate_integrate( player_instance *player ) { struct player_skate *s = &player->_skate; - /* integrate rigidbody velocities */ -#ifndef SKATE_CCD - v3f gravity = { 0.0f, -9.6f, 0.0f }; - v3_muladds( player->rb.v, gravity, k_rb_delta, player->rb.v ); - v3_muladds( player->rb.co, player->rb.v, k_rb_delta, player->rb.co ); -#endif - - float decay_rate = 1.0f - (k_rb_delta * 3.0f); + float decay_rate = 1.0f - (k_rb_delta * 3.0f), + decay_rate_y = 1.0f; -#if 0 - if( s->state.activity == k_skate_activity_air ) + if( s->state.activity >= k_skate_activity_grind_any ) { - float dist = 1.0f-(s->land_dist/4.0f); - decay_rate = 0.5f * vg_maxf( dist*dist, 0.0f ); + decay_rate = 1.0f-vg_lerpf( 3.0f, 20.0f, s->grind_strength ) * k_rb_delta; + decay_rate_y = decay_rate; } -#endif float wx = v3_dot( player->rb.w, player->rb.to_world[0] ) * decay_rate, - wy = v3_dot( player->rb.w, player->rb.to_world[1] ), + wy = v3_dot( player->rb.w, player->rb.to_world[1] ) * decay_rate_y, wz = v3_dot( player->rb.w, player->rb.to_world[2] ) * decay_rate; v3_muls( player->rb.to_world[0], wx, player->rb.w ); v3_muladds( player->rb.w, player->rb.to_world[1], wy, player->rb.w ); v3_muladds( player->rb.w, player->rb.to_world[2], wz, player->rb.w ); -#ifndef SKATE_CCD - 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 ); - } -#endif - - /* integrate steering velocities */ -#if 0 - v4f rotate; - float l = (s->state.activity == k_skate_activity_air)? 0.04f: 0.24f; - - s->state.steery_s = vg_lerpf( s->state.steery_s, s->state.steery, l ); - s->state.steerx_s = vg_lerpf( s->state.steerx_s, s->state.steerx, l ); - - q_axis_angle( rotate, player->rb.to_world[1], s->state.steery_s ); - q_mul( rotate, player->rb.q, player->rb.q ); - - q_axis_angle( rotate, player->rb.to_world[0], s->state.steerx_s ); - q_mul( rotate, player->rb.q, player->rb.q ); - - s->state.steerx = 0.0f; - s->state.steery = 0.0f; -#endif - s->state.flip_time += s->state.flip_rate * k_rb_delta; rb_update_transform( &player->rb ); } @@ -1405,7 +1566,7 @@ VG_STATIC void player__skate_pre_update( player_instance *player ) } int trick_id; - if( (s->state.lift_frames > 0) && + if( (s->state.activity == k_skate_activity_air) && (trick_id = player_skate_trick_input( player )) ) { if( (vg.time - s->state.jump_time) < 0.1f ) @@ -1428,29 +1589,566 @@ VG_STATIC void player__skate_pre_update( player_instance *player ) } } } -} +} + +VG_STATIC void player__skate_post_update( player_instance *player ) +{ + struct player_skate *s = &player->_skate; + +#if 0 + for( int i=0; iprediction_count; i++ ) + { + struct land_prediction *p = &s->predictions[i]; + + for( int j=0; jlog_length - 1; j ++ ) + vg_line( p->log[j], p->log[j+1], p->colour ); + + vg_line_cross( p->log[p->log_length-1], p->colour, 0.25f ); + + v3f p1; + v3_add( p->log[p->log_length-1], p->n, p1 ); + vg_line( p->log[p->log_length-1], p1, 0xffffffff ); + + vg_line_pt3( p->apex, 0.02f, 0xffffffff ); + } + + vg_line_pt3( s->state.apex, 0.200f, 0xff0000ff ); + vg_line_pt3( s->state.apex, 0.201f, 0xff00ffff ); +#endif +} + +/* + * truck alignment model at ra(local) + * returns 1 if valid surface: + * surface_normal will be filled out with an averaged normal vector + * axel_dir will be the direction from left to right wheels + * + * returns 0 if no good surface found + */ +VG_STATIC +int skate_compute_surface_alignment( player_instance *player, + v3f ra, u32 colour, + v3f surface_normal, v3f axel_dir ) +{ + struct player_skate *s = &player->_skate; + + v3f truck, left, right; + m4x3_mulv( player->rb.to_world, ra, truck ); + v3_muladds( truck, player->rb.to_world[0], -k_board_width, left ); + v3_muladds( truck, player->rb.to_world[0], k_board_width, right ); + + vg_line( left, right, colour ); + + v3_muladds( left, player->rb.to_world[1], 0.1f, left ); + v3_muladds( right, player->rb.to_world[1], 0.1f, right ); + + float k_max_truck_flex = VG_PIf * 0.25f; + + ray_hit ray_l, ray_r; + ray_l.dist = 0.2f; + ray_r.dist = 0.2f; + + v3f dir; + v3_muls( player->rb.to_world[1], -1.0f, dir ); + + int res_l = ray_world( left, dir, &ray_l ), + res_r = ray_world( right, dir, &ray_r ); + + /* ignore bad normals */ + if( res_l ) + if( v3_dot( ray_l.normal, player->rb.to_world[1] ) < 0.7071f ) + res_l = 0; + + if( res_r ) + if( v3_dot( ray_r.normal, player->rb.to_world[1] ) < 0.7071f ) + res_r = 0; + + v3f v0; + v3f midpoint; + v3f tangent_average; + v3_muladds( truck, player->rb.to_world[1], -k_board_radius, midpoint ); + v3_zero( tangent_average ); + + if( res_l || res_r ) + { + v3f p0, p1, t; + v3_copy( midpoint, p0 ); + v3_copy( midpoint, p1 ); + + if( res_l ) + { + v3_copy( ray_l.pos, p0 ); + v3_cross( ray_l.normal, player->rb.to_world[0], t ); + v3_add( t, tangent_average, tangent_average ); + } + if( res_r ) + { + v3_copy( ray_r.pos, p1 ); + v3_cross( ray_r.normal, player->rb.to_world[0], t ); + v3_add( t, tangent_average, tangent_average ); + } + + v3_sub( p1, p0, v0 ); + v3_normalize( v0 ); + } + else + { + /* fallback: use the closes point to the trucks */ + v3f closest; + int idx = bh_closest_point( world.geo_bh, midpoint, closest, 0.1f ); + + if( idx != -1 ) + { + u32 *tri = &world.scene_geo->arrindices[ idx * 3 ]; + v3f verts[3]; + + for( int j=0; j<3; j++ ) + v3_copy( world.scene_geo->arrvertices[ tri[j] ].co, verts[j] ); + + v3f vert0, vert1, n; + v3_sub( verts[1], verts[0], vert0 ); + v3_sub( verts[2], verts[0], vert1 ); + v3_cross( vert0, vert1, n ); + v3_normalize( n ); + + if( v3_dot( n, player->rb.to_world[1] ) < 0.3f ) + return 0; + + v3_cross( n, player->rb.to_world[2], v0 ); + v3_muladds( v0, player->rb.to_world[2], + -v3_dot( player->rb.to_world[2], v0 ), v0 ); + v3_normalize( v0 ); + + v3f t; + v3_cross( n, player->rb.to_world[0], t ); + v3_add( t, tangent_average, tangent_average ); + } + else + return 0; + } + + v3_muladds( truck, v0, k_board_width, right ); + v3_muladds( truck, v0, -k_board_width, left ); + + vg_line( left, right, VG__WHITE ); + + v3_normalize( tangent_average ); + v3_cross( v0, tangent_average, surface_normal ); + v3_copy( v0, axel_dir ); + + return 1; +} + +VG_STATIC void skate_weight_distribute( player_instance *player ) +{ + struct player_skate *s = &player->_skate; + v3_zero( s->weight_distribution ); + + int reverse_dir = v3_dot( player->rb.to_world[2], player->rb.v ) < 0.0f?1:-1; + + if( s->state.manual_direction == 0 ) + { + if( (player->input_js1v->axis.value > 0.7f) && + (s->state.activity == k_skate_activity_ground) && + (s->state.jump_charge <= 0.01f) ) + s->state.manual_direction = reverse_dir; + } + else + { + if( player->input_js1v->axis.value < 0.1f ) + { + s->state.manual_direction = 0; + } + else + { + if( reverse_dir != s->state.manual_direction ) + { +#if 0 + player__dead_transition( player ); +#endif + return; + } + } + } + + if( s->state.manual_direction ) + { + float amt = vg_minf( player->input_js1v->axis.value * 8.0f, 1.0f ); + s->weight_distribution[2] = k_board_length * amt * + (float)s->state.manual_direction; + } + + /* TODO: Fall back on land normal */ + /* TODO: Lerp weight distribution */ + /* TODO: Can start manual only if not charge jump */ + if( s->state.manual_direction ) + { + v3f plane_z; + + m3x3_mulv( player->rb.to_world, s->weight_distribution, plane_z ); + v3_negate( plane_z, plane_z ); + + v3_muladds( plane_z, s->surface_picture, + -v3_dot( plane_z, s->surface_picture ), plane_z ); + v3_normalize( plane_z ); + + v3_muladds( plane_z, s->surface_picture, 0.3f, plane_z ); + v3_normalize( plane_z ); + + v3f p1; + v3_muladds( player->rb.co, plane_z, 1.5f, p1 ); + vg_line( player->rb.co, p1, VG__GREEN ); + + v3f refdir; + v3_muls( player->rb.to_world[2], -(float)s->state.manual_direction, + refdir ); + + rb_effect_spring_target_vector( &player->rb, refdir, plane_z, + k_manul_spring, k_manul_dampener, + s->substep_delta ); + } +} + +VG_STATIC void skate_adjust_up_direction( player_instance *player ) +{ + struct player_skate *s = &player->_skate; + + if( s->state.activity == k_skate_activity_ground ) + { + v3f target; + v3_copy( s->surface_picture, target ); + + target[1] += 2.0f * s->surface_picture[1]; + v3_normalize( target ); + + v3_lerp( s->state.up_dir, target, + 8.0f * s->substep_delta, s->state.up_dir ); + } + else if( s->state.activity == k_skate_activity_air ) + { + v3_lerp( s->state.up_dir, player->rb.to_world[1], + 8.0f * s->substep_delta, s->state.up_dir ); + } + else + { + /* FIXME UNDEFINED! */ + vg_warn( "Undefined up target!\n" ); + + v3_lerp( s->state.up_dir, (v3f){0.0f,1.0f,0.0f}, + 12.0f * s->substep_delta, s->state.up_dir ); + } +} + +VG_STATIC int skate_point_visible( v3f origin, v3f target ) +{ + v3f dir; + v3_sub( target, origin, dir ); + + ray_hit ray; + ray.dist = v3_length( dir ); + v3_muls( dir, 1.0f/ray.dist, dir ); + ray.dist -= 0.025f; + + if( ray_world( origin, dir, &ray ) ) + return 0; + + return 1; +} + +VG_STATIC void skate_grind_orient( struct grind_info *inf, m3x3f mtx ) +{ + /* TODO: Is N and Dir really orthogonal? */ + v3_copy( inf->dir, mtx[0] ); + v3_copy( inf->n, mtx[1] ); + v3_cross( mtx[0], mtx[1], mtx[2] ); +} + +VG_STATIC void skate_grind_truck_apply( player_instance *player, + v3f grind_co, struct grind_info *inf, + float strength ) +{ + struct player_skate *s = &player->_skate; + + v3f delta; + v3_sub( inf->co, grind_co, delta ); + + m3x3f mtx, mtx_inv; + skate_grind_orient( inf, mtx ); + m3x3_transpose( mtx, mtx_inv ); + + /* decay 'force' */ + v3f v_grind; + m3x3_mulv( mtx_inv, player->rb.v, v_grind ); + + float decay = 1.0f - ( k_rb_delta * k_grind_decayxy * strength ); + v3_mul( v_grind, (v3f){ 1.0f, decay, decay }, v_grind ); + m3x3_mulv( mtx, v_grind, player->rb.v ); + + /* spring force */ + v3_muladds( player->rb.v, delta, k_spring_force*strength*k_rb_delta, + player->rb.v ); + + /* friction force */ + float a = 1.0f-fabsf( v3_dot( player->rb.to_world[2], inf->dir ) ), + dir = vg_signf( v3_dot( player->rb.v, inf->dir ) ), + F = a * -dir * k_grind_max_friction; + + v3_muladds( player->rb.v, inf->dir, F*k_rb_delta*strength, player->rb.v ); + + /* orientation */ + rb_effect_spring_target_vector( &player->rb, player->rb.to_world[1], + inf->n, + k_grind_spring, + k_grind_dampener, + k_rb_delta ); + vg_line_arrow( player->rb.co, inf->n, 1.0f, VG__GREEN ); + + /* TODO: This was a nice idea, but we should just apply steering to the + * target vector instead of this strange feedback loop thing! */ + v3f target_fwd; + m3x3_mulv( mtx, s->grind_vec, target_fwd ); + + rb_effect_spring_target_vector( &player->rb, player->rb.to_world[2], + target_fwd, + k_grind_spring*strength, + k_grind_dampener*strength, + k_rb_delta ); + + vg_line_arrow( player->rb.co, target_fwd, 1.0f, VG__YELOW ); + + v3f new_target; + m3x3_mulv( mtx_inv, player->rb.to_world[2], new_target ); + v3_lerp( s->grind_vec, new_target, k_rb_delta * 0.1f, s->grind_vec ); + + s->grind_strength = strength; + + /* Fake contact */ + struct grind_limit *limit = &s->limits[ s->limit_count ++ ]; + m4x3_mulv( player->rb.to_local, grind_co, limit->ra ); + m3x3_mulv( player->rb.to_local, inf->n, limit->n ); + limit->p = 0.0f; +} + +VG_STATIC int skate_grind_truck_singular( player_instance *player, float sign ) +{ + struct grind_info inf; + + v3f wheel_co = { 0.0f, 0.0f, sign * k_board_length }, + grind_co = { 0.0f, -k_board_radius, sign * k_board_length }; + + m4x3_mulv( player->rb.to_world, wheel_co, wheel_co ); + m4x3_mulv( player->rb.to_world, grind_co, grind_co ); + + /* Exit condition: lost grind tracking */ + if( !skate_grind_scansq( player, grind_co, player->rb.v, 0.3f, &inf ) ) + return 0; + + /* Exit condition: cant see grind target directly */ + if( !skate_point_visible( wheel_co, inf.co ) ) + return 0; + + /* Exit condition: minimum velocity not reached, but allow a bit of error */ + float dv = fabsf(v3_dot( player->rb.v, inf.dir )), + minv = k_grind_axel_min_vel*0.8f; + + if( dv < minv ) + return 0; + + float t = vg_clampf( (dv-minv)/(k_grind_axel_min_vel-minv), 0.0f, 1.0f ); + skate_grind_truck_apply( player, grind_co, &inf, t ); + return 1; +} + +VG_STATIC int skate_truck_entry_condition( player_instance *player, float sign ) +{ + struct player_skate *s = &player->_skate; + struct grind_info inf; + + /* TODO: Trash compactor this */ + v3f ra = { 0.0f, -k_board_radius, sign * k_board_length }; + + v3f raw, wsp; + m3x3_mulv( player->rb.to_world, ra, raw ); + v3_add( player->rb.co, raw, wsp ); + + if( skate_grind_scansq( player, + wsp, player->rb.v, 0.3, + &inf ) ) + { + if( fabsf(v3_dot( player->rb.v, inf.dir )) < k_grind_axel_min_vel ) + return 0; + + /* velocity should be at least 60% aligned */ + v3f pv, axis; + v3_cross( inf.n, inf.dir, axis ); + v3_muladds( player->rb.v, inf.n, -v3_dot( player->rb.v, inf.n ), pv ); + + if( v3_length2( pv ) < 0.0001f ) + return 0; + v3_normalize( pv ); + + if( fabsf(v3_dot( pv, inf.dir )) < k_grind_axel_max_angle ) + return 0; + + + v3f local_co, local_dir, local_n; + m4x3_mulv( player->rb.to_local, inf.co, local_co ); + m3x3_mulv( player->rb.to_local, inf.dir, local_dir ); + m3x3_mulv( player->rb.to_local, inf.n, local_n ); + + v2f delta = { local_co[0], local_co[2] - k_board_length*sign }; + + float truck_height = -(k_board_radius+0.03f); + + v3f rv; + v3_cross( player->rb.w, raw, rv ); + v3_add( player->rb.v, rv, rv ); + + if( (local_co[1] >= truck_height) && + (v2_length2( delta ) <= k_board_radius*k_board_radius) && + (v3_dot( rv, inf.n ) < 0.1f) ) + { + m3x3f mtx; + skate_grind_orient( &inf, mtx ); + m3x3_transpose( mtx, mtx ); + m3x3_mulv( mtx, player->rb.to_world[2], s->grind_vec ); + + skate_grind_truck_apply( player, wsp, &inf, 1.0f ); + return 1; + } + } + + return 0; +} + +VG_STATIC enum skate_activity skate_availible_grind( player_instance *player ) +{ + struct player_skate *s = &player->_skate; + + /* + * BOARDSLIDE + * ------------------------------------ + */ + + struct grind_info grind_center; + if( skate_grind_scansq( player, + player->rb.co, + player->rb.to_world[0], k_board_length, + &grind_center ) ) + { + v3f local_co, local_dir, local_n; + m4x3_mulv( player->rb.to_local, grind_center.co, local_co ); + m3x3_mulv( player->rb.to_local, grind_center.dir, local_dir ); + m3x3_mulv( player->rb.to_local, grind_center.n, local_n ); + + if( (fabsf(local_co[2]) <= k_board_length) && /* within wood area */ + (local_co[1] >= 0.0f) && /* at deck level */ + (fabsf(local_dir[0]) >= 0.5f) ) /* perpendicular to us */ + { + /* compute position on center line */ + + v3f intersection; + v3_muladds( local_co, local_dir, local_co[0]/-local_dir[0], + intersection ); + v3_copy( intersection, s->weight_distribution ); + + + /* TODO: alignment & strengths should be proportional to speed */ + /* dont apply correction in connecting velocities */ + /* friciton */ + + v3f ideal_v, diff; + v3_muls( grind_center.dir, + v3_dot( player->rb.v, grind_center.dir ), ideal_v ); + + v3_sub( ideal_v, player->rb.v, diff ); + v3_muladds( player->rb.v, diff, k_grind_aligment * k_rb_delta, + player->rb.v ); + + + /* direction alignment */ + v3f dir, perp; + v3_cross( local_dir, local_n, perp ); + v3_muls( local_dir, vg_signf(local_dir[0]), dir ); + v3_muls( perp, vg_signf(perp[2]), perp ); + + m3x3_mulv( player->rb.to_world, dir, dir ); + m3x3_mulv( player->rb.to_world, perp, perp ); + + rb_effect_spring_target_vector( &player->rb, player->rb.to_world[0], + dir, + k_grind_spring, k_grind_dampener, + k_rb_delta ); + + rb_effect_spring_target_vector( &player->rb, player->rb.to_world[2], + perp, + k_grind_spring, k_grind_dampener, + k_rb_delta ); + + vg_line_arrow( player->rb.co, dir, 0.5f, VG__GREEN ); + vg_line_arrow( player->rb.co, perp, 0.5f, VG__BLUE ); + + return k_skate_activity_grind_boardslide; + } + } -VG_STATIC void player__skate_post_update( player_instance *player ) -{ - struct player_skate *s = &player->_skate; - for( int i=0; iprediction_count; i++ ) + if( s->state.activity == k_skate_activity_grind_back50 ) { - struct land_prediction *p = &s->predictions[i]; - - for( int j=0; jlog_length - 1; j ++ ) - vg_line( p->log[j], p->log[j+1], p->colour ); + int result = skate_grind_truck_singular( player, 1.0f ), + front = skate_truck_entry_condition( player, -1.0f ); - vg_line_cross( p->log[p->log_length-1], p->colour, 0.25f ); + const enum skate_activity table[] = + { /* result | front */ + k_skate_activity_undefined, /* 0 0 */ + k_skate_activity_grind_front50, /* 0 1 */ + k_skate_activity_grind_back50, /* 1 0 */ + k_skate_activity_grind_5050 /* 1 1 */ + }; - v3f p1; - v3_add( p->log[p->log_length-1], p->n, p1 ); - vg_line( p->log[p->log_length-1], p1, 0xffffffff ); + return table[ result<<1 | front ]; + } + else if( s->state.activity == k_skate_activity_grind_front50 ) + { + int result = skate_grind_truck_singular( player, -1.0f ), + back = skate_truck_entry_condition( player, 1.0f ); - vg_line_pt3( p->apex, 0.02f, 0xffffffff ); + const enum skate_activity table[] = + { /* result | back */ + k_skate_activity_undefined, /* 0 0 */ + k_skate_activity_grind_back50, /* 0 1 */ + k_skate_activity_grind_front50, /* 1 0 */ + k_skate_activity_grind_5050 /* 1 1 */ + }; + + return table[ result<<1 | back ]; + } + else if( s->state.activity == k_skate_activity_grind_5050 ) + { + /* FIXME */ + return k_skate_activity_grind_back50; } + else + { + int front = skate_truck_entry_condition( player, -1.0f ), + back = skate_truck_entry_condition( player, 1.0f ); - vg_line_pt3( s->state.apex, 0.200f, 0xff0000ff ); - vg_line_pt3( s->state.apex, 0.201f, 0xff00ffff ); + const enum skate_activity table[] = + { /* front | back */ + k_skate_activity_undefined, /* 0 0 */ + k_skate_activity_grind_back50, /* 0 1 */ + k_skate_activity_grind_front50, /* 1 0 */ + k_skate_activity_grind_5050 /* 1 1 */ + }; + + return table[ front<<1 | back ]; + } + + return 0; +} + +VG_STATIC void skate_grind_boardslide( player_instance *player ) +{ + } VG_STATIC void player__skate_update( player_instance *player ) @@ -1479,13 +2177,13 @@ VG_STATIC void player__skate_update( player_instance *player ) { { { 0.0f, 0.0f, -k_board_length }, - .radius = 0.07f, + .radius = k_board_radius, .apply_angular = 1, .colour = VG__RED }, { { 0.0f, 0.0f, k_board_length }, - .radius = 0.07f, + .radius = k_board_radius, .apply_angular = 1, .colour = VG__GREEN }, @@ -1504,240 +2202,77 @@ VG_STATIC void player__skate_update( player_instance *player ) }; const int k_wheel_count = 2; -#if 0 - if( skate_grind_scansq( player, (v3f){ 0.0f, 0.0f, -k_board_length } ) ) - { -#if 0 - wheel_states[0] = 0; - wheel_states[1] = 0; -#endif - } - - if( skate_grind_scansq( player, (v3f){ 0.0f, 0.0f, k_board_length } ) ) - { -#if 0 - wheel_states[2] = 0; - wheel_states[3] = 0; -#endif - } -#endif s->substep = k_rb_delta; s->substep_delta = s->substep; - int substep_count = 0; - - + s->limit_count = 0; + int substep_count = 0; + v3_zero( s->surface_picture ); - /* - * Phase 2: Truck alignment (spring/dampener model) - * it uses the first two colliders as truck positions - * -------------------------------------------------------------------------- - */ + for( int i=0; irb.to_world, wheels[i].pos, truck ); - v3_muladds( truck, player->rb.to_world[0], -k_board_width, left ); - v3_muladds( truck, player->rb.to_world[0], k_board_width, right ); - - vg_line( left, right, wheels[i].colour ); - - v3_muladds( left, player->rb.to_world[1], 0.1f, left ); - v3_muladds( right, player->rb.to_world[1], 0.1f, right ); - - float k_max_truck_flex = VG_PIf * 0.25f; - - ray_hit ray_l, ray_r; - ray_l.dist = 0.2f; - ray_r.dist = 0.2f; - - v3f dir; - v3_muls( player->rb.to_world[1], -1.0f, dir ); - - int res_l = ray_world( left, dir, &ray_l ), - res_r = ray_world( right, dir, &ray_r ); - - /* ignore bad normals */ - if( res_l ) - { - if( v3_dot( ray_l.normal, player->rb.to_world[1] ) < 0.7071f ) - res_l = 0; - else - v3_add( ray_l.normal, surface_picture, surface_picture ); - } - - if( res_r ) - { - if( v3_dot( ray_r.normal, player->rb.to_world[1] ) < 0.7071f ) - res_r = 0; - else - v3_add( ray_r.normal, surface_picture, surface_picture ); - } - - v3f v0; - v3f midpoint; - v3_muladds( truck, player->rb.to_world[1], -wheels[i].radius, midpoint ); - - if( res_l || res_r ) - { - v3f p0, p1; - v3_copy( midpoint, p0 ); - v3_copy( midpoint, p1 ); - - if( res_l ) v3_copy( ray_l.pos, p0 ); - if( res_r ) v3_copy( ray_r.pos, p1 ); - - v3_sub( p1, p0, v0 ); - v3_normalize( v0 ); - } - else - { - /* fallback: use the closes point to the trucks */ - v3f closest; - int idx = bh_closest_point( world.geo_bh, midpoint, closest, 0.1f ); - - if( idx != -1 ) - { - u32 *tri = &world.scene_geo->arrindices[ idx * 3 ]; - v3f verts[3]; - - for( int j=0; j<3; j++ ) - v3_copy( world.scene_geo->arrvertices[ tri[j] ].co, verts[j] ); - - v3f vert0, vert1, n; - v3_sub( verts[1], verts[0], vert0 ); - v3_sub( verts[2], verts[0], vert1 ); - v3_cross( vert0, vert1, n ); - v3_normalize( n ); - - if( v3_dot( n, player->rb.to_world[1] ) < 0.3f ) - continue; - - v3_cross( n, player->rb.to_world[2], v0 ); - v3_muladds( v0, player->rb.to_world[2], - -v3_dot( player->rb.to_world[2], v0 ), v0 ); - v3_normalize( v0 ); - } - else - continue; - } - - v3_muladds( truck, v0, k_board_width, right ); - v3_muladds( truck, v0, -k_board_width, left ); - - vg_line( left, right, VG__WHITE ); - - rb_effect_spring_target_vector( &player->rb, player->rb.to_world[0], v0, - k_board_spring, k_board_dampener, - s->substep_delta ); + s->state.activity = grindable_activity; + goto grinding; } - /* - * Phase 2a: Manual alignment (spring/dampener model) - * -------------------------------------------------------------------------- - */ - - v3f weight, world_cog; - v3_zero( weight ); - - int reverse_dir = v3_dot( player->rb.to_world[2], player->rb.v ) < 0.0f?1:-1; - - if( s->state.manual_direction == 0 ) - { - if( (player->input_js1v->axis.value > 0.7f) && - (s->state.activity == k_skate_activity_ground) && - (s->state.jump_charge <= 0.01f) ) - s->state.manual_direction = reverse_dir; - } - else + int contact_count = 0; + for( int i=0; i<2; i++ ) { - if( player->input_js1v->axis.value < 0.1f ) - { - s->state.manual_direction = 0; - } - else + v3f normal, axel; + if( skate_compute_surface_alignment( player, wheels[i].pos, + wheels[i].colour, normal, axel ) ) { - if( reverse_dir != s->state.manual_direction ) - { - player__dead_transition( player ); - return; - } - } - } + rb_effect_spring_target_vector( &player->rb, player->rb.to_world[0], + axel, + k_board_spring, k_board_dampener, + s->substep_delta ); - if( s->state.manual_direction ) - { - float amt = vg_minf( player->input_js1v->axis.value * 8.0f, 1.0f ); - weight[2] = k_board_length * amt * (float)s->state.manual_direction; + v3_add( normal, s->surface_picture, s->surface_picture ); + contact_count ++; + } } - if( v3_length2( surface_picture ) > 0.001f ) + if( contact_count ) { - v3_normalize( surface_picture ); - - v3f target; - v3_copy( surface_picture, target ); + s->state.activity = k_skate_activity_ground; + v3_normalize( s->surface_picture ); - target[1] += 2.0f * surface_picture[1]; - v3_normalize( target ); - - v3_lerp( s->state.up_dir, target, - 8.0f * s->substep_delta, s->state.up_dir ); + skate_apply_friction_model( player ); + skate_weight_distribute( player ); + skate_apply_pump_model( player ); } else { - v3_lerp( s->state.up_dir, player->rb.to_world[1], - 8.0f * s->substep_delta, s->state.up_dir ); + s->state.activity = k_skate_activity_air; + skate_apply_air_model( player ); } +grinding:; - /* TODO: Fall back on land normal */ - /* TODO: Lerp weight distribution */ - /* TODO: Can start manual only if not charge jump */ - if( v3_length2( surface_picture ) > 0.001f && - v3_length2( weight ) > 0.001f && - s->state.manual_direction ) + if( s->state.activity == k_skate_activity_grind_back50 ) + wheels[1].state = k_collider_state_disabled; + if( s->state.activity == k_skate_activity_grind_front50 ) + wheels[0].state = k_collider_state_disabled; + if( s->state.activity == k_skate_activity_grind_5050 ) { - v3f plane_z; - - m3x3_mulv( player->rb.to_world, weight, plane_z ); - v3_negate( plane_z, plane_z ); - - v3_muladds( plane_z, surface_picture, - -v3_dot( plane_z, surface_picture ), plane_z ); - v3_normalize( plane_z ); - - v3_muladds( plane_z, surface_picture, 0.3f, plane_z ); - v3_normalize( plane_z ); - - v3f p1; - v3_muladds( player->rb.co, plane_z, 1.5f, p1 ); - vg_line( player->rb.co, p1, VG__GREEN ); - - v3f refdir; - v3_muls( player->rb.to_world[2], -(float)s->state.manual_direction, - refdir ); - - rb_effect_spring_target_vector( &player->rb, refdir, plane_z, - k_manul_spring, k_manul_dampener, - s->substep_delta ); + wheels[0].state = k_collider_state_disabled; + wheels[1].state = k_collider_state_disabled; } - - - - - - + /* all activities */ + skate_apply_steering_model( player ); + skate_adjust_up_direction( player ); + skate_apply_cog_model( player ); + skate_apply_jump_model( player ); + skate_apply_grab_model( player ); + skate_apply_trick_model( player ); begin_collision:; @@ -1751,9 +2286,6 @@ begin_collision:; m4x3_mulv( player->rb.to_world, s->state.head_position, head_wp0 ); v3_copy( player->rb.co, start_co ); - for( int i=0; isubstep ); } @@ -1833,8 +2365,8 @@ begin_collision:; * -------------------------------------------------------------------------- */ m4x3_mulv( player->rb.to_world, s->state.head_position, head_wp1 ); - vg_line( head_wp0, head_wp1, VG__RED ); +#if 0 float t; v3f n; if( (v3_dist2( head_wp0, head_wp1 ) > 0.001f) && @@ -1843,106 +2375,11 @@ begin_collision:; v3_lerp( start_co, player->rb.co, t, player->rb.co ); rb_update_transform( &player->rb ); -#if 0 player__dead_transition( player ); -#endif return; } - - /* - * Phase 2-1+0.5: Grind collision - * -------------------------------------------------------------------------- - */ - - for( int i=0; i<1; i++ ) - { - - /* - * Grind collision detection - * ------------------------------------------------ - */ - v3f grind_co, grind_n, grind_dir; - if( skate_grind_scansq( player, player->rb.co, - grind_co, grind_dir, grind_n ) ) - { -#if 0 - rb_ct *ct = &manifold[ manifold_len ++ ]; - - v3_copy( truck, ct->co ); - v3_copy( grind_n, ct->n ); - ct->p = vg_maxf( 0.0f, ct->co[1] - truck[1] ); -#endif - - v3f target_dir; - v3_cross( grind_dir, (v3f){0.0f,1.0f,0.0f}, target_dir ); - target_dir[1] = 0.0f; - - if( v3_length2( target_dir ) <= 0.001f ) - continue; - - if( fabsf(v3_dot( player->rb.v, grind_dir )) < 0.7071f ) - continue; - - v3_copy( grind_co, player->rb.co ); - - q_axis_angle( player->rb.q, (v3f){0.0f,1.0f,0.0f}, - -atan2f( target_dir[2], target_dir[0] ) ); - - wheels[0].state = k_collider_state_disabled; - wheels[1].state = k_collider_state_disabled; - v3_muls( grind_dir, v3_dot(player->rb.v,grind_dir), player->rb.v ); - v3_zero( player->rb.w ); - - rb_update_transform( &player->rb ); - - -#if 0 - v3f displacement, dir; - v3_sub( truck, grind_co, displacement ); - v3_copy( displacement, dir ); - v3_normalize( dir ); - - v3f rv, raW; - q_mulv( player->rb.q, wheels[i].pos, raW ); - - v3_cross( player->rb.w, raW, rv ); - v3_add( player->rb.v, rv, rv ); - - v3_muladds( rv, player->rb.to_world[2], - -v3_dot( rv, player->rb.to_world[2] ), rv ); - - v3f Fd, Fs, F; - v3_muls( displacement, -k_grind_spring, Fs ); - v3_muls( rv, -k_grind_dampener, Fd ); - - v3_add( Fd, Fs, F ); - v3_muls( F, s->substep_delta, F ); - - v3_add( player->rb.v, F, player->rb.v ); - v3f wa; - v3_cross( raW, F, wa ); - v3_add( player->rb.w, wa, player->rb.w ); - - rb_effect_spring_target_vector( &player->rb, player->rb.to_world[1], - grind_n, - k_board_spring, k_board_dampener, - s->substep_delta ); - - v3f adj; - v3_cross( grind_dir, (v3f){0.0f,1.0f,0.0f}, adj ); - rb_effect_spring_target_vector( &player->rb, player->rb.to_world[2], - adj, - k_grind_spring, k_grind_dampener, - s->substep_delta ); #endif - s->state.activity = k_skate_activity_grind; - } - else - s->state.activity = k_skate_activity_ground; - } - - /* * Phase 1: Regular collision detection * TODO: Me might want to automatically add contacts from CCD, @@ -1985,6 +2422,35 @@ begin_collision:; manifold_len += l; } + float grind_radius = k_board_radius * 0.75f; + rb_capsule capsule = { .height = (k_board_length+0.2f)*2.0f, + .radius=grind_radius }; + m4x3f mtx; + v3_muls( player->rb.to_world[0], 1.0f, mtx[0] ); + v3_muls( player->rb.to_world[2], -1.0f, mtx[1] ); + v3_muls( player->rb.to_world[1], 1.0f, mtx[2] ); + v3_muladds( player->rb.to_world[3], player->rb.to_world[1], + grind_radius + k_board_radius*0.25f, mtx[3] ); + + rb_ct *cman = &manifold[manifold_len]; + + int l = rb_capsule__scene( mtx, &capsule, NULL, &world.rb_geo.inf.scene, + cman ); + manifold_len += l; + + debug_capsule( mtx, capsule.radius, capsule.height, VG__WHITE ); + + /* add limits */ + for( int i=0; ilimit_count; i++ ) + { + struct grind_limit *limit = &s->limits[i]; + rb_ct *ct = &manifold[ manifold_len ++ ]; + m4x3_mulv( player->rb.to_world, limit->ra, ct->co ); + m3x3_mulv( player->rb.to_world, limit->n, ct->n ); + ct->p = limit->p; + ct->type = k_contact_type_default; + } + /* * Phase 3: Dynamics * -------------------------------------------------------------------------- @@ -2021,8 +2487,9 @@ begin_collision:; m3x3_mul( iI, player->rb.to_local, iIw ); m3x3_mul( player->rb.to_world, iIw, iIw ); - m4x3_mulv( player->rb.to_world, weight, world_cog ); - vg_line_pt3( world_cog, 0.1f, VG__BLACK ); + v3f world_cog; + m4x3_mulv( player->rb.to_world, s->weight_distribution, world_cog ); + vg_line_pt3( world_cog, 0.02f, VG__BLACK ); for( int j=0; j<10; j++ ) { @@ -2049,9 +2516,6 @@ begin_collision:; vn = v3_dot( rv, ct->n ), lambda = normal_mass * ( -vn ); - /* FIXME! */ - v3_muladds( player->rb.co, ct->n, ct->bias*0.02f, player->rb.co ); - float temp = ct->norm_impulse; ct->norm_impulse = vg_maxf( temp + lambda, 0.0f ); lambda = ct->norm_impulse - temp; @@ -2070,6 +2534,11 @@ begin_collision:; } } + v3f dt; + rb_depenetrate( manifold, manifold_len, dt ); + v3_add( dt, player->rb.co, player->rb.co ); + rb_update_transform( &player->rb ); + substep_count ++; if( s->substep >= 0.0001f ) @@ -2090,30 +2559,8 @@ begin_collision:; wheels[i].colour }[ wheels[i].state ]); } -#if 0 - skate_apply_grind_model( player, &manifold[manifold_len], grind_len ); -#endif - - skate_apply_interface_model( player, manifold, manifold_len ); - - skate_apply_pump_model( player ); - skate_apply_cog_model( player ); - - skate_apply_grab_model( player ); - skate_apply_friction_model( player ); - skate_apply_jump_model( player ); - skate_apply_air_model( player ); - skate_apply_trick_model( player ); - skate_integrate( player ); - - vg_line_pt3( s->state.cog, 0.1f, VG__WHITE ); - vg_line_pt3( s->state.cog, 0.11f, VG__WHITE ); - vg_line_pt3( s->state.cog, 0.12f, VG__WHITE ); - vg_line_pt3( s->state.cog, 0.13f, VG__WHITE ); - vg_line_pt3( s->state.cog, 0.14f, VG__WHITE ); - - vg_line( player->rb.co, s->state.cog, VG__RED ); + vg_line_pt3( s->state.cog, 0.02f, VG__WHITE ); teleport_gate *gate; if( (gate = world_intersect_gates( player->rb.co, s->state.prev_pos )) ) @@ -2151,11 +2598,21 @@ VG_STATIC void player__skate_im_gui( player_instance *player ) player->rb.w[1], player->rb.w[2] ); - player__debugtext( 1, "activity: %s", - (const char *[]){ "k_skate_activity_air", - "k_skate_activity_ground", - "k_skate_activity_grind }" } - [s->state.activity] ); + const char *activity_txt[] = + { + "air", + "ground", + "undefined (INVALID)", + "grind_any (INVALID)", + "grind_boardslide", + "grind_noseslide", + "grind_tailslide", + "grind_back50", + "grind_front50", + "grind_5050" + }; + + player__debugtext( 1, "activity: %s", activity_txt[s->state.activity] ); #if 0 player__debugtext( 1, "steer_s: %5.2f %5.2f [%.2f %.2f]", s->state.steerx_s, s->state.steery_s, @@ -2230,8 +2687,7 @@ VG_STATIC void player__skate_animate( player_instance *player, /* movement information */ { - int iair = (s->state.activity == k_skate_activity_air) || - (s->state.activity == k_skate_activity_grind ); + int iair = s->state.activity == k_skate_activity_air; float dirz = s->state.reverse > 0.0f? 0.0f: 1.0f, dirx = s->state.slip < 0.0f? 0.0f: 1.0f, @@ -2384,11 +2840,11 @@ VG_STATIC void player__skate_animate( player_instance *player, } v3f p1, p2; - m4x3_mulv( player->rb.to_world, up, p1 ); - m4x3_mulv( player->rb.to_world, ndir, p2 ); + m3x3_mulv( player->rb.to_world, up, p1 ); + m3x3_mulv( player->rb.to_world, ndir, p2 ); - vg_line( player->rb.co, p1, VG__PINK ); - vg_line( player->rb.co, p2, VG__CYAN ); + vg_line_arrow( player->rb.co, p1, 0.25f, VG__PINK ); + vg_line_arrow( player->rb.co, p2, 0.25f, VG__PINK ); } @@ -2397,23 +2853,10 @@ VG_STATIC void player__skate_animate( player_instance *player, *kf_foot_l = &dest->pose[av->id_ik_foot_l-1], *kf_foot_r = &dest->pose[av->id_ik_foot_r-1]; - v3f bo; - v3_muls( s->board_offset, add_grab_mod, bo ); - - v3_add( bo, kf_board->co, kf_board->co ); - v3_add( bo, kf_foot_l->co, kf_foot_l->co ); - v3_add( bo, kf_foot_r->co, kf_foot_r->co ); - -#if 0 - m3x3f c; - q_m3x3( s->board_rotation, c ); -#endif v4f qtotal; - v4f qtrickr, qyawr, qpitchr, qrollr; v3f eulerr; - v3_muls( s->board_trick_residuald, VG_TAUf, eulerr ); @@ -2423,26 +2866,12 @@ VG_STATIC void player__skate_animate( player_instance *player, q_axis_angle( qrollr, (v3f){0.0f,0.0f,1.0f}, eulerr[2] ); q_mul( qpitchr, qrollr, qtrickr ); - q_mul( qyawr, qtrickr, qtrickr ); - q_mul( s->board_rotation, qtrickr, qtotal ); + q_mul( qyawr, qtrickr, qtotal ); q_normalize( qtotal ); q_mul( qtotal, kf_board->q, kf_board->q ); - v3f d; - v3_sub( kf_foot_l->co, bo, d ); - q_mulv( qtotal, d, d ); - v3_add( bo, d, kf_foot_l->co ); - - v3_sub( kf_foot_r->co, bo, d ); - q_mulv( qtotal, d, d ); - v3_add( bo, d, kf_foot_r->co ); - - q_mul( s->board_rotation, kf_board->q, kf_board->q ); - q_normalize( kf_board->q ); - - /* trick rotation */ v4f qtrick, qyaw, qpitch, qroll; v3f euler; diff --git a/player_skate.h b/player_skate.h index 11c66e9..e3f639a 100644 --- a/player_skate.h +++ b/player_skate.h @@ -13,7 +13,14 @@ struct player_skate { k_skate_activity_air, k_skate_activity_ground, - k_skate_activity_grind + k_skate_activity_undefined, + k_skate_activity_grind_any, + k_skate_activity_grind_boardslide, + k_skate_activity_grind_noseslide, + k_skate_activity_grind_tailslide, + k_skate_activity_grind_back50, + k_skate_activity_grind_front50, + k_skate_activity_grind_5050 } activity, activity_prev; @@ -63,6 +70,36 @@ struct player_skate state, state_gate_storage; + + /* animation */ + struct skeleton_anim *anim_stand, *anim_highg, *anim_slide, + *anim_air, + *anim_push, *anim_push_reverse, + *anim_ollie, *anim_ollie_reverse, + *anim_grabs, *anim_stop; + v3f + board_trick_residualv, + board_trick_residuald; + + float blend_slide, + blend_z, + blend_x, + blend_fly, + blend_stand, + blend_push, + blend_jump, + blend_airdir; + + v2f wobble; + + /* + * Physics + * ---------------------------------------------------- + */ + + float substep, + substep_delta; + struct land_prediction { v3f log[50]; @@ -87,33 +124,19 @@ struct player_skate float land_dist; v3f land_normal; - /* animation */ - struct skeleton_anim *anim_stand, *anim_highg, *anim_slide, - *anim_air, - *anim_push, *anim_push_reverse, - *anim_ollie, *anim_ollie_reverse, - *anim_grabs, *anim_stop; - v3f board_offset, - board_trick_residualv, - board_trick_residuald; - v4f board_rotation; - - float blend_slide, - blend_z, - blend_x, - blend_fly, - blend_stand, - blend_push, - blend_jump, - blend_airdir; - - float substep, - substep_delta; + v3f surface_picture, + weight_distribution, + grind_vec; - v2f wobble; + float grind_strength; - float debug_normal_pressure; - u32 device_id_walk; + struct grind_limit + { + v3f ra, n; + float p; + } + limits[3]; + u32 limit_count; }; VG_STATIC void player__skate_bind ( player_instance *player ); diff --git a/player_walk.c b/player_walk.c index d916c26..d5f4aa0 100644 --- a/player_walk.c +++ b/player_walk.c @@ -464,20 +464,7 @@ VG_STATIC void player__walk_update( player_instance *player ) * Depenetrate */ v3f dt; - v3_zero( dt ); - for( int j=0; j<8; j++ ) - { - for( int i=0; in, dt ), - remaining = (ct->p-k_penetration_slop) - resolved_amt, - apply = vg_maxf( remaining, 0.0f ) * 0.3f; - - v3_muladds( dt, ct->n, apply, dt ); - } - } + rb_depenetrate( manifold, len, dt ); v3_add( dt, player->rb.co, player->rb.co ); /* TODO: Stepping...... diff --git a/rigidbody.h b/rigidbody.h index 5ca3e1e..746c684 100644 --- a/rigidbody.h +++ b/rigidbody.h @@ -214,13 +214,19 @@ VG_STATIC void rb_tangent_basis( v3f n, v3f tx, v3f ty ) VG_STATIC void rb_debug_contact( rb_ct *ct ) { - if( ct->type != k_contact_type_disabled ) + v3f p1; + v3_muladds( ct->co, ct->n, 0.05f, p1 ); + + if( ct->type == k_contact_type_default ) { - v3f p1; - v3_muladds( ct->co, ct->n, 0.05f, p1 ); vg_line_pt3( ct->co, 0.0125f, 0xff0000ff ); vg_line( ct->co, p1, 0xffffffff ); } + else if( ct->type == k_contact_type_edge ) + { + vg_line_pt3( ct->co, 0.0125f, 0xff00ffc0 ); + vg_line( ct->co, p1, 0xffffffff ); + } } VG_STATIC void debug_sphere( m4x3f m, float radius, u32 colour ) @@ -2004,6 +2010,26 @@ VG_STATIC void rb_prepare_contact( rb_ct *ct, float timestep ) ct->tangent_impulse[1] = 0.0f; } +/* calculate total move. manifold should belong to ONE object only */ +VG_STATIC void rb_depenetrate( rb_ct *manifold, int len, v3f dt ) +{ + v3_zero( dt ); + + for( int j=0; j<7; j++ ) + { + for( int i=0; in, dt ), + remaining = (ct->p-k_penetration_slop) - resolved_amt, + apply = vg_maxf( remaining, 0.0f ) * 0.4f; + + v3_muladds( dt, ct->n, apply, dt ); + } + } +} + /* * Initializing things like tangent vectors */