a lot
authorhgn <hgodden00@gmail.com>
Sun, 17 Jul 2022 04:46:44 +0000 (05:46 +0100)
committerhgn <hgodden00@gmail.com>
Sun, 17 Jul 2022 04:46:44 +0000 (05:46 +0100)
29 files changed:
blender_export.py
character.h
common.h
main.c
model.h
models/mp_dev.mdl
models/rs_cars.mdl [new file with mode: 0644]
models/rs_foliage.mdl
player.h
rigidbody.h
shaders/character.fs
shaders/character.h
shaders/character.vs
shaders/common_world.glsl
shaders/gpos.fs
shaders/gpos.h
shaders/planeinf.h
shaders/sky.h
shaders/standard.h
shaders/standard.vs
shaders/terrain.fs
shaders/terrain.h
shaders/unlit.h
shaders/vblend.fs
shaders/vblend.h
shaders/water.fs
shaders/water.h
traffic.h [new file with mode: 0644]
world.h

index 16d1f381e201f2c9625c335690a4404134d38150..05f687a42530d0b9b670fcc6b54621b10f32bcb1 100644 (file)
@@ -1,4 +1,5 @@
 import bpy, math, gpu
+import cProfile
 from ctypes import *
 from mathutils import *
 from gpu_extras.batch import batch_for_shader
@@ -75,7 +76,8 @@ class mdl_header(Structure):
 
 class classtype_gate(Structure):
    _pack_ = 1
-   _fields_ = [("target",c_uint32)]
+   _fields_ = [("target",c_uint32),
+               ("target1",c_uint32)]
 
 class classtype_block(Structure):
    _pack_ = 1
@@ -89,6 +91,11 @@ class classtype_water(Structure):
    _pack_ = 1
    _fields_ = [("temp",c_uint32)]
 
+class classtype_car_path(Structure):
+   _pack_ = 1
+   _fields_ = [("target",c_uint32),
+               ("target1",c_uint32)]
+
 # Exporter
 # ==============================================================================
 
@@ -227,10 +234,11 @@ def write_model(name):
          block = classtype_block()
          block.bbx[0][0] =  source.v0[0]
          block.bbx[0][1] =  source.v0[2]
-         block.bbx[0][2] = -source.v0[1]
+         block.bbx[0][2] = -source.v1[1]
+
          block.bbx[1][0] =  source.v1[0]
          block.bbx[1][1] =  source.v1[2]
-         block.bbx[1][2] = -source.v1[1]
+         block.bbx[1][2] = -source.v0[1]
          entdata_buffer += [block]
 
       elif classtype == 'k_classtype_spawn':
@@ -238,6 +246,20 @@ def write_model(name):
 
       elif classtype == 'k_classtype_water':
          node.classtype = 4
+      elif classtype == 'k_classtype_car_path':
+         node.classtype = 5
+         entdata_length += sizeof( classtype_car_path )
+
+         pn = classtype_car_path()
+         pn.target = 0
+         pn.target1 = 0
+
+         if obj.cv_data.target != None: 
+            pn.target = obj.cv_data.target.cv_data.uid
+         if obj.cv_data.target1 != None: 
+            pn.target1 = obj.cv_data.target1.cv_data.uid
+
+         entdata_buffer += [pn]
 
       # Process meshes
       #
@@ -296,11 +318,21 @@ def write_model(name):
                   if data.vertex_colors:
                      colour = data.vertex_colors.active.data[li].color
 
-                  key = (round(co[0],4),round(co[1],4),round(co[2],4),\
-                         round(norm[0],4),round(norm[1],4),round(norm[2],4),\
-                         round(uv[0],4),round(uv[1],4),\
-                         round(colour[0],4),round(colour[1],4),\
-                         round(colour[2],4),round(colour[3],4))
+                  TOLERENCE = 4
+                  m = float(10**TOLERENCE)
+
+                  key = (int(co[0]*m+0.5),\
+                         int(co[1]*m+0.5),\
+                         int(co[2]*m+0.5),\
+                         int(norm[0]*m+0.5),\
+                         int(norm[1]*m+0.5),\
+                         int(norm[2]*m+0.5),\
+                         int(uv[0]*m+0.5),\
+                         int(uv[1]*m+0.5),\
+                         int(colour[0]*m+0.5),\
+                         int(colour[1]*m+0.5),\
+                         int(colour[2]*m+0.5),\
+                         int(colour[3]*m+0.5))
 
                   if key in boffa:
                      indice_buffer += [boffa[key]]
@@ -407,11 +439,34 @@ def cv_draw():
    gpu.state.line_width_set(2.0)
    gpu.state.face_culling_set('BACK')
    gpu.state.depth_test_set('NONE')
-   gpu.state.blend_set('ADDITIVE')
+   gpu.state.blend_set('NONE')
 
    verts = []
    colours = []
 
+   def drawbezier(p0,h0,p1,h1,c0,c1):
+      nonlocal verts, colours
+
+      verts += [p0]
+      verts += [h0]
+      colours += [(0.5,0.5,0.5,1.0),(0.5,0.5,0.5,1)]
+      verts += [p1]
+      verts += [h1]
+      colours += [(1.0,1.0,1,1),(1,1,1,1)]
+      
+      last = p0
+      for i in range(10):
+         t = (i+1)/10
+         a0 = 1-t
+
+         tt = t*t
+         ttt = tt*t
+         p=ttt*p1+(3*tt-3*ttt)*h1+(3*ttt-6*tt+3*t)*h0+(3*tt-ttt-3*t+1)*p0
+         verts += [(last[0],last[1],last[2])]
+         verts += [(p[0],p[1],p[2])]
+         colours += [c0*a0+c1*(1-a0),c0*a0+c1*(1-a0)]
+         last = p
+
    for obj in bpy.context.collection.objects:
       if obj.cv_data.classtype == 'k_classtype_gate':
          if obj.cv_data.target != None:
@@ -444,6 +499,45 @@ def cv_draw():
             verts += [(v1[0],v1[1],v1[2])]
             colours += [(1,1,0,1),(1,1,0,1)]
 
+      elif obj.cv_data.classtype == 'k_classtype_spawn':
+         vs = [None]*4
+         vs[0] = obj.matrix_world @ Vector((0,0,0))
+         vs[1] = obj.matrix_world @ Vector((0,2,0))
+         vs[2] = obj.matrix_world @ Vector((0.5,1,0))
+         vs[3] = obj.matrix_world @ Vector((-0.5,1,0))
+         indices = [(0,1),(1,2),(1,3)]
+         for l in indices:
+            v0 = vs[l[0]]
+            v1 = vs[l[1]]
+            verts += [(v0[0],v0[1],v0[2])]
+            verts += [(v1[0],v1[1],v1[2])]
+            colours += [(0,1,1,1),(0,1,1,1)]
+
+      elif obj.cv_data.classtype == 'k_classtype_car_path':
+         p0 = obj.location
+         h0 = obj.matrix_world @ Vector((1,0,0))
+
+         v0 = obj.matrix_world.to_quaternion() @ Vector((1,0,0))
+         c0 = Vector((v0.x*0.5+0.5, v0.y*0.5+0.5, 0.0, 1.0))
+
+         if obj.cv_data.target != None:
+            p1 = obj.cv_data.target.location
+            h1 = obj.cv_data.target.matrix_world @ Vector((-1,0,0))
+
+            v1 = obj.cv_data.target.matrix_world.to_quaternion()@Vector((1,0,0))
+            c1 = Vector((v1.x*0.5+0.5, v1.y*0.5+0.5, 0.0, 1.0))
+
+            drawbezier( p0, h0, p1, h1, c0, c1 )
+
+         if obj.cv_data.target1 != None:
+            p1 = obj.cv_data.target1.location
+            h1 = obj.cv_data.target1.matrix_world @ Vector((-1,0,0))
+
+            v1 = obj.cv_data.target1.matrix_world.to_quaternion()@Vector((1,0,0))
+            c1 = Vector((v1.x*0.5+0.5, v1.y*0.5+0.5, 0.0, 1.0))
+
+            drawbezier( p0, h0, p1, h1, c0, c1 )
+
    lines = batch_for_shader(\
          cv_view_shader, 'LINES', \
          { "pos":verts, "color":colours })
@@ -468,6 +562,8 @@ class CV_OBJ_SETTINGS(bpy.types.PropertyGroup):
 
    target: bpy.props.PointerProperty( type=bpy.types.Object, name="target", \
          poll=cv_poll_target )
+   target1: bpy.props.PointerProperty( type=bpy.types.Object, name="target1", \
+         poll=cv_poll_target )
 
    classtype: bpy.props.EnumProperty(
       name="Format", 
@@ -476,7 +572,8 @@ class CV_OBJ_SETTINGS(bpy.types.PropertyGroup):
       ('k_classtype_gate', "k_classtype_gate", "", 1),
       ('k_classtype_block', "k_classtype_block", "", 2),
       ('k_classtype_spawn', "k_classtype_spawn", "", 3),
-      ('k_classtype_water', "k_classtype_water", "", 4)
+      ('k_classtype_water', "k_classtype_water", "", 4),
+      ('k_classtype_car_path', "k_classtype_car_path", "", 5)
       ])
 
 class CV_OBJ_PANEL(bpy.types.Panel):
@@ -493,6 +590,9 @@ class CV_OBJ_PANEL(bpy.types.Panel):
 
       if active_object.cv_data.classtype == 'k_classtype_gate':
          _.layout.prop( active_object.cv_data, "target" )
+      elif active_object.cv_data.classtype == 'k_classtype_car_path':
+         _.layout.prop( active_object.cv_data, "target" )
+         _.layout.prop( active_object.cv_data, "target1" )
       elif active_object.cv_data.classtype == 'k_classtype_block':
          mesh = active_object.data
 
@@ -513,13 +613,19 @@ class CV_INTERFACE(bpy.types.Panel):
       layout = _.layout
       layout.operator( "carve.compile_all" )
 
+def test_compile():
+   for col in bpy.data.collections["export"].children:
+      write_model( col.name )
+
 class CV_COMPILE(bpy.types.Operator):
    bl_idname="carve.compile_all"
    bl_label="Compile All"
 
    def execute(_,context):
-      for col in bpy.data.collections["export"].children:
-         write_model( col.name )
+      test_compile()
+      #cProfile.runctx("test_compile()",globals(),locals(),sort=1)
+      #for col in bpy.data.collections["export"].children:
+      #   write_model( col.name )
 
       return {'FINISHED'}
 
index 5ce456f24a1648493bd2056aa57156c4efac401b..39ee472ab436a5805e49f8694cd825af37215ee0 100644 (file)
@@ -441,11 +441,14 @@ void character_final_pose( struct character *ch, v3f cog,
          tilt = vg_clampf(cog[2], -1.0f, 1.0f) * 0.3f;
 
    v4f rz; m4x3f tr;
-   q_axis_angle( rz, (v3f){0.0f,0.0f,1.0f}, -cog[0]*0.6f );
+   q_axis_angle( rz, (v3f){0.0f,0.0f,1.0f}, -cog[0]*0.3f );
    q_m3x3( rz, tr );
-   v3_copy( (v3f){0.0f,dip,tilt}, tr[3] );
+   m3x3_identity( tr );
+   //v3_copy( (v3f){0.0f,dip,tilt}, tr[3] );
+   v3_copy( cog, tr[3] );
 
-   m4x3_mulv( tr, pose->b0, npose.b0 );
+   v3_muladd( pose->b0, tr[3], (v3f){0.85f,1.0f,1.0f}, npose.b0 );
+   //m4x3_mulv( tr, pose->b0, npose.b0 );
    m4x3_mulv( tr, pose->b1, npose.b1 );
    m4x3_mulv( tr, pose->p, npose.p );
    m4x3_mulv( tr, pose->pl, npose.pl );
index 21251546e00336095bbc8b857d3e17d77e4093c4..2ee2d7cd55f70669b1cc310d12da17e31f68bbc1 100644 (file)
--- a/common.h
+++ b/common.h
@@ -15,7 +15,8 @@ enum classtype
    k_classtype_gate = 1,
    k_classtype_block = 2,
    k_classtype_spawn = 3,
-   k_classtype_water = 4
+   k_classtype_water = 4,
+   k_classtype_car_path = 5
 };
 
 /* TODO: he needs a home somewhere */
