shader_vblend_register();
shader_unlit_register();
+ player_register();
world_register();
- character_register();
water_register();
gate_register();
}
render_init();
gate_init();
world_init();
- character_init();
audio_init();
}
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;
if( sv_scene == 0 )
{
- character_load( &player.mdl, "ch_new" );
- character_init_ragdoll( &player.mdl );
+ player_load_model( "ch_new" );
world_load();
/*
* Copyright 2021-2022 (C) Mount0 Software, Harry Godden - All Rights Reserved
+ * -----------------------------------------------------------------------------
+ *
+ * Player head module
+ *
+ * -----------------------------------------------------------------------------
*/
#ifndef PLAYER_H
#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,
static int walk_grid_iterations = 1;
static float fc_speed = 10.0f;
+/*
+ * -----------------------------------------------------------------------------
+ * Memory
+ * -----------------------------------------------------------------------------
+ */
+
static struct gplayer
{
/* Physics */
u32 land_target_colours[22];
int land_log_count;
m3x3f vr,vr_pstep;
-
- struct character mdl;
v3f handl_target, handr_target,
handl, handr;
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 =
{
};
/*
- * 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; i<len; i++ )
- {
- struct contact *ct = &manifold[i];
-
- /*normal */
- float vn = -v3_dot( player.rb.v, 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 );
-
- 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<len; i++ )
- {
- v3_add( manifold[i].n, surface_avg, surface_avg );
-
-#if 0
- if( manifold[i].element_id <= world.sm_geo_std_oob.vertex_count )
- {
- player.is_dead = 1;
- character_ragdoll_copypose( &player.mdl, player.rb.v );
- return;
- }
-#endif
- }
-
- v3_normalize( surface_avg );
-
- if( v3_dot( player.rb.v, surface_avg ) > 0.5f )
- {
- player_start_air();
- }
- else
- player.in_air = 0;
- }
-
- for( int j=0; j<5; j++ )
- {
- for( int i=0; i<len; i++ )
- {
- struct contact *ct = &manifold[i];
-
- v3f dv, delta;
- v3_sub( ct->co, player.rb.co, delta );
- v3_cross( player.rb.w, delta, dv );
- v3_add( player.rb.v, dv, dv );
-
- float vn = -v3_dot( dv, ct->n );
- vn += ct->bias;
-
- float temp = ct->norm_impulse;
- ct->norm_impulse = vg_maxf( temp + vn, 0.0f );
- vn = ct->norm_impulse - temp;
-
- v3f impulse;
- v3_muls( ct->n, vn, impulse );
-
- if( fabsf(v3_dot( impulse, player.rb.forward )) > 10.0f ||
- fabsf(v3_dot( impulse, player.rb.up )) > 50.0f )
- {
- player.is_dead = 1;
- character_ragdoll_copypose( &player.mdl, player.rb.v );
- return;
- }
-
- v3_add( impulse, player.rb.v, player.rb.v );
- v3_cross( delta, impulse, impulse );
-
- /*
- * W Impulses are limited to the Y and X axises, we don't really want
- * roll angular velocities being included.
- *
- * Can also tweak the resistance of each axis here by scaling the wx,wy
- * components.
- */
-
- float wy = v3_dot( player.rb.up, impulse ),
- wx = v3_dot( player.rb.right, impulse )*1.5f;
-
- v3_muladds( player.rb.w, player.rb.up, wy, player.rb.w );
- v3_muladds( player.rb.w, player.rb.right, wx, player.rb.w );
- }
- }
-
- 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; i<world.routes.gate_count; i++ )
- {
- struct route_gate *rg = &world.routes.gates[i];
- teleport_gate *gate = &rg->gate;
-
- 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<vg_list_size(ambient_sprites); i++ )
- {
- if( !audio_player_is_playing( &ambient_sprites[i] ) )
- {
- sprite_avail = i;
- break;
- }
- }
-
- if( sprite_avail != -1 )
- {
- v3f waterpos;
- enum audio_sprite_type sprite_type =
- audio_sample_sprite_random( player.rb.co, waterpos );
-
- if( sprite_type != k_audio_sprite_type_none )
- {
- audio_player *avail = &ambient_sprites[ sprite_avail ];
-
- audio_player_set_vol( avail, 20.0f );
- audio_player_set_flags( avail, AUDIO_FLAG_SPACIAL_3D );
- audio_player_set_position( avail, waterpos );
-
- if( sprite_type == k_audio_sprite_type_grass )
- {
- audio_player_playclip( avail, &audio_grass[rand()%4] );
- }
- else if( sprite_type == k_audio_sprite_type_water )
- {
- audio_player_playclip( avail, &audio_water[rand()%6] );
- }
- }
- }
-
- if( freecam || player.is_dead || !player.on_board )
- {
- audio_player_set_vol( &audio_player0, 0.0f );
- audio_player_set_vol( &audio_player1, 0.0f );
- audio_player_set_vol( &audio_player2, 0.0f );
-
- int walk_phase = 0;
- if( vg_fractf(player.walk_timer) > 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; i<world.spawn_count; i++ )
- {
- r = &world.spawns[i];
- if( !strcmp( r->name, 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; i<world.spawn_count; i++ )
- {
- r = &world.spawns[i];
- float d = v3_dist2( r->co, 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; i<player.land_log_count; i++ )
- draw_cross( player.land_target_log[i],
+ vg_line_cross( player.land_target_log[i],
player.land_target_colours[i], 0.25f);
if( vg_get_axis("grabl")>0.0f)
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();
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 );
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 */
--- /dev/null
+#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 */
--- /dev/null
+#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<vg_list_size(ambient_sprites); i++ )
+ {
+ if( !audio_player_is_playing( &ambient_sprites[i] ) )
+ {
+ sprite_avail = i;
+ break;
+ }
+ }
+
+ if( sprite_avail != -1 )
+ {
+ v3f waterpos;
+ enum audio_sprite_type sprite_type =
+ audio_sample_sprite_random( player.rb.co, waterpos );
+
+ if( sprite_type != k_audio_sprite_type_none )
+ {
+ audio_player *avail = &ambient_sprites[ sprite_avail ];
+
+ audio_player_set_vol( avail, 20.0f );
+ audio_player_set_flags( avail, AUDIO_FLAG_SPACIAL_3D );
+ audio_player_set_position( avail, waterpos );
+
+ if( sprite_type == k_audio_sprite_type_grass )
+ {
+ audio_player_playclip( avail, &audio_grass[rand()%4] );
+ }
+ else if( sprite_type == k_audio_sprite_type_water )
+ {
+ audio_player_playclip( avail, &audio_water[rand()%6] );
+ }
+ }
+ }
+
+ if( freecam || player.is_dead || !player.on_board )
+ {
+ audio_player_set_vol( &audio_player0, 0.0f );
+ audio_player_set_vol( &audio_player1, 0.0f );
+ audio_player_set_vol( &audio_player2, 0.0f );
+
+ int walk_phase = 0;
+ if( vg_fractf(player.walk_timer) > 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 */
#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];
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; i<ch->sk.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; j<ch->ragdoll_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; i<ch->ragdoll_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; i<ch->ragdoll_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; i<ch->ragdoll_count; i ++ )
- rb_debug( &ch->ragdoll[i].rb, 0xff00ff00 );
-}
-
-static void character_ragdoll_iter( struct character *ch )
-{
- rb_solver_reset();
-
- for( int i=0; i<ch->ragdoll_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; j<ch->ragdoll_count; j++ )
+ for( int i=0; i<vg_list_size(anims); i++ )
{
- struct ragdoll_part *pj = &ch->ragdoll[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; i<vg_list_size(bones); i++ )
{
- rb_solve_contacts( rb_contact_buffer, rb_contact_count );
+ *bones[i].bone_id = skeleton_bone_id( &mdl->sk, bones[i].name );
- for( int j=0; j<ch->ragdoll_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; i<ch->ragdoll_count; i++ )
- rb_iter( &ch->ragdoll[i].rb );
-
- /* SHOES */
+ player_init_ragdoll( src );
+ free( src );
+ return 1;
- for( int i=0; i<ch->ragdoll_count; i++ )
- rb_update_transform( &ch->ragdoll[i].rb );
+il_free_err:
+ free( src );
+ return 0;
}
#endif
--- /dev/null
+/*
+ * 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; i<len; i++ )
+ {
+ struct contact *ct = &manifold[i];
+
+ /*normal */
+ float vn = -v3_dot( player.rb.v, 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 );
+
+ 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<len; i++ )
+ {
+ v3_add( manifold[i].n, surface_avg, surface_avg );
+ }
+
+ v3_normalize( surface_avg );
+
+ if( v3_dot( player.rb.v, surface_avg ) > 0.5f )
+ {
+ player_start_air();
+ }
+ else
+ player.in_air = 0;
+ }
+
+ for( int j=0; j<5; j++ )
+ {
+ for( int i=0; i<len; i++ )
+ {
+ struct contact *ct = &manifold[i];
+
+ v3f dv, delta;
+ v3_sub( ct->co, player.rb.co, delta );
+ v3_cross( player.rb.w, delta, dv );
+ v3_add( player.rb.v, dv, dv );
+
+ float vn = -v3_dot( dv, ct->n );
+ vn += ct->bias;
+
+ float temp = ct->norm_impulse;
+ ct->norm_impulse = vg_maxf( temp + vn, 0.0f );
+ vn = ct->norm_impulse - temp;
+
+ v3f impulse;
+ v3_muls( ct->n, vn, impulse );
+
+ if( fabsf(v3_dot( impulse, player.rb.forward )) > 10.0f ||
+ fabsf(v3_dot( impulse, player.rb.up )) > 50.0f )
+ {
+ player_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; i<world.routes.gate_count; i++ )
+ {
+ struct route_gate *rg = &world.routes.gates[i];
+ teleport_gate *gate = &rg->gate;
+
+ 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; i<world.spawn_count; i++ )
+ {
+ r = &world.spawns[i];
+ if( !strcmp( r->name, 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; i<world.spawn_count; i++ )
+ {
+ r = &world.spawns[i];
+ float d = v3_dist2( r->co, 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 */
--- /dev/null
+#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; i<mdl->sk.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; j<mdl->ragdoll_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; i<mdl->ragdoll_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; i<mdl->ragdoll_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; i<mdl->ragdoll_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; i<mdl->ragdoll_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; j<mdl->ragdoll_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; j<mdl->ragdoll_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; i<mdl->ragdoll_count; i++ )
+ rb_iter( &mdl->ragdoll[i].rb );
+
+ /* SHOES */
+ for( int i=0; i<mdl->ragdoll_count; i++ )
+ rb_update_transform( &mdl->ragdoll[i].rb );
+}
+
+#endif /* PLAYER_RAGDOLL_H */
if( !strcmp( skele->bones[i].name, name ))
return i;
}
+
return 0;
}