LPR - Walking
[carveJwlIkooP6JGAAIwe30JlM.git] / player_physics_walk.h
diff --git a/player_physics_walk.h b/player_physics_walk.h
new file mode 100644 (file)
index 0000000..35dbf29
--- /dev/null
@@ -0,0 +1,327 @@
+/*
+ * Copyright (C) 2021-2023 Mt.ZERO Software, Harry Godden - All Rights Reserved
+ */
+
+#define PLAYER_PHYSICS_WALK_H
+#ifndef PLAYER_PHYSICS_WALK_H
+#define PLAYER_PHYSICS_WALK_H
+
+#include "player.h"
+#include "camera.h"
+
+VG_STATIC struct player_walk
+{
+   struct
+   {
+      enum walk_activityaaaaaa
+      {
+         k_walk_activity_airaaaa,
+         k_walk_activity_groundaaaaaa
+      }
+      activity;
+   }
+   state;
+
+   rigidbody capsule;      /* TODO: rigidbody::collider_capsule */
+}
+player_walky = 
+{
+   .capsule = { .inf.capsule.height = 2.0f, .inf.capsule.radius = 0.3f }
+};
+
+VG_STATIC void player_walk_collider_configuration(void)
+{
+   v3_add( player.rb.co, (v3f){0.0f,1.0f,0.0f}, player_walky.capsule.co );
+   rb_update_transform( &player_walky.capsule );
+
+#if 0
+   float h0 = 0.3f,
+         h1 = 0.9f;
+
+   rigidbody *rbf = &player.collide_front,
+             *rbb = &player.collide_back;
+
+   v3_add( phys->rb.co, (v3f){0.0f,h0,0.0f}, rbf->co );
+   v3_add( phys->rb.co, (v3f){0.0f,h1,0.0f}, rbb->co );
+   v3_copy( rbf->co, rbf->to_world[3] );
+   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 );
+#endif
+}
+
+__attribute__ ((deprecated))
+VG_STATIC int player_walk_surface_standable( v3f n )
+{
+   return v3_dot( n, (v3f){0.0f,1.0f,0.0f} ) > 0.5f;
+}
+
+__attribute__ ((deprecated))
+VG_STATIC void player_walk_stepdown( struct player_walk *w )
+{
+   float max_dist = 0.4f;
+
+   v3f pa, pb;
+   v3_copy( player.rb.co, pa );
+   pa[1] += 0.3f;
+
+   v3_muladds( pa, (v3f){0.01f,1.0f,0.01f}, -max_dist, pb );
+   vg_line( pa, pb, 0xff000000 );
+
+   /* TODO: Make #define */
+   float r = 0.3f,
+         t;
+
+   v3f n;
+   if( spherecast_world( pa, pb, r, &t, n ) != -1 )
+   {
+      if( player_walk_surface_standable( n ) )
+      {
+         w->state.activity = k_walk_activity_ground;
+         v3_lerp( pa, pb, t+0.001f, player.rb.co );
+         player.rb.co[1] -= 0.3f;
+      }
+   }
+}
+
+__attribute__ ((deprecated))
+VG_STATIC void player_walk_physics( struct player_walk *w )
+{
+   v3_zero( player.rb.w );
+   q_axis_angle( player.rb.q, (v3f){0.0f,1.0f,0.0f}, -player.angles[0] );
+
+   rb_ct manifold[64];
+   int len;
+
+   v3f forward_dir = { sinf(player.angles[0]),0.0f,-cosf(player.angles[0]) };
+   v3f right_dir = { -forward_dir[2], 0.0f, forward_dir[0] };
+
+   v2f walk = { player.input_walkh->axis.value,
+                player.input_walkv->axis.value };
+
+   if( freecam )
+      v2_zero( walk );
+   
+   if( v2_length2(walk) > 0.001f )
+      v2_normalize_clamp( walk );
+
+   if( w->state.activity == k_walk_activity_air )
+   {
+      player_walk_collider_configuration();
+
+      /* allow player to accelerate a bit */
+      v3f walk_3d;
+      v3_muls( forward_dir, walk[1], walk_3d );
+      v3_muladds( walk_3d, right_dir, walk[0], walk_3d );
+
+      float current_vel = fabsf(v3_dot( walk_3d, player.rb.v )),
+            new_vel     = current_vel + VG_TIMESTEP_FIXED*999999.0f,
+            clamped_new = vg_clampf( new_vel, 0.0f, k_walkspeed ),
+            vel_diff    = vg_maxf( 0.0f, clamped_new - current_vel );
+
+      v3_muladds( player.rb.v, right_dir,   walk[0] * vel_diff, player.rb.v );
+      v3_muladds( player.rb.v, forward_dir, walk[1] * vel_diff, player.rb.v );
+
+      len = rb_capsule_scene( &w->capsule, &world.rb_geo, manifold );
+      rb_presolve_contacts( manifold, len );
+
+      for( int i=0; i<len; i++ )
+      {
+         struct contact *ct = &manifold[i];
+         if( v3_dot( ct->n, (v3f){0.0f,1.0f,0.0f} ) > 0.5f )
+            w->state.activity = k_walk_activity_ground;
+      }
+      
+      /*
+       * TODO: Compression
+       */
+      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.rb.v[1] += -k_gravity * k_rb_delta;
+      v3_muladds( player.rb.co, player.rb.v, k_rb_delta, player.rb.co );
+   }
+   else
+   {
+      player.walk = v2_length( walk );
+
+      if( player.input_walk->button.value )
+         v2_muls( walk, 0.5f, walk );
+
+      v2_muls( walk, k_walkspeed * VG_TIMESTEP_FIXED, walk );
+
+      /* Do XY translation */
+      v3f walk_apply, walk_measured;
+      v3_zero( walk_apply );
+      v3_muladds( walk_apply, right_dir,   walk[0], walk_apply );
+      v3_muladds( walk_apply, forward_dir, walk[1], walk_apply );
+      v3_add( walk_apply, player.rb.co, player.rb.co );
+
+      /* Directly resolve collisions */
+      player_walk_collider_configuration();
+      len = rb_capsule_scene( &w->capsule, &world.rb_geo, manifold );
+
+      v3f dt;
+      v3_zero( dt );
+      for( int j=0; j<8; j++ )
+      {
+         for( int i=0; i<len; i++ )
+         {
+            struct contact *ct = &manifold[i];
+
+            float resolved_amt = v3_dot( ct->n, dt ),
+                  remaining    = (ct->p-k_penetration_slop) - resolved_amt,
+                  apply        = vg_maxf( remaining, 0.0f ) * 0.3f;
+
+            v3_muladds( dt, ct->n, apply, dt );
+         }
+      }
+      v3_add( dt, player.rb.co, player.rb.co );
+
+      v3_add( dt, walk_apply, walk_measured );
+      v3_divs( walk_measured, VG_TIMESTEP_FIXED, player.rb.v );
+
+      if( len )
+      {
+         struct world_material *surface_mat = world_contact_material(manifold);
+         player.surface_prop = surface_mat->info.surface_prop;
+      }
+
+      w->state.activity = k_walk_activity_air;
+
+      /* jump */
+      if( player.input_jump->button.value )
+      {
+         player.rb.v[1] = 5.0f;
+         return;
+      }
+
+      /* Check if grounded by current manifold */
+      for( int i=0; i<len; i++ )
+      {
+         struct contact *ct = &manifold[i];
+         if( player_walk_surface_standable( ct->n ) )
+            w->state.activity = k_walk_activity_ground;
+      }
+      
+      /* otherwise... */
+      if( w->state.activity == k_walk_activity_air )
+         player_walk_stepdown( w );
+   }
+}
+
+__attribute__ ((deprecated))
+VG_STATIC void player_walk_animate( void *_w, 
+                                    struct skeleton *sk, player_pose pose )
+{
+   struct player_walk *w = _w;
+
+   {
+      float fly = (w->state.activity == k_walk_activity_air)? 1.0f: 0.0f,
+            rate;
+
+      if( w->state.activity == k_walk_activity_air )
+         rate = 2.4f;
+      else
+         rate = 9.0f;
+
+      player.ffly  = vg_lerpf( player.ffly, fly, rate*vg.time_delta );
+      player.frun  = vg_lerpf( player.frun,
+                               player.walk *
+                               (1.0f + player.input_walk->button.value*0.5f),
+                               2.0f*vg.time_delta );
+   }
+
+   player_pose apose, bpose;
+
+   if( player.walk > 0.025f )
+   {
+      /* TODO move */
+      float walk_norm = 30.0f/(float)player.mdl.anim_walk->length,
+            run_norm  = 30.0f/(float)player.mdl.anim_run->length,
+            walk_adv  = vg_lerpf( walk_norm, run_norm, player.walk );
+
+      player.walk_timer += walk_adv * vg.time_delta;
+   }
+   else
+   {
+      player.walk_timer = 0.0f;
+   }
+
+   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_clampf( player.frun*15.0f, 0.0f, 1.0f ),
+         idle_walk = vg_clampf( (player.frun-0.1f)/(1.0f-0.1f), 0.0f, 1.0f );
+
+   /* walk/run */
+   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 );
+
+   /* idle */
+   skeleton_sample_anim( sk, player.mdl.anim_idle, vg.time*0.1f, bpose );
+   skeleton_lerp_pose( sk, apose, bpose, 1.0f-idle_walk, apose );
+
+   /* air */
+   skeleton_sample_anim( sk, player.mdl.anim_jump, vg.time*0.6f, bpose );
+   skeleton_lerp_pose( sk, apose, bpose, player.ffly, 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.visual_transform[3], mtx[3] );
+
+   skeleton_apply_transform( &player.mdl.sk, mtx );
+   skeleton_debug( &player.mdl.sk );
+}
+
+#endif /* PLAYER_PHYSICS_WALK_H */