diff --git a/main.c b/main.c
index 84de4100c203110e5c6da57f5c0512623196bc5b..ca2b99717ff0ad232772de970ca33156bf01a257 100644 (file)
--- a/main.c
+++ b/main.c
@@ -90,7 +90,7 @@ static int playermodel( int argc, char const *argv[] )
 void vg_start(void)
 {
    vg_convar_push( (struct vg_convar){
-      .name = "freecam",
+      .name = "fc",
       .data = &freecam,
       .data_type = k_convar_dtype_i32,
       .opt_i32 = { .min=0, .max=1, .clamp=1 },
@@ -163,6 +163,8 @@ void vg_free(void)
 void vg_update(void)
 {
    player_update();
+   world_update();
+   //traffic_visualize( world.traffic, world.traffic_count );
 }
 
 static void vg_framebuffer_resize( int w, int h )
diff --git a/model.h b/model.h
index 4ed39d4780500a4d876c19955ed4aea24dbb8f46..a65f11fd2ae20dcdde75ab1e72f6a50545af56f6 100644 (file)
--- a/model.h
+++ b/model.h
@@ -50,8 +50,9 @@ struct mdl_node
    v3f co;
    v4f q;
    v3f s;
-
-   u32 submesh_start,
+   
+   union{ u32 submesh_start, sub_uid; };
+   u32 
        submesh_count,
        classtype,
        offset,
@@ -94,6 +95,11 @@ struct classtype_water
    u32 temp;
 };
 
+struct classtype_car_path
+{
+   u32 target, target1;
+};
+
 #pragma pack(pop)
 
 /*
@@ -361,9 +367,9 @@ static mdl_material *mdl_material_from_id( mdl_header *mdl, u32 id )
 static void mdl_node_transform( mdl_node *pnode, m4x3f transform )
 {
    q_m3x3( pnode->q, transform );
-   transform[0][0] *= pnode->s[0];
-   transform[1][1] *= pnode->s[1];
-   transform[2][2] *= pnode->s[2];
+   v3_muls( transform[0], pnode->s[0], transform[0] );
+   v3_muls( transform[1], pnode->s[1], transform[1] );
+   v3_muls( transform[2], pnode->s[2], transform[2] );
    v3_copy( pnode->co, transform[3] );
 }
 
index bea2f18907a608e08736f08757c9b3df6015f028..84d6cc30964be682f30ec85f3e87daa746c2fea3 100644 (file)
Binary files a/models/mp_dev.mdl and b/models/mp_dev.mdl differ
diff --git a/models/rs_cars.mdl b/models/rs_cars.mdl
new file mode 100644 (file)
index 0000000..3b643c1
Binary files /dev/null and b/models/rs_cars.mdl differ
index 43d9033761ee7af6d5be230beed9726640b8dfc3..b65581bc03924cb03da8727bdde198d917ee27fe 100644 (file)
Binary files a/models/rs_foliage.mdl and b/models/rs_foliage.mdl differ
index bd7304dc614550c8978345c08cce9fb893a363ae..0e38d856aa7ccdb09cc57bd5a09a4c2a176b962b 100644 (file)
--- a/player.h
+++ b/player.h
@@ -6,18 +6,43 @@
 #include "character.h"
 #include "bvh.h"
 
+/* 
+ * Constants 
+ */
+
+static float 
+   k_walkspeed             = 2.0f,
+   k_board_radius          = 0.25f,
+   k_board_length          = 0.65f,
+   k_board_allowance       = 0.04f,
+   k_friction_lat          = 8.68f,
+   k_friction_resistance   = 0.02f,
+   k_max_push_speed        = 16.0f,
+   k_push_accel            = 5.0f,
+   k_push_cycle_rate       = 8.0f,
+   k_steer_ground          = 2.5f,
+   k_steer_air             = 3.6f,
+   k_steer_air_lerp        = 0.3f,
+   k_downforce             = 5.0f;
+
 static int freecam = 0;
-static float k_walkspeed = 2.0f; 
 static int walk_grid_iterations = 1;
 
 static struct gplayer
 {
    /* Physics */
-   v3f co, v, a, v_last, m, bob, vl;
+   v3f co, v, w, a, v_last, m, bob, vl;
+
+   /* Utility */
+   v3f up, right, forward;
+
    v4f rot;
    float vswitch, slip, slip_last,
          reverse;
 
+   int rf, rb;
+   v3f ctf, ctb;
+
    float iY;   /* Yaw inertia */
    int in_air, is_dead, on_board;
 
@@ -50,10 +75,9 @@ player =
    .on_board = 1
 };
 
-static float *player_cam_pos(void)
-{
-   return player.camera_pos;
-}
+/* 
+ * Player API
+ */
 
 static void player_transform_update(void)
 {
@@ -62,67 +86,16 @@ static void player_transform_update(void)
    v3_copy( player.co, player.to_world[3] );
 
    m4x3_invert_affine( player.to_world, player.to_local );
-}
-
-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.co );
-
-         if( d < min_dist )
-         {
-            min_dist = d;
-            rp = r;
-         }
-      }
-   }
-
-   if( !rp )
-   {
-      vg_error( "No spawn found\n" );
-      return 0;
-   }
-
-   v4_copy( r->q, player.rot );
-   v3_copy( r->co, player.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;
-
-   player_transform_update();
-   m3x3_mulv( player.to_world, (v3f){ 0.0f, 0.0f, -0.2f }, player.v );
-   return 1;
+   m3x3_mulv( player.to_world, (v3f){1.0f,0.0f, 0.0f}, player.right );
+   m3x3_mulv( player.to_world, (v3f){0.0f,1.0f, 0.0f}, player.up );
+   m3x3_mulv( player.to_world, (v3f){0.0f,0.0f,-1.0f}, player.forward );
 }
 
+/*
+ * Free camera movement
+ */
+
 static void player_mouseview(void)
 {
    if( gui_want_mouse() )
@@ -176,6 +149,10 @@ static void player_freecam(void)
    v3_add( move_vel, player.camera_pos, player.camera_pos );
 }
 
+/*
+ * Player Physics Implementation
+ */
+
 static void apply_gravity( v3f vel, float const timestep )
 {
    v3f gravity = { 0.0f, -9.6f, 0.0f };
@@ -186,9 +163,15 @@ static void apply_gravity( v3f vel, float const timestep )
  * 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
  */
 static void player_start_air(void)
 {
+   if( player.in_air )
+      return;
+
    player.in_air = 1;
 
    float pstep = ktimestep*10.0f;
@@ -198,9 +181,8 @@ static void player_start_air(void)
 
    float k_bias = 0.97f;
 
-   v3f axis, vup;
-   m3x3_mulv( player.to_world, (v3f){0.0f,1.0f,0.0f}, vup );
-   v3_cross( vup, player.v, axis );
+   v3f axis;
+   v3_cross( player.up, player.v, axis );
    v3_normalize( axis );
    player.land_log_count = 0;
    
@@ -279,120 +261,32 @@ static void player_start_air(void)
          }
       }
    }
-
-   //v3_rotate( player.v, best_velocity_mod, axis, player.v );
-   
-   return;
-   v3_muls( player.v, best_velocity_mod, player.v );
 }
 
-static int sample_if_resistant( v3f pos )
-{
-   v3f ground;
-   v3_copy( pos, ground );
-   ground[1] += 4.0f;
-   
-   ray_hit hit;
-   hit.dist = INFINITY;
-
-   if( ray_world( ground, (v3f){0.0f,-1.0f,0.0f}, &hit ))
-   {
-      v3f angle;
-      v3_copy( player.v, angle );
-      v3_normalize( angle );
-      float resistance = v3_dot( hit.normal, angle );
-
-      if( resistance < 0.25f )
-      {
-         v3_copy( hit.pos, pos );
-         return 1;
-      }
-   }
-
-   return 0;
-}
-
-static float stable_force( float current, float diff )
+static void draw_cross(v3f pos,u32 colour, float scale)
 {
-   float new = current + diff;
-
-   if( new * current < 0.0f )
-      return 0.0f;
-
-   return new;
+   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_ground(void)
+static void player_physics_control(void)
 {
-   /* 
-    * Getting surface collision points,
-    * the contact manifold is a triangle for simplicity.
+   /*
+    * Computing localized friction forces for controlling the character
+    * Friction across X is significantly more than Z
     */
-   v3f contact_front, contact_back, contact_norm, vup, vside,
-       axis;
-   
-   float klength = 0.65f;
-   m4x3_mulv( player.to_world, (v3f){ 0.15f,0.0f,-klength}, contact_norm );
-   m4x3_mulv( player.to_world, (v3f){-0.15f,0.0f,-klength}, contact_front );
-   m4x3_mulv( player.to_world, (v3f){ 0.00f,0.0f, klength}, contact_back );
-   m3x3_mulv( player.to_world, (v3f){ 0.0f, 1.0f, 0.0f}, vup );
-   m3x3_mulv( player.to_world, (v3f){ 1.0f, 0.0f, 0.0f}, vside );
-
-   v3f cn0, cn1, cn2;
-   
-   int contact_count = 
-      sample_if_resistant( contact_front ) +
-      sample_if_resistant( contact_back ) +
-      sample_if_resistant( contact_norm );
-   
-   if( contact_count < 3 )
-   {
-      player_start_air();
-      return;
-   }
-
-   v3f norm;
-   v3f v0, v1;
-   v3_sub( contact_norm, contact_front, v0 );
-   v3_sub( contact_back, contact_front, v1 );
-   v3_cross( v1, v0, norm );
-   v3_normalize( norm );
-
-   vg_line( contact_norm, contact_front, 0xff00ff00 );
-   vg_line( contact_back, contact_front, 0xff0000ff );
-
-   /* Surface alignment */
-   float angle = v3_dot( vup, norm );
-   v3_cross( vup, norm, axis );
-
-   if( angle < 0.999f )
-   {
-      v4f correction;
-      q_axis_angle( correction, axis, acosf(angle) );
-      q_mul( correction, player.rot, player.rot );
-   }
-
-   float resistance = v3_dot( norm, player.v );
-   if( resistance >= 0.0f )
-   {
-      player_start_air();
-      return;
-   }
-   else
-   {
-      v3_muladds( player.v, norm, -resistance, player.v );
-   }
-   
-   /* This is where velocity integration used to be */
-
-   float slip = 0.0f;
-
-   player.co[1] = (contact_front[1]+contact_back[1])*0.5f;
 
    v3f vel;
    m3x3_mulv( player.to_local, player.v, vel );
-
-   /* Calculate local forces */
+   float slip = 0.0f;
    
    if( fabsf(vel[2]) > 0.01f )
       slip = fabsf(-vel[0] / vel[2]) * vg_signf(vel[0]);
@@ -408,9 +302,7 @@ static void player_physics_ground(void)
    for( int i=0; i<5; i++ )
    {
       vel[2] = stable_force( vel[2], vg_signf( vel[2] ) * fwd_resistance );
-
-      /* This used to be -7.0, then -10.0 */
-      vel[0] = stable_force( vel[0], vg_signf( vel[0] ) * -8.5f *substep );
+      vel[0] = stable_force( vel[0], vg_signf( vel[0] ) * -8.78f *substep );
    }
    
    static double start_push = 0.0;
@@ -419,74 +311,32 @@ static void player_physics_ground(void)
 
    if( !vg_get_button("break") && vg_get_button( "push" ) )
    {
-      float const k_maxpush = 16.0f,
-                  k_pushaccel = 5.0f;
-
-      float cycle_time = vg_time-start_push,
-            amt = k_pushaccel * (sinf( cycle_time * 8.0f )*0.5f+0.5f)*ktimestep,
+      float cycle_time = (vg_time-start_push)*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_maxpush );
-      new_vel -= vg_minf(current, k_maxpush);
+            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;
    }
    
    m3x3_mulv( player.to_world, vel, player.v );
-
-   if( vg_get_button( "yawl" ) )
-      player.iY +=  3.6f * ktimestep;
-   if( vg_get_button( "yawr" ) )
-      player.iY -=  3.6f * ktimestep;
    
    float steer = vg_get_axis( "horizontal" );
-   player.iY -= vg_signf(steer)*powf(steer,2.0f) * 2.5f * ktimestep;
+   player.iY -= vg_signf(steer)*powf(steer,2.0f) * k_steer_ground * ktimestep;
    
-   /* Too much lean and it starts to look like a snowboard here */
    v2_lerp( player.board_xy, (v2f){ slip*0.25f, 0.0f }, 
          ktimestep*5.0f, player.board_xy);
 }
 
