From 2ca677a0ec9d00db46a8b97bec30dbea8280a79b Mon Sep 17 00:00:00 2001 From: hgn Date: Tue, 4 Oct 2022 04:42:12 +0100 Subject: [PATCH] refactor player --- main.c | 8 +- player.h | 1397 ++++---------------------------------------- player_animation.h | 314 ++++++++++ player_audio.h | 156 +++++ player_model.h | 310 +++------- player_physics.h | 799 +++++++++++++++++++++++++ player_ragdoll.h | 215 +++++++ skeleton.h | 1 + 8 files changed, 1657 insertions(+), 1543 deletions(-) create mode 100644 player_animation.h create mode 100644 player_audio.h create mode 100644 player_physics.h create mode 100644 player_ragdoll.h diff --git a/main.c b/main.c index 1b10625..0732057 100644 --- a/main.c +++ b/main.c @@ -63,8 +63,8 @@ void vg_register(void) shader_vblend_register(); shader_unlit_register(); + player_register(); world_register(); - character_register(); water_register(); gate_register(); } @@ -75,7 +75,6 @@ static void init_other(void) render_init(); gate_init(); world_init(); - character_init(); audio_init(); } @@ -102,7 +101,7 @@ static int playermodel( int argc, char const *argv[] ) glmesh old_mesh = player.mdl.mesh; - if( character_load( &player.mdl, argv[0] ) ) + if( player_load_model( argv[0] ) ) mesh_free( &old_mesh ); return 1; @@ -233,8 +232,7 @@ void vg_start(void) if( sv_scene == 0 ) { - character_load( &player.mdl, "ch_new" ); - character_init_ragdoll( &player.mdl ); + player_load_model( "ch_new" ); world_load(); diff --git a/player.h b/player.h index 6fb74dc..92db1fb 100644 --- a/player.h +++ b/player.h @@ -1,5 +1,10 @@ /* * Copyright 2021-2022 (C) Mount0 Software, Harry Godden - All Rights Reserved + * ----------------------------------------------------------------------------- + * + * Player head module + * + * ----------------------------------------------------------------------------- */ #ifndef PLAYER_H @@ -8,14 +13,9 @@ #include "audio.h" #include "common.h" #include "world.h" -//#include "character.h" -#include "player_model.h" +#include "skeleton.h" #include "bvh.h" -/* - * Constants - */ - static float k_walkspeed = 7.0f, /* no longer used */ k_runspeed = 14.0f, @@ -43,6 +43,12 @@ static int freecam = 0; static int walk_grid_iterations = 1; static float fc_speed = 10.0f; +/* + * ----------------------------------------------------------------------------- + * Memory + * ----------------------------------------------------------------------------- + */ + static struct gplayer { /* Physics */ @@ -73,8 +79,6 @@ static struct gplayer u32 land_target_colours[22]; int land_log_count; m3x3f vr,vr_pstep; - - struct character mdl; v3f handl_target, handr_target, handl, handr; @@ -97,9 +101,48 @@ static struct gplayer fsetup, walk_timer, fonboard; - -v3f last_step_pos; int step_phase; + + /* player model */ + struct player_model + { + glmesh mesh; + struct skeleton sk; + 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, + *anim_walk, *anim_run, *anim_idle; + + u32 id_hip, + id_ik_hand_l, + id_ik_hand_r, + id_ik_elbow_l, + id_ik_elbow_r, + id_head; + + v3f cam_pos; + + struct ragdoll_part + { + u32 bone_id; + v3f offset; + + u32 use_limits; + v3f limits[2]; + + rigidbody rb; + u32 parent; + } + *ragdoll; + u32 ragdoll_count; + + int shoes[2]; + } + mdl; } player = { @@ -110,1303 +153,44 @@ player = }; /* - * Player API - */ - -static float *player_get_pos(void) -{ - return player.rb.co; -} - - -/* - * Free camera movement + * API */ +static float *player_get_pos(void); +static void player_kill(void); +static float *player_cam_pos(void); -static void player_mouseview(void) -{ - if( gui_want_mouse() ) - return; - - static v2f mouse_last, - view_vel = { 0.0f, 0.0f }; - - if( vg_get_button_down( "primary" ) ) - v2_copy( vg_mouse, mouse_last ); - - else if( vg_get_button( "primary" ) ) - { - v2f delta; - v2_sub( vg_mouse, mouse_last, delta ); - v2_copy( vg_mouse, mouse_last ); - - v2_muladds( view_vel, delta, 0.001f, view_vel ); - } - - v2_muladds( view_vel, - (v2f){ vg_get_axis("h1"), vg_get_axis("v1") }, - 0.05f, view_vel ); - v2_muls( view_vel, 0.93f, view_vel ); - v2_add( view_vel, player.angles, player.angles ); - player.angles[1] = vg_clampf( player.angles[1], -VG_PIf*0.5f, VG_PIf*0.5f ); -} - -static void player_freecam(void) -{ - player_mouseview(); - - float movespeed = fc_speed; - v3f lookdir = { 0.0f, 0.0f, -1.0f }, - sidedir = { 1.0f, 0.0f, 0.0f }; - - m3x3_mulv( player.camera, lookdir, lookdir ); - m3x3_mulv( player.camera, sidedir, sidedir ); - - static v3f move_vel = { 0.0f, 0.0f, 0.0f }; - if( vg_get_button( "forward" ) ) - v3_muladds( move_vel, lookdir, ktimestep * movespeed, move_vel ); - if( vg_get_button( "back" ) ) - v3_muladds( move_vel, lookdir, ktimestep *-movespeed, move_vel ); - if( vg_get_button( "left" ) ) - v3_muladds( move_vel, sidedir, ktimestep *-movespeed, move_vel ); - if( vg_get_button( "right" ) ) - v3_muladds( move_vel, sidedir, ktimestep * movespeed, move_vel ); - - v3_muls( move_vel, 0.7f, move_vel ); - v3_add( move_vel, player.camera_pos, player.camera_pos ); -} - -/* - * Player Physics Implementation +/* + * Submodules */ +#include "player_physics.h" +#include "player_ragdoll.h" +#include "player_model.h" +#include "player_animation.h" +#include "player_audio.h" -static void apply_gravity( v3f vel, float const timestep ) -{ - v3f gravity = { 0.0f, -9.6f, 0.0f }; - v3_muladds( vel, gravity, timestep, vel ); -} - -/* - * TODO: The angle bias should become greater when launching from a steeper - * angle and skewed towords more 'downwards' angles when launching from - * shallower trajectories - * - * it should also be tweaked by the controller left stick being pushed - * up or down +/* + * ----------------------------------------------------------------------------- + * Events + * ----------------------------------------------------------------------------- */ -static void player_start_air(void) -{ - if( player.in_air ) - return; - - player.in_air = 1; - - float pstep = ktimestep*10.0f; - float best_velocity_delta = -9999.9f; - float k_bias = 0.96f; - v3f axis; - v3_cross( player.rb.up, player.rb.v, axis ); - v3_normalize( axis ); - player.land_log_count = 0; - - m3x3_identity( player.vr ); - - for( int m=-3;m<=12; m++ ) - { - float vmod = ((float)m / 15.0f)*0.09f; - - v3f pco, pco1, pv; - v3_copy( player.rb.co, pco ); - v3_muls( player.rb.v, k_bias, pv ); - - /* - * Try different 'rotations' of the velocity to find the best possible - * landing normal. This conserves magnitude at the expense of slightly - * unrealistic results - */ - - m3x3f vr; - v4f vr_q; - - q_axis_angle( vr_q, axis, vmod ); - q_m3x3( vr_q, vr ); - - m3x3_mulv( vr, pv, pv ); - v3_muladds( pco, pv, pstep, pco ); - - for( int i=0; i<50; i++ ) - { - v3_copy( pco, pco1 ); - apply_gravity( pv, pstep ); - - m3x3_mulv( vr, pv, pv ); - v3_muladds( pco, pv, pstep, pco ); - - ray_hit contact; - v3f vdir; - - v3_sub( pco, pco1, vdir ); - contact.dist = v3_length( vdir ); - v3_divs( vdir, contact.dist, vdir); - - if( ray_world( pco1, vdir, &contact )) - { - float land_delta = v3_dot( pv, contact.normal ); - u32 scolour = (u8)(vg_minf(-land_delta * 2.0f, 255.0f)); - - /* Bias prediction towords ramps */ - if( ray_hit_is_ramp( &contact ) ) - { - land_delta *= 0.1f; - scolour |= 0x0000a000; - } - - if( (land_delta < 0.0f) && (land_delta > best_velocity_delta) ) - { - best_velocity_delta = land_delta; - - v3_copy( contact.pos, player.land_target ); - - m3x3_copy( vr, player.vr_pstep ); - q_axis_angle( vr_q, axis, vmod*0.1f ); - q_m3x3( vr_q, player.vr ); - } - - v3_copy( contact.pos, - player.land_target_log[player.land_log_count] ); - player.land_target_colours[player.land_log_count] = - 0xff000000 | scolour; - - player.land_log_count ++; - - break; - } - } - } -} - -static void draw_cross(v3f pos,u32 colour, float scale) +static void player_register(void) /* 0 */ { - v3f p0, p1; - v3_add( (v3f){ scale,0.0f,0.0f}, pos, p0 ); - v3_add( (v3f){-scale,0.0f,0.0f}, pos, p1 ); - vg_line( p0, p1, colour ); - v3_add( (v3f){0.0f, scale,0.0f}, pos, p0 ); - v3_add( (v3f){0.0f,-scale,0.0f}, pos, p1 ); - vg_line( p0, p1, colour ); - v3_add( (v3f){0.0f,0.0f, scale}, pos, p0 ); - v3_add( (v3f){0.0f,0.0f,-scale}, pos, p1 ); - vg_line( p0, p1, colour ); -} - -static void player_physics_control(void) -{ - /* - * Computing localized friction forces for controlling the character - * Friction across X is significantly more than Z - */ - - v3f vel; - m3x3_mulv( player.rb.to_local, player.rb.v, vel ); - float slip = 0.0f; - - if( fabsf(vel[2]) > 0.01f ) - slip = fabsf(-vel[0] / vel[2]) * vg_signf(vel[0]); - - if( fabsf( slip ) > 1.2f ) - slip = vg_signf( slip ) * 1.2f; - player.slip = slip; - player.reverse = -vg_signf(vel[2]); - - float substep = ktimestep * 0.2f; - float fwd_resistance = (vg_get_button( "break" )? 5.0f: 0.02f) * -substep; - - for( int i=0; i<5; i++ ) - { - vel[2] = stable_force( vel[2], vg_signf( vel[2] ) * fwd_resistance ); - vel[0] = stable_force( vel[0], - vg_signf( vel[0] ) * -k_friction_lat*substep ); - } - - static double start_push = 0.0; - if( vg_get_button_down( "push" ) ) - start_push = vg_time; - - if( vg_get_button( "jump" ) ) - { - player.jump += ktimestep * k_jump_charge_speed; - - if( !player.jump_charge ) - player.jump_dir = player.reverse > 0.0f? 1: 0; - - player.jump_charge = 1; - } - - if( !vg_get_button("break") && vg_get_button( "push" ) ) - { - player.pushing = 1.0f; - player.push_time = vg_time-start_push; - - float cycle_time = player.push_time*k_push_cycle_rate, - amt = k_push_accel * (sinf(cycle_time)*0.5f+0.5f)*ktimestep, - current = v3_length( vel ), - new_vel = vg_minf( current + amt, k_max_push_speed ); - - new_vel -= vg_minf(current, k_max_push_speed); - vel[2] -= new_vel * player.reverse; - } - - /* Pumping */ - static float previous = 0.0f; - float delta = previous - player.grab, - pump = delta * k_pump_force*ktimestep; - previous = player.grab; - - v3f p1; - v3_muladds( player.rb.co, player.rb.up, pump, p1 ); - vg_line( player.rb.co, p1, 0xff0000ff ); - - vel[1] += pump; - - - m3x3_mulv( player.rb.to_world, vel, player.rb.v ); - - float steer = vg_get_axis( "horizontal" ); - player.iY -= vg_signf(steer)*powf(steer,2.0f) * k_steer_ground * ktimestep; - - v2_lerp( player.board_xy, (v2f){ slip*0.25f, 0.0f }, - ktimestep*5.0f, player.board_xy); + player_model_register(); } -static void player_physics_control_air(void) +static void player_init(void) /* 1 */ { - m3x3_mulv( player.vr, player.rb.v, player.rb.v ); - draw_cross( player.land_target, 0xff0000ff, 0.25f ); - - ray_hit hit; - - /* - * Prediction - */ - float pstep = ktimestep*10.0f; - - v3f pco, pco1, pv; - v3_copy( player.rb.co, pco ); - v3_copy( player.rb.v, pv ); - - float time_to_impact = 0.0f; - float limiter = 1.0f; - - for( int i=0; i<50; i++ ) - { - v3_copy( pco, pco1 ); - m3x3_mulv( player.vr_pstep, pv, pv ); - apply_gravity( pv, pstep ); - v3_muladds( pco, pv, pstep, pco ); - - //vg_line( pco, pco1, i&0x1?0xff000000:0xffffffff ); - - ray_hit contact; - v3f vdir; - - v3_sub( pco, pco1, vdir ); - contact.dist = v3_length( vdir ); - v3_divs( vdir, contact.dist, vdir); - - float orig_dist = contact.dist; - if( ray_world( pco1, vdir, &contact )) - { - float angle = v3_dot( player.rb.up, contact.normal ); - v3f axis; - v3_cross( player.rb.up, contact.normal, axis ); - - time_to_impact += (contact.dist/orig_dist)*pstep; - limiter = vg_minf( 5.0f, time_to_impact )/5.0f; - limiter = 1.0f-limiter; - limiter *= limiter; - limiter = 1.0f-limiter; - - if( angle < 0.99f ) - { - v4f correction; - q_axis_angle( correction, axis, acosf(angle)*0.05f*(1.0f-limiter) ); - q_mul( correction, player.rb.q, player.rb.q ); - } - - draw_cross( contact.pos, 0xffff0000, 0.25f ); - break; - } - time_to_impact += pstep; - } - - player.iY -= vg_get_axis( "horizontal" ) * k_steer_air * ktimestep; - { - float iX = vg_get_axis( "vertical" ) * - player.reverse * k_steer_air * limiter * ktimestep; - - static float siX = 0.0f; - siX = vg_lerpf( siX, iX, k_steer_air_lerp ); - - v4f rotate; - q_axis_angle( rotate, player.rb.right, siX ); - q_mul( rotate, player.rb.q, player.rb.q ); - } - - v2f target = {0.0f,0.0f}; - v2_muladds( target, (v2f){ vg_get_axis("h1"), vg_get_axis("v1") }, - player.grab, target ); - v2_lerp( player.board_xy, target, ktimestep*3.0f, player.board_xy ); -} + player_model_init(); -static void player_init(void) -{ rb_init( &player.collide_front ); rb_init( &player.collide_back ); } -static void player_walk_physics(void) -{ - rigidbody *rbf = &player.collide_front, - *rbb = &player.collide_back; - - m3x3_copy( player.rb.to_world, player.collide_front.to_world ); - m3x3_copy( player.rb.to_world, player.collide_back.to_world ); - - float h0 = 0.3f, - h1 = 0.9f; - - m4x3_mulv( player.rb.to_world, (v3f){0.0f,h0,0.0f}, rbf->co ); - v3_copy( rbf->co, rbf->to_world[3] ); - m4x3_mulv( player.rb.to_world, (v3f){0.0f,h1,0.0f}, rbb->co ); - v3_copy( rbb->co, rbb->to_world[3] ); - - m4x3_invert_affine( rbf->to_world, rbf->to_local ); - m4x3_invert_affine( rbb->to_world, rbb->to_local ); - - rb_update_bounds( rbf ); - rb_update_bounds( rbb ); - - rb_debug( rbf, 0xff0000ff ); - rb_debug( rbb, 0xff0000ff ); - - rb_ct manifold[64]; - int len = 0; - - len += rb_sphere_scene( rbf, &world.rb_geo, manifold+len ); - len += rb_sphere_scene( rbb, &world.rb_geo, manifold+len ); - - rb_presolve_contacts( manifold, len ); - - for( int j=0; j<5; j++ ) - { - for( int i=0; in ); - vn += ct->bias; - - float temp = ct->norm_impulse; - ct->norm_impulse = vg_maxf( temp + vn, 0.0f ); - vn = ct->norm_impulse - temp; - - v3f impulse; - v3_muls( ct->n, vn, impulse ); - - v3_add( impulse, player.rb.v, player.rb.v ); - - /* friction */ - for( int j=0; j<2; j++ ) - { - float f = k_friction * ct->norm_impulse, - vt = v3_dot( player.rb.v, ct->t[j] ), - lambda = -vt; - - float temp = ct->tangent_impulse[j]; - ct->tangent_impulse[j] = vg_clampf( temp + lambda, -f, f ); - lambda = ct->tangent_impulse[j] - temp; - - v3_muladds( player.rb.v, ct->t[j], lambda, player.rb.v ); - } - } - } - - player.in_air = len==0?1:0; - - v3_zero( player.rb.w ); - q_axis_angle( player.rb.q, (v3f){0.0f,1.0f,0.0f}, -player.angles[0] ); - - v3f forward_dir = { sinf(player.angles[0]),0.0f,-cosf(player.angles[0]) }; - - v3f p1; - v3_muladds( player.rb.co, forward_dir, 2.0f, p1 ); - vg_line( player.rb.co, p1, 0xff0000ff ); - - float move_dead = 0.1f, - move = vg_get_axis("grabr")*0.5f + 0.5f - move_dead; - - if( move > 0.0f ) - { - float move_norm = move * (1.0f/(1.0f-move_dead)), - speed = vg_lerpf( 0.1f*k_runspeed, k_runspeed, move_norm ), - amt = k_walk_accel * ktimestep, - zvel = v3_dot( player.rb.v, forward_dir ), - new_vel = vg_minf( zvel + amt, speed ), - diff = new_vel - vg_minf( zvel, speed ); - - v3_muladds( player.rb.v, forward_dir, diff, player.rb.v ); - - /* TODO move */ - float walk_norm = 30.0f/(float)player.mdl.anim_walk->length, - run_norm = 30.0f/(float)player.mdl.anim_run->length ; - - player.walk_timer += ktimestep * vg_lerpf( walk_norm,run_norm,move_norm ); - } - else - { - player.walk_timer = 0.0f; - } - - player.rb.v[0] *= 1.0f - (ktimestep*k_walk_friction); - player.rb.v[2] *= 1.0f - (ktimestep*k_walk_friction); -} - -static void player_physics(void) -{ - /* - * Update collision fronts - */ - - rigidbody *rbf = &player.collide_front, - *rbb = &player.collide_back; - - m3x3_copy( player.rb.to_world, player.collide_front.to_world ); - m3x3_copy( player.rb.to_world, player.collide_back.to_world ); - - player.air_blend = vg_lerpf( player.air_blend, player.in_air, 0.1f ); - float h = player.air_blend*0.2f; - - m4x3_mulv( player.rb.to_world, (v3f){0.0f,h,-k_board_length}, rbf->co ); - v3_copy( rbf->co, rbf->to_world[3] ); - m4x3_mulv( player.rb.to_world, (v3f){0.0f,h, k_board_length}, rbb->co ); - v3_copy( rbb->co, rbb->to_world[3] ); - - m4x3_invert_affine( rbf->to_world, rbf->to_local ); - m4x3_invert_affine( rbb->to_world, rbb->to_local ); - - rb_update_bounds( rbf ); - rb_update_bounds( rbb ); - - rb_debug( rbf, 0xff00ffff ); - rb_debug( rbb, 0xffffff00 ); - - rb_ct manifold[64]; - int len = 0; - - len += rb_sphere_scene( rbf, &world.rb_geo, manifold+len ); - len += rb_sphere_scene( rbb, &world.rb_geo, manifold+len ); - - rb_presolve_contacts( manifold, len ); - v3f surface_avg = {0.0f, 0.0f, 0.0f}; - - if( !len ) - { - player_start_air(); - } - else - { - for( int i=0; i 0.5f ) - { - player_start_air(); - } - else - player.in_air = 0; - } - - for( int j=0; j<5; j++ ) - { - for( int i=0; ico, player.rb.co, delta ); - v3_cross( player.rb.w, delta, dv ); - v3_add( player.rb.v, dv, dv ); - - float vn = -v3_dot( dv, ct->n ); - vn += ct->bias; - - float temp = ct->norm_impulse; - ct->norm_impulse = vg_maxf( temp + vn, 0.0f ); - vn = ct->norm_impulse - temp; - - v3f impulse; - v3_muls( ct->n, vn, impulse ); - - if( fabsf(v3_dot( impulse, player.rb.forward )) > 10.0f || - fabsf(v3_dot( impulse, player.rb.up )) > 50.0f ) - { - player.is_dead = 1; - character_ragdoll_copypose( &player.mdl, player.rb.v ); - return; - } - - v3_add( impulse, player.rb.v, player.rb.v ); - v3_cross( delta, impulse, impulse ); - - /* - * W Impulses are limited to the Y and X axises, we don't really want - * roll angular velocities being included. - * - * Can also tweak the resistance of each axis here by scaling the wx,wy - * components. - */ - - float wy = v3_dot( player.rb.up, impulse ), - wx = v3_dot( player.rb.right, impulse )*1.5f; - - v3_muladds( player.rb.w, player.rb.up, wy, player.rb.w ); - v3_muladds( player.rb.w, player.rb.right, wx, player.rb.w ); - } - } - - float grabt = vg_get_axis( "grabr" )*0.5f+0.5f; - player.grab = vg_lerpf( player.grab, grabt, 0.14f ); - player.pushing = 0.0f; - - if( !player.in_air ) - { - v3f axis; - float angle = v3_dot( player.rb.up, surface_avg ); - v3_cross( player.rb.up, surface_avg, axis ); - - //float cz = v3_dot( player.rb.forward, axis ); - //v3_muls( player.rb.forward, cz, axis ); - - if( angle < 0.999f ) - { - v4f correction; - q_axis_angle( correction, axis, acosf(angle)*0.3f ); - q_mul( correction, player.rb.q, player.rb.q ); - } - - v3_muladds( player.rb.v, player.rb.up, - -k_downforce*ktimestep, player.rb.v ); - - player_physics_control(); - - if( !player.jump_charge && player.jump > 0.2f ) - { - v3f jumpdir; - - /* Launch more up if alignment is up else improve velocity */ - float aup = fabsf(v3_dot( (v3f){0.0f,1.0f,0.0f}, player.rb.up )), - mod = 0.5f, - dir = mod + aup*(1.0f-mod); - - v3_copy( player.rb.v, jumpdir ); - v3_normalize( jumpdir ); - v3_muls( jumpdir, 1.0f-dir, jumpdir ); - v3_muladds( jumpdir, player.rb.up, dir, jumpdir ); - v3_normalize( jumpdir ); - - float force = k_jump_force*player.jump; - v3_muladds( player.rb.v, jumpdir, force, player.rb.v ); - player.jump = 0.0f; - - player.jump_time = vg_time; - - audio_lock(); - audio_player_set_flags( &audio_player_extra, AUDIO_FLAG_SPACIAL_3D ); - audio_player_set_position( &audio_player_extra, player.rb.co ); - audio_player_set_vol( &audio_player_extra, 20.0f ); - audio_player_playclip( &audio_player_extra, &audio_jumps[rand()%4] ); - audio_unlock(); - } - } - else - { - player_physics_control_air(); - } - - if( !player.jump_charge ) - { - player.jump -= k_jump_charge_speed * ktimestep; - } - player.jump_charge = 0; - player.jump = vg_clampf( player.jump, 0.0f, 1.0f ); -} - -static void player_do_motion(void) -{ - float horizontal = vg_get_axis("horizontal"), - vertical = vg_get_axis("vertical"); - - if( player.on_board ) - player_physics(); - else - player_walk_physics(); - - /* Integrate velocity */ - v3f prevco; - v3_copy( player.rb.co, prevco ); - - apply_gravity( player.rb.v, ktimestep ); - v3_muladds( player.rb.co, player.rb.v, ktimestep, player.rb.co ); - - /* Real angular velocity integration */ - v3_lerp( player.rb.w, (v3f){0.0f,0.0f,0.0f}, 0.125f, player.rb.w ); - if( v3_length2( player.rb.w ) > 0.0f ) - { - v4f rotation; - v3f axis; - v3_copy( player.rb.w, axis ); - - float mag = v3_length( axis ); - v3_divs( axis, mag, axis ); - q_axis_angle( rotation, axis, mag*k_rb_delta ); - q_mul( rotation, player.rb.q, player.rb.q ); - } - - /* Faux angular velocity */ - v4f rotate; - - static float siY = 0.0f; - float lerpq = player.in_air? 0.04f: 0.3f; - siY = vg_lerpf( siY, player.iY, lerpq ); - - q_axis_angle( rotate, player.rb.up, siY ); - q_mul( rotate, player.rb.q, player.rb.q ); - player.iY = 0.0f; - - /* - * Gate intersection, by tracing a line over the gate planes - */ - for( int i=0; igate; - - if( gate_intersect( gate, player.rb.co, prevco ) ) - { - m4x3_mulv( gate->transport, player.rb.co, player.rb.co ); - m3x3_mulv( gate->transport, player.rb.v, player.rb.v ); - m3x3_mulv( gate->transport, player.vl, player.vl ); - m3x3_mulv( gate->transport, player.v_last, player.v_last ); - m3x3_mulv( gate->transport, player.m, player.m ); - m3x3_mulv( gate->transport, player.bob, player.bob ); - - v4f transport_rotation; - m3x3_q( gate->transport, transport_rotation ); - q_mul( transport_rotation, player.rb.q, player.rb.q ); - - world_routes_activate_gate( i ); - player.rb_gate_frame = player.rb; - player.in_air_frame = player.in_air; - player.on_board_frame = player.on_board; - - if( !player.on_board ) - { - v3f fwd_dir = {cosf(player.angles[0]), - 0.0f, - sinf(player.angles[0])}; - m3x3_mulv( gate->transport, fwd_dir, fwd_dir ); - - player.angles[0] = atan2f( fwd_dir[2], fwd_dir[0] ); - } - - m3x3_copy( player.vr, player.gate_vr_frame ); - m3x3_copy( player.vr_pstep, player.gate_vr_pstep_frame ); - - audio_lock(); - audio_play_oneshot( &audio_gate_lap, 1.0f ); - audio_unlock(); - break; - } - } - - rb_update_transform( &player.rb ); -} - -/* - * Animation - */ - -static void player_animate_offboard(void) -{ - mdl_keyframe apose[32], bpose[32]; - struct skeleton *sk = &player.mdl.sk; - - float walk_norm = (float)player.mdl.anim_walk->length/30.0f, - run_norm = (float)player.mdl.anim_run->length/30.0f, - t = player.walk_timer, - l = vg_get_axis("grabr") * 0.5f + 0.5f; - - skeleton_sample_anim( sk, player.mdl.anim_walk, t*walk_norm, apose ); - skeleton_sample_anim( sk, player.mdl.anim_run, t*run_norm, bpose ); - - skeleton_lerp_pose( sk, apose, bpose, l, apose ); - - float idle_walk = vg_minf( l * 10.0f, 1.0f); - - skeleton_sample_anim( sk, player.mdl.anim_idle, vg_time*0.1f, bpose ); - skeleton_lerp_pose( sk, apose, bpose, 1.0f-idle_walk, apose ); - - skeleton_apply_pose( &player.mdl.sk, apose, k_anim_apply_defer_ik ); - skeleton_apply_ik_pass( &player.mdl.sk ); - skeleton_apply_pose( &player.mdl.sk, apose, k_anim_apply_deffered_only ); - - v3_copy( player.mdl.sk.final_mtx[player.mdl.id_head-1][3], - player.mdl.cam_pos ); - - skeleton_apply_inverses( &player.mdl.sk ); - - m4x3f mtx; - v4f rot; - q_axis_angle( rot, (v3f){0.0f,1.0f,0.0f}, -player.angles[0] - VG_PIf*0.5f ); - q_m3x3( rot, mtx ); - v3_copy( player.rb.to_world[3], mtx[3] ); - - skeleton_apply_transform( &player.mdl.sk, mtx ); - skeleton_debug( &player.mdl.sk ); -} - -static void player_animate(void) -{ - if( !player.on_board ) - { - player_animate_offboard(); - return; - } - - /* Camera position */ - v3_sub( player.rb.v, player.v_last, player.a ); - v3_copy( player.rb.v, player.v_last ); - - v3_add( player.m, player.a, player.m ); - v3_lerp( player.m, (v3f){0.0f,0.0f,0.0f}, 0.1f, player.m ); - - player.m[0] = vg_clampf( player.m[0], -2.0f, 2.0f ); - player.m[1] = vg_clampf( player.m[1], -2.0f, 2.0f ); - player.m[2] = vg_clampf( player.m[2], -2.0f, 2.0f ); - v3_lerp( player.bob, player.m, 0.2f, player.bob ); - - /* Head */ - float lslip = fabsf(player.slip); - - float kheight = 2.0f, - kleg = 0.6f; - - v3f offset; - v3_zero( offset ); - m3x3_mulv( player.rb.to_local, player.bob, offset ); - - static float speed_wobble = 0.0f, speed_wobble_2 = 0.0f; - - float kickspeed = vg_clampf(v3_length(player.rb.v)*(1.0f/40.0f), 0.0f, 1.0f); - float kicks = (vg_randf()-0.5f)*2.0f*kickspeed; - float sign = vg_signf( kicks ); - speed_wobble = vg_lerpf( speed_wobble, kicks*kicks*sign, 0.1f ); - speed_wobble_2 = vg_lerpf( speed_wobble_2, speed_wobble, 0.04f ); - - offset[0] *= 0.26f; - offset[0] += speed_wobble_2*3.0f; - - offset[1] *= -0.3f; - offset[2] *= 0.01f; - - offset[0] = vg_clampf( offset[0], -0.8f, 0.8f ); - offset[1] = vg_clampf( offset[1], -0.5f, 0.0f ); - - /* - * Animation blending - * =========================================== - */ - - /* scalar blending information */ - float speed = v3_length( player.rb.v ); - - /* sliding */ - { - float desired = vg_clampf( lslip, 0.0f, 1.0f ); - player.fslide = vg_lerpf( player.fslide, desired, 0.04f ); - } - - /* movement information */ - { - float dirz = player.reverse > 0.0f? 0.0f: 1.0f, - dirx = player.slip < 0.0f? 0.0f: 1.0f, - fly = player.in_air? 1.0f: 0.0f; - - player.fdirz = vg_lerpf( player.fdirz, dirz, 0.04f ); - player.fdirx = vg_lerpf( player.fdirx, dirx, 0.01f ); - player.ffly = vg_lerpf( player.ffly, fly, 0.04f ); - } - - struct skeleton *sk = &player.mdl.sk; - - mdl_keyframe apose[32], bpose[32]; - mdl_keyframe ground_pose[32]; - { - /* when the player is moving fast he will crouch down a little bit */ - float stand = 1.0f - vg_clampf( speed * 0.03f, 0.0f, 1.0f ); - player.fstand = vg_lerpf( player.fstand, stand, 0.1f ); - - /* stand/crouch */ - float dir_frame = player.fdirz * (15.0f/30.0f), - stand_blend = offset[1]*-2.0f; - - skeleton_sample_anim( sk, player.mdl.anim_stand, dir_frame, apose ); - skeleton_sample_anim( sk, player.mdl.anim_highg, dir_frame, bpose ); - skeleton_lerp_pose( sk, apose, bpose, stand_blend, apose ); - - /* sliding */ - float slide_frame = player.fdirx * (15.0f/30.0f); - skeleton_sample_anim( sk, player.mdl.anim_slide, slide_frame, bpose ); - skeleton_lerp_pose( sk, apose, bpose, player.fslide, apose ); - - /* pushing */ - player.fpush = vg_lerpf( player.fpush, player.pushing, 0.1f ); - - float pt = player.push_time; - if( player.reverse > 0.0f ) - skeleton_sample_anim( sk, player.mdl.anim_push, pt, bpose ); - else - skeleton_sample_anim( sk, player.mdl.anim_push_reverse, pt, bpose ); - - skeleton_lerp_pose( sk, apose, bpose, player.fpush, apose ); - - /* trick setup */ - float jump_start_frame = 14.0f/30.0f; - float setup_frame = player.jump * jump_start_frame, - setup_blend = vg_minf( player.jump*5.0f, 1.0f ); - - float jump_frame = (vg_time - player.jump_time) + jump_start_frame; - if( jump_frame >= jump_start_frame && jump_frame <= (40.0f/30.0f) ) - setup_frame = jump_frame; - - struct skeleton_anim *jump_anim = player.jump_dir? - player.mdl.anim_ollie: - player.mdl.anim_ollie_reverse; - - skeleton_sample_anim_clamped( sk, jump_anim, setup_frame, bpose ); - skeleton_lerp_pose( sk, apose, bpose, setup_blend, ground_pose ); - } - - mdl_keyframe air_pose[32]; - { - float target = -vg_get_axis("horizontal"); - player.fairdir = vg_lerpf( player.fairdir, target, 0.04f ); - - float air_frame = (player.fairdir*0.5f+0.5f) * (15.0f/30.0f); - - skeleton_sample_anim( sk, player.mdl.anim_air, air_frame, apose ); - - static v2f grab_choice; - v2_lerp( grab_choice, (v2f){ vg_get_axis("h1"), vg_get_axis("v1") }, - 0.04f, grab_choice ); - - float ang = atan2f( grab_choice[0], grab_choice[1] ), - ang_unit = (ang+VG_PIf) * (1.0f/VG_TAUf), - grab_frame = ang_unit * (15.0f/30.0f); - - skeleton_sample_anim( sk, player.mdl.anim_grabs, grab_frame, bpose ); - skeleton_lerp_pose( sk, apose, bpose, player.grab, air_pose ); - } - - skeleton_lerp_pose( sk, ground_pose, air_pose, player.ffly, apose ); - - float add_grab_mod = 1.0f - player.ffly*player.grab; - - /* additive effects */ - apose[player.mdl.id_hip-1].co[0] += offset[0]*add_grab_mod; - apose[player.mdl.id_hip-1].co[2] += offset[2]*add_grab_mod; - apose[player.mdl.id_ik_hand_l-1].co[0] += offset[0]*add_grab_mod; - apose[player.mdl.id_ik_hand_l-1].co[2] += offset[2]*add_grab_mod; - apose[player.mdl.id_ik_hand_r-1].co[0] += offset[0]*add_grab_mod; - apose[player.mdl.id_ik_hand_r-1].co[2] += offset[2]*add_grab_mod; - apose[player.mdl.id_ik_elbow_l-1].co[0] += offset[0]*add_grab_mod; - apose[player.mdl.id_ik_elbow_l-1].co[2] += offset[2]*add_grab_mod; - apose[player.mdl.id_ik_elbow_r-1].co[0] += offset[0]*add_grab_mod; - apose[player.mdl.id_ik_elbow_r-1].co[2] += offset[2]*add_grab_mod; - - skeleton_apply_pose( &player.mdl.sk, apose, k_anim_apply_defer_ik ); - skeleton_apply_ik_pass( &player.mdl.sk ); - skeleton_apply_pose( &player.mdl.sk, apose, k_anim_apply_deffered_only ); - - v3_copy( player.mdl.sk.final_mtx[player.mdl.id_head-1][3], - player.mdl.cam_pos ); - skeleton_apply_inverses( &player.mdl.sk ); - skeleton_apply_transform( &player.mdl.sk, player.rb.to_world ); - - skeleton_debug( &player.mdl.sk ); -} - -static void player_camera_update(void) -{ - /* Update camera matrices */ - v4f qyaw, qpitch, qcam; - q_axis_angle( qyaw, (v3f){ 0.0f, 1.0f, 0.0f }, -player.angles[0] ); - q_axis_angle( qpitch, (v3f){ 1.0f, 0.0f, 0.0f }, -player.angles[1] ); - - q_mul( qyaw, qpitch, qcam ); - q_m3x3( qcam, player.camera ); - - v3_copy( player.camera_pos, player.camera[3] ); - m4x3_invert_affine( player.camera, player.camera_inverse ); -} - -static void player_animate_death_cam(void) -{ - v3f delta; - v3f head_pos; - v3_copy( player.mdl.ragdoll[0].rb.co, head_pos ); - - v3_sub( head_pos, player.camera_pos, delta ); - v3_normalize( delta ); - - v3f follow_pos; - v3_muladds( head_pos, delta, -2.5f, follow_pos ); - v3_lerp( player.camera_pos, follow_pos, 0.1f, player.camera_pos ); - - /* - * Make sure the camera stays above the ground - */ - v3f min_height = {0.0f,1.0f,0.0f}; - - v3f sample; - v3_add( player.camera_pos, min_height, sample ); - ray_hit hit; - hit.dist = min_height[1]*2.0f; - - if( ray_world( sample, (v3f){0.0f,-1.0f,0.0f}, &hit )) - v3_add( hit.pos, min_height, player.camera_pos ); - - player.camera_pos[1] = - vg_maxf( wrender.height + 2.0f, player.camera_pos[1] ); - - player.angles[0] = atan2f( delta[0], -delta[2] ); - player.angles[1] = -asinf( delta[1] ); -} - -static void player_animate_camera(void) -{ - static v3f lerp_cam = {0.0f,0.0f,0.0f}; - v3f cam_pos; - - player.fonboard = vg_lerpf(player.fonboard, player.on_board, ktimestep*1.0f); - - if( player.on_board ) - { - v3f offs = { -0.4f, 0.15f, 0.0f }; - v3_lerp( lerp_cam, player.mdl.cam_pos, 0.8f, lerp_cam ); - v3_add( lerp_cam, offs, cam_pos ); - - /* Look angles */ - v3_lerp( player.vl, player.rb.v, 0.05f, player.vl ); - - float yaw = atan2f( player.vl[0], -player.vl[2] ), - pitch = atan2f( -player.vl[1], - sqrtf( - player.vl[0]*player.vl[0] + player.vl[2]*player.vl[2] - )) * 0.7f; - - player.angles[0] = yaw; - player.angles[1] = vg_lerpf( player.angles[1], pitch + 0.30f, - player.fonboard ); - - /* Camera shake */ - static v2f shake_damp = {0.0f,0.0f}; - v2f shake = { vg_randf()-0.5f, vg_randf()-0.5f }; - v2_muls( shake, v3_length(player.rb.v)*0.3f - * (1.0f+fabsf(player.slip)), shake); - - v2_lerp( shake_damp, shake, 0.01f, shake_damp ); - shake_damp[0] *= 0.2f; - - v2_muladds( player.angles, shake_damp, 0.1f, player.angles ); - m4x3_mulv( player.rb.to_world, cam_pos, player.camera_pos ); - } - else - { - float speed = ktimestep * k_look_speed; - player.angles[0] += vg_get_axis( "horizontal" ) * speed; - player.angles[1] += vg_get_axis( "vertical" ) * speed; - - player.angles[1] = vg_clampf( player.angles[1], - -k_pitch_limit, k_pitch_limit ); - - float s = sinf(player.angles[0]) * 0.2f, - c = -cosf(player.angles[0]) * 0.2f; - v3f forward_dir = { s,0.15f,c }; - - - m4x3f mtx; - v4f rot; - q_axis_angle( rot, (v3f){0.0f,1.0f,0.0f}, - -player.angles[0] -VG_PIf*0.5f ); - q_m3x3( rot, mtx ); - v3_copy( player.rb.to_world[3], mtx[3] ); - - m4x3_mulv( mtx, player.mdl.cam_pos, cam_pos ); - v3_add( cam_pos, forward_dir, player.camera_pos ); - v3_lerp( player.vl, player.rb.v, 0.3f, player.vl ); - } -} - -/* - * Audio - */ -static void player_audio(void) -{ - static int _ding = 0; - - int last = _ding; - _ding = glfwGetKey(vg_window, GLFW_KEY_C); - - int trigger_ding = 0; - if( _ding && !last ) - trigger_ding = 1; - - static int _air = 0; - - int l2 = _air; - _air = player.in_air; - - static double last_revert = -2000.0; - - - - - audio_lock(); - - double revert_delta = vg_time - last_revert; - if( player.on_board && (!_air && l2) && (fabsf(player.slip) > 0.5f) && - (revert_delta > 0.7) ) - { - audio_player_set_position( &audio_player_extra, player.rb.co ); - audio_player_set_flags( &audio_player_extra, AUDIO_FLAG_SPACIAL_3D ); - audio_player_set_vol( &audio_player_extra, 2.0f ); - audio_player_playclip( &audio_player_extra, &audio_lands[rand()%5] ); - - last_revert = vg_time; - } - - static float air = 0.0f; - air = vg_lerpf(air, player.in_air? 1.0f: 0.0f, 5.0f*ktimestep); - - /* Spacial info */ - v3f ears = { 1.0f,0.0f,0.0f }; - v3f delta; - - float *cam = player.camera[3], - *pos = player.rb.co; - - if( trigger_ding ) - audio_player_playclip( &audio_player_extra, &audio_ding ); - - audio_player_set_position( &audio_player0, player.rb.co ); - audio_player_set_position( &audio_player1, player.rb.co ); - audio_player_set_position( &audio_player2, player.rb.co ); - audio_player_set_position( &audio_player_gate, world.render_gate_pos ); - - v3_sub( player.rb.co, player.camera[3], delta ); - v3_normalize( delta ); - m3x3_mulv( player.camera, ears, ears ); - - /* TODO, Make function */ - v3_copy( ears, vg_audio.listener_ears ); - v3_copy( player.camera[3], vg_audio.listener_pos ); - - /* Tunnel / occlusion */ - audio_sample_occlusion( player.camera[3] ); - - int sprite_avail = -1; - for( int i=0; i 0.5f ) - walk_phase = 1; - else - walk_phase = 0; - - if( (player.step_phase != walk_phase) && !player.in_air ) - { - v3_copy( player.rb.co, player.last_step_pos ); - - audio_player_set_flags( &audio_player_extra, AUDIO_FLAG_SPACIAL_3D ); - audio_player_set_position( &audio_player_extra, player.rb.co ); - audio_player_set_vol( &audio_player_extra, 6.0f ); - audio_player_playclip( &audio_player_extra, - &audio_footsteps[rand()%4] ); - } - - player.step_phase = walk_phase; - } - else - { - /* Composite */ - float speed = vg_minf(v3_length( player.rb.v )*0.1f,1.0f), - attn = speed, - slide = vg_clampf( fabsf(player.slip), 0.0f, 1.0f ), - vol0 = (1.0f-air)*attn*(1.0f-slide), - vol1 = air *attn, - vol2 = (1.0f-air)*attn*slide; - - audio_player_set_vol( &audio_player0, vol0 ); - audio_player_set_vol( &audio_player1, vol1 ); - audio_player_set_vol( &audio_player2, vol2 ); - - float reverb_amt = vol0 * audio_occlusion_current * 0.5f; - audio_player_set_pan( &audio_player3, 0.0f ); - audio_player_set_vol( &audio_player3, reverb_amt ); - } - -#if 0 - world_audio_update( cam, ears ); -#endif - audio_unlock(); -} - -/* - * Public Endpoints - */ -static float *player_cam_pos(void) -{ - return player.camera_pos; -} - -static int reset_player( int argc, char const *argv[] ) -{ - struct respawn_point *rp = NULL, *r; - - if( argc == 1 ) - { - for( int i=0; iname, argv[0] ) ) - { - rp = r; - break; - } - } - - if( !rp ) - vg_warn( "No spawn named '%s'\n", argv[0] ); - } - - if( !rp ) - { - float min_dist = INFINITY; - - for( int i=0; ico, player.rb.co ); - - vg_info( "Dist %s : %f\n", r->name, d ); - if( d < min_dist ) - { - min_dist = d; - rp = r; - } - } - } - - if( !rp ) - { - vg_error( "No spawn found\n" ); - if( !world.spawn_count ) - return 0; - - rp = &world.spawns[0]; - } - - v4_copy( rp->q, player.rb.q ); - v3_copy( rp->co, player.rb.co ); - - player.vswitch = 1.0f; - player.slip_last = 0.0f; - player.is_dead = 0; - player.in_air = 1; - m3x3_identity( player.vr ); - - player.mdl.shoes[0] = 1; - player.mdl.shoes[1] = 1; - - rb_update_transform( &player.rb ); - m3x3_mulv( player.rb.to_world, (v3f){ 0.0f, 0.0f, -1.2f }, player.rb.v ); - m3x3_identity( player.gate_vr_frame ); - m3x3_identity( player.gate_vr_pstep_frame ); - - player.rb_gate_frame = player.rb; - player.on_board_frame = player.on_board; - player.in_air_frame = player.in_air; - return 1; -} - -static void player_update(void) +static void player_update(void) /* 2 */ { for( int i=0; i0.0f) @@ -1442,14 +226,14 @@ static void player_update(void) if( (glfwGetKey( vg_window, GLFW_KEY_O ) || (player.rb.co[1] < 0.0f)) && !player.is_dead) { - character_ragdoll_copypose( &player.mdl, player.rb.v ); + player_ragdoll_copy_model( player.rb.v ); player.is_dead = 1; } if( player.is_dead ) { - character_ragdoll_iter( &player.mdl ); - character_debug_ragdoll( &player.mdl ); + player_ragdoll_iter(); + player_debug_ragdoll(); if( !freecam ) player_animate_death_cam(); @@ -1470,10 +254,10 @@ static void player_update(void) player_audio(); } -static void draw_player(void) +static void draw_player(void) /* 3 */ { if( player.is_dead ) - character_mimic_ragdoll( &player.mdl ); + player_model_copy_ragdoll(); shader_viewchar_use(); vg_tex2d_bind( &tex_characters, 0 ); @@ -1490,4 +274,27 @@ static void draw_player(void) mesh_draw( &player.mdl.mesh ); } +/* + * ----------------------------------------------------------------------------- + * API implementation + * ----------------------------------------------------------------------------- + */ + +static float *player_get_pos(void) +{ + return player.rb.co; +} + +static void player_kill(void) +{ + player.is_dead = 1; + player_ragdoll_copy_model( player.rb.v ); +} + +static float *player_cam_pos(void) +{ + return player.camera_pos; +} + + #endif /* PLAYER_H */ diff --git a/player_animation.h b/player_animation.h new file mode 100644 index 0000000..101bd22 --- /dev/null +++ b/player_animation.h @@ -0,0 +1,314 @@ +#ifndef PLAYER_ANIMATION_H +#define PLAYER_ANIMATION_H + +#include "player.h" + +static void player_animate_offboard(void) +{ + mdl_keyframe apose[32], bpose[32]; + struct skeleton *sk = &player.mdl.sk; + + float walk_norm = (float)player.mdl.anim_walk->length/30.0f, + run_norm = (float)player.mdl.anim_run->length/30.0f, + t = player.walk_timer, + l = vg_get_axis("grabr") * 0.5f + 0.5f; + + skeleton_sample_anim( sk, player.mdl.anim_walk, t*walk_norm, apose ); + skeleton_sample_anim( sk, player.mdl.anim_run, t*run_norm, bpose ); + + skeleton_lerp_pose( sk, apose, bpose, l, apose ); + + float idle_walk = vg_minf( l * 10.0f, 1.0f); + + skeleton_sample_anim( sk, player.mdl.anim_idle, vg_time*0.1f, bpose ); + skeleton_lerp_pose( sk, apose, bpose, 1.0f-idle_walk, apose ); + + skeleton_apply_pose( &player.mdl.sk, apose, k_anim_apply_defer_ik ); + skeleton_apply_ik_pass( &player.mdl.sk ); + skeleton_apply_pose( &player.mdl.sk, apose, k_anim_apply_deffered_only ); + + v3_copy( player.mdl.sk.final_mtx[player.mdl.id_head-1][3], + player.mdl.cam_pos ); + + skeleton_apply_inverses( &player.mdl.sk ); + + m4x3f mtx; + v4f rot; + q_axis_angle( rot, (v3f){0.0f,1.0f,0.0f}, -player.angles[0] - VG_PIf*0.5f ); + q_m3x3( rot, mtx ); + v3_copy( player.rb.to_world[3], mtx[3] ); + + skeleton_apply_transform( &player.mdl.sk, mtx ); + skeleton_debug( &player.mdl.sk ); +} + +static void player_animate(void) +{ + if( !player.on_board ) + { + player_animate_offboard(); + return; + } + + /* Camera position */ + v3_sub( player.rb.v, player.v_last, player.a ); + v3_copy( player.rb.v, player.v_last ); + + v3_add( player.m, player.a, player.m ); + v3_lerp( player.m, (v3f){0.0f,0.0f,0.0f}, 0.1f, player.m ); + + player.m[0] = vg_clampf( player.m[0], -2.0f, 2.0f ); + player.m[1] = vg_clampf( player.m[1], -2.0f, 2.0f ); + player.m[2] = vg_clampf( player.m[2], -2.0f, 2.0f ); + v3_lerp( player.bob, player.m, 0.2f, player.bob ); + + /* Head */ + float lslip = fabsf(player.slip); + + float kheight = 2.0f, + kleg = 0.6f; + + v3f offset; + v3_zero( offset ); + m3x3_mulv( player.rb.to_local, player.bob, offset ); + + static float speed_wobble = 0.0f, speed_wobble_2 = 0.0f; + + float kickspeed = vg_clampf(v3_length(player.rb.v)*(1.0f/40.0f), 0.0f, 1.0f); + float kicks = (vg_randf()-0.5f)*2.0f*kickspeed; + float sign = vg_signf( kicks ); + speed_wobble = vg_lerpf( speed_wobble, kicks*kicks*sign, 0.1f ); + speed_wobble_2 = vg_lerpf( speed_wobble_2, speed_wobble, 0.04f ); + + offset[0] *= 0.26f; + offset[0] += speed_wobble_2*3.0f; + + offset[1] *= -0.3f; + offset[2] *= 0.01f; + + offset[0] = vg_clampf( offset[0], -0.8f, 0.8f ); + offset[1] = vg_clampf( offset[1], -0.5f, 0.0f ); + + /* + * Animation blending + * =========================================== + */ + + /* scalar blending information */ + float speed = v3_length( player.rb.v ); + + /* sliding */ + { + float desired = vg_clampf( lslip, 0.0f, 1.0f ); + player.fslide = vg_lerpf( player.fslide, desired, 0.04f ); + } + + /* movement information */ + { + float dirz = player.reverse > 0.0f? 0.0f: 1.0f, + dirx = player.slip < 0.0f? 0.0f: 1.0f, + fly = player.in_air? 1.0f: 0.0f; + + player.fdirz = vg_lerpf( player.fdirz, dirz, 0.04f ); + player.fdirx = vg_lerpf( player.fdirx, dirx, 0.01f ); + player.ffly = vg_lerpf( player.ffly, fly, 0.04f ); + } + + struct skeleton *sk = &player.mdl.sk; + + mdl_keyframe apose[32], bpose[32]; + mdl_keyframe ground_pose[32]; + { + /* when the player is moving fast he will crouch down a little bit */ + float stand = 1.0f - vg_clampf( speed * 0.03f, 0.0f, 1.0f ); + player.fstand = vg_lerpf( player.fstand, stand, 0.1f ); + + /* stand/crouch */ + float dir_frame = player.fdirz * (15.0f/30.0f), + stand_blend = offset[1]*-2.0f; + + skeleton_sample_anim( sk, player.mdl.anim_stand, dir_frame, apose ); + skeleton_sample_anim( sk, player.mdl.anim_highg, dir_frame, bpose ); + skeleton_lerp_pose( sk, apose, bpose, stand_blend, apose ); + + /* sliding */ + float slide_frame = player.fdirx * (15.0f/30.0f); + skeleton_sample_anim( sk, player.mdl.anim_slide, slide_frame, bpose ); + skeleton_lerp_pose( sk, apose, bpose, player.fslide, apose ); + + /* pushing */ + player.fpush = vg_lerpf( player.fpush, player.pushing, 0.1f ); + + float pt = player.push_time; + if( player.reverse > 0.0f ) + skeleton_sample_anim( sk, player.mdl.anim_push, pt, bpose ); + else + skeleton_sample_anim( sk, player.mdl.anim_push_reverse, pt, bpose ); + + skeleton_lerp_pose( sk, apose, bpose, player.fpush, apose ); + + /* trick setup */ + float jump_start_frame = 14.0f/30.0f; + float setup_frame = player.jump * jump_start_frame, + setup_blend = vg_minf( player.jump*5.0f, 1.0f ); + + float jump_frame = (vg_time - player.jump_time) + jump_start_frame; + if( jump_frame >= jump_start_frame && jump_frame <= (40.0f/30.0f) ) + setup_frame = jump_frame; + + struct skeleton_anim *jump_anim = player.jump_dir? + player.mdl.anim_ollie: + player.mdl.anim_ollie_reverse; + + skeleton_sample_anim_clamped( sk, jump_anim, setup_frame, bpose ); + skeleton_lerp_pose( sk, apose, bpose, setup_blend, ground_pose ); + } + + mdl_keyframe air_pose[32]; + { + float target = -vg_get_axis("horizontal"); + player.fairdir = vg_lerpf( player.fairdir, target, 0.04f ); + + float air_frame = (player.fairdir*0.5f+0.5f) * (15.0f/30.0f); + + skeleton_sample_anim( sk, player.mdl.anim_air, air_frame, apose ); + + static v2f grab_choice; + v2_lerp( grab_choice, (v2f){ vg_get_axis("h1"), vg_get_axis("v1") }, + 0.04f, grab_choice ); + + float ang = atan2f( grab_choice[0], grab_choice[1] ), + ang_unit = (ang+VG_PIf) * (1.0f/VG_TAUf), + grab_frame = ang_unit * (15.0f/30.0f); + + skeleton_sample_anim( sk, player.mdl.anim_grabs, grab_frame, bpose ); + skeleton_lerp_pose( sk, apose, bpose, player.grab, air_pose ); + } + + skeleton_lerp_pose( sk, ground_pose, air_pose, player.ffly, apose ); + + float add_grab_mod = 1.0f - player.ffly*player.grab; + + /* additive effects */ + apose[player.mdl.id_hip-1].co[0] += offset[0]*add_grab_mod; + apose[player.mdl.id_hip-1].co[2] += offset[2]*add_grab_mod; + apose[player.mdl.id_ik_hand_l-1].co[0] += offset[0]*add_grab_mod; + apose[player.mdl.id_ik_hand_l-1].co[2] += offset[2]*add_grab_mod; + apose[player.mdl.id_ik_hand_r-1].co[0] += offset[0]*add_grab_mod; + apose[player.mdl.id_ik_hand_r-1].co[2] += offset[2]*add_grab_mod; + apose[player.mdl.id_ik_elbow_l-1].co[0] += offset[0]*add_grab_mod; + apose[player.mdl.id_ik_elbow_l-1].co[2] += offset[2]*add_grab_mod; + apose[player.mdl.id_ik_elbow_r-1].co[0] += offset[0]*add_grab_mod; + apose[player.mdl.id_ik_elbow_r-1].co[2] += offset[2]*add_grab_mod; + + skeleton_apply_pose( &player.mdl.sk, apose, k_anim_apply_defer_ik ); + skeleton_apply_ik_pass( &player.mdl.sk ); + skeleton_apply_pose( &player.mdl.sk, apose, k_anim_apply_deffered_only ); + + v3_copy( player.mdl.sk.final_mtx[player.mdl.id_head-1][3], + player.mdl.cam_pos ); + skeleton_apply_inverses( &player.mdl.sk ); + skeleton_apply_transform( &player.mdl.sk, player.rb.to_world ); + + skeleton_debug( &player.mdl.sk ); +} + +static void player_animate_death_cam(void) +{ + v3f delta; + v3f head_pos; + v3_copy( player.mdl.ragdoll[0].rb.co, head_pos ); + + v3_sub( head_pos, player.camera_pos, delta ); + v3_normalize( delta ); + + v3f follow_pos; + v3_muladds( head_pos, delta, -2.5f, follow_pos ); + v3_lerp( player.camera_pos, follow_pos, 0.1f, player.camera_pos ); + + /* + * Make sure the camera stays above the ground + */ + v3f min_height = {0.0f,1.0f,0.0f}; + + v3f sample; + v3_add( player.camera_pos, min_height, sample ); + ray_hit hit; + hit.dist = min_height[1]*2.0f; + + if( ray_world( sample, (v3f){0.0f,-1.0f,0.0f}, &hit )) + v3_add( hit.pos, min_height, player.camera_pos ); + + player.camera_pos[1] = + vg_maxf( wrender.height + 2.0f, player.camera_pos[1] ); + + player.angles[0] = atan2f( delta[0], -delta[2] ); + player.angles[1] = -asinf( delta[1] ); +} + +static void player_animate_camera(void) +{ + static v3f lerp_cam = {0.0f,0.0f,0.0f}; + v3f cam_pos; + + player.fonboard = vg_lerpf(player.fonboard, player.on_board, ktimestep*1.0f); + + if( player.on_board ) + { + v3f offs = { -0.4f, 0.15f, 0.0f }; + v3_lerp( lerp_cam, player.mdl.cam_pos, 0.8f, lerp_cam ); + v3_add( lerp_cam, offs, cam_pos ); + + /* Look angles */ + v3_lerp( player.vl, player.rb.v, 0.05f, player.vl ); + + float yaw = atan2f( player.vl[0], -player.vl[2] ), + pitch = atan2f( -player.vl[1], + sqrtf( + player.vl[0]*player.vl[0] + player.vl[2]*player.vl[2] + )) * 0.7f; + + player.angles[0] = yaw; + player.angles[1] = vg_lerpf( player.angles[1], pitch + 0.30f, + player.fonboard ); + + /* Camera shake */ + static v2f shake_damp = {0.0f,0.0f}; + v2f shake = { vg_randf()-0.5f, vg_randf()-0.5f }; + v2_muls( shake, v3_length(player.rb.v)*0.3f + * (1.0f+fabsf(player.slip)), shake); + + v2_lerp( shake_damp, shake, 0.01f, shake_damp ); + shake_damp[0] *= 0.2f; + + v2_muladds( player.angles, shake_damp, 0.1f, player.angles ); + m4x3_mulv( player.rb.to_world, cam_pos, player.camera_pos ); + } + else + { + float speed = ktimestep * k_look_speed; + player.angles[0] += vg_get_axis( "horizontal" ) * speed; + player.angles[1] += vg_get_axis( "vertical" ) * speed; + + player.angles[1] = vg_clampf( player.angles[1], + -k_pitch_limit, k_pitch_limit ); + + float s = sinf(player.angles[0]) * 0.2f, + c = -cosf(player.angles[0]) * 0.2f; + v3f forward_dir = { s,0.15f,c }; + + + m4x3f mtx; + v4f rot; + q_axis_angle( rot, (v3f){0.0f,1.0f,0.0f}, + -player.angles[0] -VG_PIf*0.5f ); + q_m3x3( rot, mtx ); + v3_copy( player.rb.to_world[3], mtx[3] ); + + m4x3_mulv( mtx, player.mdl.cam_pos, cam_pos ); + v3_add( cam_pos, forward_dir, player.camera_pos ); + v3_lerp( player.vl, player.rb.v, 0.3f, player.vl ); + } +} + +#endif /* PLAYER_ANIMATION_H */ diff --git a/player_audio.h b/player_audio.h new file mode 100644 index 0000000..01c13fc --- /dev/null +++ b/player_audio.h @@ -0,0 +1,156 @@ +#ifndef PLAYER_AUDIO_H +#define PLAYER_AUDIO_H + +#include "player.h" + +/* + * Audio + */ +static void player_audio(void) +{ + static int _ding = 0; + + int last = _ding; + _ding = glfwGetKey(vg_window, GLFW_KEY_C); + + int trigger_ding = 0; + if( _ding && !last ) + trigger_ding = 1; + + static int _air = 0; + + int l2 = _air; + _air = player.in_air; + + static double last_revert = -2000.0; + + + + + audio_lock(); + + double revert_delta = vg_time - last_revert; + if( player.on_board && (!_air && l2) && (fabsf(player.slip) > 0.5f) && + (revert_delta > 0.7) ) + { + audio_player_set_position( &audio_player_extra, player.rb.co ); + audio_player_set_flags( &audio_player_extra, AUDIO_FLAG_SPACIAL_3D ); + audio_player_set_vol( &audio_player_extra, 2.0f ); + audio_player_playclip( &audio_player_extra, &audio_lands[rand()%5] ); + + last_revert = vg_time; + } + + static float air = 0.0f; + air = vg_lerpf(air, player.in_air? 1.0f: 0.0f, 5.0f*ktimestep); + + /* Spacial info */ + v3f ears = { 1.0f,0.0f,0.0f }; + v3f delta; + + float *cam = player.camera[3], + *pos = player.rb.co; + + if( trigger_ding ) + audio_player_playclip( &audio_player_extra, &audio_ding ); + + audio_player_set_position( &audio_player0, player.rb.co ); + audio_player_set_position( &audio_player1, player.rb.co ); + audio_player_set_position( &audio_player2, player.rb.co ); + audio_player_set_position( &audio_player_gate, world.render_gate_pos ); + + v3_sub( player.rb.co, player.camera[3], delta ); + v3_normalize( delta ); + m3x3_mulv( player.camera, ears, ears ); + + /* TODO, Make function */ + v3_copy( ears, vg_audio.listener_ears ); + v3_copy( player.camera[3], vg_audio.listener_pos ); + + /* Tunnel / occlusion */ + audio_sample_occlusion( player.camera[3] ); + + int sprite_avail = -1; + for( int i=0; i 0.5f ) + walk_phase = 1; + else + walk_phase = 0; + + if( (player.step_phase != walk_phase) && !player.in_air ) + { + audio_player_set_flags( &audio_player_extra, AUDIO_FLAG_SPACIAL_3D ); + audio_player_set_position( &audio_player_extra, player.rb.co ); + audio_player_set_vol( &audio_player_extra, 6.0f ); + audio_player_playclip( &audio_player_extra, + &audio_footsteps[rand()%4] ); + } + + player.step_phase = walk_phase; + } + else + { + /* Composite */ + float speed = vg_minf(v3_length( player.rb.v )*0.1f,1.0f), + attn = speed, + slide = vg_clampf( fabsf(player.slip), 0.0f, 1.0f ), + vol0 = (1.0f-air)*attn*(1.0f-slide), + vol1 = air *attn, + vol2 = (1.0f-air)*attn*slide; + + audio_player_set_vol( &audio_player0, vol0 ); + audio_player_set_vol( &audio_player1, vol1 ); + audio_player_set_vol( &audio_player2, vol2 ); + + float reverb_amt = vol0 * audio_occlusion_current * 0.5f; + audio_player_set_pan( &audio_player3, 0.0f ); + audio_player_set_vol( &audio_player3, reverb_amt ); + } + +#if 0 + world_audio_update( cam, ears ); +#endif + audio_unlock(); +} + +#endif /* PLAYER_AUDIO_H */ diff --git a/player_model.h b/player_model.h index 1673581..a2379fc 100644 --- a/player_model.h +++ b/player_model.h @@ -5,70 +5,27 @@ #ifndef CHARACTER_H #define CHARACTER_H -#include "common.h" -#include "model.h" -#include "rigidbody.h" -#include "render.h" -#include "skeleton.h" -#include "world.h" -#include "skeleton_animator.h" +#include "player.h" +#include "player_ragdoll.h" #include "shaders/viewchar.h" -static float k_ragdoll_floatyiness = 10.0f, - k_ragdoll_floatydrag = 1.0f; - vg_tex2d tex_characters = { .path = "textures/ch_gradient.qoi" }; -static void character_register(void) +static void player_model_register(void) { shader_viewchar_register(); } -static void character_init(void) +static void player_model_init(void) { vg_tex2d_init( (vg_tex2d *[]){ &tex_characters }, 1 ); } -struct character -{ - glmesh mesh; - struct skeleton sk; - 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, - *anim_walk, *anim_run, *anim_idle; - - u32 id_hip, - id_ik_hand_l, - id_ik_hand_r, - id_ik_elbow_l, - id_ik_elbow_r, - id_head; - - v3f cam_pos; - - struct ragdoll_part - { - u32 bone_id; - v3f offset; - - u32 use_limits; - v3f limits[2]; - - rigidbody rb; - u32 parent; - } - *ragdoll; - u32 ragdoll_count; - int shoes[2]; -}; - -static int character_load( struct character *ch, const char *name ) +/* + * Load model from file (.mdl) + */ +static int player_load_model( const char *name ) { char buf[64]; @@ -77,216 +34,83 @@ static int character_load( struct character *ch, const char *name ) if( !src ) return 0; - - mdl_unpack_glmesh( src, &ch->mesh ); - skeleton_setup( &ch->sk, src ); - ch->anim_stand = skeleton_get_anim( &ch->sk, "pose_stand" ); - ch->anim_highg = skeleton_get_anim( &ch->sk, "pose_highg" ); - ch->anim_slide = skeleton_get_anim( &ch->sk, "pose_slide" ); - ch->anim_air = skeleton_get_anim( &ch->sk, "pose_air" ); - ch->anim_push = skeleton_get_anim( &ch->sk, "push" ); - ch->anim_push_reverse = skeleton_get_anim( &ch->sk, "push_reverse" ); - ch->anim_ollie = skeleton_get_anim( &ch->sk, "ollie" ); - ch->anim_ollie_reverse = skeleton_get_anim( &ch->sk, "ollie_reverse" ); - ch->anim_grabs = skeleton_get_anim( &ch->sk, "grabs" ); - ch->anim_walk = skeleton_get_anim( &ch->sk, "walk" ); - ch->anim_run = skeleton_get_anim( &ch->sk, "run" ); - ch->anim_idle = skeleton_get_anim( &ch->sk, "idle_cycle" ); - - ch->id_hip = skeleton_bone_id( &ch->sk, "hips" ); - ch->id_ik_hand_l = skeleton_bone_id( &ch->sk, "hand.IK.L" ); - ch->id_ik_hand_r = skeleton_bone_id( &ch->sk, "hand.IK.R" ); - ch->id_ik_elbow_l = skeleton_bone_id( &ch->sk, "elbow.L" ); - ch->id_ik_elbow_r = skeleton_bone_id( &ch->sk, "elbow.R" ); - ch->id_head = skeleton_bone_id( &ch->sk, "head" ); - - /* setup ragdoll */ + struct player_model *mdl = &player.mdl; - if( ch->sk.collider_count ) - { - vg_info( "Alloc: %d\n", ch->sk.collider_count ); - ch->ragdoll = malloc(sizeof(struct ragdoll_part) * ch->sk.collider_count); - ch->ragdoll_count = 0; - - for( u32 i=0; isk.bone_count; i ++ ) - { - struct skeleton_bone *bone = &ch->sk.bones[i]; - - if( bone->collider ) - { - struct ragdoll_part *rp = &ch->ragdoll[ ch->ragdoll_count ++ ]; - rp->bone_id = i; - - v3f delta; - v3_sub( bone->hitbox[1], bone->hitbox[0], delta ); - v3_muls( delta, 0.5f, delta ); - - v3_add( bone->hitbox[0], delta, rp->offset ); - - v3_copy( delta, rp->rb.bbx[1] ); - v3_muls( delta, -1.0f, rp->rb.bbx[0] ); - - q_identity( rp->rb.q ); - v3_add( bone->co, rp->offset, rp->rb.co ); - rp->rb.type = k_rb_shape_box; - rp->rb.is_world = 0; - rp->parent = 0xffffffff; - - if( bone->parent ) - { - for( u32 j=0; jragdoll_count; j++ ) - { - if( ch->ragdoll[ j ].bone_id == bone->parent ) - { - rp->parent = j; - break; - } - } - } - - /* TODO: refactor to use this style elswhere */ - struct mdl_node *pnode = mdl_node_from_id( src, bone->orig_node ); - struct classtype_bone *bone_inf = mdl_get_entdata( src, pnode ); - - rp->use_limits = bone_inf->use_limits; - v3_copy( bone_inf->angle_limits[0], rp->limits[0] ); - v3_copy( bone_inf->angle_limits[1], rp->limits[1] ); - - rb_init( &rp->rb ); - } - } - } + mdl_unpack_glmesh( src, &mdl->mesh ); + skeleton_setup( &mdl->sk, src ); - free( src ); - return 1; -} - -static void character_eval( struct character *ch ){} -static void character_draw( struct character *ch, float temp, m4x3f camera ){} -static void character_init_ragdoll_joints( struct character *ch ){} -static void character_init_ragdoll( struct character *ch ){} -static void character_ragdoll_go( struct character *ch, v3f pos ){} - -static void character_mimic_ragdoll( struct character *ch ) -{ - for( int i=0; iragdoll_count; i++ ) + /* + * Link animations + */ + struct _load_anim { - struct ragdoll_part *part = &ch->ragdoll[i]; - m4x3f offset; - m3x3_identity(offset); - v3_negate( part->offset, offset[3] ); - m4x3_mul( part->rb.to_world, offset, ch->sk.final_mtx[part->bone_id] ); + const char *name; + struct skeleton_anim **anim; } - - skeleton_apply_inverses( &ch->sk ); -} - -static void character_ragdoll_copypose( struct character *ch, v3f v ) -{ - for( int i=0; iragdoll_count; i++ ) - { - struct ragdoll_part *part = &ch->ragdoll[i]; - - v3f pos, offset; - u32 bone = part->bone_id; - - m4x3_mulv( ch->sk.final_mtx[bone], ch->sk.bones[bone].co, pos ); - m3x3_mulv( ch->sk.final_mtx[bone], part->offset, offset ); - v3_add( pos, offset, part->rb.co ); - m3x3_q( ch->sk.final_mtx[bone], part->rb.q ); - v3_copy( v, part->rb.v ); - v3_zero( part->rb.w ); - - rb_update_transform( &part->rb ); - } -} - -static void character_debug_ragdoll( struct character *ch ) -{ - for( u32 i=0; iragdoll_count; i ++ ) - rb_debug( &ch->ragdoll[i].rb, 0xff00ff00 ); -} - -static void character_ragdoll_iter( struct character *ch ) -{ - rb_solver_reset(); - - for( int i=0; iragdoll_count; i ++ ) - rb_collide( &ch->ragdoll[i].rb, &world.rb_geo ); - - rb_presolve_contacts( rb_contact_buffer, rb_contact_count ); - - v3f rv; - - float shoe_vel[2] = {0.0f,0.0f}; - for( int i=0; i<2; i++ ) - if( ch->shoes[i] ) - shoe_vel[i] = v3_length( ch->ragdoll[i].rb.v ); + anims[] = { + { "pose_stand", &mdl->anim_stand }, + { "pose_highg", &mdl->anim_highg }, + { "pose_slide", &mdl->anim_slide }, + { "pose_air", &mdl->anim_air }, + { "push", &mdl->anim_push }, + { "push_reverse", &mdl->anim_push_reverse }, + { "ollie", &mdl->anim_ollie }, + { "ollie_reverse",&mdl->anim_ollie_reverse }, + { "grabs", &mdl->anim_grabs }, + { "walk", &mdl->anim_walk }, + { "run", &mdl->anim_run }, + { "idle_cycle", &mdl->anim_idle } + }; - for( int j=0; jragdoll_count; j++ ) + for( int i=0; iragdoll[j]; - struct skeleton_bone *bj = &ch->sk.bones[pj->bone_id]; - - if( pj->parent != 0xffffffff ) + *anims[i].anim = skeleton_get_anim( &mdl->sk, anims[i].name ); + + if( !(*anims[i].anim) ) { - struct ragdoll_part *pp = &ch->ragdoll[pj->parent]; - struct skeleton_bone *bp = &ch->sk.bones[pp->bone_id]; - - v3f lca, lcb; - v3_negate( pj->offset, lca ); - v3_add( bp->co, pp->offset, lcb ); - v3_sub( bj->co, lcb, lcb ); - - rb_debug_constraint_position( &pj->rb, lca, &pp->rb, lcb ); - - if( pj->use_limits ) - { - rb_debug_constraint_limits( &pj->rb, &pp->rb, lca, pj->limits ); - } + vg_error( "Animation '%s' is missing from character '%s'\n", + anims[i].name, name ); + goto il_free_err; } - - v4f plane = {0.0f,1.0f,0.0f,0.0f}; - rb_effect_simple_bouyency( &pj->rb, plane, k_ragdoll_floatyiness, - k_ragdoll_floatydrag ); } - /* CONSTRAINTS */ - for( int i=0; i<10; i++ ) + /* + * Link bones + */ + struct _load_bone + { + const char *name; + u32 *bone_id; + } + bones[] = { + { "hips", &mdl->id_hip }, + { "hand.IK.L", &mdl->id_ik_hand_l }, + { "hand.IK.R", &mdl->id_ik_hand_r }, + { "elbow.L", &mdl->id_ik_elbow_l }, + { "elbow.R", &mdl->id_ik_elbow_r }, + { "head", &mdl->id_head } + }; + + for( int i=0; isk, bones[i].name ); - for( int j=0; jragdoll_count; j++ ) + if( !(*bones[i].bone_id) ) { - struct ragdoll_part *pj = &ch->ragdoll[j]; - struct skeleton_bone *bj = &ch->sk.bones[pj->bone_id]; - - if( pj->parent != 0xffffffff && pj->use_limits ) - { - struct ragdoll_part *pp = &ch->ragdoll[pj->parent]; - struct skeleton_bone *bp = &ch->sk.bones[pp->bone_id]; - - v3f lca, lcb; - v3_negate( pj->offset, lca ); - v3_add( bp->co, pp->offset, lcb ); - v3_sub( bj->co, lcb, lcb ); - - rb_constraint_position( &pj->rb, lca, &pp->rb, lcb ); - - rb_constraint_limits( &pj->rb, lca, &pp->rb, lcb, pj->limits ); - } + vg_error( "Required bone '%s' is missing from character '%s'\n", + bones[i].name, name ); + goto il_free_err; } } - /* INTEGRATION */ - for( int i=0; iragdoll_count; i++ ) - rb_iter( &ch->ragdoll[i].rb ); - - /* SHOES */ + player_init_ragdoll( src ); + free( src ); + return 1; - for( int i=0; iragdoll_count; i++ ) - rb_update_transform( &ch->ragdoll[i].rb ); +il_free_err: + free( src ); + return 0; } #endif diff --git a/player_physics.h b/player_physics.h new file mode 100644 index 0000000..baaa70e --- /dev/null +++ b/player_physics.h @@ -0,0 +1,799 @@ +/* + * Copyright 2021-2022 (C) Mount0 Software, Harry Godden - All Rights Reserved + * ----------------------------------------------------------------------------- + * + * Player physics and control submodule + * contains main physics models, input, and player control. + * + * ----------------------------------------------------------------------------- + */ + +#ifndef PLAYER_PHYSICS_H +#define PLAYER_PHYSICS_H + +#include "player.h" + +static void apply_gravity( v3f vel, float const timestep ) +{ + v3f gravity = { 0.0f, -9.6f, 0.0f }; + v3_muladds( vel, gravity, timestep, vel ); +} + +/* + * Called when launching into the air to predict and adjust trajectories + */ +static void player_start_air(void) +{ + if( player.in_air ) + return; + + player.in_air = 1; + + float pstep = ktimestep*10.0f; + float best_velocity_delta = -9999.9f; + float k_bias = 0.96f; + + v3f axis; + v3_cross( player.rb.up, player.rb.v, axis ); + v3_normalize( axis ); + player.land_log_count = 0; + + m3x3_identity( player.vr ); + + for( int m=-3;m<=12; m++ ) + { + float vmod = ((float)m / 15.0f)*0.09f; + + v3f pco, pco1, pv; + v3_copy( player.rb.co, pco ); + v3_muls( player.rb.v, k_bias, pv ); + + /* + * Try different 'rotations' of the velocity to find the best possible + * landing normal. This conserves magnitude at the expense of slightly + * unrealistic results + */ + + m3x3f vr; + v4f vr_q; + + q_axis_angle( vr_q, axis, vmod ); + q_m3x3( vr_q, vr ); + + m3x3_mulv( vr, pv, pv ); + v3_muladds( pco, pv, pstep, pco ); + + for( int i=0; i<50; i++ ) + { + v3_copy( pco, pco1 ); + apply_gravity( pv, pstep ); + + m3x3_mulv( vr, pv, pv ); + v3_muladds( pco, pv, pstep, pco ); + + ray_hit contact; + v3f vdir; + + v3_sub( pco, pco1, vdir ); + contact.dist = v3_length( vdir ); + v3_divs( vdir, contact.dist, vdir); + + if( ray_world( pco1, vdir, &contact )) + { + float land_delta = v3_dot( pv, contact.normal ); + u32 scolour = (u8)(vg_minf(-land_delta * 2.0f, 255.0f)); + + /* Bias prediction towords ramps */ + if( ray_hit_is_ramp( &contact ) ) + { + land_delta *= 0.1f; + scolour |= 0x0000a000; + } + + if( (land_delta < 0.0f) && (land_delta > best_velocity_delta) ) + { + best_velocity_delta = land_delta; + + v3_copy( contact.pos, player.land_target ); + + m3x3_copy( vr, player.vr_pstep ); + q_axis_angle( vr_q, axis, vmod*0.1f ); + q_m3x3( vr_q, player.vr ); + } + + v3_copy( contact.pos, + player.land_target_log[player.land_log_count] ); + player.land_target_colours[player.land_log_count] = + 0xff000000 | scolour; + + player.land_log_count ++; + + break; + } + } + } +} + +/* + * Main friction interface model + */ +static void player_physics_control(void) +{ + /* + * Computing localized friction forces for controlling the character + * Friction across X is significantly more than Z + */ + + v3f vel; + m3x3_mulv( player.rb.to_local, player.rb.v, vel ); + float slip = 0.0f; + + if( fabsf(vel[2]) > 0.01f ) + slip = fabsf(-vel[0] / vel[2]) * vg_signf(vel[0]); + + if( fabsf( slip ) > 1.2f ) + slip = vg_signf( slip ) * 1.2f; + player.slip = slip; + player.reverse = -vg_signf(vel[2]); + + float substep = ktimestep * 0.2f; + float fwd_resistance = (vg_get_button( "break" )? 5.0f: 0.02f) * -substep; + + for( int i=0; i<5; i++ ) + { + vel[2] = stable_force( vel[2], vg_signf( vel[2] ) * fwd_resistance ); + vel[0] = stable_force( vel[0], + vg_signf( vel[0] ) * -k_friction_lat*substep ); + } + + static double start_push = 0.0; + if( vg_get_button_down( "push" ) ) + start_push = vg_time; + + if( vg_get_button( "jump" ) ) + { + player.jump += ktimestep * k_jump_charge_speed; + + if( !player.jump_charge ) + player.jump_dir = player.reverse > 0.0f? 1: 0; + + player.jump_charge = 1; + } + + if( !vg_get_button("break") && vg_get_button( "push" ) ) + { + player.pushing = 1.0f; + player.push_time = vg_time-start_push; + + float cycle_time = player.push_time*k_push_cycle_rate, + amt = k_push_accel * (sinf(cycle_time)*0.5f+0.5f)*ktimestep, + current = v3_length( vel ), + new_vel = vg_minf( current + amt, k_max_push_speed ); + + new_vel -= vg_minf(current, k_max_push_speed); + vel[2] -= new_vel * player.reverse; + } + + /* Pumping */ + static float previous = 0.0f; + float delta = previous - player.grab, + pump = delta * k_pump_force*ktimestep; + previous = player.grab; + + v3f p1; + v3_muladds( player.rb.co, player.rb.up, pump, p1 ); + vg_line( player.rb.co, p1, 0xff0000ff ); + + vel[1] += pump; + + + m3x3_mulv( player.rb.to_world, vel, player.rb.v ); + + float steer = vg_get_axis( "horizontal" ); + player.iY -= vg_signf(steer)*powf(steer,2.0f) * k_steer_ground * ktimestep; + + v2_lerp( player.board_xy, (v2f){ slip*0.25f, 0.0f }, + ktimestep*5.0f, player.board_xy); +} + +/* + * Air control, no real physics + */ +static void player_physics_control_air(void) +{ + m3x3_mulv( player.vr, player.rb.v, player.rb.v ); + vg_line_cross( player.land_target, 0xff0000ff, 0.25f ); + + ray_hit hit; + + /* + * Prediction + */ + float pstep = ktimestep*10.0f; + + v3f pco, pco1, pv; + v3_copy( player.rb.co, pco ); + v3_copy( player.rb.v, pv ); + + float time_to_impact = 0.0f; + float limiter = 1.0f; + + for( int i=0; i<50; i++ ) + { + v3_copy( pco, pco1 ); + m3x3_mulv( player.vr_pstep, pv, pv ); + apply_gravity( pv, pstep ); + v3_muladds( pco, pv, pstep, pco ); + + ray_hit contact; + v3f vdir; + + v3_sub( pco, pco1, vdir ); + contact.dist = v3_length( vdir ); + v3_divs( vdir, contact.dist, vdir); + + float orig_dist = contact.dist; + if( ray_world( pco1, vdir, &contact )) + { + float angle = v3_dot( player.rb.up, contact.normal ); + v3f axis; + v3_cross( player.rb.up, contact.normal, axis ); + + time_to_impact += (contact.dist/orig_dist)*pstep; + limiter = vg_minf( 5.0f, time_to_impact )/5.0f; + limiter = 1.0f-limiter; + limiter *= limiter; + limiter = 1.0f-limiter; + + if( angle < 0.99f ) + { + v4f correction; + q_axis_angle( correction, axis, acosf(angle)*0.05f*(1.0f-limiter) ); + q_mul( correction, player.rb.q, player.rb.q ); + } + + vg_line_cross( contact.pos, 0xffff0000, 0.25f ); + break; + } + time_to_impact += pstep; + } + + player.iY -= vg_get_axis( "horizontal" ) * k_steer_air * ktimestep; + { + float iX = vg_get_axis( "vertical" ) * + player.reverse * k_steer_air * limiter * ktimestep; + + static float siX = 0.0f; + siX = vg_lerpf( siX, iX, k_steer_air_lerp ); + + v4f rotate; + q_axis_angle( rotate, player.rb.right, siX ); + q_mul( rotate, player.rb.q, player.rb.q ); + } + + v2f target = {0.0f,0.0f}; + v2_muladds( target, (v2f){ vg_get_axis("h1"), vg_get_axis("v1") }, + player.grab, target ); + v2_lerp( player.board_xy, target, ktimestep*3.0f, player.board_xy ); +} + +/* + * Entire Walking physics model + * TODO: sleep when under certain velotiy + */ +static void player_walk_physics(void) +{ + rigidbody *rbf = &player.collide_front, + *rbb = &player.collide_back; + + m3x3_copy( player.rb.to_world, player.collide_front.to_world ); + m3x3_copy( player.rb.to_world, player.collide_back.to_world ); + + float h0 = 0.3f, + h1 = 0.9f; + + m4x3_mulv( player.rb.to_world, (v3f){0.0f,h0,0.0f}, rbf->co ); + v3_copy( rbf->co, rbf->to_world[3] ); + m4x3_mulv( player.rb.to_world, (v3f){0.0f,h1,0.0f}, rbb->co ); + v3_copy( rbb->co, rbb->to_world[3] ); + + m4x3_invert_affine( rbf->to_world, rbf->to_local ); + m4x3_invert_affine( rbb->to_world, rbb->to_local ); + + rb_update_bounds( rbf ); + rb_update_bounds( rbb ); + + rb_debug( rbf, 0xff0000ff ); + rb_debug( rbb, 0xff0000ff ); + + rb_ct manifold[64]; + int len = 0; + + len += rb_sphere_scene( rbf, &world.rb_geo, manifold+len ); + len += rb_sphere_scene( rbb, &world.rb_geo, manifold+len ); + + rb_presolve_contacts( manifold, len ); + + for( int j=0; j<5; j++ ) + { + for( int i=0; in ); + vn += ct->bias; + + float temp = ct->norm_impulse; + ct->norm_impulse = vg_maxf( temp + vn, 0.0f ); + vn = ct->norm_impulse - temp; + + v3f impulse; + v3_muls( ct->n, vn, impulse ); + + v3_add( impulse, player.rb.v, player.rb.v ); + + /* friction */ + for( int j=0; j<2; j++ ) + { + float f = k_friction * ct->norm_impulse, + vt = v3_dot( player.rb.v, ct->t[j] ), + lambda = -vt; + + float temp = ct->tangent_impulse[j]; + ct->tangent_impulse[j] = vg_clampf( temp + lambda, -f, f ); + lambda = ct->tangent_impulse[j] - temp; + + v3_muladds( player.rb.v, ct->t[j], lambda, player.rb.v ); + } + } + } + + player.in_air = len==0?1:0; + + v3_zero( player.rb.w ); + q_axis_angle( player.rb.q, (v3f){0.0f,1.0f,0.0f}, -player.angles[0] ); + + v3f forward_dir = { sinf(player.angles[0]),0.0f,-cosf(player.angles[0]) }; + + v3f p1; + v3_muladds( player.rb.co, forward_dir, 2.0f, p1 ); + vg_line( player.rb.co, p1, 0xff0000ff ); + + float move_dead = 0.1f, + move = vg_get_axis("grabr")*0.5f + 0.5f - move_dead; + + if( move > 0.0f ) + { + float move_norm = move * (1.0f/(1.0f-move_dead)), + speed = vg_lerpf( 0.1f*k_runspeed, k_runspeed, move_norm ), + amt = k_walk_accel * ktimestep, + zvel = v3_dot( player.rb.v, forward_dir ), + new_vel = vg_minf( zvel + amt, speed ), + diff = new_vel - vg_minf( zvel, speed ); + + v3_muladds( player.rb.v, forward_dir, diff, player.rb.v ); + + /* TODO move */ + float walk_norm = 30.0f/(float)player.mdl.anim_walk->length, + run_norm = 30.0f/(float)player.mdl.anim_run->length ; + + player.walk_timer += ktimestep * vg_lerpf( walk_norm,run_norm,move_norm ); + } + else + { + player.walk_timer = 0.0f; + } + + player.rb.v[0] *= 1.0f - (ktimestep*k_walk_friction); + player.rb.v[2] *= 1.0f - (ktimestep*k_walk_friction); +} + +/* + * Physics collision detection, and control + */ +static void player_physics(void) +{ + /* + * Update collision fronts + */ + + rigidbody *rbf = &player.collide_front, + *rbb = &player.collide_back; + + m3x3_copy( player.rb.to_world, player.collide_front.to_world ); + m3x3_copy( player.rb.to_world, player.collide_back.to_world ); + + player.air_blend = vg_lerpf( player.air_blend, player.in_air, 0.1f ); + float h = player.air_blend*0.2f; + + m4x3_mulv( player.rb.to_world, (v3f){0.0f,h,-k_board_length}, rbf->co ); + v3_copy( rbf->co, rbf->to_world[3] ); + m4x3_mulv( player.rb.to_world, (v3f){0.0f,h, k_board_length}, rbb->co ); + v3_copy( rbb->co, rbb->to_world[3] ); + + m4x3_invert_affine( rbf->to_world, rbf->to_local ); + m4x3_invert_affine( rbb->to_world, rbb->to_local ); + + rb_update_bounds( rbf ); + rb_update_bounds( rbb ); + + rb_debug( rbf, 0xff00ffff ); + rb_debug( rbb, 0xffffff00 ); + + rb_ct manifold[64]; + int len = 0; + + len += rb_sphere_scene( rbf, &world.rb_geo, manifold+len ); + len += rb_sphere_scene( rbb, &world.rb_geo, manifold+len ); + + rb_presolve_contacts( manifold, len ); + v3f surface_avg = {0.0f, 0.0f, 0.0f}; + + if( !len ) + { + player_start_air(); + } + else + { + for( int i=0; i 0.5f ) + { + player_start_air(); + } + else + player.in_air = 0; + } + + for( int j=0; j<5; j++ ) + { + for( int i=0; ico, player.rb.co, delta ); + v3_cross( player.rb.w, delta, dv ); + v3_add( player.rb.v, dv, dv ); + + float vn = -v3_dot( dv, ct->n ); + vn += ct->bias; + + float temp = ct->norm_impulse; + ct->norm_impulse = vg_maxf( temp + vn, 0.0f ); + vn = ct->norm_impulse - temp; + + v3f impulse; + v3_muls( ct->n, vn, impulse ); + + if( fabsf(v3_dot( impulse, player.rb.forward )) > 10.0f || + fabsf(v3_dot( impulse, player.rb.up )) > 50.0f ) + { + player_kill(); + return; + } + + v3_add( impulse, player.rb.v, player.rb.v ); + v3_cross( delta, impulse, impulse ); + + /* + * W Impulses are limited to the Y and X axises, we don't really want + * roll angular velocities being included. + * + * Can also tweak the resistance of each axis here by scaling the wx,wy + * components. + */ + + float wy = v3_dot( player.rb.up, impulse ), + wx = v3_dot( player.rb.right, impulse )*1.5f; + + v3_muladds( player.rb.w, player.rb.up, wy, player.rb.w ); + v3_muladds( player.rb.w, player.rb.right, wx, player.rb.w ); + } + } + + float grabt = vg_get_axis( "grabr" )*0.5f+0.5f; + player.grab = vg_lerpf( player.grab, grabt, 0.14f ); + player.pushing = 0.0f; + + if( !player.in_air ) + { + v3f axis; + float angle = v3_dot( player.rb.up, surface_avg ); + v3_cross( player.rb.up, surface_avg, axis ); + + //float cz = v3_dot( player.rb.forward, axis ); + //v3_muls( player.rb.forward, cz, axis ); + + if( angle < 0.999f ) + { + v4f correction; + q_axis_angle( correction, axis, acosf(angle)*0.3f ); + q_mul( correction, player.rb.q, player.rb.q ); + } + + v3_muladds( player.rb.v, player.rb.up, + -k_downforce*ktimestep, player.rb.v ); + + player_physics_control(); + + if( !player.jump_charge && player.jump > 0.2f ) + { + v3f jumpdir; + + /* Launch more up if alignment is up else improve velocity */ + float aup = fabsf(v3_dot( (v3f){0.0f,1.0f,0.0f}, player.rb.up )), + mod = 0.5f, + dir = mod + aup*(1.0f-mod); + + v3_copy( player.rb.v, jumpdir ); + v3_normalize( jumpdir ); + v3_muls( jumpdir, 1.0f-dir, jumpdir ); + v3_muladds( jumpdir, player.rb.up, dir, jumpdir ); + v3_normalize( jumpdir ); + + float force = k_jump_force*player.jump; + v3_muladds( player.rb.v, jumpdir, force, player.rb.v ); + player.jump = 0.0f; + + player.jump_time = vg_time; + + audio_lock(); + audio_player_set_flags( &audio_player_extra, AUDIO_FLAG_SPACIAL_3D ); + audio_player_set_position( &audio_player_extra, player.rb.co ); + audio_player_set_vol( &audio_player_extra, 20.0f ); + audio_player_playclip( &audio_player_extra, &audio_jumps[rand()%4] ); + audio_unlock(); + } + } + else + { + player_physics_control_air(); + } + + if( !player.jump_charge ) + { + player.jump -= k_jump_charge_speed * ktimestep; + } + player.jump_charge = 0; + player.jump = vg_clampf( player.jump, 0.0f, 1.0f ); +} + +static void player_do_motion(void) +{ + float horizontal = vg_get_axis("horizontal"), + vertical = vg_get_axis("vertical"); + + if( player.on_board ) + player_physics(); + else + player_walk_physics(); + + /* Integrate velocity */ + v3f prevco; + v3_copy( player.rb.co, prevco ); + + apply_gravity( player.rb.v, ktimestep ); + v3_muladds( player.rb.co, player.rb.v, ktimestep, player.rb.co ); + + /* Real angular velocity integration */ + v3_lerp( player.rb.w, (v3f){0.0f,0.0f,0.0f}, 0.125f, player.rb.w ); + if( v3_length2( player.rb.w ) > 0.0f ) + { + v4f rotation; + v3f axis; + v3_copy( player.rb.w, axis ); + + float mag = v3_length( axis ); + v3_divs( axis, mag, axis ); + q_axis_angle( rotation, axis, mag*k_rb_delta ); + q_mul( rotation, player.rb.q, player.rb.q ); + } + + /* Faux angular velocity */ + v4f rotate; + + static float siY = 0.0f; + float lerpq = player.in_air? 0.04f: 0.3f; + siY = vg_lerpf( siY, player.iY, lerpq ); + + q_axis_angle( rotate, player.rb.up, siY ); + q_mul( rotate, player.rb.q, player.rb.q ); + player.iY = 0.0f; + + /* + * Gate intersection, by tracing a line over the gate planes + */ + for( int i=0; igate; + + if( gate_intersect( gate, player.rb.co, prevco ) ) + { + m4x3_mulv( gate->transport, player.rb.co, player.rb.co ); + m3x3_mulv( gate->transport, player.rb.v, player.rb.v ); + m3x3_mulv( gate->transport, player.vl, player.vl ); + m3x3_mulv( gate->transport, player.v_last, player.v_last ); + m3x3_mulv( gate->transport, player.m, player.m ); + m3x3_mulv( gate->transport, player.bob, player.bob ); + + v4f transport_rotation; + m3x3_q( gate->transport, transport_rotation ); + q_mul( transport_rotation, player.rb.q, player.rb.q ); + + world_routes_activate_gate( i ); + player.rb_gate_frame = player.rb; + player.in_air_frame = player.in_air; + player.on_board_frame = player.on_board; + + if( !player.on_board ) + { + v3f fwd_dir = {cosf(player.angles[0]), + 0.0f, + sinf(player.angles[0])}; + m3x3_mulv( gate->transport, fwd_dir, fwd_dir ); + + player.angles[0] = atan2f( fwd_dir[2], fwd_dir[0] ); + } + + m3x3_copy( player.vr, player.gate_vr_frame ); + m3x3_copy( player.vr_pstep, player.gate_vr_pstep_frame ); + + audio_lock(); + audio_play_oneshot( &audio_gate_lap, 1.0f ); + audio_unlock(); + break; + } + } + + rb_update_transform( &player.rb ); +} + +/* + * Free camera movement + */ +static void player_mouseview(void) +{ + if( gui_want_mouse() ) + return; + + static v2f mouse_last, + view_vel = { 0.0f, 0.0f }; + + if( vg_get_button_down( "primary" ) ) + v2_copy( vg_mouse, mouse_last ); + + else if( vg_get_button( "primary" ) ) + { + v2f delta; + v2_sub( vg_mouse, mouse_last, delta ); + v2_copy( vg_mouse, mouse_last ); + + v2_muladds( view_vel, delta, 0.001f, view_vel ); + } + + v2_muladds( view_vel, + (v2f){ vg_get_axis("h1"), vg_get_axis("v1") }, + 0.05f, view_vel ); + v2_muls( view_vel, 0.93f, view_vel ); + v2_add( view_vel, player.angles, player.angles ); + player.angles[1] = vg_clampf( player.angles[1], -VG_PIf*0.5f, VG_PIf*0.5f ); +} + +static void player_freecam(void) +{ + player_mouseview(); + + float movespeed = fc_speed; + v3f lookdir = { 0.0f, 0.0f, -1.0f }, + sidedir = { 1.0f, 0.0f, 0.0f }; + + m3x3_mulv( player.camera, lookdir, lookdir ); + m3x3_mulv( player.camera, sidedir, sidedir ); + + static v3f move_vel = { 0.0f, 0.0f, 0.0f }; + if( vg_get_button( "forward" ) ) + v3_muladds( move_vel, lookdir, ktimestep * movespeed, move_vel ); + if( vg_get_button( "back" ) ) + v3_muladds( move_vel, lookdir, ktimestep *-movespeed, move_vel ); + if( vg_get_button( "left" ) ) + v3_muladds( move_vel, sidedir, ktimestep *-movespeed, move_vel ); + if( vg_get_button( "right" ) ) + v3_muladds( move_vel, sidedir, ktimestep * movespeed, move_vel ); + + v3_muls( move_vel, 0.7f, move_vel ); + v3_add( move_vel, player.camera_pos, player.camera_pos ); +} + +static void player_camera_update(void) +{ + /* Update camera matrices */ + v4f qyaw, qpitch, qcam; + q_axis_angle( qyaw, (v3f){ 0.0f, 1.0f, 0.0f }, -player.angles[0] ); + q_axis_angle( qpitch, (v3f){ 1.0f, 0.0f, 0.0f }, -player.angles[1] ); + + q_mul( qyaw, qpitch, qcam ); + q_m3x3( qcam, player.camera ); + + v3_copy( player.camera_pos, player.camera[3] ); + m4x3_invert_affine( player.camera, player.camera_inverse ); +} + +static int reset_player( int argc, char const *argv[] ) +{ + struct respawn_point *rp = NULL, *r; + + if( argc == 1 ) + { + for( int i=0; iname, argv[0] ) ) + { + rp = r; + break; + } + } + + if( !rp ) + vg_warn( "No spawn named '%s'\n", argv[0] ); + } + + if( !rp ) + { + float min_dist = INFINITY; + + for( int i=0; ico, player.rb.co ); + + vg_info( "Dist %s : %f\n", r->name, d ); + if( d < min_dist ) + { + min_dist = d; + rp = r; + } + } + } + + if( !rp ) + { + vg_error( "No spawn found\n" ); + if( !world.spawn_count ) + return 0; + + rp = &world.spawns[0]; + } + + v4_copy( rp->q, player.rb.q ); + v3_copy( rp->co, player.rb.co ); + + player.vswitch = 1.0f; + player.slip_last = 0.0f; + player.is_dead = 0; + player.in_air = 1; + m3x3_identity( player.vr ); + + player.mdl.shoes[0] = 1; + player.mdl.shoes[1] = 1; + + rb_update_transform( &player.rb ); + m3x3_mulv( player.rb.to_world, (v3f){ 0.0f, 0.0f, -1.2f }, player.rb.v ); + m3x3_identity( player.gate_vr_frame ); + m3x3_identity( player.gate_vr_pstep_frame ); + + player.rb_gate_frame = player.rb; + player.on_board_frame = player.on_board; + player.in_air_frame = player.in_air; + return 1; +} + +#endif /* PLAYER_PHYSICS_H */ diff --git a/player_ragdoll.h b/player_ragdoll.h new file mode 100644 index 0000000..9079bbc --- /dev/null +++ b/player_ragdoll.h @@ -0,0 +1,215 @@ +#ifndef PLAYER_RAGDOLL_H +#define PLAYER_RAGDOLL_H + +#include "player.h" + +static float k_ragdoll_floatyiness = 10.0f, + k_ragdoll_floatydrag = 1.0f; + +/* + * Setup ragdoll colliders + */ +static void player_init_ragdoll( mdl_header *src ) +{ + struct player_model *mdl = &player.mdl; + + if( !mdl->sk.collider_count ) + { + mdl->ragdoll_count = 0; + return; + } + + mdl->ragdoll = malloc(sizeof(struct ragdoll_part) * mdl->sk.collider_count); + mdl->ragdoll_count = 0; + + for( u32 i=0; isk.bone_count; i ++ ) + { + struct skeleton_bone *bone = &mdl->sk.bones[i]; + + if( bone->collider ) + { + struct ragdoll_part *rp = &mdl->ragdoll[ mdl->ragdoll_count ++ ]; + rp->bone_id = i; + + v3f delta; + v3_sub( bone->hitbox[1], bone->hitbox[0], delta ); + v3_muls( delta, 0.5f, delta ); + + v3_add( bone->hitbox[0], delta, rp->offset ); + + v3_copy( delta, rp->rb.bbx[1] ); + v3_muls( delta, -1.0f, rp->rb.bbx[0] ); + + q_identity( rp->rb.q ); + v3_add( bone->co, rp->offset, rp->rb.co ); + rp->rb.type = k_rb_shape_box; + rp->rb.is_world = 0; + rp->parent = 0xffffffff; + + if( bone->parent ) + { + for( u32 j=0; jragdoll_count; j++ ) + { + if( mdl->ragdoll[ j ].bone_id == bone->parent ) + { + rp->parent = j; + break; + } + } + } + + /* TODO: refactor to use this style elswhere */ + struct mdl_node *pnode = mdl_node_from_id( src, bone->orig_node ); + struct classtype_bone *bone_inf = mdl_get_entdata( src, pnode ); + + rp->use_limits = bone_inf->use_limits; + v3_copy( bone_inf->angle_limits[0], rp->limits[0] ); + v3_copy( bone_inf->angle_limits[1], rp->limits[1] ); + + rb_init( &rp->rb ); + } + } +} + +/* + * Make the player model copy the ragdoll + */ +static void player_model_copy_ragdoll(void) +{ + struct player_model *mdl = &player.mdl; + + for( int i=0; iragdoll_count; i++ ) + { + struct ragdoll_part *part = &mdl->ragdoll[i]; + m4x3f offset; + m3x3_identity(offset); + v3_negate( part->offset, offset[3] ); + m4x3_mul( part->rb.to_world, offset, mdl->sk.final_mtx[part->bone_id] ); + } + + skeleton_apply_inverses( &mdl->sk ); +} + +/* + * Make the ragdoll copy the player model + */ +static void player_ragdoll_copy_model( v3f v ) +{ + struct player_model *mdl = &player.mdl; + + for( int i=0; iragdoll_count; i++ ) + { + struct ragdoll_part *part = &mdl->ragdoll[i]; + + v3f pos, offset; + u32 bone = part->bone_id; + + m4x3_mulv( mdl->sk.final_mtx[bone], mdl->sk.bones[bone].co, pos ); + m3x3_mulv( mdl->sk.final_mtx[bone], part->offset, offset ); + v3_add( pos, offset, part->rb.co ); + m3x3_q( mdl->sk.final_mtx[bone], part->rb.q ); + v3_copy( v, part->rb.v ); + v3_zero( part->rb.w ); + + rb_update_transform( &part->rb ); + } +} + +/* + * Draw rigidbody colliders for ragdoll + */ +static void player_debug_ragdoll(void) +{ + struct player_model *mdl = &player.mdl; + + for( u32 i=0; iragdoll_count; i ++ ) + rb_debug( &mdl->ragdoll[i].rb, 0xff00ff00 ); +} + +/* + * Ragdoll physics step + */ +static void player_ragdoll_iter(void) +{ + struct player_model *mdl = &player.mdl; + rb_solver_reset(); + + for( int i=0; iragdoll_count; i ++ ) + rb_collide( &mdl->ragdoll[i].rb, &world.rb_geo ); + + rb_presolve_contacts( rb_contact_buffer, rb_contact_count ); + + v3f rv; + +#if 0 + float shoe_vel[2] = {0.0f,0.0f}; + for( int i=0; i<2; i++ ) + if( mdl->shoes[i] ) + shoe_vel[i] = v3_length( mdl->ragdoll[i].rb.v ); +#endif + + for( int j=0; jragdoll_count; j++ ) + { + struct ragdoll_part *pj = &mdl->ragdoll[j]; + struct skeleton_bone *bj = &mdl->sk.bones[pj->bone_id]; + + if( pj->parent != 0xffffffff ) + { + struct ragdoll_part *pp = &mdl->ragdoll[pj->parent]; + struct skeleton_bone *bp = &mdl->sk.bones[pp->bone_id]; + + v3f lca, lcb; + v3_negate( pj->offset, lca ); + v3_add( bp->co, pp->offset, lcb ); + v3_sub( bj->co, lcb, lcb ); + + rb_debug_constraint_position( &pj->rb, lca, &pp->rb, lcb ); + + if( pj->use_limits ) + { + rb_debug_constraint_limits( &pj->rb, &pp->rb, lca, pj->limits ); + } + } + + v4f plane = {0.0f,1.0f,0.0f,0.0f}; + rb_effect_simple_bouyency( &pj->rb, plane, k_ragdoll_floatyiness, + k_ragdoll_floatydrag ); + } + + /* CONSTRAINTS */ + for( int i=0; i<10; i++ ) + { + rb_solve_contacts( rb_contact_buffer, rb_contact_count ); + + for( int j=0; jragdoll_count; j++ ) + { + struct ragdoll_part *pj = &mdl->ragdoll[j]; + struct skeleton_bone *bj = &mdl->sk.bones[pj->bone_id]; + + if( (pj->parent != 0xffffffff) && pj->use_limits ) + { + struct ragdoll_part *pp = &mdl->ragdoll[pj->parent]; + struct skeleton_bone *bp = &mdl->sk.bones[pp->bone_id]; + + v3f lca, lcb; + v3_negate( pj->offset, lca ); + v3_add( bp->co, pp->offset, lcb ); + v3_sub( bj->co, lcb, lcb ); + + rb_constraint_position( &pj->rb, lca, &pp->rb, lcb ); + + rb_constraint_limits( &pj->rb, lca, &pp->rb, lcb, pj->limits ); + } + } + } + + /* INTEGRATION */ + for( int i=0; iragdoll_count; i++ ) + rb_iter( &mdl->ragdoll[i].rb ); + + /* SHOES */ + for( int i=0; iragdoll_count; i++ ) + rb_update_transform( &mdl->ragdoll[i].rb ); +} + +#endif /* PLAYER_RAGDOLL_H */ diff --git a/skeleton.h b/skeleton.h index dc8342b..d74e660 100644 --- a/skeleton.h +++ b/skeleton.h @@ -61,6 +61,7 @@ static u32 skeleton_bone_id( struct skeleton *skele, const char *name ) if( !strcmp( skele->bones[i].name, name )) return i; } + return 0; } -- 2.25.1