From 0a5ec40708e7d128511cac04f84d85055e6fc164 Mon Sep 17 00:00:00 2001 From: hgn Date: Wed, 15 Feb 2023 01:54:44 +0000 Subject: [PATCH] better? grinds --- common.h | 19 +- maps_src/mp_gridmap.mdl | Bin 1434112 -> 1405760 bytes player.c | 3 + player_skate.c | 1605 +++++++++++++++++++++++++-------------- player_skate.h | 75 +- player_walk.c | 15 +- rigidbody.h | 32 +- 7 files changed, 1114 insertions(+), 635 deletions(-) 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 d7ae80c1c7518d383b5dab42b86cfcba300255ed..9240f938b2f42323f9c15315b7a437a8a8b1788f 100644 GIT binary patch delta 21632 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 aD6Fojfz8P_d0hs&!FOX#mx1nw7ylnR|F4_? delta 50305 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 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 */ -- 2.25.1