-static void draw_cross(v3f pos,u32 colour, float scale)
-{
-   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_air(void)
+static void player_physics_control_air(void)
 {
    m3x3_mulv( player.vr, player.v, player.v );
-   draw_cross( player.land_target, 0xff0000ff, 1 );
+   draw_cross( player.land_target, 0xff0000ff, 0.25f );
 
-   v3f ground_pos;
-   v3_copy( player.co, ground_pos );
-   ground_pos[1] += 4.0f;
-   
    ray_hit hit;
-   hit.dist = INFINITY;
-   if( ray_world( ground_pos, (v3f){0.0f,-1.0f,0.0f}, &hit ))
-   {
-      if( hit.pos[1] > player.co[1] )
-      {
-         player.in_air = 0;
-         
-         if( !ray_hit_is_ramp( &hit ) )
-         {
-            player.is_dead = 1;
-            character_ragdoll_copypose( &player.mdl, player.v );
-         }
 
-         return;
-      }
-   }
-   
-   /* Prediction 
+   /* 
+    * Prediction 
     */
    float pstep = ktimestep*10.0f;
 
@@ -536,19 +386,19 @@ static void player_physics_air(void)
             q_mul( correction, player.rot, player.rot );
          }
 
-         draw_cross( contact.pos, 0xffff0000, 1 );
+         draw_cross( contact.pos, 0xffff0000, 0.25f );
          break;
       }
       time_to_impact += pstep;
    }
 
-   player.iY -= vg_get_axis( "horizontal" ) * 3.6f * ktimestep;
+   player.iY -= vg_get_axis( "horizontal" ) * k_steer_air * ktimestep;
    {
-
       float iX = vg_get_axis( "vertical" ) * 
-         player.reverse * 3.6f * limiter * ktimestep;
+         player.reverse * k_steer_air * limiter * ktimestep;
+
       static float siX = 0.0f;
-      siX = vg_lerpf( siX, iX, 0.3f );
+      siX = vg_lerpf( siX, iX, k_steer_air_lerp );
       
       v4f rotate;
       v3f vside;
@@ -565,16 +415,160 @@ static void player_physics_air(void)
    v2_lerp( player.board_xy, target, ktimestep*3.0f, player.board_xy );
 }
 
+static void player_physics(void)
+{
+   m4x3f mboard;
+   v3_copy( player.to_world[0], mboard[0] );
+   v3_copy( player.to_world[2], mboard[1] );
+   v3_copy( player.to_world[1], mboard[2] );
+   m4x3_mulv( player.to_world, (v3f){ 0.0f, 0.3f, 0.0f }, mboard[3] );
+
+   debug_capsule( mboard, k_board_length*2.0f, k_board_radius, 0xff0000ff );
+
+   boxf region = {{ -k_board_radius, -k_board_length, -k_board_radius },
+                  {  k_board_radius,  k_board_length,  k_board_radius }};
+   m4x3_transform_aabb( mboard, region );
+   
+   u32 geo[256];
+   v3f tri[3];
+   int len = bh_select( &world.geo.bhtris, region, geo, 256 );
+
+   v3f poles[2];
+   m4x3_mulv(mboard, (v3f){0.0f,-k_board_length+k_board_radius,0.0f}, poles[0]);
+   m4x3_mulv(mboard, (v3f){0.0f, k_board_length-k_board_radius,0.0f}, poles[1]);
+   
+   struct contact manifold[12];
+   int manifold_count = 0;
+
+   v3f surface_avg = {0.0f, 0.0f, 0.0f};
+
+   for( int i=0; i<len; i++ )
+   {
+      u32 *ptri = &world.geo.indices[ geo[i]*3 ];
+
+      for( int j=0; j<3; j++ )
+         v3_copy( world.geo.verts[ptri[j]].co, tri[j] );
+
+      vg_line(tri[0],tri[1],0xff00ff00 );
+      vg_line(tri[1],tri[2],0xff00ff00 );
+      vg_line(tri[2],tri[0],0xff00ff00 );
+
+      v3f co, norm;
+      float p;
+
+      for( int j=0; j<2; j++ )
+      {
+         if( sphere_vs_triangle( poles[j], k_board_radius, tri,co,norm,&p) )
+         {
+            if(manifold_count >= vg_list_size(manifold))
+            {
+               vg_error("Manifold overflow!\n");
+               break;
+            }
+
+            v3f p1;
+            v3_muladds( poles[j], norm, p, p1 );
+            vg_line( poles[j], p1, 0xffffffff );
+
+            struct contact *ct = &manifold[manifold_count ++];
+            v3_sub( co, player.co, ct->delta );
+            v3_copy( co, ct->co );
+            v3_copy( norm, ct->n );
+            ct->bias = -0.2f*k_rb_rate*vg_minf(0.0f,-p+k_board_allowance);
+            ct->norm_impulse = 0.0f;
+
+            v3_add( norm, surface_avg, surface_avg );
+         }
+      }
+   }
+
+   if( !manifold_count )
+   {
+      player_start_air();
+   }
+   else
+   {
+      v3_normalize( surface_avg );
+
+      if( v3_dot( player.v, surface_avg ) > 0.5f )
+      {
+         player_start_air();
+      }
+      else
+         player.in_air = 0;
+   }
+
+   for( int j=0; j<10; j++ )
+   {
+      for( int i=0; i<manifold_count; i++ )
+      {
+         struct contact *ct = &manifold[i];
+         
+         v3f dv;
+         v3_cross( player.w, ct->delta, dv );
+         v3_add( player.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 );
+         v3_add( impulse, player.v, player.v );
+
+         v3_cross( ct->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.up, impulse ),
+               wx = v3_dot( player.right, impulse );
+
+         v3_muladds( player.w, player.up, wy, player.w );
+         v3_muladds( player.w, player.right, wx, player.w );
+      }
+   }
+
+   if( !player.in_air )
+   {
+      v3f axis;
+      float angle = v3_dot( player.up, surface_avg );
+      v3_cross( player.up, surface_avg, axis );
+
+      float cz = v3_dot( player.forward, axis );
+      v3_muls( player.forward, cz, axis );
+
+      if( angle < 0.999f )
+      {
+         v4f correction;
+         q_axis_angle( correction, axis, acosf(angle)*0.3f );
+         q_mul( correction, player.rot, player.rot );
+      }
+
+      v3_muladds( player.v, player.up, -k_downforce*ktimestep, player.v );
+      player_physics_control();
+   }
+   else
+   {
+      player_physics_control_air();
+   }
+}
+
 static void player_do_motion(void)
 {
    float horizontal = vg_get_axis("horizontal"),
          vertical = vg_get_axis("vertical");
 
-   if( player.in_air )
-      player_physics_air();
-
-   if( !player.in_air )
-      player_physics_ground();
+   player_physics();
    
    /* Integrate velocity */
    v3f prevco;
@@ -583,22 +577,34 @@ static void player_do_motion(void)
    apply_gravity( player.v, ktimestep );
    v3_muladds( player.co, player.v, ktimestep, player.co );
 
-   /* Integrate inertia */
-   v4f rotate; v3f vup = {0.0f,1.0f,0.0f};
-   m3x3_mulv( player.to_world, vup, vup );
+   /* Real angular velocity integration */
+   v3_lerp( player.w, (v3f){0.0f,0.0f,0.0f}, 0.125f, player.w );
+   if( v3_length2( player.w ) > 0.0f )
+   {
+      v4f rotation;
+      v3f axis;
+      v3_copy( player.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.rot, player.rot );
+   }
 
-   static float siY = 0.0f;
+   /* 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, vup, siY );
+   q_axis_angle( rotate, player.up, siY );
    q_mul( rotate, player.rot, player.rot );
+   player.iY = 0.0f;
 
-   player.iY = 0.0f; /* temp */
-
-   /* GATE COLLISION */
-
+   /* 
+    * Gate intersection, by tracing a line over the gate planes 
+    */
    for( int i=0; i<world.gate_count; i++ )
    {
       teleport_gate *gate = &world.gates[i];
@@ -620,20 +626,13 @@ static void player_do_motion(void)
       }
    }
 
-   /* Camera and character */
    player_transform_update();
-   
-   v3_lerp( player.vl, player.v, 0.05f, player.vl );
-
-   player.angles[0] = atan2f(  player.vl[0], -player.vl[2] );
-   player.angles[1] = atan2f( -player.vl[1], sqrtf(player.vl[0]*player.vl[0]+
-                                            player.vl[2]*player.vl[2]) ) * 0.7f;
 }
 
-static int player_walkgrid_tri_walkable( u32 tri[3] )
-{
-   return tri[0] < world.sm_surface.vertex_count;
-}
+/*
+ * Walkgrid implementation,
+ *  loosely based of cmuratoris youtube video 'Killing the Walkmonster'
+ */
 
 #define WALKGRID_SIZE 16
 struct walkgrid
@@ -671,6 +670,11 @@ struct walkgrid
    float h;
 };
 
