update helpers/location to 'frosted' ui
[carveJwlIkooP6JGAAIwe30JlM.git] / player_ragdoll.c
index 3dc7ba6e51d8b2e657b84234f386502787d50642..6f9d430c56cefff23fe7ca69ed577f8e0597327b 100644 (file)
@@ -1,34 +1,97 @@
-#ifndef PLAYER_RAGDOLL_C
-#define PLAYER_RAGDOLL_C
+#pragma once
+#include "vg/vg_rigidbody.h"
+#include "vg/vg_rigidbody_collision.h"
+#include "vg/vg_rigidbody_constraints.h"
+#include "scene_rigidbody.h"
 
 #include "player.h"
+#include "player_dead.h"
 #include "audio.h"
 
-VG_STATIC void player_ragdoll_init(void)
+static float k_ragdoll_floatyiness = 20.0f,
+             k_ragdoll_floatydrag  = 1.0f,
+             k_ragdoll_limit_scale = 1.0f,
+             k_ragdoll_spring = 127.0f,
+             k_ragdoll_dampening = 15.0f,
+             k_ragdoll_correction = 0.5f,
+             k_ragdoll_angular_drag = 0.08f,
+             k_ragdoll_active_threshold = 5.0f;
+
+static int   k_ragdoll_div = 1,
+             ragdoll_frame = 0,
+             k_ragdoll_debug_collider = 1,
+             k_ragdoll_debug_constraints = 0;
+
+static int dev_ragdoll_saveload(int argc, const char *argv[]){
+   if( argc != 2 ){
+      vg_info( "Usage: ragdoll load/save filepath\n" );
+      return 1;
+   }
+
+   if( !strcmp(argv[0],"save") ){
+      FILE *fp = fopen( argv[1], "wb" );
+      if( !fp ){
+         vg_error( "Failed to open file\n" );
+         return 1;
+      }
+      fwrite( &localplayer.ragdoll.parts, 
+               sizeof(localplayer.ragdoll.parts), 1, fp );
+      fclose( fp );
+   }
+   else if( !strcmp(argv[0],"load") ){
+      FILE *fp = fopen( argv[1], "rb" );
+      if( !fp ){
+         vg_error( "Failed to open file\n" );
+         return 1;
+      }
+
+      fread( &localplayer.ragdoll.parts, 
+              sizeof(localplayer.ragdoll.parts), 1, fp );
+      fclose( fp );
+   }
+   else {
+      vg_error( "Unknown command: %s (options are: save,load)\n", argv[0] );
+      return 1;
+   }
+   
+   return 0;
+}
+
+void player_ragdoll_init(void)
 {
+   VG_VAR_F32( k_ragdoll_active_threshold );
+   VG_VAR_F32( k_ragdoll_angular_drag );
+   VG_VAR_F32( k_ragdoll_correction );
    VG_VAR_F32( k_ragdoll_limit_scale );
+   VG_VAR_F32( k_ragdoll_spring );
+   VG_VAR_F32( k_ragdoll_dampening );
    VG_VAR_I32( k_ragdoll_div );
    VG_VAR_I32( k_ragdoll_debug_collider );
    VG_VAR_I32( k_ragdoll_debug_constraints );
+   vg_console_reg_cmd( "ragdoll", dev_ragdoll_saveload, NULL );
 }
 