+static int player_walkgrid_tri_walkable( u32 tri[3] )
+{
+   return tri[0] < world.sm_surface.vertex_count;
+}
+
 /*
  * Get a sample at this pole location, will return 1 if the sample is valid,
  * and pos will be updated to be the intersection location.
@@ -1581,6 +1585,10 @@ static void player_walkgrid(void)
    player_transform_update();
 }
 
+/*
+ * Animation
+ */
+
 static void player_animate(void)
 {
    /* Camera position */
@@ -1589,16 +1597,14 @@ static void player_animate(void)
 
    v3_add( player.m, player.a, player.m );
    v3_lerp( player.m, (v3f){0.0f,0.0f,0.0f}, 0.1f, player.m );
-   v3f target;
-   
+
    player.m[0] = vg_clampf( player.m[0], -2.0f, 2.0f );
-   player.m[1] = vg_clampf( player.m[1], -0.2f, 5.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_copy( player.m, target );
-   v3_lerp( player.bob, target, 0.2f, player.bob );
+   v3_lerp( player.bob, player.m, 0.2f, player.bob );
 
    /* Head */
-   float lslip = fabsf(player.slip); //vg_minf( 0.4f, slip );
+   float lslip = fabsf(player.slip);
                               
    float grabt = vg_get_axis( "grabr" )*0.5f+0.5f;
    player.grab = vg_lerpf( player.grab, grabt, 0.04f );
@@ -1606,19 +1612,26 @@ static void player_animate(void)
    float kheight = 2.0f,
          kleg = 0.6f;
 
-   v3f head;
-   head[0] = 0.0f;
-   head[1] = (0.3f+cosf(lslip)*0.5f*(1.0f-player.grab*0.7f)) * kheight;
-   head[2] = 0.0f;
-
    v3f offset;
    m3x3_mulv( player.to_local, player.bob, offset );
 
-   offset[0] *= 0.3333f;
-   offset[1] *= -0.25f;
-   offset[2] *= 0.7f;
-   v3_muladds( head, offset, 0.7f, head );
-   head[1] = vg_clampf( head[1], 0.3f, kheight );
+   static float speed_wobble = 0.0f, speed_wobble_2 = 0.0f;
+   
+   float kickspeed = vg_clampf(v3_length(player.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
@@ -1634,22 +1647,21 @@ static void player_animate(void)
    float speed = v3_length( player.v );
    
    fstand = vg_lerpf(fstand, 1.0f-vg_clampf(speed*0.03f,0.0f,1.0f),0.1f);
-   fslide = vg_lerpf(fslide, vg_clampf(lslip+fabsf(offset[0])*0.2f,
-            0.0f,1.0f), 0.04f);
+   fslide = vg_lerpf(fslide, vg_clampf(lslip,0.0f,1.0f), 0.04f);
    fdirz = vg_lerpf(fdirz, player.reverse > 0.0f? 1.0f: 0.0f, 0.04f );
-   fdirx = vg_lerpf(fdirx, player.slip < 0.0f?    1.0f: 0.0f, 0.04f );
+   fdirx = vg_lerpf(fdirx, player.slip < 0.0f?    1.0f: 0.0f, 0.01f );
    ffly = vg_lerpf(ffly, player.in_air?           1.0f: 0.0f, 0.04f );
 
    character_pose_reset( &player.mdl );
 
    /* TODO */
-   fstand = 1.0f;
+   float fstand1 = 1.0f-(1.0f-fstand)*0.3f;
 
    float amt_air = ffly*ffly,
          amt_ground = 1.0f-amt_air,
          amt_std = (1.0f-fslide) * amt_ground,
-         amt_stand = amt_std * fstand,
-         amt_aero = amt_std * (1.0f-fstand),
+         amt_stand = amt_std * fstand1,
+         amt_aero = amt_std * (1.0f-fstand1),
          amt_slide = amt_ground * fslide;
 
    character_final_pose( &player.mdl, offset, &pose_stand, amt_stand*fdirz );
@@ -1667,23 +1679,6 @@ static void player_animate(void)
    character_final_pose( &player.mdl, (v3f){0.0f,0.0f,0.0f}, 
          &pose_fly, amt_air );
    
-#if 0
-   static float fupper = 0.0f;
-   fupper = vg_lerpf( fupper, -vg_get_axis("horizontal")*0.2f, 0.1f );
-   character_yaw_upper( &player.mdl, fupper );
-#endif
-
-   /* Camera position */
-   v3_lerp( player.smooth_localcam, player.mdl.cam_pos, 0.08f, 
-            player.smooth_localcam );
-   v3_muladds( player.smooth_localcam, offset, 0.7f, player.camera_pos );
-   player.camera_pos[1] = vg_clampf( player.camera_pos[1], 0.3f, kheight );
-
-   m4x3_mulv( player.to_world, player.camera_pos, player.camera_pos );
-
-   player.air_blend = vg_lerpf( player.air_blend, player.in_air, 0.04f );
-   v3_muladds( player.camera_pos, player.v, -0.05f*player.air_blend, 
-         player.camera_pos );
 
    /* 
     * Additive effects
@@ -1695,11 +1690,6 @@ static void player_animate(void)
    v3f localv;
    m3x3_mulv( player.to_local, player.v, localv );
 
-#if 0
-   v3_muladds( arm_l->end, localv, -0.01f, arm_l->end );
-   v3_muladds( arm_r->end, localv, -0.01f, arm_r->end );
-#endif
-
    /* New board transformation */
    v4f board_rotation; v3f board_location;
 
@@ -1773,8 +1763,8 @@ static void player_animate(void)
       }
    }
 
-   v3_lerp( player.handl, player.handl_target, 0.1f, player.handl );
-   v3_lerp( player.handr, player.handr_target, 0.1f, player.handr );
+   v3_lerp( player.handl, player.handl_target, 1.0f, player.handl );
+   v3_lerp( player.handr, player.handr_target, 1.0f, player.handr );
 
    v3_copy( player.handl, player.mdl.ik_arm_l.end );
    v3_copy( player.handr, player.mdl.ik_arm_r.end );
@@ -1788,277 +1778,81 @@ static void player_animate(void)
    player.mdl.rhead = rhead;
 }
 
-static int giftwrapXZ( v3f *points, int *output, int len )
+static void player_camera_update(void)
 {
-   int l, p, q, count;
-
-   if( len < 3 )
-      return 0;
-
-   l = 0;
-   for( int i=1; i<len; i++ )
-      if( points[i][0] < points[l][0] )
-         l = i;
-
-   p = l;
-   count = 0;
-   do
-   {
-      if( count >= len )
-      {
-         vg_error ("MANIFOLD ERR (%d)\n", count );
-         return 0;
-      }
-      output[ count ++ ] = p;
-
-      q = (p+1)%len;
-
-      for( int i=0; i<len; i++ )
-      {
-         float orient = 
-             (points[i][2]-points[p][2])*(points[q][0]-points[i][0]) -
-             (points[i][0]-points[p][0])*(points[q][2]-points[i][2]);
-
-         if( orient > 0.0001f )
-         {
-            q = i;
-         }
-      }
-      p = q;
-   }
-   while( p != l );
-   
-   return count;
+   /* Update camera matrices */
+   m4x3_identity( player.camera );
+   m4x3_rotate_y( player.camera, -player.angles[0] );
+   m4x3_rotate_x( player.camera, -player.angles[1] );
+   v3_copy( player.camera_pos, player.camera[3] );
+   m4x3_invert_affine( player.camera, player.camera_inverse );
 }
-static void player_do_collision( rigidbody *rb )
-{
-   /*
-    * If point is inside box
-    *   find normal (theres 8 simple pyramid regions for this, x>y/dim .. etc)
-    *   find distance (same sorta thing)
-    *
-    * apply normal impulse to rotation
-    * correct position based on new penetration amount if needed
-    * apply normal impulse to velocity
-    */
-
-   v3f pfront, pback;
-   m4x3_mulv( player.to_world, (v3f){ 0.0f,0.0f,-1.0f }, pfront );
-   m4x3_mulv( player.to_world, (v3f){ 0.0f,0.0f, 1.0f }, pback );
-
-   float const kheight = 2.0f;
-   
-   v3f verts[8];
-
-   v3f a, b;
-   v3_copy( rb->bbx[0], a );
-   v3_copy( rb->bbx[1], b );
-
-   m4x3f compound;
-   m4x3_mul( player.to_local, rb->to_world, compound );
-   
-       m4x3_mulv( compound, (v3f){ a[0], a[1], a[2] }, verts[0] );
-       m4x3_mulv( compound, (v3f){ a[0], b[1], a[2] }, verts[1] );
-       m4x3_mulv( compound, (v3f){ b[0], b[1], a[2] }, verts[2] );
-       m4x3_mulv( compound, (v3f){ b[0], a[1], a[2] }, verts[3] );
-       m4x3_mulv( compound, (v3f){ a[0], a[1], b[2] }, verts[4] );
-       m4x3_mulv( compound, (v3f){ a[0], b[1], b[2] }, verts[5] );
-       m4x3_mulv( compound, (v3f){ b[0], b[1], b[2] }, verts[6] );
-       m4x3_mulv( compound, (v3f){ b[0], a[1], b[2] }, verts[7] );
-
-   int const indices[12][2] = {
-      {0,1},{1,2},{2,3},{3,0},{4,5},{5,6},{6,7},{7,4},
-      {0,4},{1,5},{2,6},{3,7}
-   };
-   
-   v3f hull[12*2 + 8];
-   int hull_indices[12*2 + 8];
-   int hull_len = 0;
-
-   for( int i=0; i<8; i++ )
-   {
-      int ia = indices[i][0];
-      float ya = verts[ia][1];
-
-      if( ya > 0.2f && ya < kheight )
-      {
-         int add_point = 1;
-         for( int j=0; j<hull_len; j++ )
-         {
-            v2f delta = { verts[ia][0]-hull[j][0], verts[ia][2]-hull[j][2] };
-            if( v2_length2( delta ) < 0.0004f )
-            {
-               add_point = 0;
-               break;
-            }
-         }
 
-         if( add_point )
-            v3_copy( verts[ia], hull[hull_len] );
-
-         hull[hull_len ++][1] = 0.2f;
-      }
-   }
-
-   for( int i=0; i<vg_list_size(indices); i++ )
-   {
-      int ia = indices[i][0],
-          ib = indices[i][1];
-
-      v3f p0, p1;
+static void player_animate_death_cam(void)
+{
+   v3f delta;
+   v3f head_pos;
+   v3_copy( player.mdl.ragdoll[k_chpart_head].co, head_pos );
 
-      float ya = verts[ia][1],
-            yb = verts[ib][1],
-            d = 1.0f/(yb-ya),
-            qa;
+   v3_sub( head_pos, player.camera_pos, delta );
+   v3_normalize( delta );
 
-      float planes[] = { 0.2f, kheight };
-      
-      for( int k=0; k<vg_list_size(planes); k++ )
-      {
-         float clip = planes[k];
+   v3f follow_pos;
+   v3_muladds( head_pos, delta, -2.5f, follow_pos );
+   v3_lerp( player.camera_pos, follow_pos, 0.1f, player.camera_pos );
 
-         if( (ya-clip) * (yb-clip) < 0.0f )
-         {
-            v3_muls( verts[ia], (yb-clip)*d, p0 );
-            v3_muladds( p0, verts[ib], -(ya-clip)*d, p0 );
-            
-            int add_point = 1;
-            for( int j=0; j<hull_len; j++ )
-            {
-               v2f delta = { p0[0]-hull[j][0], p0[2]-hull[j][2] };
-               if( v2_length2( delta ) < 0.0004f )
-               {
-                  add_point = 0;
-                  break;
-               }
-            }
+   /* 
+    * Make sure the camera stays above the ground
+    */
+   v3f min_height = {0.0f,1.0f,0.0f};
 
-            if( add_point )
-               v3_copy( p0, hull[hull_len ++] );
+   v3f sample;
+   v3_add( player.camera_pos, min_height, sample );
+   ray_hit hit;
+   hit.dist = min_height[1]*2.0f;
 
-            m4x3_mulv( player.to_world, p0, p0 );
-            vg_line_pt3( p0, 0.1f, 0xffffff00 );
-         }
-      }
-   }
+   if( ray_world( sample, (v3f){0.0f,-1.0f,0.0f}, &hit ))
+      v3_add( hit.pos, min_height, player.camera_pos );
 
-   if( hull_len < 3 ) 
-      return;
+   player.camera_pos[1] = 
+      vg_maxf( wrender.height + 2.0f, player.camera_pos[1] );
 
-   int len = giftwrapXZ( hull, hull_indices, hull_len );
-   for( int i=0; i<len; i++ )
-   {
-      v3f p0, p1, p2, p3;
-      v3_copy( hull[hull_indices[i]], p0 );
-      v3_copy( hull[hull_indices[(i+1)%len]], p1 );
-      p0[1] = 0.2f;
-      p1[1] = 0.2f;
-      v3_add( p0, (v3f){0,kheight-0.2f,0}, p2 );
-      v3_add( p1, (v3f){0,kheight-0.2f,0}, p3 );
-         
-      m4x3_mulv( player.to_world, p0, p0 );
-      m4x3_mulv( player.to_world, p1, p1 );
-      m4x3_mulv( player.to_world, p2, p2 );
-      m4x3_mulv( player.to_world, p3, p3 );
-
-      vg_line2( p0, p1, 0xff00ffff, 0xff000000 );
-      vg_line( p2, p3, 0xff00ffff );
-      vg_line( p0, p2, 0xff00ffa0 );
-   }
+   player.angles[0] = atan2f( delta[0], -delta[2] ); 
+   player.angles[1] = -asinf( delta[1] );
+}
 
-   v2f endpoints[] = {{ 0.0f, -1.0f },{ 0.0f, 1.0f }};
+static void player_animate_camera(void)
+{
+   v3f offs = { -0.29f, 0.08f, 0.0f };
+   m3x3_mulv( player.to_world, offs, offs );
+   m4x3_mulv( player.to_world, player.mdl.ik_body.end, player.camera_pos );
+   v3_add( offs, player.camera_pos, player.camera_pos );
    
-   for( int j=0; j<vg_list_size(endpoints); j++ )
-   {
-      v2f point;
-      v2_copy( endpoints[j], point );
-
-      int collide = 1;
-      float min_dist = 99999.9f;
-      v2f normal = {0.0f,0.0f};
-      for( int i=0; i<len; i++ )
-      {
-         v2f p0, p1;
-         p0[0] = hull[hull_indices[i]][0];
-         p0[1] = hull[hull_indices[i]][2];
-         p1[0] = hull[hull_indices[(i+1)%len]][0];
-         p1[1] = hull[hull_indices[(i+1)%len]][2];
-         
-         v2f t,n, rel;
-         v2_sub( p1, p0, t );
-         n[0] = -t[1];
-         n[1] =  t[0];
-         v2_normalize(n);
-         
-         v2_sub( point, p0, rel );
-         float d = -v2_dot( n, rel ) + 0.5f;
-
-         if( d < 0.0f )
-         {
-            collide = 0;
-            break;
-         }
-
-         if( d < min_dist )
-         {
-            min_dist = d;
-            v2_copy( n, normal );
-         }
-      }
-
-      if( collide )
-      {
-         v3f p0, p1;
-         p0[0] =  0.0f;
-         p0[1] =  0.2f;
-         p0[2] = -1.0f;
-
-         p1[0] = p0[0] + normal[0]*min_dist;
-         p1[1] = p0[1];
-         p1[2] = p0[2] + normal[1]*min_dist;
-         
-         m4x3_mulv( player.to_world, p0, p0 );
-         m4x3_mulv( player.to_world, p1, p1 );
-
-         vg_line( p0, p1, 0xffffffff );
-         
-         v3f vel;
-         m3x3_mulv( player.to_local, player.v, vel );
-         vel[1] = vel[2];
+   /* Look angles */
+   v3_lerp( player.vl, player.v, 0.05f, player.vl );
 
-         float vn = vg_maxf( -v2_dot( vel, normal ), 0.0f );
-         vn += -0.2f * (1.0f/k_rb_delta) * vg_minf( 0.0f, -min_dist+0.04f );
-         
-         v2f impulse;
-         if( vn > 14.0f )
-         {
-            player.is_dead = 1;
-            character_ragdoll_copypose( &player.mdl, player.v );
-            return;
-         }
+   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;
 
-         if( vn > 0.0f )
-         {
-            v2_muls( normal, min_dist, impulse );
-            float rotation = v2_cross( point, impulse )*0.08f;
-            v4f rot;
-            v3f up = {0.0f,1.0f,0.0f};
-            m3x3_mulv( player.to_world, up, up );
-            q_axis_angle( rot, up, -rotation );
-            q_mul( rot, player.rot, player.rot );
-         }
+   player.angles[0] = yaw;
+   player.angles[1] = pitch + 0.30f;
 
-         v2_muls( normal, vn*0.03f, impulse );
-         v3f impulse_world = { impulse[0], 0.0f, impulse[1] };
+   /* 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.v)*0.3f *(1.0f+fabsf(player.slip)), shake);
+   v2_lerp( shake_damp, shake, 0.01f, shake_damp );
+   shake_damp[0] *= 0.2f;
 
-         m3x3_mulv( player.to_world, impulse_world, impulse_world );
-         v3_add( impulse_world, player.v, player.v );
-      }
-   }
+   v2_muladds( player.angles, shake_damp, 0.1f, player.angles );
 }
 
+/* 
+ * Audio
+ */
 static void player_audio(void)
 {
    float speed = vg_minf(v3_length( player.v )*0.1f,1.0f),
@@ -2104,10 +1898,84 @@ static void player_audio(void)
    }
 }
 
+/*
+ * 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;
+
+         vg_info( "%f %f %f\n", player.co[0], player.co[1], player.co[2] );
+      for( int i=0; i<world.spawn_count; i++ )
+      {
+         r = &world.spawns[i];
+         float d = v3_dist2( r->co, player.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.rot );
+   v3_copy( rp->co, player.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;
+
+   player_transform_update();
+   m3x3_mulv( player.to_world, (v3f){ 0.0f, 0.0f, -1.2f }, player.v );
+   return 1;
+}
+
 static void player_update(void)
 {
    for( int i=0; i<player.land_log_count; i++ )
-      draw_cross( player.land_target_log[i], player.land_target_colours[i], 1);
+      draw_cross( player.land_target_log[i], 
+            player.land_target_colours[i], 0.25f);
 
    if( vg_get_axis("grabl")>0.0f)
       reset_player(0,NULL);
@@ -2117,91 +1985,35 @@ static void player_update(void)
       player.on_board ^= 0x1;
    }
 
-   if( freecam )
+   if( player.is_dead )
    {
-      player_freecam();
+      character_ragdoll_iter( &player.mdl );
+      character_debug_ragdoll( &player.mdl );
+
+      if( !freecam )
+         player_animate_death_cam();
    }
    else
    {
-      if( player.is_dead )
+      if( player.on_board )
       {
-         /* 
-          * Follow camera
-          */
-         character_ragdoll_iter( &player.mdl );
-         character_debug_ragdoll( &player.mdl );
-
-         v3f delta;
-         v3f head_pos;
-         v3_copy( player.mdl.ragdoll[k_chpart_head].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};
+         player_do_motion();
+         player_animate();
 
-         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] );
+         if( !freecam )
+            player_animate_camera();
       }
       else
       {
-         if( player.on_board )
-         {
-            bh_debug_node(&world.bhcubes, 0, 
-                  player.camera_pos, 0xff80ff00 );
-
-            u32 colliders[16];
-            boxf wbox = {{ -2.0f, -2.0f, -2.0f },
-                         {  2.0f,  2.0f,  2.0f }};
-            m4x3_transform_aabb( player.to_world, wbox );
-            int len = bh_select( &world.bhcubes, wbox, colliders, 32 );
-
-            for( int i=0; i<len; i++ )
-               player_do_collision( &world.temp_rbs[colliders[i]] );
-
-            player_do_motion();
-            player_animate();
-
-            v3f offs = { -0.29f, 0.08f, 0.0f };
-           m3x3_mulv( player.to_world, offs, offs );
-   m4x3_mulv( player.to_world, player.mdl.ik_body.end, player.camera_pos );
-   //m4x3_mulv( player.mdl.matrices[k_chpart_head], offs, player.camera_pos );
-   //         v3_copy( player.mdl.matrices[k_chpart_head][3], player.camera_pos );
-           v3_add( offs, player.camera_pos, player.camera_pos );
-         }
-         else
-         {
-            player_walkgrid();
-         }
+         player_walkgrid();
       }
    }
 
-   player_audio();
+   if( freecam )
+      player_freecam();
 
-   /* Update camera matrices */
-   m4x3_identity( player.camera );
-   m4x3_rotate_y( player.camera, -player.angles[0] );
-   m4x3_rotate_x( player.camera, -0.30f -player.angles[1] );
-   v3_copy( player.camera_pos, player.camera[3] );
-   m4x3_invert_affine( player.camera, player.camera_inverse );
+   player_camera_update();
+   player_audio();
 }
 
 static void draw_player(void)
index 845237f2e5e7b3f15ca7b15576da890d5e4557a8..eeed74500a8db526484d788bd935d2bcf276fc6d 100644 (file)
@@ -13,13 +13,24 @@ static bh_system bh_system_rigidbodies;
 #define RIGIDBODY_H
 
 #define RB_DEPR 
-#define k_rb_delta (1.0f/60.0f)
+#define k_rb_rate  60.0f
+#define k_rb_delta (1.0f/k_rb_rate)
 
 typedef struct rigidbody rigidbody;
 struct rigidbody
 {
    v3f co, v, I;
    v4f q;
+
+   enum rb_shape
+   {
+      k_rb_shape_box,
+      k_rb_shape_capsule
+   } 
+   type;
+   v3f top, bottom;
+   float radius;
+
    boxf bbx, bbx_world;
    float inv_mass;
 
@@ -69,7 +80,6 @@ static void rb_iter( rigidbody *rb )
 
    /* intergrate velocity */
    v3_muladds( rb->co, rb->v, k_rb_delta, rb->co );
-
    v3_lerp( rb->I, (v3f){0.0f,0.0f,0.0f}, 0.0025f, rb->I );
 
    /* inegrate inertia */
@@ -405,44 +415,7 @@ static void rb_constraint_position( rigidbody *ra, v3f lca,
 static void rb_debug( rigidbody *rb, u32 colour )
 {
    v3f *box = rb->bbx;
-   v3f p000, p001, p010, p011, p100, p101, p110, p111;
-
-   p000[0]=box[0][0];p000[1]=box[0][1];p000[2]=box[0][2];
-   p001[0]=box[0][0];p001[1]=box[0][1];p001[2]=box[1][2];
-   p010[0]=box[0][0];p010[1]=box[1][1];p010[2]=box[0][2];
-   p011[0]=box[0][0];p011[1]=box[1][1];p011[2]=box[1][2];
-
-   p100[0]=box[1][0];p100[1]=box[0][1];p100[2]=box[0][2];
-   p101[0]=box[1][0];p101[1]=box[0][1];p101[2]=box[1][2];
-   p110[0]=box[1][0];p110[1]=box[1][1];p110[2]=box[0][2];
-   p111[0]=box[1][0];p111[1]=box[1][1];p111[2]=box[1][2];
-
-   m4x3_mulv( rb->to_world, p000, p000 );
-   m4x3_mulv( rb->to_world, p001, p001 );
-   m4x3_mulv( rb->to_world, p010, p010 );
-   m4x3_mulv( rb->to_world, p011, p011 );
-   m4x3_mulv( rb->to_world, p100, p100 );
-   m4x3_mulv( rb->to_world, p101, p101 );
-   m4x3_mulv( rb->to_world, p110, p110 );
-   m4x3_mulv( rb->to_world, p111, p111 );
-   
-   vg_line( p000, p001, colour );
-   vg_line( p001, p011, colour );
-   vg_line( p011, p010, colour );
-   vg_line( p010, p000, colour );
-
-   vg_line( p100, p101, colour );
-   vg_line( p101, p111, colour );
-   vg_line( p111, p110, colour );
-   vg_line( p110, p100, colour );
-
-   vg_line( p100, p000, colour );
-   vg_line( p101, p001, colour );
-   vg_line( p110, p010, colour );
-   vg_line( p111, p011, colour );
-
-   vg_line( p000, p110, colour );
-   vg_line( p100, p010, colour );
+   vg_line_boxf_transformed( rb->to_world, rb->bbx, colour );
 }
 
 /*
@@ -511,6 +484,8 @@ static void rb_build_manifold_rb_static( rigidbody *ra, rigidbody *rb_static )
        m4x3_mulv( ra->to_world, (v3f){ b[0], b[1], b[2] }, verts[6] );
        m4x3_mulv( ra->to_world, (v3f){ b[0], a[1], b[2] }, verts[7] );
 
+   vg_line_boxf_transformed( rb_static->to_world, rb_static->bbx, 0xff0000ff );
+
    int count = 0;
 
    for( int i=0; i<8; i++ )
@@ -546,6 +521,267 @@ static void rb_build_manifold_rb_static( rigidbody *ra, rigidbody *rb_static )
    }
 }
 
+/* 
+ * Capsule phyics
+ */
+
+static float closest_segment_segment( v3f p1, v3f q1, v3f p2, v3f q2, 
+   float *s, float *t, v3f c1, v3f c2)
+{
+   v3f d1,d2,r;
+   v3_sub( q1, p1, d1 );
+   v3_sub( q2, p2, d2 );
+   v3_sub( p1, p2, r );
+
+   float a = v3_length2( d1 ),
+         e = v3_length2( d2 ),
+         f = v3_dot( d2, r );
+
+   const float kEpsilon = 0.0001f;
+
+   if( a <= kEpsilon && e <= kEpsilon )
+   {
+      *s = 0.0f;
+      *t = 0.0f;
+      v3_copy( p1, c1 );
+      v3_copy( p2, c2 );
+
+      v3f v0;
+      v3_sub( c1, c2, v0 );
+
+      return v3_length2( v0 );
+   }
+   
+   if( a<= kEpsilon )
+   {
+      *s = 0.0f;
+      *t = vg_clampf( f / e, 0.0f, 1.0f );
+   }
+   else
+   {
+      float c = v3_dot( d1, r );
+      if( e <= kEpsilon )
+      {
+         *t = 0.0f;
+         *s = vg_clampf( -c / a, 0.0f, 1.0f );
+      }
+      else
+      {
+         float b = v3_dot(d1,d2),
+               d = a*e-b*b;
+
+         if( d != 0.0f )
+         {
+            *s = vg_clampf((b*f - c*e)/d, 0.0f, 1.0f);
+         }
+         else
+         {
+            *s = 0.0f;
+         }
+
+         *t = (b*(*s)+f) / e;
+
+         if( *t < 0.0f )
+         {
+            *t = 0.0f;
+            *s = vg_clampf( -c / a, 0.0f, 1.0f );
+         }
+         else if( *t > 1.0f )
+         {
+            *t = 1.0f;
+            *s = vg_clampf((b-c)/a,0.0f,1.0f);
+         }
+      }
+   }
+
+   v3_muladds( p1, d1, *s, c1 );
+   v3_muladds( p2, d2, *t, c2 );
+
+   v3f v0;
+   v3_sub( c1, c2, v0 );
+   return v3_length2( v0 );
+}
+
+static void closest_point_segment( v3f a, v3f b, v3f point, v3f dest )
+{
+   v3f v0, v1;
+   v3_sub( b, a, v0 );
+   v3_sub( point, a, v1 );
+
+   float t = v3_dot( v1, v0 ) / v3_length2(v0);
+   v3_muladds( a, v0, vg_clampf(t,0.0f,1.0f), dest );
+}
+
+/* Real-Time Collision Detection */
+static void closest_on_triangle( v3f p, v3f tri[3], v3f dest )
+{
+   v3f ab, ac, ap;
+   float d1, d2;
+
+   /* Region outside A */
+   v3_sub( tri[1], tri[0], ab );
+   v3_sub( tri[2], tri[0], ac );
+   v3_sub( p, tri[0], ap );
+   
+   d1 = v3_dot(ab,ap);
+   d2 = v3_dot(ac,ap);
+   if( d1 <= 0.0f && d2 <= 0.0f ) 
+   {
+      v3_copy( tri[0], dest );
+      v3_copy( (v3f){INFINITY,INFINITY,INFINITY}, dest );
+      return;
+   }
+
+   /* Region outside B */
+   v3f bp;
+   float d3, d4;
+
+   v3_sub( p, tri[1], bp );
+   d3 = v3_dot( ab, bp );
+   d4 = v3_dot( ac, bp );
+
+   if( d3 >= 0.0f && d4 <= d3 )
+   {
+      v3_copy( tri[1], dest );
+      v3_copy( (v3f){INFINITY,INFINITY,INFINITY}, dest );
+      return;
+   }
+   
+   /* Edge region of AB */
+   float vc = d1*d4 - d3*d2;
+   if( vc <= 0.0f && d1 >= 0.0f && d3 <= 0.0f )
+   {
+      float v = d1 / (d1-d3);
+      v3_muladds( tri[0], ab, v, dest );
+      v3_copy( (v3f){INFINITY,INFINITY,INFINITY}, dest );
+      return;
+   }
+
+   /* Region outside C */
+   v3f cp;
+   float d5, d6;
+   v3_sub( p, tri[2], cp );
+   d5 = v3_dot(ab, cp);
+   d6 = v3_dot(ac, cp);
+   
+   if( d6 >= 0.0f && d5 <= d6 )
+   {
+      v3_copy( tri[2], dest );
+      v3_copy( (v3f){INFINITY,INFINITY,INFINITY}, dest );
+      return;
+   }
+
+   /* Region of AC */
+   float vb = d5*d2 - d1*d6;
+   if( vb <= 0.0f && d2 >= 0.0f && d6 <= 0.0f )
+   {
+      float w = d2 / (d2-d6);
+      v3_muladds( tri[0], ac, w, dest );
+      v3_copy( (v3f){INFINITY,INFINITY,INFINITY}, dest );
+      return;
+   }
+
+   /* Region of BC */
+   float va = d3*d6 - d5*d4;
+   if( va <= 0.0f && (d4-d3) >= 0.0f && (d5-d6) >= 0.0f )
+   {
+      float w = (d4-d3) / ((d4-d3) + (d5-d6));
+      v3f bc;
+      v3_sub( tri[2], tri[1], bc );
+      v3_muladds( tri[1], bc, w, dest );
+      v3_copy( (v3f){INFINITY,INFINITY,INFINITY}, dest );
+      return;
+   }
+
+   /* P inside region, Q via barycentric coordinates uvw */
+   float d = 1.0f/(va+vb+vc),
+         v = vb*d,
+         w = vc*d;
+
+   v3_muladds( tri[0], ab, v, dest );
+   v3_muladds( dest, ac, w, dest );
+}
+
+static int sphere_vs_triangle( v3f c, float r, v3f tri[3], 
+      v3f co, v3f norm, float *p )
+{
+   v3f delta;
+   closest_on_triangle( c, tri, co );
+
+   v3_sub( c, co, delta );
+
+
+   float d = v3_length2( delta );
+   if( d < r*r )
+   {
+      v3f ab, ac, tn;
+      v3_sub( tri[1], tri[0], ab );
+      v3_sub( tri[2], tri[0], ac );
+      v3_cross( ac, ab, tn );
+
+      if( v3_dot( delta, tn ) > 0.0f )
+         v3_muls( delta, -1.0f, delta );
+
+      vg_line_pt3( co, 0.05f, 0xff00ff00 );
+
+      d = sqrtf(d);
+      v3_muls( delta, 1.0f/d, norm );
+
+      *p = r-d;
+      return 1;
+   }
+
+   return 0;
+}
+
+static void debug_capsule( m4x3f m, float height, float radius, u32 colour )
+{
+   v3f last = { 0.0f, 0.0f, radius };
+   m4x3f lower, upper;
+   m3x3_copy( m, lower );
+   m3x3_copy( m, upper );
+   m4x3_mulv( m, (v3f){0.0f,-height*0.5f+radius,0.0f}, lower[3] );
+   m4x3_mulv( m, (v3f){0.0f, height*0.5f-radius,0.0f}, upper[3] );
+   
+   for( int i=0; i<16; i++ )
+   {
+      float t = ((float)(i+1) * (1.0f/16.0f)) * VG_PIf * 2.0f,
+            s = sinf(t),
+            c = cosf(t);
+
+      v3f p = { s*radius, 0.0f, c*radius };
+
+      v3f p0, p1;
+      m4x3_mulv( lower, p, p0 );
+      m4x3_mulv( lower, last, p1 );
+      vg_line( p0, p1, colour );
+
+      m4x3_mulv( upper, p, p0 );
+      m4x3_mulv( upper, last, p1 );
+      vg_line( p0, p1, colour );
+
+      v3_copy( p, last );
+   }
+
+   for( int i=0; i<4; i++ )
+   {
+      float t = ((float)(i) * (1.0f/4.0f)) * VG_PIf * 2.0f,
+            s = sinf(t),
+            c = cosf(t);
+
+      v3f p = { s*radius, 0.0f, c*radius };
+
+      v3f p0, p1;
+      m4x3_mulv( lower, p, p0 );
+      m4x3_mulv( upper, p, p1 );
+      vg_line( p0, p1, colour );
+
+      m4x3_mulv( lower, (v3f){0.0f,-radius,0.0f}, p0 );
+      m4x3_mulv( upper, (v3f){0.0f, radius,0.0f}, p1 );
+      vg_line( p0, p1, colour );
+   }
+}
+
 /*
  * BVH implementation, this is ONLY for static rigidbodies, its to slow for
  * realtime use.
index cf62679587bb9ee47ab2c42c25de9f9176c0b18a..2b13d764be0f2a048355d31b9bebd2287b60e4db 100644 (file)
@@ -8,6 +8,7 @@ in vec4 aColour;
 in vec2 aUv;
 in vec3 aNorm;
 in vec3 aCo;
+in vec3 aWorldCo;
 in float aOpacity;
 
 #include "common_world.glsl"
@@ -17,7 +18,7 @@ void main()
    vec3 vfrag = texture( uTexMain, aUv ).rgb;
 
    // Lighting
-   vec3 halfview = uCamera - aCo;
+   vec3 halfview = uCamera - aWorldCo;
    float fdist = length( halfview );
    halfview /= fdist;
 
index 83fdff36ff5ff1fd1369e0591b7c2cc35fd40857..e592a94cddb753285badc6a66d3906bee818150d 100644 (file)
@@ -24,6 +24,7 @@ static struct vg_shader _shader_character = {
 "out vec2 aUv;\n"
 "out vec3 aNorm;\n"
 "out vec3 aCo;\n"
+"out vec3 aWorldCo;\n"
 "out float aOpacity;\n"
 "\n"
 "void main()\n"
@@ -35,7 +36,8 @@ static struct vg_shader _shader_character = {
 "   aColour = a_colour;\n"
 "   aUv = a_uv;\n"
 "   aNorm = mat3(uMdl) * a_norm;\n"
-"   aCo = world_pos;\n"
+"   aWorldCo = world_pos;\n"
+"   aCo = a_co;\n"
 "   aOpacity = max(clip_pos.w*3.0,0.1);//  1.0-(gl_Position.y+0.5)*uOpacity;\n"
 "}\n"
 ""},
@@ -53,6 +55,7 @@ static struct vg_shader _shader_character = {
 "in vec2 aUv;\n"
 "in vec3 aNorm;\n"
 "in vec3 aCo;\n"
+"in vec3 aWorldCo;\n"
 "in float aOpacity;\n"
 "\n"
 "#line       1        1 \n"
@@ -108,7 +111,7 @@ static struct vg_shader _shader_character = {
 "\n"
 "float shadow_sample( vec3 vdir )\n"
 "{\n"
-"   vec3 sample_pos = aCo + vdir;\n"
+"   vec3 sample_pos = aWorldCo + vdir;\n"
 "   float height_sample = world_depth_sample( sample_pos );\n"
 "\n"
 "   float fdelta = height_sample - sample_pos.y;\n"
@@ -153,14 +156,14 @@ static struct vg_shader _shader_character = {
 "   return mix( vfrag, vec3(0.55,0.76,1.0), min( 1.0, dist ) );\n"
 "}\n"
 "\n"
-"#line     14        0 \n"
+"#line     15        0 \n"
 "\n"
 "void main()\n"
 "{\n"
 "   vec3 vfrag = texture( uTexMain, aUv ).rgb;\n"
 "\n"
 "   // Lighting\n"
-"   vec3 halfview = uCamera - aCo;\n"
+"   vec3 halfview = uCamera - aWorldCo;\n"
 "   float fdist = length( halfview );\n"
 "   halfview /= fdist;\n"
 "\n"
index 997ecc5fc0d3ae1e504d4366a92fb6b628d1cfc1..6d90ce6fe7a3162d7aec8bf09e2143fc1a6541ef 100644 (file)
@@ -8,6 +8,7 @@ out vec4 aColour;
 out vec2 aUv;
 out vec3 aNorm;
 out vec3 aCo;
+out vec3 aWorldCo;
 out float aOpacity;
 
 void main()
@@ -19,6 +20,7 @@ void main()
    aColour = a_colour;
    aUv = a_uv;
    aNorm = mat3(uMdl) * a_norm;
-   aCo = world_pos;
+   aWorldCo = world_pos;
+   aCo = a_co;
    aOpacity = max(clip_pos.w*3.0,0.1);//  1.0-(gl_Position.y+0.5)*uOpacity;
 }
index 4d9b0d3d6a7211f6cdb38499338264ab252ffc84..5e1a8919070e9f1865a1250c745f98f329222ed1 100644 (file)
@@ -50,7 +50,7 @@ float world_depth_sample( vec3 pos )
 
 float shadow_sample( vec3 vdir )
 {
-   vec3 sample_pos = aCo + vdir;
+   vec3 sample_pos = aWorldCo + vdir;
    float height_sample = world_depth_sample( sample_pos );
 
    float fdelta = height_sample - sample_pos.y;
index b21f7a7ff6b2b0bfb59112dd3c2bf9a841a76b94..77dac50175d01bb334342b87a38f1c19c6430dcf 100644 (file)
@@ -6,6 +6,7 @@ in vec4 aColour;
 in vec2 aUv;
 in vec3 aNorm;
 in vec3 aCo;
+in vec3 aWorldCo;
 
 #include "common_world.glsl"
 
index 4e44e353b43e52b305839efbcf633505e161a64a..d140abbf36a45208faa2b9733aabc657000e47c0 100644 (file)
@@ -23,14 +23,17 @@ static struct vg_shader _shader_gpos = {
 "out vec2 aUv;\n"
 "out vec3 aNorm;\n"
 "out vec3 aCo;\n"
+"out vec3 aWorldCo;\n"
 "\n"
 "void main()\n"
 "{\n"
-"   gl_Position = uPv * vec4( uMdl * vec4(a_co,1.0), 1.0 );\n"
+"   vec3 world_pos = uMdl * vec4(a_co,1.0);\n"
+"   gl_Position = uPv * vec4( world_pos, 1.0 );\n"
 "   aColour = a_colour;\n"
 "   aUv = a_uv;\n"
 "   aNorm = mat3(uMdl) * a_norm;\n"
 "   aCo = a_co;\n"
+"   aWorldCo = world_pos;\n"
 "}\n"
 ""},
    .fs = 
@@ -45,6 +48,7 @@ static struct vg_shader _shader_gpos = {
 "in vec2 aUv;\n"
 "in vec3 aNorm;\n"
 "in vec3 aCo;\n"
+"in vec3 aWorldCo;\n"
 "\n"
 "#line       1        1 \n"
 "layout (std140) uniform ub_world_lighting\n"
@@ -99,7 +103,7 @@ static struct vg_shader _shader_gpos = {
 "\n"
 "float shadow_sample( vec3 vdir )\n"
 "{\n"
-"   vec3 sample_pos = aCo + vdir;\n"
+"   vec3 sample_pos = aWorldCo + vdir;\n"
 "   float height_sample = world_depth_sample( sample_pos );\n"
 "\n"
 "   float fdelta = height_sample - sample_pos.y;\n"
@@ -144,7 +148,7 @@ static struct vg_shader _shader_gpos = {
 "   return mix( vfrag, vec3(0.55,0.76,1.0), min( 1.0, dist ) );\n"
 "}\n"
 "\n"
-"#line     11        0 \n"
+"#line     12        0 \n"
 "\n"
 "// Water blending\n"
 "// ==============\n"
index d2a227a15d5f9801137ac19b511d721530e5167a..abd7a0caf67cb6466f392fcf00dca07dc65ce9d2 100644 (file)
@@ -23,14 +23,17 @@ static struct vg_shader _shader_planeinf = {
 "out vec2 aUv;\n"
 "out vec3 aNorm;\n"
 "out vec3 aCo;\n"
+"out vec3 aWorldCo;\n"
 "\n"
 "void main()\n"
 "{\n"
-"   gl_Position = uPv * vec4( uMdl * vec4(a_co,1.0), 1.0 );\n"
+"   vec3 world_pos = uMdl * vec4(a_co,1.0);\n"
+"   gl_Position = uPv * vec4( world_pos, 1.0 );\n"
 "   aColour = a_colour;\n"
 "   aUv = a_uv;\n"
 "   aNorm = mat3(uMdl) * a_norm;\n"
 "   aCo = a_co;\n"
+"   aWorldCo = world_pos;\n"
 "}\n"
 ""},
    .fs = 
index 3ef9613fc5a81070c02d80288cf067d0429432fa..09329f5aa8b1305196882faa08d72c6776412e99 100644 (file)
@@ -23,14 +23,17 @@ static struct vg_shader _shader_sky = {
 "out vec2 aUv;\n"
 "out vec3 aNorm;\n"
 "out vec3 aCo;\n"
+"out vec3 aWorldCo;\n"
 "\n"
 "void main()\n"
 "{\n"
-"   gl_Position = uPv * vec4( uMdl * vec4(a_co,1.0), 1.0 );\n"
+"   vec3 world_pos = uMdl * vec4(a_co,1.0);\n"
+"   gl_Position = uPv * vec4( world_pos, 1.0 );\n"
 "   aColour = a_colour;\n"
 "   aUv = a_uv;\n"
 "   aNorm = mat3(uMdl) * a_norm;\n"
 "   aCo = a_co;\n"
+"   aWorldCo = world_pos;\n"
 "}\n"
 ""},
    .fs = 
index 59aa1d95b85c6d088d19a51a550241866428436d..1b41b9aade599868d203300d7744aa4d5469014b 100644 (file)
@@ -23,14 +23,17 @@ static struct vg_shader _shader_standard = {
 "out vec2 aUv;\n"
 "out vec3 aNorm;\n"
 "out vec3 aCo;\n"
+"out vec3 aWorldCo;\n"
 "\n"
 "void main()\n"
 "{\n"
-"   gl_Position = uPv * vec4( uMdl * vec4(a_co,1.0), 1.0 );\n"
+"   vec3 world_pos = uMdl * vec4(a_co,1.0);\n"
+"   gl_Position = uPv * vec4( world_pos, 1.0 );\n"
 "   aColour = a_colour;\n"
 "   aUv = a_uv;\n"
 "   aNorm = mat3(uMdl) * a_norm;\n"
 "   aCo = a_co;\n"
+"   aWorldCo = world_pos;\n"
 "}\n"
 ""},
    .fs = 
index db7f98033ae326ee291becfb969556d2746c0fb1..ac062ae0373609130e4990fb93cb857e127c094c 100644 (file)
@@ -7,12 +7,15 @@ out vec4 aColour;
 out vec2 aUv;
 out vec3 aNorm;
 out vec3 aCo;
+out vec3 aWorldCo;
 
 void main()
 {
-   gl_Position = uPv * vec4( uMdl * vec4(a_co,1.0), 1.0 );
+   vec3 world_pos = uMdl * vec4(a_co,1.0);
+   gl_Position = uPv * vec4( world_pos, 1.0 );
    aColour = a_colour;
    aUv = a_uv;
    aNorm = mat3(uMdl) * a_norm;
    aCo = a_co;
+   aWorldCo = world_pos;
 }
index 456d08e779c28e605a408bb41b21c08b41eb6aa5..de8958d2d31847c7e587e803b4cd4c70f3754d7a 100644 (file)
@@ -8,6 +8,7 @@ in vec4 aColour;
 in vec2 aUv;
 in vec3 aNorm;
 in vec3 aCo;
+in vec3 aWorldCo;
 
 #include "common_world.glsl"
 
index 436ca1fc06559333cc88d454a6fe48c9838e21bc..1c87e35d351d3ef02a3450aa76f131cfb1c819a1 100644 (file)
@@ -23,14 +23,17 @@ static struct vg_shader _shader_terrain = {
 "out vec2 aUv;\n"
 "out vec3 aNorm;\n"
 "out vec3 aCo;\n"
+"out vec3 aWorldCo;\n"
 "\n"
 "void main()\n"
 "{\n"
-"   gl_Position = uPv * vec4( uMdl * vec4(a_co,1.0), 1.0 );\n"
+"   vec3 world_pos = uMdl * vec4(a_co,1.0);\n"
+"   gl_Position = uPv * vec4( world_pos, 1.0 );\n"
 "   aColour = a_colour;\n"
 "   aUv = a_uv;\n"
 "   aNorm = mat3(uMdl) * a_norm;\n"
 "   aCo = a_co;\n"
+"   aWorldCo = world_pos;\n"
 "}\n"
 ""},
    .fs = 
@@ -47,6 +50,7 @@ static struct vg_shader _shader_terrain = {
 "in vec2 aUv;\n"
 "in vec3 aNorm;\n"
 "in vec3 aCo;\n"
+"in vec3 aWorldCo;\n"
 "\n"
 "#line       1        1 \n"
 "layout (std140) uniform ub_world_lighting\n"
@@ -101,7 +105,7 @@ static struct vg_shader _shader_terrain = {
 "\n"
 "float shadow_sample( vec3 vdir )\n"
 "{\n"
-"   vec3 sample_pos = aCo + vdir;\n"
+"   vec3 sample_pos = aWorldCo + vdir;\n"
 "   float height_sample = world_depth_sample( sample_pos );\n"
 "\n"
 "   float fdelta = height_sample - sample_pos.y;\n"
@@ -146,7 +150,7 @@ static struct vg_shader _shader_terrain = {
 "   return mix( vfrag, vec3(0.55,0.76,1.0), min( 1.0, dist ) );\n"
 "}\n"
 "\n"
-"#line     13        0 \n"
+"#line     14        0 \n"
 "\n"
 "void main()\n"
 "{\n"
index afba67c310462740ab8063f1663ad62ed74c193e..8fbf00b17a293a9d8fa5d9a11b81941f84c7db32 100644 (file)
@@ -23,14 +23,17 @@ static struct vg_shader _shader_unlit = {
 "out vec2 aUv;\n"
 "out vec3 aNorm;\n"
 "out vec3 aCo;\n"
+"out vec3 aWorldCo;\n"
 "\n"
 "void main()\n"
 "{\n"
-"   gl_Position = uPv * vec4( uMdl * vec4(a_co,1.0), 1.0 );\n"
+"   vec3 world_pos = uMdl * vec4(a_co,1.0);\n"
+"   gl_Position = uPv * vec4( world_pos, 1.0 );\n"
 "   aColour = a_colour;\n"
 "   aUv = a_uv;\n"
 "   aNorm = mat3(uMdl) * a_norm;\n"
 "   aCo = a_co;\n"
+"   aWorldCo = world_pos;\n"
 "}\n"
 ""},
    .fs = 
index 0849e69c8919e1e1620a393662758c51eb52cf4c..023721e7264829bad890d59fb821e003029efa97 100644 (file)
@@ -9,6 +9,7 @@ in vec4 aColour;
 in vec2 aUv;
 in vec3 aNorm;
 in vec3 aCo;
+in vec3 aWorldCo;
 
 #include "common_world.glsl"
 
@@ -43,7 +44,7 @@ void main()
    }
 
    // Lighting
-   vec3 halfview = uCamera - aCo;
+   vec3 halfview = uCamera - aWorldCo;
    float fdist = length( halfview );
    halfview /= fdist;
 
index 45782ad90fe506b6bedf2eff6c406cd0566974d8..3f5bfdc14ed136eb21c6856f253926a1af0e797f 100644 (file)
@@ -23,14 +23,17 @@ static struct vg_shader _shader_vblend = {
 "out vec2 aUv;\n"
 "out vec3 aNorm;\n"
 "out vec3 aCo;\n"
+"out vec3 aWorldCo;\n"
 "\n"
 "void main()\n"
 "{\n"
-"   gl_Position = uPv * vec4( uMdl * vec4(a_co,1.0), 1.0 );\n"
+"   vec3 world_pos = uMdl * vec4(a_co,1.0);\n"
+"   gl_Position = uPv * vec4( world_pos, 1.0 );\n"
 "   aColour = a_colour;\n"
 "   aUv = a_uv;\n"
 "   aNorm = mat3(uMdl) * a_norm;\n"
 "   aCo = a_co;\n"
+"   aWorldCo = world_pos;\n"
 "}\n"
 ""},
    .fs = 
@@ -48,6 +51,7 @@ static struct vg_shader _shader_vblend = {
 "in vec2 aUv;\n"
 "in vec3 aNorm;\n"
 "in vec3 aCo;\n"
+"in vec3 aWorldCo;\n"
 "\n"
 "#line       1        1 \n"
 "layout (std140) uniform ub_world_lighting\n"
@@ -102,7 +106,7 @@ static struct vg_shader _shader_vblend = {
 "\n"
 "float shadow_sample( vec3 vdir )\n"
 "{\n"
-"   vec3 sample_pos = aCo + vdir;\n"
+"   vec3 sample_pos = aWorldCo + vdir;\n"
 "   float height_sample = world_depth_sample( sample_pos );\n"
 "\n"
 "   float fdelta = height_sample - sample_pos.y;\n"
@@ -147,7 +151,7 @@ static struct vg_shader _shader_vblend = {
 "   return mix( vfrag, vec3(0.55,0.76,1.0), min( 1.0, dist ) );\n"
 "}\n"
 "\n"
-"#line     14        0 \n"
+"#line     15        0 \n"
 "\n"
 "void main()\n"
 "{\n"
@@ -180,7 +184,7 @@ static struct vg_shader _shader_vblend = {
 "   }\n"
 "\n"
 "   // Lighting\n"
-"   vec3 halfview = uCamera - aCo;\n"
+"   vec3 halfview = uCamera - aWorldCo;\n"
 "   float fdist = length( halfview );\n"
 "   halfview /= fdist;\n"
 "\n"
index 0be43893cb803fea710a7c86a7f689f5b4c7163c..9f9e8be54e5dc41302173050a2711a999a46eb28 100644 (file)
@@ -13,6 +13,7 @@ in vec4 aColour;
 in vec2 aUv;
 in vec3 aNorm;
 in vec3 aCo;
+in vec3 aWorldCo;
 
 #include "common_world.glsl"
 
index 2cdc2719dbd6a5f8dc16fe5b82d03377dae5c3b1..8a1d4df962f0e37088a409dd4cf231f1392f76fb 100644 (file)
@@ -23,14 +23,17 @@ static struct vg_shader _shader_water = {
 "out vec2 aUv;\n"
 "out vec3 aNorm;\n"
 "out vec3 aCo;\n"
+"out vec3 aWorldCo;\n"
 "\n"
 "void main()\n"
 "{\n"
-"   gl_Position = uPv * vec4( uMdl * vec4(a_co,1.0), 1.0 );\n"
+"   vec3 world_pos = uMdl * vec4(a_co,1.0);\n"
+"   gl_Position = uPv * vec4( world_pos, 1.0 );\n"
 "   aColour = a_colour;\n"
 "   aUv = a_uv;\n"
 "   aNorm = mat3(uMdl) * a_norm;\n"
 "   aCo = a_co;\n"
+"   aWorldCo = world_pos;\n"
 "}\n"
 ""},
    .fs = 
@@ -52,6 +55,7 @@ static struct vg_shader _shader_water = {
 "in vec2 aUv;\n"
 "in vec3 aNorm;\n"
 "in vec3 aCo;\n"
+"in vec3 aWorldCo;\n"
 "\n"
 "#line       1        1 \n"
 "layout (std140) uniform ub_world_lighting\n"
@@ -106,7 +110,7 @@ static struct vg_shader _shader_water = {
 "\n"
 "float shadow_sample( vec3 vdir )\n"
 "{\n"
-"   vec3 sample_pos = aCo + vdir;\n"
+"   vec3 sample_pos = aWorldCo + vdir;\n"
 "   float height_sample = world_depth_sample( sample_pos );\n"
 "\n"
 "   float fdelta = height_sample - sample_pos.y;\n"
@@ -151,7 +155,7 @@ static struct vg_shader _shader_water = {
 "   return mix( vfrag, vec3(0.55,0.76,1.0), min( 1.0, dist ) );\n"
 "}\n"
 "\n"
-"#line     18        0 \n"
+"#line     19        0 \n"
 "\n"
 "vec4 water_surf( vec3 halfview, vec3 vnorm, float depthvalue, \n"
 "      vec4 beneath, vec4 above )\n"
diff --git a/traffic.h b/traffic.h
new file mode 100644 (file)
index 0000000..8850306
--- /dev/null
+++ b/traffic.h
@@ -0,0 +1,232 @@
+#ifndef TRAFFIC_H
+#define TRAFFIC_H
+
+#include "common.h"
+#include "model.h"
+#include "rigidbody.h"
+
+typedef struct traffic_node traffic_node;
+typedef struct traffic_driver traffic_driver;
+
+struct traffic_node
+{
+   v3f co, h;
+
+   union
+   {
+      struct{ traffic_node *next, *next1; };
+      struct{ mdl_node *mn_next, *mn_next1; };
+   };
+};
+
+struct traffic_driver
+{
+   m4x3f transform;
+
+   traffic_node *current;
+   int option;
+   float t, speed;
+};
+
+static void eval_bezier_time( v3f p0, v3f p1, v3f h0, v3f h1, float t, v3f p )
+{
+   float tt = t*t,
+         ttt = tt*t;
+
+   v3_muls( p1, ttt, p );
+   v3_muladds( p, h1, 3.0f*tt  -3.0f*ttt, p );
+   v3_muladds( p, h0, 3.0f*ttt -6.0f*tt  +3.0f*t, p );
+   v3_muladds( p, p0, 3.0f*tt  -ttt -3.0f*t +1.0f, p );
+}
+
+static float eval_bezier_length( v3f p0, v3f p1, v3f h0, v3f h1, int res )
+{
+   float length = 0.0f, m = 1.0f/(float)res;
+   v3f l, p;
+   v3_copy( p0, l );
+
+   for( int i=0; i<res; i++ )
+   {
+      float t = (float)(i+1)*m;
+      eval_bezier_time(p0,p1,h0,h1,t,p);
+      length += v3_dist( p,l );
+      v3_copy( p, l );
+   }
+
+   return length;
+}
+
+static void traffic_finalize( traffic_node *system, int count )
+{
+   for( int i=0; i<count; i++ )
+   {
+      traffic_node *tn = &system[i];
+
+      if( tn->mn_next )
+         tn->next = &system[ tn->mn_next->sub_uid ];
+      if( tn->mn_next1 )
+         tn->next1 = &system[ tn->mn_next1->sub_uid ];
+   }
+}
+
+static void traffic_visualize_link( traffic_node *ta, traffic_node *tb )
+{
+   v3f p0, p1, h0, h1, p, l;
+
+   if( !tb ) return;
+
+   v3_copy( ta->co, p0 );
+   v3_muladds( ta->co, ta->h,  1.0f, h0 );
+   v3_copy( tb->co, p1 );
+   v3_muladds( tb->co, tb->h, -1.0f, h1 );
+   v3_copy( p0, l );
+
+   vg_line_pt3( h0, 0.2f, 0xff00ff00 );
+   vg_line_pt3( h1, 0.2f, 0xffff00ff );
+   vg_line( p0, h0, 0xff000000 );
+   vg_line( p1, h1, 0xff000000 );
+
+   for( int i=0; i<5; i++ )
+   {
+      float t = (float)(i+1)/5.0f;
+      eval_bezier_time( p0, p1, h0, h1, t, p );
+
+      vg_line( p, l, 0xffffffff );
+      v3_copy( p, l );
+   }
+}
+
+static void sample_wheel_floor( v3f pos )
+{
+   v3f ground;
+   v3_copy( pos, ground );
+   ground[1] += 4.0f;
+   
+   ray_hit hit;
+   hit.dist = 8.0f;
+
+   if( ray_world( ground, (v3f){0.0f,-1.0f,0.0f}, &hit ))
+   {
+      v3_copy( hit.pos, pos );
+   }
+}
+
+static void traffic_drive( traffic_driver *driver )
+{
+   traffic_node *next, *current = driver->current;
+
+   if( !current ) return;
+   next = driver->option==0? current->next: current->next1;
+   
+   if( driver->t > 1.0f )
+   {
+      driver->t = driver->t - floorf( driver->t );
+      driver->current = driver->option==0? current->next: current->next1;
+      driver->option = 0;
+      
+      current = driver->current;
+      if( !current )
+         return;
+
+      if( current->next && current->next1 )
+         if( vg_randf() > 0.5f )
+            driver->option = 1;
+   }
+
+   traffic_visualize_link( current, next );
+
+   /*
+    * Calculate the speed of the curve at the current point. On the reference
+    * curve the rate should come out to be exactly 1 ktimestep traveled.
+    * Dividing this distance by ktimestep gives us the modifier to use.
+    */
+   v3f p0,p1,h0,h1,pc,pn;
+   
+   v3_copy( current->co, p0 );
+   v3_muladds( current->co, current->h, 1.0f, h0 );
+   v3_copy( next->co, p1 );
+   v3_muladds( next->co, next->h, -1.0f, h1 );
+
+   eval_bezier_time( p0,p1,h0,h1, driver->t, pc );
+   eval_bezier_time( p0,p1,h0,h1, driver->t + ktimestep, pn );
+
+   float mod = ktimestep / v3_dist( pc, pn );
+   v3f dir,side,up;
+   v3_sub( pn, pc, dir );
+   v3_normalize(dir);
+   
+   /*
+    * Stick the car on the ground by casting rays where the wheels are
+    */
+   side[0] = -dir[2];
+   side[1] =  0.0f;
+   side[2] =  dir[0];
+   v3_normalize(side);
+
+   v3f fl, fr, bc;
+   v3_muladds( pc, dir, 2.0f, fr );
+   v3_muladds( pc, dir, 2.0f, fl );
+   v3_muladds( pc, dir, -2.0f, bc );
+   v3_muladds( fr, side, 1.0f, fr );
+   v3_muladds( fl, side, -1.0f, fl );
+
+   sample_wheel_floor( fl );
+   sample_wheel_floor( fr );
+   sample_wheel_floor( bc );
+
+   vg_line( fl, fr, 0xff00ffff );
+   vg_line( fr, bc, 0xff00ffff );
+   vg_line( bc, fl, 0xff00ffff );
+
+   v3f norm;
+   v3f v0, v1;
+   v3_sub( fr, fl, v0 );
+   v3_sub( bc, fl, v1 );
+   v3_cross( v1, v0, norm );
+   v3_normalize( norm );
+
+   /* 
+    * Jesus take the wheel
+    */
+   float steer_penalty = 1.0f-v3_dot( dir, driver->transform[0] );
+   steer_penalty /= ktimestep;
+   steer_penalty *= 30.0f;
+   
+   float target_speed = vg_maxf( 16.0f * (1.0f-steer_penalty), 0.1f ),
+         accel = target_speed - driver->speed;
+   driver->speed = stable_force( driver->speed, accel*ktimestep*2.0f );
+   driver->t += driver->speed*mod*ktimestep;
+
+   /* 
+    * Update transform
+    */
+   v3_cross( dir, norm, side );
+   v3_copy( dir, driver->transform[0] );
+   v3_copy( norm, driver->transform[1] );
+   v3_copy( side, driver->transform[2] );
+
+   v3_add( fl, fr, pc );
+   v3_add( bc, pc, pc );
+   v3_muls( pc, 1.0f/3.0f, pc );
+   v3_copy( pc, driver->transform[3] );
+}
+
+static void traffic_visualize( traffic_node *system, int count )
+{
+   for( int i=0; i<count; i++ )
+   {
+      traffic_node *tn = &system[i];
+
+      traffic_visualize_link( tn, tn->next );
+      traffic_visualize_link( tn, tn->next1 );
+   }
+}
+
+static void traffic_visualize_car( traffic_driver *driver )
+{
+   vg_line_boxf_transformed( driver->transform, 
+                                       (boxf){{-1.0f,0.0f,-0.5f},
+                                              { 1.0f,0.0f, 0.5f}}, 0xff00ff00 );
+}
+
+#endif /* TRAFFIC_H */
diff --git a/world.h b/world.h
index d8b36165c20d2225dcd90621e98d03c5cc7afdd4..add08b7c30d1180a1125a8a4c9a10dd2c9102c5e 100644 (file)
--- a/world.h
+++ b/world.h
@@ -14,6 +14,7 @@ static int ray_world( v3f pos, v3f dir, ray_hit *hit );
 #include "bvh.h"
 #include "lighting.h"
 #include "model.h"
+#include "traffic.h"
 
 #include "shaders/terrain.h"
 #include "shaders/sky.h"
@@ -37,6 +38,12 @@ static struct gworld
 
    teleport_gate gates[64];
    u32 gate_count;
+
+   /* Paths */
+   traffic_node traffic[128];
+   u32 traffic_count;
+
+   traffic_driver van_man[6];
    
    /* Physics */
    rigidbody temp_rbs[128];
@@ -45,10 +52,13 @@ static struct gworld
    
    /* Rendering & geometry */
    scene geo, foliage, props;
-   mdl_submesh sm_surface;
+   mdl_submesh sm_surface, sm_other;
 
    glmesh skybox, skydome;
    mdl_submesh dome_upper, dome_lower;
+
+   glmesh cars;
+   mdl_submesh car_holden;
 }
 world;
 