-VG_STATIC void player_init_ragdoll_bone_collider( struct skeleton_bone *bone,
-                                                  struct ragdoll_part *rp )
+void player_init_ragdoll_bone_collider( struct skeleton_bone *bone,
+                                        struct ragdoll_part *rp )
 {
+   f32 k_density = 8.0f,
+       k_inertia_scale = 2.0f;
+
    m4x3_identity( rp->collider_mtx );
 
+   rp->type = bone->collider;
    if( bone->collider == k_bone_collider_box ){
       v3f delta;
       v3_sub( bone->hitbox[1], bone->hitbox[0], delta );
       v3_muls( delta, 0.5f, delta );
       v3_add( bone->hitbox[0], delta, rp->collider_mtx[3] );
 
-      v3_copy( delta, rp->obj.rb.bbx[1] );
-      v3_muls( delta, -1.0f, rp->obj.rb.bbx[0] );
+      v3_muls( delta, -1.0f, rp->inf.box[0] );
+      v3_copy( delta,        rp->inf.box[1] );
 
-      q_identity( rp->obj.rb.q );
-      rp->obj.type = k_rb_shape_box;
       rp->colour = 0xffcccccc;
+
+      rb_setbody_box( &rp->rb, rp->inf.box, k_density, k_inertia_scale );
    }
    else if( bone->collider == k_bone_collider_capsule ){
       v3f v0, v1, tx, ty;
@@ -48,9 +111,9 @@ VG_STATIC void player_init_ragdoll_bone_collider( struct skeleton_bone *bone,
       v1[ major_axis ] = 1.0f;
       v3_tangent_basis( v1, tx, ty );
       
-      float r = (fabsf(v3_dot(tx,v0)) + fabsf(v3_dot(ty,v0))) * 0.25f,
-            l = fabsf(v0[ major_axis ]);
-      
+      rp->inf.capsule.r = (fabsf(v3_dot(tx,v0)) + fabsf(v3_dot(ty,v0))) * 0.25f;
+      rp->inf.capsule.h = fabsf(v0[ major_axis ]);
+
       /* orientation */
       v3_muls( tx, -1.0f, rp->collider_mtx[0] );
       v3_muls( v1, -1.0f, rp->collider_mtx[1] );
@@ -58,11 +121,10 @@ VG_STATIC void player_init_ragdoll_bone_collider( struct skeleton_bone *bone,
       v3_add( bone->hitbox[0], bone->hitbox[1], rp->collider_mtx[3] );
       v3_muls( rp->collider_mtx[3], 0.5f, rp->collider_mtx[3] );
 
-      rp->obj.type = k_rb_shape_capsule;
-      rp->obj.inf.capsule.height = l;
-      rp->obj.inf.capsule.radius = r;
-
       rp->colour = 0xff000000 | (0xff << (major_axis*8));
+      
+      rb_setbody_capsule( &rp->rb, rp->inf.capsule.r, rp->inf.capsule.h, 
+                           k_density, k_inertia_scale );
    }
    else{
       vg_warn( "type: %u\n", bone->collider );
@@ -72,18 +134,17 @@ VG_STATIC void player_init_ragdoll_bone_collider( struct skeleton_bone *bone,
    m4x3_invert_affine( rp->collider_mtx, rp->inv_collider_mtx );
 
    /* Position collider into rest */
-   m3x3_q( rp->collider_mtx, rp->obj.rb.q );
-   v3_add( rp->collider_mtx[3], bone->co, rp->obj.rb.co );
-   v3_zero( rp->obj.rb.v );
-   v3_zero( rp->obj.rb.w );
-   rb_init_object( &rp->obj );
+   m3x3_q( rp->collider_mtx, rp->rb.q );
+   v3_add( rp->collider_mtx[3], bone->co, rp->rb.co );
+   v3_zero( rp->rb.v );
+   v3_zero( rp->rb.w );
+   rb_update_matrices( &rp->rb );
 }
 
 /*
  * Get parent index in the ragdoll
  */
-VG_STATIC u32 ragdoll_bone_parent( struct player_ragdoll *rd,
-                                   struct player_avatar *av, u32 bone_id )
+u32 ragdoll_bone_parent( struct player_ragdoll *rd, u32 bone_id )
 {
    for( u32 j=0; j<rd->part_count; j++ )
       if( rd->parts[ j ].bone_id == bone_id )
@@ -94,21 +155,21 @@ VG_STATIC u32 ragdoll_bone_parent( struct player_ragdoll *rd,
 }
 
 /*
- * Setup ragdoll colliders 
+ * Setup ragdoll colliders from skeleton
  */
-VG_STATIC void player_setup_ragdoll_from_avatar( struct player_ragdoll *rd,
-                                                 struct player_avatar *av )
+void setup_ragdoll_from_skeleton( struct skeleton *sk,
+                                  struct player_ragdoll *rd )
 {
    rd->part_count = 0;
 
-   if( !av->sk.collider_count )
+   if( !sk->collider_count )
       return;
 
    rd->position_constraints_count = 0;
    rd->cone_constraints_count = 0;
 
-   for( u32 i=1; i<av->sk.bone_count; i ++ ){
-      struct skeleton_bone *bone = &av->sk.bones[i];
+   for( u32 i=1; i<sk->bone_count; i ++ ){
+      struct skeleton_bone *bone = &sk->bones[i];
 
       /*
        * Bones with colliders
@@ -119,7 +180,10 @@ VG_STATIC void player_setup_ragdoll_from_avatar( struct player_ragdoll *rd,
       if( rd->part_count > vg_list_size(rd->parts) )
          vg_fatal_error( "Playermodel has too many colliders" );
 
-      struct ragdoll_part *rp = &rd->parts[ rd->part_count ++ ];
+      u32 part_id = rd->part_count;
+      rd->part_count ++;
+
+      struct ragdoll_part *rp = &rd->parts[ part_id ];
       rp->bone_id = i;
       rp->parent = 0xffffffff;
 
@@ -131,20 +195,24 @@ VG_STATIC void player_setup_ragdoll_from_avatar( struct player_ragdoll *rd,
       if( !bone->parent )
          continue;
 
-      rp->parent = ragdoll_bone_parent( rd, av, bone->parent );
-      
+      rp->parent = ragdoll_bone_parent( rd, bone->parent );
       
       if( bone->orig_bone->flags & k_bone_flag_cone_constraint ){
-         struct rb_constr_pos *c = 
-            &rd->position_constraints[ rd->position_constraints_count ++ ];
+         u32 conid = rd->position_constraints_count;
+         rd->position_constraints_count ++;
+
+         struct rb_constr_pos *c = &rd->position_constraints[ conid ];
 
-         struct skeleton_bone *bj = &av->sk.bones[rp->bone_id];
+         struct skeleton_bone *bj = &sk->bones[rp->bone_id];
          struct ragdoll_part  *pp = &rd->parts[rp->parent];
-         struct skeleton_bone *bp = &av->sk.bones[pp->bone_id];
+         struct skeleton_bone *bp = &sk->bones[pp->bone_id];
+         
+         rd->constraint_associations[conid][0] = rp->parent;
+         rd->constraint_associations[conid][1] = part_id;
 
          /* Convention: rba -- parent, rbb -- child */
-         c->rba = &pp->obj.rb;
-         c->rbb = &rp->obj.rb;
+         c->rba = &pp->rb;
+         c->rbb = &rp->rb;
 
          v3f delta;
          v3_sub( bj->co, bp->co, delta );
@@ -157,8 +225,8 @@ VG_STATIC void player_setup_ragdoll_from_avatar( struct player_ragdoll *rd,
          struct rb_constr_swingtwist *a = 
             &rd->cone_constraints[ rd->cone_constraints_count ++ ];
 
-         a->rba = &pp->obj.rb;
-         a->rbb = &rp->obj.rb;
+         a->rba = &pp->rb;
+         a->rbb = &rp->rb;
          a->conet = cosf( inf->conet )-0.0001f;
          
          /* Store constraint in local space vectors */
@@ -186,8 +254,7 @@ VG_STATIC void player_setup_ragdoll_from_avatar( struct player_ragdoll *rd,
 /*
  * Make avatar copy the ragdoll
  */
-VG_STATIC void copy_ragdoll_pose_to_avatar( struct player_ragdoll *rd,
-                                            struct player_avatar *av )
+void copy_ragdoll_pose_to_localplayer( struct player_ragdoll *rd )
 {
    for( int i=0; i<rd->part_count; i++ ){
       struct ragdoll_part *part = &rd->parts[i];
@@ -198,76 +265,90 @@ VG_STATIC void copy_ragdoll_pose_to_avatar( struct player_ragdoll *rd,
       v3f co_int;
 
       float substep = vg.time_fixed_extrapolate;
-      v3_lerp( part->prev_co, part->obj.rb.co, substep, co_int );
-      q_nlerp( part->prev_q, part->obj.rb.q, substep, q_int );
+      v3_lerp( part->prev_co, part->rb.co, substep, co_int );
+      q_nlerp( part->prev_q, part->rb.q, substep, q_int );
 
       q_m3x3( q_int, mtx );
       v3_copy( co_int, mtx[3] );
 
-      m4x3_mul( mtx, part->inv_collider_mtx, av->sk.final_mtx[part->bone_id] );
+      m4x3_mul( mtx, part->inv_collider_mtx, 
+                localplayer.final_mtx[part->bone_id] );
    }
 
-   for( u32 i=1; i<av->sk.bone_count; i++ ){
-      struct skeleton_bone *sb = &av->sk.bones[i];
+   for( u32 i=1; i<localplayer.skeleton.bone_count; i++ ){
+      struct skeleton_bone *sb = &localplayer.skeleton.bones[i];
 
       if( sb->parent && !sb->collider ){
          v3f delta;
-         v3_sub( av->sk.bones[i].co, av->sk.bones[sb->parent].co, delta );
+         v3_sub( localplayer.skeleton.bones[i].co, 
+                 localplayer.skeleton.bones[sb->parent].co, delta );
 
          m4x3f posemtx;
          m3x3_identity( posemtx );
          v3_copy( delta, posemtx[3] );
 
          /* final matrix */
-         m4x3_mul( av->sk.final_mtx[sb->parent], posemtx, av->sk.final_mtx[i] );
+         m4x3_mul( localplayer.final_mtx[sb->parent], posemtx, 
+                   localplayer.final_mtx[i] );
       }
    }
 
-   skeleton_apply_inverses( &av->sk );
+   skeleton_apply_inverses( &localplayer.skeleton, localplayer.final_mtx );
 }
 
 /*
  * Make the ragdoll copy the player model
  */
-VG_STATIC void copy_avatar_pose_to_ragdoll( struct player_avatar *av,
-                                            struct player_ragdoll *rd,
-                                            v3f velocity )
+void copy_localplayer_to_ragdoll( struct player_ragdoll *rd, 
+                                  enum player_die_type type )
 {
+   v3f centroid;
+
+   v3f *bone_mtx = localplayer.final_mtx[localplayer.id_hip];
+   m4x3_mulv( bone_mtx, 
+              localplayer.skeleton.bones[localplayer.id_hip].co, centroid );
+
    for( int i=0; i<rd->part_count; i++ ){
       struct ragdoll_part *part = &rd->parts[i];
 
       v3f pos, offset;
       u32 bone = part->bone_id;
 
-      m4x3_mulv( av->sk.final_mtx[bone], av->sk.bones[bone].co, pos );
-      m3x3_mulv( av->sk.final_mtx[bone], part->collider_mtx[3], offset );
-      v3_add( pos, offset, part->obj.rb.co );
+      v3f *bone_mtx = localplayer.final_mtx[bone];
+
+      m4x3_mulv( bone_mtx, localplayer.skeleton.bones[bone].co, pos );
+      m3x3_mulv( bone_mtx, part->collider_mtx[3], offset );
+      v3_add( pos, offset, part->rb.co );
 
       m3x3f r;
-      m3x3_mul( av->sk.final_mtx[bone], part->collider_mtx, r );
-      m3x3_q( r, part->obj.rb.q );
+      m3x3_mul( bone_mtx, part->collider_mtx, r );
+      m3x3_q( r, part->rb.q );
+
+      v3f ra, v;
+      v3_sub( part->rb.co, centroid, ra );
+      v3_cross( localplayer.rb.w, ra, v );
+      v3_add( localplayer.rb.v, v, part->rb.v );
+
+      if( type == k_player_die_type_feet ){
+         if( (bone == localplayer.id_foot_l) || 
+             (bone == localplayer.id_foot_r) ){
+            v3_zero( part->rb.v );
+         }
+      }
 
-      v3_copy( velocity, part->obj.rb.v );
-      v3_zero( part->obj.rb.w );
+      v3_copy( localplayer.rb.w, part->rb.w );
 
-      v3_copy( part->obj.rb.co, part->prev_co );
-      v4_copy( part->obj.rb.q, part->prev_q );
+      v3_copy( part->rb.co, part->prev_co );
+      v4_copy( part->rb.q, part->prev_q );
       
-      rb_update_transform( &part->obj.rb );
+      rb_update_matrices( &part->rb );
    }
 }
 
-/*
- * Draw rigidbody colliders for ragdoll
- */
-VG_STATIC void player_debug_ragdoll(void)
-{
-}
-
 /*
  * Ragdoll physics step
  */
-VG_STATIC void player_ragdoll_iter( struct player_ragdoll *rd )
+void player_ragdoll_iter( struct player_ragdoll *rd )
 {
    world_instance *world = world_current_instance();
 
@@ -283,32 +364,36 @@ VG_STATIC void player_ragdoll_iter( struct player_ragdoll *rd )
    
    float contact_velocities[256];
 
+   rigidbody _null = {0};
+   _null.inv_mass = 0.0f;
+   m3x3_zero( _null.iI );
+
    for( int i=0; i<rd->part_count; i ++ ){
-      v4_copy( rd->parts[i].obj.rb.q, rd->parts[i].prev_q );
-      v3_copy( rd->parts[i].obj.rb.co, rd->parts[i].prev_co );
+      v4_copy( rd->parts[i].rb.q, rd->parts[i].prev_q );
+      v3_copy( rd->parts[i].rb.co, rd->parts[i].prev_co );
 
       if( rb_global_has_space() ){
          rb_ct *buf = rb_global_buffer();
 
          int l;
          
-         if( rd->parts[i].obj.type == k_rb_shape_capsule ){
-            l = rb_capsule__scene( rd->parts[i].obj.rb.to_world,
-                                   &rd->parts[i].obj.inf.capsule,
-                                   NULL, &world->rb_geo.inf.scene, buf,
+         if( rd->parts[i].type == k_bone_collider_capsule ){
+            l = rb_capsule__scene( rd->parts[i].rb.to_world,
+                                   &rd->parts[i].inf.capsule,
+                                   NULL, world->geo_bh, buf,
                                    k_material_flag_ghosts );
          }
-         else if( rd->parts[i].obj.type == k_rb_shape_box ){
-            l = rb_box__scene( rd->parts[i].obj.rb.to_world,
-                               rd->parts[i].obj.rb.bbx,
-                               NULL, &world->rb_geo.inf.scene, buf,
+         else if( rd->parts[i].type == k_bone_collider_box ){
+            l = rb_box__scene( rd->parts[i].rb.to_world,
+                               rd->parts[i].inf.box,
+                               NULL, world->geo_bh, buf,
                                k_material_flag_ghosts );
          }
          else continue;
 
          for( int j=0; j<l; j++ ){
-            buf[j].rba = &rd->parts[i].obj.rb;
-            buf[j].rbb = &world->rb_geo.rb;
+            buf[j].rba = &rd->parts[i].rb;
+            buf[j].rbb = &_null;
          }
 
          rb_contact_count += l;
@@ -324,23 +409,23 @@ VG_STATIC void player_ragdoll_iter( struct player_ragdoll *rd )
             if( !rb_global_has_space() )
                break;
 
-            if( rd->parts[j].obj.type != k_rb_shape_capsule )
+            if( rd->parts[j].type != k_bone_collider_capsule )
                continue;
 
-            if( rd->parts[i].obj.type != k_rb_shape_capsule )
+            if( rd->parts[i].type != k_bone_collider_capsule )
                continue;
 
             rb_ct *buf = rb_global_buffer();
 
-            int l = rb_capsule__capsule( rd->parts[i].obj.rb.to_world,
-                                         &rd->parts[i].obj.inf.capsule,
-                                         rd->parts[j].obj.rb.to_world,
-                                         &rd->parts[j].obj.inf.capsule,
+            int l = rb_capsule__capsule( rd->parts[i].rb.to_world,
+                                         &rd->parts[i].inf.capsule,
+                                         rd->parts[j].rb.to_world,
+                                         &rd->parts[j].inf.capsule,
                                          buf );
 
             for( int k=0; k<l; k++ ){
-               buf[k].rba = &rd->parts[i].obj.rb;
-               buf[k].rbb = &rd->parts[j].obj.rb;
+               buf[k].rba = &rd->parts[i].rb;
+               buf[k].rbb = &rd->parts[j].rb;
             }
 
             rb_contact_count += l;
@@ -348,12 +433,15 @@ VG_STATIC void player_ragdoll_iter( struct player_ragdoll *rd )
       }
    }
 
-   if( world->water.enabled ){
-      for( int j=0; j<rd->part_count; j++ ){
+   if( localplayer.drowned )
+   {
+      for( int j=0; j<rd->part_count; j++ )
+      {
          struct ragdoll_part *pj = &rd->parts[j];
 
-         if( run_sim ){
-            rb_effect_simple_bouyency( &pj->obj.rb, world->water.plane,
+         if( run_sim )
+         {
+            rb_effect_simple_bouyency( &pj->rb, world->water.plane,
                                         k_ragdoll_floatyiness,
                                         k_ragdoll_floatydrag );
          }
@@ -375,7 +463,8 @@ VG_STATIC void player_ragdoll_iter( struct player_ragdoll *rd )
       contact_velocities[i] = vn;
    }
 
-   rb_presolve_contacts( rb_contact_buffer, rb_contact_count );
+   rb_presolve_contacts( rb_contact_buffer, vg.time_fixed_delta, 
+                         rb_contact_count );
    rb_presolve_swingtwist_constraints( rd->cone_constraints,
                                        rd->cone_constraints_count );
 
@@ -383,8 +472,18 @@ VG_STATIC void player_ragdoll_iter( struct player_ragdoll *rd )
     * DEBUG
     */
    if( k_ragdoll_debug_collider ){
-      for( u32 i=0; i<rd->part_count; i ++ )
-         rb_object_debug( &rd->parts[i].obj, rd->parts[i].colour );
+      for( u32 i=0; i<rd->part_count; i ++ ){
+         struct ragdoll_part *rp = &rd->parts[i];
+
+         if( rp->type == k_bone_collider_capsule ){
+            vg_line_capsule( rp->rb.to_world, 
+                             rp->inf.capsule.r, rp->inf.capsule.h, rp->colour );
+         }
+         else if( rp->type == k_bone_collider_box ){
+            vg_line_boxf_transformed( rp->rb.to_world, 
+                                      rp->inf.box, rp->colour );
+         }
+      }
    }
 
    if( k_ragdoll_debug_constraints ){
@@ -396,28 +495,43 @@ VG_STATIC void player_ragdoll_iter( struct player_ragdoll *rd )
    }
 
    /*
-    * SOLVE CONSTRAINTS 
+    * SOLVE CONSTRAINTS  & Integrate
     */
    if( run_sim ){
-      for( int i=0; i<16; i++ ){
-         rb_solve_contacts( rb_contact_buffer, rb_contact_count );
-         rb_solve_swingtwist_constraints( rd->cone_constraints, 
-                                          rd->cone_constraints_count );
+      /* the solver is not very quickly converging so... */
+      for( int i=0; i<40; i++ ){
+         if( i<20 ){
+            rb_solve_contacts( rb_contact_buffer, rb_contact_count );
+            rb_solve_swingtwist_constraints( rd->cone_constraints, 
+                                             rd->cone_constraints_count );
+            rb_postsolve_swingtwist_constraints( rd->cone_constraints, 
+                                                 rd->cone_constraints_count );
+         }
          rb_solve_position_constraints( rd->position_constraints, 
                                         rd->position_constraints_count );
       }
 
-      for( int i=0; i<rd->part_count; i++ )
-         rb_iter( &rd->parts[i].obj.rb );
-
-      for( int i=0; i<rd->part_count; i++ )
-         rb_update_transform( &rd->parts[i].obj.rb );
-
+      rb_correct_position_constraints( rd->position_constraints,
+                                       rd->position_constraints_count, 
+                                       k_ragdoll_correction * 0.5f );
       rb_correct_swingtwist_constraints( rd->cone_constraints, 
-                                         rd->cone_constraints_count, 0.25f );
+                                         rd->cone_constraints_count,
+                                         k_ragdoll_correction * 0.25f );
+
+      for( int i=0; i<rd->part_count; i++ ){
+         rb_iter( &rd->parts[i].rb );
+
+         v3f w;
+         v3_copy( rd->parts[i].rb.w, w );
+         if( v3_length2( w ) > 0.00001f ){
+            v3_normalize( w );
+            v3_muladds( rd->parts[i].rb.w, w, -k_ragdoll_angular_drag,
+                        rd->parts[i].rb.w );
+         }
+      }
 
-      rb_correct_position_constraints( rd->position_constraints,
-                                       rd->position_constraints_count, 0.5f );
+      for( int i=0; i<rd->part_count; i++ )
+         rb_update_matrices( &rd->parts[i].rb );
    }
 
    rb_ct *stress = NULL;
@@ -441,6 +555,66 @@ VG_STATIC void player_ragdoll_iter( struct player_ragdoll *rd )
 
    static u32 temp_filter = 0;
 
+   /* 
+    * motorized joints 
+    */
+   if( run_sim && 
+         (v3_length2(player_dead.v_lpf)>(k_ragdoll_active_threshold*
+                                         k_ragdoll_active_threshold)) ){
+      mdl_keyframe anim[32];
+      skeleton_sample_anim( &localplayer.skeleton, player_dead.anim_bail,
+                            0.0f, anim );
+
+      for( u32 i=0; i<rd->cone_constraints_count; i ++ ){
+         rb_constr_swingtwist *st = &rd->cone_constraints[i];
+         rb_constr_pos *pc = &rd->position_constraints[i];
+
+         v3f va, vap;
+
+         m3x3_mulv( st->rbb->to_world, st->coneva, va );
+
+         /* calculate va as seen in rest position, from the perspective of the
+          * parent object, mapped to pose world space using the parents 
+          * transform. thats our target */
+
+         u32 id_p = rd->constraint_associations[i][0],
+             id_a = rd->constraint_associations[i][1];
+
+         struct ragdoll_part *pa = &rd->parts[ id_a ],
+                             *pp = &rd->parts[ id_p ];
+
+         mdl_keyframe *kf = &anim[ pa->bone_id-1 ];
+         m3x3_mulv( pa->collider_mtx, st->coneva, vap );
+         q_mulv( kf->q, vap, vap );
+
+         /* This could be a transfer function */
+         m3x3_mulv( pp->inv_collider_mtx, vap, vap );
+         m3x3_mulv( st->rba->to_world, vap, vap );
+
+         f32 d = v3_dot( vap, va ),
+             a = acosf( vg_clampf( d, -1.0f, 1.0f ) );
+
+         v3f axis;
+         v3_cross( vap, va, axis );
+
+         f32 Fs = -a * k_ragdoll_spring,
+             Fd = -v3_dot( st->rbb->w, axis ) * k_ragdoll_dampening,
+             F  = Fs+Fd;
+
+         v3f torque;
+         v3_muls( axis, F, torque );
+         v3_muladds( st->rbb->w, torque, vg.time_fixed_delta, st->rbb->w );
+
+         /* apply a adjustment to keep velocity at joint 0 */
+#if 0
+         v3f wcb, vcb;
+         m3x3_mulv( st->rbb->to_world, pc->lcb, wcb );
+         v3_cross( torque, wcb, vcb );
+         v3_muladds( st->rbb->v, vcb, vg.time_fixed_delta, st->rbb->v );
+#endif
+      }
+   }
+
    if( temp_filter ){
       temp_filter --;
       return;
@@ -449,9 +623,8 @@ VG_STATIC void player_ragdoll_iter( struct player_ragdoll *rd )
    if( stress ){
       temp_filter = 20;
       audio_lock();
-      audio_oneshot_3d( &audio_hits[vg_randu32()%5], stress->co, 20.0f, 1.0f );
+      audio_oneshot_3d( &audio_hits[vg_randu32(&vg.rand)%5], 
+                        stress->co, 20.0f, 1.0f );
       audio_unlock();
    }
 }
-
-#endif /* PLAYER_RAGDOLL_C */