@@ -166,6 +176,7 @@ static void world_load(void)
    world.spawn_count = 0;
    world.gate_count = 0;
    world.rb_count = 0;
+   world.traffic_count = 0;
 
    scene_init( &world.geo );
    scene_init( &world.props );
@@ -194,6 +205,9 @@ static void world_load(void)
 
    if( mat_surf )
       add_all_if_material( &world.geo, mworld, mat_surf );
+   if( mat_vertex_blend )
+      add_all_if_material( &world.geo, mworld, mat_vertex_blend );
+
 
    scene_copy_slice( &world.geo, &world.sm_surface );
 
@@ -222,17 +236,21 @@ static void world_load(void)
       else if( pnode->classtype == k_classtype_gate )
       {
          struct classtype_gate *entgate = mdl_get_entdata( mworld, pnode );
-         mdl_node *pother = mdl_node_from_id( mworld, entgate->target );
-         
-         teleport_gate *gate = &world.gates[ world.gate_count ++ ];
-         
-         v3_copy( pnode->co,  gate->co[0] );
-         v3_copy( pother->co, gate->co[1] );
-         v4_copy( pnode->q,   gate->q[0] );
-         v4_copy( pother->q,  gate->q[1] );
-         v2_copy( pnode->s,   gate->dims );
 
-         gate_transform_update( gate );
+         if( entgate->target )
+         {
+            mdl_node *pother = mdl_node_from_id( mworld, entgate->target );
+            
+            teleport_gate *gate = &world.gates[ world.gate_count ++ ];
+            
+            v3_copy( pnode->co,  gate->co[0] );
+            v3_copy( pother->co, gate->co[1] );
+            v4_copy( pnode->q,   gate->q[0] );
+            v4_copy( pother->q,  gate->q[1] );
+            v2_copy( pnode->s,   gate->dims );
+
+            gate_transform_update( gate );
+         }
       }
       else if( pnode->classtype == k_classtype_block )
       {
@@ -276,7 +294,28 @@ static void world_load(void)
             water_set_surface( &surf, pnode->co[1] );
          }
       }
+      else if( pnode->classtype == k_classtype_car_path )
+      {
+         struct classtype_car_path *p = mdl_get_entdata( mworld, pnode );
+         traffic_node *tn = &world.traffic[ world.traffic_count ];
+         tn->mn_next = NULL;
+         tn->mn_next1 = NULL;
+         
+         if( p->target ) tn->mn_next = mdl_node_from_id( mworld, p->target );
+         if( p->target1 ) tn->mn_next1 = mdl_node_from_id( mworld, p->target1 );
+
+         m4x3f transform;
+         mdl_node_transform( pnode, transform );
+         m3x3_mulv( transform, (v3f){1.0f,0.0f,0.0f}, tn->h );
+         v3_copy( transform[3], tn->co );
+
+         pnode->sub_uid = world.traffic_count ++;
+      }
    }
+   
+   traffic_finalize( world.traffic, world.traffic_count );
+   for( int i=0; i<vg_list_size(world.van_man); i++ )
+      world.van_man[i].current =&world.traffic[vg_randint(world.traffic_count)];
 
    scene_upload( &world.props );
 
@@ -339,6 +378,7 @@ static void world_load(void)
 
    winfo->g_water_fog = 0.04f;
    render_update_lighting_ub();
+
 }
 
 static void world_init(void)
@@ -356,6 +396,21 @@ static void world_init(void)
    world.dome_lower = *mdl_node_submesh( msky, nlower, 0 );
    world.dome_upper = *mdl_node_submesh( msky, nupper, 0 );
    free(msky);
+
+   mdl_header *mcars = mdl_load( "models/rs_cars.mdl" );
+   mdl_unpack_glmesh( mcars, &world.cars );
+   mdl_node *nholden = mdl_node_from_name( mcars, "holden" );
+   world.car_holden = *mdl_node_submesh( mcars, nholden, 0 );
+   free(mcars);
+}
+
+static void world_update(void)
+{
+   for( int i=0; i<vg_list_size(world.van_man); i++ )
+   {
+      traffic_drive( &world.van_man[i] );
+      traffic_visualize_car( &world.van_man[i] );
+   }
 }
 
 /*
@@ -385,6 +440,14 @@ static void render_props( m4x4f projection, v3f camera )
 
    scene_bind( &world.props );
    scene_draw( &world.props );
+
+   mesh_bind( &world.cars );
+
+   for( int i=0; i<vg_list_size(world.van_man); i++ )
+   {
+      shader_vblend_uMdl( world.van_man[i].transform );
+      mdl_draw_submesh( &world.car_holden );
+   }
 }
 
 static void render_terrain( m4x4f projection, v3f camera )