gate frame fix
authorhgn <hgodden00@gmail.com>
Tue, 4 Oct 2022 15:59:29 +0000 (16:59 +0100)
committerhgn <hgodden00@gmail.com>
Tue, 4 Oct 2022 15:59:29 +0000 (16:59 +0100)
15 files changed:
common.h
gate.h
main.c
network.h
player.h
player_animation.h
player_audio.h
player_physics.h
water.h [deleted file]
world.h
world_gen.h [new file with mode: 0644]
world_render.h [new file with mode: 0644]
world_routes.h
world_sfd.h
world_water.h [new file with mode: 0644]

index f4239630e4c5f60081c3f460c8c82556e66ae754..e4015a8d196750f17d50d94c3d72aca19b217f03 100644 (file)
--- a/common.h
+++ b/common.h
@@ -47,7 +47,6 @@ static void eval_bezier_time( v3f p0, v3f p1, v3f h0, v3f h1, float t, v3f p )
    v3_muladds( p, p0, 3.0f*tt  -ttt -3.0f*t +1.0f, p );
 }
 
-/* TODO: he needs a home somewhere */
-static float *player_get_pos(void);
+static int network_scores_updated = 0;
 
 #endif /* COMMON_H */
diff --git a/gate.h b/gate.h
index 175a8c68572faa1e63fa2927bec186a1f7f36b45..fec28266caa1aaf176898565bfb52d488825a686 100644 (file)
--- a/gate.h
+++ b/gate.h
@@ -6,7 +6,7 @@
 #include "render.h"
 #include "shaders/gate.h"
 #include "shaders/gatelq.h"
-#include "water.h"
+#include "world_water.h"
 
 typedef struct teleport_gate teleport_gate;
 
diff --git a/main.c b/main.c
index 0732057dab95eb68745b26280e5e15670240c13f..e967bcf9bb3c8acf3cf85c714102434663111cd5 100644 (file)
--- a/main.c
+++ b/main.c
@@ -54,7 +54,6 @@ static int cl_ui = 1;
 #include "anim_test.h"
 
 #include "gate.h"
-#include "water.h"
 
 void vg_register(void)
 {
@@ -65,7 +64,6 @@ void vg_register(void)
 
    player_register();
    world_register();
-   water_register();
    gate_register();
 }
 
@@ -237,7 +235,7 @@ void vg_start(void)
       world_load();
 
       reset_player( 1, (const char *[]){ "start" } );
-      rb_init( &player.rb );
+      rb_init( &player.phys.rb );
 
       network_init();
    }
@@ -270,7 +268,7 @@ void vg_update(void)
    {
       network_update();
       player_update();
-      world_update();
+      world_update( player.phys.rb.co );
       //traffic_visualize( world.traffic, world.traffic_count );
       //
       /* TEMP */
@@ -305,7 +303,7 @@ static void draw_origin_axis(void)
 
 static void render_main_game(void)
 {
-   float speed = freecam? 0.0f: v3_length( player.rb.v );
+   float speed = freecam? 0.0f: v3_length( player.phys.rb.v );
    v3f shake = { vg_randf()-0.5f, vg_randf()-0.5f, vg_randf()-0.5f };
    v3_muls( shake, speed*0.01f, shake );
 
@@ -336,7 +334,7 @@ static void render_main_game(void)
    render_water_surface( vg_pv, player.camera );
 
    vg_tex2d_bind( &tex_water, 1 ); /*TODO: ?*/
-   render_world_gates( vg_pv, player.rb.co, player.camera );
+   render_world_gates( vg_pv, player.phys.rb.co, player.camera );
    
    /* Copy the RGB of what we have into the background buffer */
    glBindFramebuffer( GL_READ_FRAMEBUFFER, 0 );
@@ -437,15 +435,15 @@ static void run_debug_info(void)
 {
    char buf[40];
    
-   snprintf( buf, 40, "%.2fm/s", v3_length( player.rb.v ) );
+   snprintf( buf, 40, "%.2fm/s", v3_length( player.phys.rb.v ) );
    gui_text( (ui_px [2]){ 0, 0 }, buf, 1, k_text_align_left );
    
    snprintf( buf, 40, "%.2f %.2f %.2f m/s", 
-         player.a[0], player.a[1], player.a[2] );
+         player.phys.a[0], player.phys.a[1], player.phys.a[2] );
    gui_text( (ui_px [2]){ 0, 20 }, buf, 1, k_text_align_left );
 
    snprintf( buf, 40, "pos %.2f %.2f %.2f", 
-         player.rb.co[0], player.rb.co[1], player.rb.co[2] );
+         player.phys.rb.co[0], player.phys.rb.co[1], player.phys.rb.co[2] );
    gui_text( (ui_px [2]){ 0, 40 }, buf, 1, k_text_align_left );
 
    if( vg_gamepad_ready )
index ef3455de14f77f873f8425a1e7d46405f57da949..a9b5c8f3423b79093053abca15af6ddba098a361 100644 (file)
--- a/network.h
+++ b/network.h
@@ -38,7 +38,6 @@ static void network_submit_highscore( u32 trackid, u16 points, u16 time );
 static u8 steam_app_ticket[ 1024 ];
 static u32 steam_app_ticket_length;
 static int network_name_update = 1;
-static int network_scores_updated = 0;
 
 static HSteamNetConnection cremote;
 static ESteamNetworkingConnectionState cremote_state = 
index 92db1fbca3ddf564550b47614405116719bfa04e..e4c0fced5366a51cbfa083bd514d53e030705629 100644 (file)
--- a/player.h
+++ b/player.h
@@ -52,33 +52,34 @@ static float fc_speed = 10.0f;
 static struct gplayer
 {
    /* Physics */
-   rigidbody rb, collide_front, collide_back, rb_gate_frame;
+   rigidbody collide_front, collide_back;
 
-   /* TODO: eugh */
-   m3x3f gate_vr_frame, gate_vr_pstep_frame;
-   int on_board_frame, in_air_frame;
+   struct player_phys
+   {
+      rigidbody rb, rb_gate_frame;
+      float iY, siY;   /* Yaw inertia */
+
+      v3f a, v_last, m, bob, vl;
 
-   v3f a, v_last, m, bob, vl;
+      /* Utility */
+      float vswitch, slip, slip_last,
+            reverse;
 
-   /* Utility */
-   float vswitch, slip, slip_last,
-         reverse;
+      float grab, jump;
+      int in_air, on_board, jump_charge, jump_dir;
 
-   float iY;   /* Yaw inertia */
-   int in_air, is_dead, on_board;
+      m3x3f vr,vr_pstep;
+   }
+   phys, 
+   phys_gate_frame;
 
-   v2f board_xy;
-   float grab;
-   float pitch;
    float pushing, push_time;
-   float jump;
-   int jump_charge, jump_dir;
-   
+   int is_dead;
+
    v3f land_target;
    v3f land_target_log[22];
    u32 land_target_colours[22];
    int land_log_count;
-   m3x3f vr,vr_pstep;
 
    v3f handl_target, handr_target,
        handl, handr;
@@ -100,7 +101,9 @@ static struct gplayer
          fairdir,
          fsetup,
          walk_timer,
+         fjump,
          fonboard;
+
    int step_phase;
 
    /* player model */
@@ -146,8 +149,6 @@ static struct gplayer
 }
 player = 
 {
-   .on_board = 0,
-
    .collide_front = { .type = k_rb_shape_sphere, .inf.sphere.radius = 0.3f },
    .collide_back  = { .type = k_rb_shape_sphere, .inf.sphere.radius = 0.3f }
 };
@@ -158,6 +159,8 @@ player =
 static float *player_get_pos(void);
 static void player_kill(void);
 static float *player_cam_pos(void);
+static void player_save_frame(void);
+static void player_restore_frame(void);
 
 /* 
  * Submodules
@@ -189,29 +192,23 @@ static void player_init(void)                                            /* 1 */
 
 static void player_update(void)                                          /* 2 */
 {
+   struct player_phys *phys = &player.phys;
+
    for( int i=0; i<player.land_log_count; i++ )
       vg_line_cross( player.land_target_log[i], 
             player.land_target_colours[i], 0.25f);
 
    if( vg_get_axis("grabl")>0.0f)
    {
-      player.rb = player.rb_gate_frame;
-      player.on_board = player.on_board_frame;
-      player.in_air = player.in_air_frame;
-      m3x3_copy( player.gate_vr_frame, player.vr );
-      m3x3_copy( player.gate_vr_pstep_frame, player.vr_pstep );
       player.is_dead = 0;
-      player.in_air = 1;
+      player_restore_frame();
 
-
-      if( !player.on_board )
+      if( !phys->on_board )
       {
-         player.angles[0] = atan2f( -player.rb.forward[2], 
-                                    -player.rb.forward[0] );
+         player.angles[0] = atan2f( -phys->rb.forward[2], 
+                                    -phys->rb.forward[0] );
       }
 
-      m3x3_identity( player.vr );
-
       player.mdl.shoes[0] = 1;
       player.mdl.shoes[1] = 1;
 
@@ -220,13 +217,13 @@ static void player_update(void)                                          /* 2 */
 
    if( vg_get_button_down( "switchmode" ) )
    {
-      player.on_board ^= 0x1;
+      phys->on_board ^= 0x1;
    }
 
-   if( (glfwGetKey( vg_window, GLFW_KEY_O ) || (player.rb.co[1] < 0.0f)) &&
+   if( (glfwGetKey( vg_window, GLFW_KEY_O ) || (phys->rb.co[1] < 0.0f)) &&
        !player.is_dead)
    {
-      player_ragdoll_copy_model( player.rb.v );
+      player_ragdoll_copy_model( phys->rb.v );
       player.is_dead = 1;
    }
 
@@ -282,13 +279,13 @@ static void draw_player(void)                                            /* 3 */
 
 static float *player_get_pos(void)
 {
-   return player.rb.co;
+   return player.phys.rb.co;
 }
 
 static void player_kill(void)
 {
    player.is_dead = 1;
-   player_ragdoll_copy_model( player.rb.v );
+   player_ragdoll_copy_model( player.phys.rb.v );
 }
 
 static float *player_cam_pos(void)
index 101bd22e50bd8335c7a0eebcd4e501e47aba61d1..33dc3bf82d31e033302f8171cffc8d44669ca45c 100644 (file)
@@ -5,6 +5,8 @@
 
 static void player_animate_offboard(void)
 {
+   struct player_phys *phys = &player.phys;
+
    mdl_keyframe apose[32], bpose[32];
    struct skeleton *sk = &player.mdl.sk;
 
@@ -36,7 +38,7 @@ static void player_animate_offboard(void)
    v4f rot;
    q_axis_angle( rot, (v3f){0.0f,1.0f,0.0f}, -player.angles[0] - VG_PIf*0.5f );
    q_m3x3( rot, mtx );
-   v3_copy( player.rb.to_world[3], mtx[3] );
+   v3_copy( phys->rb.to_world[3], mtx[3] );
 
    skeleton_apply_transform( &player.mdl.sk, mtx );
    skeleton_debug( &player.mdl.sk );
@@ -44,37 +46,39 @@ static void player_animate_offboard(void)
 
 static void player_animate(void)
 {
-   if( !player.on_board )
+   struct player_phys *phys = &player.phys;
+
+   if( !phys->on_board )
    {
       player_animate_offboard();
       return;
    }
 
    /* Camera position */
-   v3_sub( player.rb.v, player.v_last, player.a );
-   v3_copy( player.rb.v, player.v_last );
+   v3_sub( phys->rb.v, phys->v_last, phys->a );
+   v3_copy( phys->rb.v, phys->v_last );
 
-   v3_add( player.m, player.a, player.m );
-   v3_lerp( player.m, (v3f){0.0f,0.0f,0.0f}, 0.1f, player.m );
+   v3_add( phys->m, phys->a, phys->m );
+   v3_lerp( phys->m, (v3f){0.0f,0.0f,0.0f}, 0.1f, phys->m );
 
-   player.m[0] = vg_clampf( player.m[0], -2.0f, 2.0f );
-   player.m[1] = vg_clampf( player.m[1], -2.0f, 2.0f );
-   player.m[2] = vg_clampf( player.m[2], -2.0f, 2.0f );
-   v3_lerp( player.bob, player.m, 0.2f, player.bob );
+   phys->m[0] = vg_clampf( phys->m[0], -2.0f, 2.0f );
+   phys->m[1] = vg_clampf( phys->m[1], -2.0f, 2.0f );
+   phys->m[2] = vg_clampf( phys->m[2], -2.0f, 2.0f );
+   v3_lerp( phys->bob, phys->m, 0.2f, phys->bob );
 
    /* Head */
-   float lslip = fabsf(player.slip);
+   float lslip = fabsf(phys->slip);
 
    float kheight = 2.0f,
          kleg = 0.6f;
 
    v3f offset;
    v3_zero( offset );
-   m3x3_mulv( player.rb.to_local, player.bob, offset );
+   m3x3_mulv( phys->rb.to_local, phys->bob, offset );
 
    static float speed_wobble = 0.0f, speed_wobble_2 = 0.0f;
    
-   float kickspeed = vg_clampf(v3_length(player.rb.v)*(1.0f/40.0f), 0.0f, 1.0f);
+   float kickspeed = vg_clampf(v3_length(phys->rb.v)*(1.0f/40.0f), 0.0f, 1.0f);
    float kicks = (vg_randf()-0.5f)*2.0f*kickspeed;
    float sign = vg_signf( kicks );
    speed_wobble = vg_lerpf( speed_wobble, kicks*kicks*sign, 0.1f );
@@ -95,7 +99,7 @@ static void player_animate(void)
     */
 
    /* scalar blending information */
-   float speed = v3_length( player.rb.v );
+   float speed = v3_length( phys->rb.v );
    
    /* sliding */
    {
@@ -105,9 +109,9 @@ static void player_animate(void)
    
    /* movement information */
    {
-      float dirz = player.reverse > 0.0f? 0.0f: 1.0f,
-            dirx = player.slip < 0.0f?    0.0f: 1.0f,
-            fly  = player.in_air?         1.0f: 0.0f;
+      float dirz = phys->reverse > 0.0f? 0.0f: 1.0f,
+            dirx = phys->slip < 0.0f?    0.0f: 1.0f,
+            fly  = phys->in_air?         1.0f: 0.0f;
 
       player.fdirz = vg_lerpf( player.fdirz, dirz, 0.04f );
       player.fdirx = vg_lerpf( player.fdirx, dirx, 0.01f );
@@ -140,7 +144,7 @@ static void player_animate(void)
       player.fpush = vg_lerpf( player.fpush, player.pushing, 0.1f );
 
       float pt = player.push_time;
-      if( player.reverse > 0.0f )
+      if( phys->reverse > 0.0f )
          skeleton_sample_anim( sk, player.mdl.anim_push, pt, bpose );
       else
          skeleton_sample_anim( sk, player.mdl.anim_push_reverse, pt, bpose );
@@ -149,14 +153,17 @@ static void player_animate(void)
 
       /* trick setup */
       float jump_start_frame = 14.0f/30.0f;
-      float setup_frame = player.jump * jump_start_frame,
-            setup_blend = vg_minf( player.jump*5.0f, 1.0f );
+
+      player.fjump = vg_lerpf( player.fjump, phys->jump, 0.08f );
+
+      float setup_frame = phys->jump * jump_start_frame,
+            setup_blend = vg_minf( player.fjump, 1.0f );
       
       float jump_frame = (vg_time - player.jump_time) + jump_start_frame;
       if( jump_frame >= jump_start_frame && jump_frame <= (40.0f/30.0f) )
          setup_frame = jump_frame;
 
-      struct skeleton_anim *jump_anim = player.jump_dir?
+      struct skeleton_anim *jump_anim = phys->jump_dir?
                                         player.mdl.anim_ollie:
                                         player.mdl.anim_ollie_reverse;
 
@@ -182,25 +189,28 @@ static void player_animate(void)
             grab_frame = ang_unit * (15.0f/30.0f);
 
       skeleton_sample_anim( sk, player.mdl.anim_grabs, grab_frame, bpose );
-      skeleton_lerp_pose( sk, apose, bpose, player.grab, air_pose );
+      skeleton_lerp_pose( sk, apose, bpose, phys->grab, air_pose );
    }
 
    skeleton_lerp_pose( sk, ground_pose, air_pose, player.ffly, apose );
 
-   float add_grab_mod = 1.0f - player.ffly*player.grab;
+   float add_grab_mod = 1.0f - player.ffly*phys->grab;
 
    /* additive effects */
-   apose[player.mdl.id_hip-1].co[0] += offset[0]*add_grab_mod;
-   apose[player.mdl.id_hip-1].co[2] += offset[2]*add_grab_mod;
-   apose[player.mdl.id_ik_hand_l-1].co[0] += offset[0]*add_grab_mod;
-   apose[player.mdl.id_ik_hand_l-1].co[2] += offset[2]*add_grab_mod;
-   apose[player.mdl.id_ik_hand_r-1].co[0] += offset[0]*add_grab_mod;
-   apose[player.mdl.id_ik_hand_r-1].co[2] += offset[2]*add_grab_mod;
-   apose[player.mdl.id_ik_elbow_l-1].co[0] += offset[0]*add_grab_mod;
-   apose[player.mdl.id_ik_elbow_l-1].co[2] += offset[2]*add_grab_mod;
-   apose[player.mdl.id_ik_elbow_r-1].co[0] += offset[0]*add_grab_mod;
-   apose[player.mdl.id_ik_elbow_r-1].co[2] += offset[2]*add_grab_mod;
-
+   {
+      u32 apply_to[] = { player.mdl.id_hip, 
+                         player.mdl.id_ik_hand_l,
+                         player.mdl.id_ik_hand_r,
+                         player.mdl.id_ik_elbow_l,
+                         player.mdl.id_ik_elbow_r };
+
+      for( int i=0; i<vg_list_size(apply_to); i ++ )
+      {
+         apose[apply_to[i]-1].co[0] += offset[0]*add_grab_mod;
+         apose[apply_to[i]-1].co[2] += offset[2]*add_grab_mod;
+      }
+   }
+   
    skeleton_apply_pose( &player.mdl.sk, apose, k_anim_apply_defer_ik );
    skeleton_apply_ik_pass( &player.mdl.sk );
    skeleton_apply_pose( &player.mdl.sk, apose, k_anim_apply_deffered_only );
@@ -208,7 +218,7 @@ static void player_animate(void)
    v3_copy( player.mdl.sk.final_mtx[player.mdl.id_head-1][3], 
                player.mdl.cam_pos );
    skeleton_apply_inverses( &player.mdl.sk );
-   skeleton_apply_transform( &player.mdl.sk, player.rb.to_world );
+   skeleton_apply_transform( &player.mdl.sk, phys->rb.to_world );
 
    skeleton_debug( &player.mdl.sk );
 }
@@ -248,24 +258,26 @@ static void player_animate_death_cam(void)
 
 static void player_animate_camera(void)
 {
+   struct player_phys *phys = &player.phys;
+
    static v3f lerp_cam = {0.0f,0.0f,0.0f};
    v3f cam_pos;
 
-   player.fonboard = vg_lerpf(player.fonboard, player.on_board, ktimestep*1.0f);
+   player.fonboard = vg_lerpf(player.fonboard, phys->on_board, ktimestep*1.0f);
 
-   if( player.on_board )
+   if( phys->on_board )
    {
       v3f offs = { -0.4f, 0.15f, 0.0f };
       v3_lerp( lerp_cam, player.mdl.cam_pos, 0.8f, lerp_cam );
       v3_add( lerp_cam, offs, cam_pos );
 
       /* Look angles */
-      v3_lerp( player.vl, player.rb.v, 0.05f, player.vl );
+      v3_lerp( phys->vl, phys->rb.v, 0.05f, phys->vl );
 
-      float yaw = atan2f(  player.vl[0], -player.vl[2] ),
-          pitch = atan2f( -player.vl[1], 
+      float yaw = atan2f(  phys->vl[0], -phys->vl[2] ),
+          pitch = atan2f( -phys->vl[1], 
                 sqrtf(
-                  player.vl[0]*player.vl[0] + player.vl[2]*player.vl[2]
+                  phys->vl[0]*phys->vl[0] + phys->vl[2]*phys->vl[2]
                 )) * 0.7f;
 
       player.angles[0] = yaw;
@@ -275,14 +287,14 @@ static void player_animate_camera(void)
       /* Camera shake */
       static v2f shake_damp = {0.0f,0.0f};
       v2f shake = { vg_randf()-0.5f, vg_randf()-0.5f };
-      v2_muls( shake, v3_length(player.rb.v)*0.3f 
-                           * (1.0f+fabsf(player.slip)), shake);
+      v2_muls( shake, v3_length(phys->rb.v)*0.3f 
+                           * (1.0f+fabsf(phys->slip)), shake);
 
       v2_lerp( shake_damp, shake, 0.01f, shake_damp );
       shake_damp[0] *= 0.2f;
 
       v2_muladds( player.angles, shake_damp, 0.1f, player.angles );
-      m4x3_mulv( player.rb.to_world, cam_pos, player.camera_pos );
+      m4x3_mulv( phys->rb.to_world, cam_pos, player.camera_pos );
    }
    else
    {
@@ -303,11 +315,11 @@ static void player_animate_camera(void)
       q_axis_angle( rot, (v3f){0.0f,1.0f,0.0f}, 
                               -player.angles[0] -VG_PIf*0.5f );
       q_m3x3( rot, mtx );
-      v3_copy( player.rb.to_world[3], mtx[3] );
+      v3_copy( phys->rb.to_world[3], mtx[3] );
 
       m4x3_mulv( mtx, player.mdl.cam_pos, cam_pos );
       v3_add( cam_pos, forward_dir, player.camera_pos );
-      v3_lerp( player.vl, player.rb.v, 0.3f, player.vl );
+      v3_lerp( phys->vl, phys->rb.v, 0.3f, phys->vl );
    }
 }
 
index 01c13fcd7db6bc3f9f01d0b93827b732d6f0cb4d..b5ba7359fc85524c8b02f2c9b4d59d9c87a440db 100644 (file)
@@ -8,6 +8,8 @@
  */
 static void player_audio(void)
 {
+   struct player_phys *phys = &player.phys;
+
    static int _ding = 0;
    
    int last = _ding;
@@ -20,7 +22,7 @@ static void player_audio(void)
    static int _air = 0;
 
    int l2 = _air;
-   _air = player.in_air;
+   _air = phys->in_air;
 
    static double last_revert = -2000.0;
 
@@ -30,10 +32,10 @@ static void player_audio(void)
    audio_lock();
    
    double revert_delta = vg_time - last_revert;
-   if( player.on_board && (!_air && l2) && (fabsf(player.slip) > 0.5f) && 
+   if( phys->on_board && (!_air && l2) && (fabsf(phys->slip) > 0.5f) && 
          (revert_delta > 0.7) )
    {
-      audio_player_set_position( &audio_player_extra, player.rb.co );
+      audio_player_set_position( &audio_player_extra, phys->rb.co );
       audio_player_set_flags( &audio_player_extra, AUDIO_FLAG_SPACIAL_3D );
       audio_player_set_vol( &audio_player_extra, 2.0f );
       audio_player_playclip( &audio_player_extra, &audio_lands[rand()%5] );
@@ -42,24 +44,24 @@ static void player_audio(void)
    }
 
    static float air = 0.0f;
-   air = vg_lerpf(air, player.in_air? 1.0f: 0.0f, 5.0f*ktimestep);
+   air = vg_lerpf(air, phys->in_air? 1.0f: 0.0f, 5.0f*ktimestep);
 
    /* Spacial info */
    v3f ears = { 1.0f,0.0f,0.0f };
    v3f delta;
 
    float *cam = player.camera[3],
-         *pos = player.rb.co;
+         *pos = phys->rb.co;
    
    if( trigger_ding )
       audio_player_playclip( &audio_player_extra, &audio_ding );
 
-   audio_player_set_position( &audio_player0, player.rb.co );
-   audio_player_set_position( &audio_player1, player.rb.co );
-   audio_player_set_position( &audio_player2, player.rb.co );
+   audio_player_set_position( &audio_player0, phys->rb.co );
+   audio_player_set_position( &audio_player1, phys->rb.co );
+   audio_player_set_position( &audio_player2, phys->rb.co );
    audio_player_set_position( &audio_player_gate, world.render_gate_pos );
 
-   v3_sub( player.rb.co, player.camera[3], delta );
+   v3_sub( phys->rb.co, player.camera[3], delta );
    v3_normalize( delta );
    m3x3_mulv( player.camera, ears, ears );
 
@@ -84,7 +86,7 @@ static void player_audio(void)
    {
       v3f waterpos;
       enum audio_sprite_type sprite_type = 
-         audio_sample_sprite_random( player.rb.co, waterpos );
+         audio_sample_sprite_random( phys->rb.co, waterpos );
 
       if( sprite_type != k_audio_sprite_type_none )
       {
@@ -105,7 +107,7 @@ static void player_audio(void)
       }
    }
    
-   if( freecam || player.is_dead || !player.on_board )
+   if( freecam || player.is_dead || !phys->on_board )
    {
       audio_player_set_vol( &audio_player0, 0.0f );
       audio_player_set_vol( &audio_player1, 0.0f );
@@ -117,10 +119,10 @@ static void player_audio(void)
       else
          walk_phase = 0;
 
-      if( (player.step_phase != walk_phase) && !player.in_air )
+      if( (player.step_phase != walk_phase) && !phys->in_air )
       {
          audio_player_set_flags( &audio_player_extra, AUDIO_FLAG_SPACIAL_3D );
-         audio_player_set_position( &audio_player_extra, player.rb.co );
+         audio_player_set_position( &audio_player_extra, phys->rb.co );
          audio_player_set_vol( &audio_player_extra, 6.0f );
          audio_player_playclip( &audio_player_extra, 
                                           &audio_footsteps[rand()%4] );
@@ -131,9 +133,9 @@ static void player_audio(void)
    else
    {
       /* Composite */
-      float speed = vg_minf(v3_length( player.rb.v )*0.1f,1.0f),
+      float speed = vg_minf(v3_length( phys->rb.v )*0.1f,1.0f),
             attn  = speed,
-            slide = vg_clampf( fabsf(player.slip), 0.0f, 1.0f ),
+            slide = vg_clampf( fabsf(phys->slip), 0.0f, 1.0f ),
             vol0  = (1.0f-air)*attn*(1.0f-slide),
             vol1  =       air *attn,
             vol2  = (1.0f-air)*attn*slide;
index baaa70e919a0328ec6760fc077101c33943c9787..f9584a2cd3fb916d4e7c1b8305f08bc8c0dfad2d 100644 (file)
@@ -24,29 +24,31 @@ static void apply_gravity( v3f vel, float const timestep )
  */
 static void player_start_air(void)
 {
-   if( player.in_air )
+   struct player_phys *phys = &player.phys;
+
+   if( phys->in_air )
       return;
 
-   player.in_air = 1;
+   phys->in_air = 1;
 
    float pstep = ktimestep*10.0f;
    float best_velocity_delta = -9999.9f;
    float k_bias = 0.96f;
 
    v3f axis;
-   v3_cross( player.rb.up, player.rb.v, axis );
+   v3_cross( phys->rb.up, phys->rb.v, axis );
    v3_normalize( axis );
    player.land_log_count = 0;
    
-   m3x3_identity( player.vr );
+   m3x3_identity( phys->vr );
 
    for( int m=-3;m<=12; m++ )
    {
       float vmod = ((float)m / 15.0f)*0.09f;
 
       v3f pco, pco1, pv;
-      v3_copy( player.rb.co, pco );
-      v3_muls( player.rb.v, k_bias, pv );
+      v3_copy( phys->rb.co, pco );
+      v3_muls( phys->rb.v, k_bias, pv );
 
       /* 
        * Try different 'rotations' of the velocity to find the best possible
@@ -96,9 +98,9 @@ static void player_start_air(void)
 
                v3_copy( contact.pos, player.land_target );
                
-               m3x3_copy( vr, player.vr_pstep );
+               m3x3_copy( vr, phys->vr_pstep );
                q_axis_angle( vr_q, axis, vmod*0.1f );
-               q_m3x3( vr_q, player.vr );
+               q_m3x3( vr_q, phys->vr );
             }
 
             v3_copy( contact.pos, 
@@ -119,13 +121,15 @@ static void player_start_air(void)
  */
 static void player_physics_control(void)
 {
+   struct player_phys *phys = &player.phys;
+
    /*
     * Computing localized friction forces for controlling the character
     * Friction across X is significantly more than Z
     */
 
    v3f vel;
-   m3x3_mulv( player.rb.to_local, player.rb.v, vel );
+   m3x3_mulv( phys->rb.to_local, phys->rb.v, vel );
    float slip = 0.0f;
    
    if( fabsf(vel[2]) > 0.01f )
@@ -133,8 +137,8 @@ static void player_physics_control(void)
 
    if( fabsf( slip ) > 1.2f )
       slip = vg_signf( slip ) * 1.2f;
-   player.slip = slip;
-   player.reverse = -vg_signf(vel[2]);
+   phys->slip = slip;
+   phys->reverse = -vg_signf(vel[2]);
 
    float substep = ktimestep * 0.2f;
    float fwd_resistance = (vg_get_button( "break" )? 5.0f: 0.02f) * -substep;
@@ -152,12 +156,12 @@ static void player_physics_control(void)
 
    if( vg_get_button( "jump" ) )
    {
-      player.jump += ktimestep * k_jump_charge_speed;
+      phys->jump += ktimestep * k_jump_charge_speed;
 
-      if( !player.jump_charge )
-         player.jump_dir = player.reverse > 0.0f? 1: 0;
+      if( !phys->jump_charge )
+         phys->jump_dir = phys->reverse > 0.0f? 1: 0;
 
-      player.jump_charge = 1;
+      phys->jump_charge = 1;
    }
 
    if( !vg_get_button("break") && vg_get_button( "push" ) )
@@ -171,29 +175,26 @@ static void player_physics_control(void)
             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;
+      vel[2] -= new_vel * phys->reverse;
    }
 
    /* Pumping */
    static float previous = 0.0f;
-   float delta = previous - player.grab,
+   float delta = previous - phys->grab,
           pump = delta * k_pump_force*ktimestep;
-   previous = player.grab;
+   previous = phys->grab;
 
    v3f p1;
-   v3_muladds( player.rb.co, player.rb.up, pump, p1 );
-   vg_line( player.rb.co, p1, 0xff0000ff );
+   v3_muladds( phys->rb.co, phys->rb.up, pump, p1 );
+   vg_line( phys->rb.co, p1, 0xff0000ff );
 
    vel[1] += pump;
 
    
-   m3x3_mulv( player.rb.to_world, vel, player.rb.v );
+   m3x3_mulv( phys->rb.to_world, vel, phys->rb.v );
    
    float steer = vg_get_axis( "horizontal" );
-   player.iY -= vg_signf(steer)*powf(steer,2.0f) * k_steer_ground * ktimestep;
-   
-   v2_lerp( player.board_xy, (v2f){ slip*0.25f, 0.0f }, 
-         ktimestep*5.0f, player.board_xy);
+   phys->iY -= vg_signf(steer)*powf(steer,2.0f) * k_steer_ground * ktimestep;
 }
 
 /*
@@ -201,7 +202,9 @@ static void player_physics_control(void)
  */
 static void player_physics_control_air(void)
 {
-   m3x3_mulv( player.vr, player.rb.v, player.rb.v );
+   struct player_phys *phys = &player.phys;
+
+   m3x3_mulv( phys->vr, phys->rb.v, phys->rb.v );
    vg_line_cross( player.land_target, 0xff0000ff, 0.25f );
 
    ray_hit hit;
@@ -212,8 +215,8 @@ static void player_physics_control_air(void)
    float pstep = ktimestep*10.0f;
 
    v3f pco, pco1, pv;
-   v3_copy( player.rb.co, pco );
-   v3_copy( player.rb.v, pv );
+   v3_copy( phys->rb.co, pco );
+   v3_copy( phys->rb.v, pv );
    
    float time_to_impact = 0.0f;
    float limiter = 1.0f;
@@ -221,7 +224,7 @@ static void player_physics_control_air(void)
    for( int i=0; i<50; i++ )
    {
       v3_copy( pco, pco1 );
-      m3x3_mulv( player.vr_pstep, pv, pv );
+      m3x3_mulv( phys->vr_pstep, pv, pv );
       apply_gravity( pv, pstep );
       v3_muladds( pco, pv, pstep, pco );
       
@@ -235,9 +238,9 @@ static void player_physics_control_air(void)
       float orig_dist = contact.dist;
       if( ray_world( pco1, vdir, &contact ))
       {
-         float angle = v3_dot( player.rb.up, contact.normal );
+         float angle = v3_dot( phys->rb.up, contact.normal );
          v3f axis; 
-         v3_cross( player.rb.up, contact.normal, axis );
+         v3_cross( phys->rb.up, contact.normal, axis );
 
          time_to_impact += (contact.dist/orig_dist)*pstep;
          limiter = vg_minf( 5.0f, time_to_impact )/5.0f;
@@ -249,7 +252,7 @@ static void player_physics_control_air(void)
          {
             v4f correction;
             q_axis_angle( correction, axis, acosf(angle)*0.05f*(1.0f-limiter) );
-            q_mul( correction, player.rb.q, player.rb.q );
+            q_mul( correction, phys->rb.q, phys->rb.q );
          }
 
          vg_line_cross( contact.pos, 0xffff0000, 0.25f );
@@ -258,23 +261,22 @@ static void player_physics_control_air(void)
       time_to_impact += pstep;
    }
 
-   player.iY -= vg_get_axis( "horizontal" ) * k_steer_air * ktimestep;
+   phys->iY -= vg_get_axis( "horizontal" ) * k_steer_air * ktimestep;
    {
       float iX = vg_get_axis( "vertical" ) * 
-         player.reverse * k_steer_air * limiter * ktimestep;
+         phys->reverse * k_steer_air * limiter * ktimestep;
 
       static float siX = 0.0f;
       siX = vg_lerpf( siX, iX, k_steer_air_lerp );
       
       v4f rotate;
-      q_axis_angle( rotate, player.rb.right, siX );
-      q_mul( rotate, player.rb.q, player.rb.q );
+      q_axis_angle( rotate, phys->rb.right, siX );
+      q_mul( rotate, phys->rb.q, phys->rb.q );
    }
    
    v2f target = {0.0f,0.0f};
    v2_muladds( target, (v2f){ vg_get_axis("h1"), vg_get_axis("v1") },
-               player.grab, target );
-   v2_lerp( player.board_xy, target, ktimestep*3.0f, player.board_xy );
+               phys->grab, target );
 }
 
 /*
@@ -283,18 +285,19 @@ static void player_physics_control_air(void)
  */
 static void player_walk_physics(void)
 {
+   struct player_phys *phys = &player.phys;
    rigidbody *rbf = &player.collide_front,
              *rbb = &player.collide_back;
 
-   m3x3_copy( player.rb.to_world, player.collide_front.to_world );
-   m3x3_copy( player.rb.to_world, player.collide_back.to_world );
+   m3x3_copy( phys->rb.to_world, player.collide_front.to_world );
+   m3x3_copy( phys->rb.to_world, player.collide_back.to_world );
    
    float h0 = 0.3f,
          h1 = 0.9f;
 
-   m4x3_mulv( player.rb.to_world, (v3f){0.0f,h0,0.0f}, rbf->co );
+   m4x3_mulv( phys->rb.to_world, (v3f){0.0f,h0,0.0f}, rbf->co );
    v3_copy( rbf->co, rbf->to_world[3] );
-   m4x3_mulv( player.rb.to_world, (v3f){0.0f,h1,0.0f}, rbb->co );
+   m4x3_mulv( phys->rb.to_world, (v3f){0.0f,h1,0.0f}, rbb->co );
    v3_copy( rbb->co, rbb->to_world[3] );
 
    m4x3_invert_affine( rbf->to_world, rbf->to_local );
@@ -321,7 +324,7 @@ static void player_walk_physics(void)
          struct contact *ct = &manifold[i];
          
          /*normal */
-         float vn = -v3_dot( player.rb.v, ct->n );
+         float vn = -v3_dot( phys->rb.v, ct->n );
          vn += ct->bias;
 
          float temp = ct->norm_impulse;
@@ -331,34 +334,34 @@ static void player_walk_physics(void)
          v3f impulse;
          v3_muls( ct->n, vn, impulse );
 
-         v3_add( impulse, player.rb.v, player.rb.v );
+         v3_add( impulse, phys->rb.v, phys->rb.v );
 
          /* friction */
          for( int j=0; j<2; j++ )
          {
             float     f = k_friction * ct->norm_impulse,
-                     vt = v3_dot( player.rb.v, ct->t[j] ),
+                     vt = v3_dot( phys->rb.v, ct->t[j] ),
                  lambda = -vt;
             
             float temp = ct->tangent_impulse[j];
             ct->tangent_impulse[j] = vg_clampf( temp + lambda, -f, f );
             lambda = ct->tangent_impulse[j] - temp;
 
-            v3_muladds( player.rb.v, ct->t[j], lambda, player.rb.v );
+            v3_muladds( phys->rb.v, ct->t[j], lambda, phys->rb.v );
          }
       }
    }
 
-   player.in_air = len==0?1:0;
+   phys->in_air = len==0?1:0;
    
-   v3_zero( player.rb.w );
-   q_axis_angle( player.rb.q, (v3f){0.0f,1.0f,0.0f}, -player.angles[0] );
+   v3_zero( phys->rb.w );
+   q_axis_angle( phys->rb.q, (v3f){0.0f,1.0f,0.0f}, -player.angles[0] );
 
    v3f forward_dir = { sinf(player.angles[0]),0.0f,-cosf(player.angles[0]) };
 
    v3f p1;
-   v3_muladds( player.rb.co, forward_dir, 2.0f, p1 );
-   vg_line( player.rb.co, p1, 0xff0000ff );
+   v3_muladds( phys->rb.co, forward_dir, 2.0f, p1 );
+   vg_line( phys->rb.co, p1, 0xff0000ff );
 
    float move_dead = 0.1f,
          move = vg_get_axis("grabr")*0.5f + 0.5f - move_dead;
@@ -368,11 +371,11 @@ static void player_walk_physics(void)
       float move_norm = move * (1.0f/(1.0f-move_dead)),
             speed     = vg_lerpf( 0.1f*k_runspeed, k_runspeed, move_norm ),
             amt       = k_walk_accel * ktimestep,
-            zvel      = v3_dot( player.rb.v, forward_dir ),
+            zvel      = v3_dot( phys->rb.v, forward_dir ),
             new_vel   = vg_minf( zvel + amt, speed ),
             diff      = new_vel - vg_minf( zvel, speed );
 
-      v3_muladds( player.rb.v, forward_dir, diff, player.rb.v );
+      v3_muladds( phys->rb.v, forward_dir, diff, phys->rb.v );
 
       /* TODO move */
       float walk_norm = 30.0f/(float)player.mdl.anim_walk->length,
@@ -385,8 +388,8 @@ static void player_walk_physics(void)
       player.walk_timer = 0.0f;
    }
 
-   player.rb.v[0] *= 1.0f - (ktimestep*k_walk_friction);
-   player.rb.v[2] *= 1.0f - (ktimestep*k_walk_friction);
+   phys->rb.v[0] *= 1.0f - (ktimestep*k_walk_friction);
+   phys->rb.v[2] *= 1.0f - (ktimestep*k_walk_friction);
 }
 
 /*
@@ -394,6 +397,7 @@ static void player_walk_physics(void)
  */
 static void player_physics(void)
 {
+   struct player_phys *phys = &player.phys;
    /*
     * Update collision fronts
     */
@@ -401,15 +405,15 @@ static void player_physics(void)
    rigidbody *rbf = &player.collide_front,
              *rbb = &player.collide_back;
 
-   m3x3_copy( player.rb.to_world, player.collide_front.to_world );
-   m3x3_copy( player.rb.to_world, player.collide_back.to_world );
+   m3x3_copy( phys->rb.to_world, player.collide_front.to_world );
+   m3x3_copy( phys->rb.to_world, player.collide_back.to_world );
    
-   player.air_blend = vg_lerpf( player.air_blend, player.in_air, 0.1f );
+   player.air_blend = vg_lerpf( player.air_blend, phys->in_air, 0.1f );
    float h = player.air_blend*0.2f;
 
-   m4x3_mulv( player.rb.to_world, (v3f){0.0f,h,-k_board_length}, rbf->co );
+   m4x3_mulv( phys->rb.to_world, (v3f){0.0f,h,-k_board_length}, rbf->co );
    v3_copy( rbf->co, rbf->to_world[3] );
-   m4x3_mulv( player.rb.to_world, (v3f){0.0f,h, k_board_length}, rbb->co );
+   m4x3_mulv( phys->rb.to_world, (v3f){0.0f,h, k_board_length}, rbb->co );
    v3_copy( rbb->co, rbb->to_world[3] );
 
    m4x3_invert_affine( rbf->to_world, rbf->to_local );
@@ -443,12 +447,12 @@ static void player_physics(void)
 
       v3_normalize( surface_avg );
 
-      if( v3_dot( player.rb.v, surface_avg ) > 0.5f )
+      if( v3_dot( phys->rb.v, surface_avg ) > 0.5f )
       {
          player_start_air();
       }
       else
-         player.in_air = 0;
+         phys->in_air = 0;
    }
 
    for( int j=0; j<5; j++ )
@@ -458,9 +462,9 @@ static void player_physics(void)
          struct contact *ct = &manifold[i];
          
          v3f dv, delta;
-         v3_sub( ct->co, player.rb.co, delta ); 
-         v3_cross( player.rb.w, delta, dv );
-         v3_add( player.rb.v, dv, dv );
+         v3_sub( ct->co, phys->rb.co, delta ); 
+         v3_cross( phys->rb.w, delta, dv );
+         v3_add( phys->rb.v, dv, dv );
 
          float vn = -v3_dot( dv, ct->n );
          vn += ct->bias;
@@ -472,14 +476,14 @@ static void player_physics(void)
          v3f impulse;
          v3_muls( ct->n, vn, impulse );
 
-         if( fabsf(v3_dot( impulse, player.rb.forward )) > 10.0f ||
-             fabsf(v3_dot( impulse, player.rb.up )) > 50.0f )
+         if( fabsf(v3_dot( impulse, phys->rb.forward )) > 10.0f ||
+             fabsf(v3_dot( impulse, phys->rb.up )) > 50.0f )
          {
             player_kill();
             return;
          }
 
-         v3_add( impulse, player.rb.v, player.rb.v );
+         v3_add( impulse, phys->rb.v, phys->rb.v );
          v3_cross( delta, impulse, impulse );
 
          /*
@@ -490,23 +494,23 @@ static void player_physics(void)
           * components.
           */
          
-         float wy = v3_dot( player.rb.up, impulse ),
-               wx = v3_dot( player.rb.right, impulse )*1.5f;
+         float wy = v3_dot( phys->rb.up, impulse ),
+               wx = v3_dot( phys->rb.right, impulse )*1.5f;
 
-         v3_muladds( player.rb.w, player.rb.up, wy, player.rb.w );
-         v3_muladds( player.rb.w, player.rb.right, wx, player.rb.w );
+         v3_muladds( phys->rb.w, phys->rb.up, wy, phys->rb.w );
+         v3_muladds( phys->rb.w, phys->rb.right, wx, phys->rb.w );
       }
    }
 
    float grabt = vg_get_axis( "grabr" )*0.5f+0.5f;
-   player.grab = vg_lerpf( player.grab, grabt, 0.14f );
+   phys->grab = vg_lerpf( phys->grab, grabt, 0.14f );
    player.pushing = 0.0f;
 
-   if( !player.in_air )
+   if( !phys->in_air )
    {
       v3f axis;
-      float angle = v3_dot( player.rb.up, surface_avg );
-      v3_cross( player.rb.up, surface_avg, axis );
+      float angle = v3_dot( phys->rb.up, surface_avg );
+      v3_cross( phys->rb.up, surface_avg, axis );
 
       //float cz = v3_dot( player.rb.forward, axis );
       //v3_muls( player.rb.forward, cz, axis );
@@ -515,38 +519,38 @@ static void player_physics(void)
       {
          v4f correction;
          q_axis_angle( correction, axis, acosf(angle)*0.3f );
-         q_mul( correction, player.rb.q, player.rb.q );
+         q_mul( correction, phys->rb.q, phys->rb.q );
       }
 
-      v3_muladds( player.rb.v, player.rb.up, 
-            -k_downforce*ktimestep, player.rb.v );
+      v3_muladds( phys->rb.v, phys->rb.up, 
+            -k_downforce*ktimestep, phys->rb.v );
 
       player_physics_control();
 
-      if( !player.jump_charge && player.jump > 0.2f )
+      if( !phys->jump_charge && phys->jump > 0.2f )
       {
          v3f jumpdir;
          
          /* Launch more up if alignment is up else improve velocity */
-         float aup = fabsf(v3_dot( (v3f){0.0f,1.0f,0.0f}, player.rb.up )),
+         float aup = fabsf(v3_dot( (v3f){0.0f,1.0f,0.0f}, phys->rb.up )),
                mod = 0.5f,
                dir = mod + aup*(1.0f-mod);
 
-         v3_copy( player.rb.v, jumpdir );
+         v3_copy( phys->rb.v, jumpdir );
          v3_normalize( jumpdir );
          v3_muls( jumpdir, 1.0f-dir, jumpdir );
-         v3_muladds( jumpdir, player.rb.up, dir, jumpdir );
+         v3_muladds( jumpdir, phys->rb.up, dir, jumpdir );
          v3_normalize( jumpdir );
          
-         float force = k_jump_force*player.jump;
-         v3_muladds( player.rb.v, jumpdir, force, player.rb.v );
-         player.jump = 0.0f;
+         float force = k_jump_force*phys->jump;
+         v3_muladds( phys->rb.v, jumpdir, force, phys->rb.v );
+         phys->jump = 0.0f;
 
          player.jump_time = vg_time;
 
          audio_lock();
          audio_player_set_flags( &audio_player_extra, AUDIO_FLAG_SPACIAL_3D );
-         audio_player_set_position( &audio_player_extra, player.rb.co );
+         audio_player_set_position( &audio_player_extra, phys->rb.co );
          audio_player_set_vol( &audio_player_extra, 20.0f );
          audio_player_playclip( &audio_player_extra, &audio_jumps[rand()%4] );
          audio_unlock();
@@ -557,55 +561,67 @@ static void player_physics(void)
       player_physics_control_air();
    }
 
-   if( !player.jump_charge )
+   if( !phys->jump_charge )
    {
-      player.jump -= k_jump_charge_speed * ktimestep;
+      phys->jump -= k_jump_charge_speed * ktimestep;
    }
-   player.jump_charge = 0;
-   player.jump = vg_clampf( player.jump, 0.0f, 1.0f );
+   phys->jump_charge = 0;
+   phys->jump = vg_clampf( phys->jump, 0.0f, 1.0f );
+}
+
+static void player_save_frame(void)
+{
+   player.phys_gate_frame = player.phys;
+}
+
+static void player_restore_frame(void)
+{
+   player.phys = player.phys_gate_frame;
+   rb_update_transform( &player.phys.rb );
 }
 
 static void player_do_motion(void)
 {
+   struct player_phys *phys = &player.phys;
+
    float horizontal = vg_get_axis("horizontal"),
          vertical = vg_get_axis("vertical");
 
-   if( player.on_board )
+   if( phys->on_board )
       player_physics();
    else
       player_walk_physics();
    
    /* Integrate velocity */
    v3f prevco;
-   v3_copy( player.rb.co, prevco );
+   v3_copy( phys->rb.co, prevco );
    
-   apply_gravity( player.rb.v, ktimestep );
-   v3_muladds( player.rb.co, player.rb.v, ktimestep, player.rb.co );
+   apply_gravity( phys->rb.v, ktimestep );
+   v3_muladds( phys->rb.co, phys->rb.v, ktimestep, phys->rb.co );
 
    /* Real angular velocity integration */
-   v3_lerp( player.rb.w, (v3f){0.0f,0.0f,0.0f}, 0.125f, player.rb.w );
-   if( v3_length2( player.rb.w ) > 0.0f )
+   v3_lerp( phys->rb.w, (v3f){0.0f,0.0f,0.0f}, 0.125f, phys->rb.w );
+   if( v3_length2( phys->rb.w ) > 0.0f )
    {
       v4f rotation;
       v3f axis;
-      v3_copy( player.rb.w, axis );
+      v3_copy( phys->rb.w, axis );
       
       float mag = v3_length( axis );
       v3_divs( axis, mag, axis );
       q_axis_angle( rotation, axis, mag*k_rb_delta );
-      q_mul( rotation, player.rb.q, player.rb.q );
+      q_mul( rotation, phys->rb.q, phys->rb.q );
    }
 
    /* Faux angular velocity */
    v4f rotate; 
 
-   static float siY = 0.0f;
-   float lerpq = player.in_air? 0.04f: 0.3f;
-   siY = vg_lerpf( siY, player.iY, lerpq );
+   float lerpq = phys->in_air? 0.04f: 0.3f;
+   phys->siY = vg_lerpf( phys->siY, phys->iY, lerpq );
 
-   q_axis_angle( rotate, player.rb.up, siY );
-   q_mul( rotate, player.rb.q, player.rb.q );
-   player.iY = 0.0f;
+   q_axis_angle( rotate, phys->rb.up, phys->siY );
+   q_mul( rotate, phys->rb.q, phys->rb.q );
+   phys->iY = 0.0f;
 
    /* 
     * Gate intersection, by tracing a line over the gate planes 
@@ -615,25 +631,22 @@ static void player_do_motion(void)
       struct route_gate *rg = &world.routes.gates[i];
       teleport_gate *gate = &rg->gate;
 
-      if( gate_intersect( gate, player.rb.co, prevco ) )
+      if( gate_intersect( gate, phys->rb.co, prevco ) )
       {
-         m4x3_mulv( gate->transport, player.rb.co, player.rb.co );
-         m3x3_mulv( gate->transport, player.rb.v, player.rb.v );
-         m3x3_mulv( gate->transport, player.vl, player.vl );
-         m3x3_mulv( gate->transport, player.v_last, player.v_last );
-         m3x3_mulv( gate->transport, player.m, player.m );
-         m3x3_mulv( gate->transport, player.bob, player.bob );
+         m4x3_mulv( gate->transport, phys->rb.co, phys->rb.co );
+         m3x3_mulv( gate->transport, phys->rb.v, phys->rb.v );
+         m3x3_mulv( gate->transport, phys->vl, phys->vl );
+         m3x3_mulv( gate->transport, phys->v_last, phys->v_last );
+         m3x3_mulv( gate->transport, phys->m, phys->m );
+         m3x3_mulv( gate->transport, phys->bob, phys->bob );
 
          v4f transport_rotation;
          m3x3_q( gate->transport, transport_rotation );
-         q_mul( transport_rotation, player.rb.q, player.rb.q );
+         q_mul( transport_rotation, phys->rb.q, phys->rb.q );
          
          world_routes_activate_gate( i );
-         player.rb_gate_frame = player.rb;
-         player.in_air_frame = player.in_air;
-         player.on_board_frame = player.on_board;
 
-         if( !player.on_board )
+         if( !phys->on_board )
          {
             v3f fwd_dir = {cosf(player.angles[0]),
                            0.0f,
@@ -643,8 +656,7 @@ static void player_do_motion(void)
             player.angles[0] = atan2f( fwd_dir[2], fwd_dir[0] );
          }
          
-         m3x3_copy( player.vr, player.gate_vr_frame );
-         m3x3_copy( player.vr_pstep, player.gate_vr_pstep_frame );
+         player_save_frame();
 
          audio_lock();
          audio_play_oneshot( &audio_gate_lap, 1.0f );
@@ -653,7 +665,7 @@ static void player_do_motion(void)
       }
    }
    
-   rb_update_transform( &player.rb );
+   rb_update_transform( &phys->rb );
 }
 
 /*
@@ -728,6 +740,7 @@ static void player_camera_update(void)
 
 static int reset_player( int argc, char const *argv[] )
 {
+   struct player_phys *phys = &player.phys;
    struct respawn_point *rp = NULL, *r;
 
    if( argc == 1 )
@@ -753,7 +766,7 @@ static int reset_player( int argc, char const *argv[] )
       for( int i=0; i<world.spawn_count; i++ )
       {
          r = &world.spawns[i];
-         float d = v3_dist2( r->co, player.rb.co );
+         float d = v3_dist2( r->co, phys->rb.co );
          
          vg_info( "Dist %s : %f\n", r->name, d );
          if( d < min_dist )
@@ -773,26 +786,23 @@ static int reset_player( int argc, char const *argv[] )
       rp = &world.spawns[0];
    }
 
-   v4_copy( rp->q, player.rb.q );
-   v3_copy( rp->co, player.rb.co );
-
-   player.vswitch = 1.0f;
-   player.slip_last = 0.0f;
    player.is_dead = 0;
-   player.in_air = 1;
-   m3x3_identity( player.vr );
+
+   v4_copy( rp->q, phys->rb.q );
+   v3_copy( rp->co, phys->rb.co );
+   v3_zero( phys->rb.v );
+
+   phys->vswitch = 1.0f;
+   phys->slip_last = 0.0f;
+   phys->in_air = 1;
+   phys->on_board = 0;
+   m3x3_identity( phys->vr );
 
    player.mdl.shoes[0] = 1;
    player.mdl.shoes[1] = 1;
    
-   rb_update_transform( &player.rb );
-   m3x3_mulv( player.rb.to_world, (v3f){ 0.0f, 0.0f, -1.2f }, player.rb.v );
-   m3x3_identity( player.gate_vr_frame );
-   m3x3_identity( player.gate_vr_pstep_frame );
-
-   player.rb_gate_frame = player.rb;
-   player.on_board_frame = player.on_board;
-   player.in_air_frame = player.in_air;
+   rb_update_transform( &phys->rb );
+   player_save_frame();
    return 1;
 }
 
diff --git a/water.h b/water.h
deleted file mode 100644 (file)
index 1de2554..0000000
--- a/water.h
+++ /dev/null
@@ -1,255 +0,0 @@
-#include "common.h"
-#include "model.h"
-
-static void water_register(void);
-static void water_init(void);
-static void water_fb_resize(void);
-static void water_set_surface( glmesh *surf, float height );
-static float water_height(void);
-
-#ifndef WATER_H
-#define WATER_H
-
-#include "world.h"
-#include "render.h"
-#include "shaders/water.h"
-#include "scene.h"
-
-vg_tex2d tex_water_surf = { .path = "textures/water_surf.qoi" };
-
-static struct
-{
-   struct framebuffer fbreflect, fbdepth;
-   glmesh mdl;
-
-   boxf depthbounds;
-   int depth_computed;
-
-   float height;
-   int enabled;
-   v4f plane;
-}
-wrender =
-{
-   .fbreflect = { .format = GL_RGB,  .div = 3 },
-   .fbdepth   = { .format = GL_RGBA, .div = 4 }
-};
-
-static float water_height(void)
-{
-   return wrender.height;
-}
-
-static void water_register(void)
-{
-   shader_water_register();
-}
-
-static void water_init(void)
-{
-   /* TODO: probably dont do this every time */
-   wrender.enabled = 1;
-   
-   fb_init( &wrender.fbreflect );
-   fb_init( &wrender.fbdepth );
-}
-
-static void water_fb_resize(void)
-{
-   if( !wrender.enabled )
-      return;
-   
-   fb_resize( &wrender.fbreflect );
-   fb_resize( &wrender.fbdepth );
-}
-
-#if 0
-static void water_compute_depth( boxf bounds )
-{
-   if( !wrender.enabled )
-      return;
-
-#ifdef VG_RELEASE
-   int const kres = 512;
-#else
-   int const kres = 1024;
-#endif
-
-   vg_info( "Computing depth map\n" );
-   float *img = malloc( kres*kres*sizeof(float) );
-
-   boxf interior;
-   v3_add(bounds[0],(v3f){1.0f,1.0f,1.0f},interior[0]);
-   v3_sub(bounds[1],(v3f){1.0f,1.0f,1.0f},interior[1]);
-
-   v3f volume;
-   v3_sub( interior[1], interior[0], volume );
-   box_copy( interior, wrender.depthbounds );
-   
-   for( int y=0; y<kres; y++ )
-   {
-      for( int x=0; x<kres; x++ )
-      {
-         v3f pos = { x, 0.0f, y };
-         pos[0] += 0.5f;
-         pos[2] += 0.5f;
-         v3_divs( pos, kres+1, pos );
-         v3_muladd( interior[0], pos, volume, pos );
-         pos[1] = 2000.0f;
-         
-         ray_hit hit;
-         hit.dist = INFINITY;
-         float *dst = &img[ y*kres+x ];
-
-         if( ray_world( pos, (v3f){0.0f,-1.0f,0.0f}, &hit ))
-         {
-            *dst = hit.pos[1];
-         }
-         else
-         {
-            *dst = 0.0f;
-         }
-      }
-   }
-
-   if( wrender.depth_computed )
-      glDeleteTextures( 1, &wrender.depthmap );
-
-   glGenTextures( 1, &wrender.depthmap );
-   glBindTexture( GL_TEXTURE_2D, wrender.depthmap );
-   glTexImage2D( GL_TEXTURE_2D, 0, GL_R32F, kres, kres, 0, 
-         GL_RED, GL_FLOAT, img );
-
-   vg_tex2d_mipmap();
-   vg_tex2d_linear_mipmap();
-   vg_tex2d_clamp();
-
-   wrender.depth_computed = 1;
-   free( img );
-   vg_success( "Done.\n" );
-}
-#endif
-
-static void water_set_surface( glmesh *surf, float height )
-{
-   wrender.mdl = *surf;
-   wrender.height = height;
-
-   v4_copy( (v4f){ 0.0f, 1.0f, 0.0f, height }, wrender.plane );
-}
-
-static void render_water_texture( m4x3f camera )
-{
-   if( !wrender.enabled )
-      return;
-
-   /* Draw reflection buffa */
-   fb_use( &wrender.fbreflect );
-   glClear( GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT );
-
-   m4x3f new_cam, inverse;
-   v3_copy( camera[3], new_cam[3] );
-   new_cam[3][1] -= 2.0f * (camera[3][1] - wrender.height);
-
-   m3x3f flip;
-   m3x3_identity( flip );
-   flip[1][1] = -1.0f;
-   m3x3_mul( flip, camera, new_cam );
-
-
-   v3f p0;
-   m3x3_mulv( new_cam, (v3f){0.0f,0.0f,-1.0f}, p0 );
-   v3_add( new_cam[3], p0, p0 );
-   vg_line( new_cam[3], p0, 0xffffffff );
-
-   m4x4f view;
-   vg_line_pt3( new_cam[3], 0.3f, 0xff00ffff );
-
-   m4x3_invert_affine( new_cam, inverse );
-   m4x3_expand( inverse, view );
-
-   v4f clippa = { 0.0f, 1.0f, 0.0f, wrender.height-0.1f };
-   m4x3_mulp( inverse, clippa, clippa );
-   clippa[3] *= -1.0f;
-
-   m4x4f projection;
-   m4x4_projection( projection,
-         gpipeline.fov,
-         (float)vg_window_x / (float)vg_window_y, 
-         0.1f, 900.0f );
-   plane_clip_projection( projection, clippa );
-   m4x4_mul( projection, view, projection );
-
-   glCullFace( GL_FRONT );
-   render_world( projection, new_cam );
-   glCullFace( GL_BACK );
-
-
-   /* Draw beneath texture */
-   fb_use( &wrender.fbdepth );
-   glClear( GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT );
-
-   m4x3_invert_affine( camera, inverse );
-   m4x3_expand( inverse, view );
-
-   float bias = -(camera[3][1]-wrender.height)*0.1f;
-   v4f clippb = { 0.0f, -1.0f, 0.0f, -(wrender.height) + bias };
-   m4x3_mulp( inverse, clippb, clippb );
-   clippb[3] *= -1.0f;
-
-   m4x4_projection( projection,
-         gpipeline.fov,
-         (float)vg_window_x / (float)vg_window_y, 
-         0.1f, 900.0f );
-
-   plane_clip_projection( projection, clippb );
-   m4x4_mul( projection, view, projection );
-   render_world_depth( projection, camera );
-   
-   glViewport( 0, 0, vg_window_x, vg_window_y );
-}
-
-static void render_water_surface( m4x4f pv, m4x3f camera )
-{
-   if( !wrender.enabled )
-      return;
-
-   /* Draw surface */
-   shader_water_use();
-   
-   fb_bindtex( &wrender.fbreflect, 0 );
-   shader_water_uTexMain( 0 );
-
-   vg_tex2d_bind( &tex_water_surf, 1 );
-   shader_water_uTexDudv( 1 );
-   shader_water_uInvRes( (v2f){
-         1.0f / (float)vg_window_x,
-         1.0f / (float)vg_window_y });
-
-   shader_link_standard_ub( _shader_water.id, 2 );
-
-   fb_bindtex( &wrender.fbdepth, 3 );
-   shader_water_uTexBack( 3 );
-   shader_water_uTime( vg_time );
-   shader_water_uCamera( camera[3] );
-   shader_water_uSurfaceY( wrender.height );
-
-   shader_water_uPv( pv );
-
-   m4x3f full;
-   m4x3_identity( full );
-   full[3][1] = wrender.height;
-
-   shader_water_uMdl( full );
-
-   glEnable(GL_BLEND);
-   glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
-   glBlendEquation(GL_FUNC_ADD);
-
-   mesh_bind( &wrender.mdl );
-   mesh_draw( &wrender.mdl );
-
-   glDisable(GL_BLEND);
-}
-
-#endif /* WATER_H */
diff --git a/world.h b/world.h
index cf721e8402c097d95bcc5a5ba274acb9c43f0a5a..007d6721b93a0cf5294487ec667f9ab604b2cc26 100644 (file)
--- a/world.h
+++ b/world.h
@@ -5,10 +5,11 @@ static int ray_world( v3f pos, v3f dir, ray_hit *hit );
 #ifndef WORLD_H
 #define WORLD_H
 
+#include "network.h"
+#include "network_msg.h"
 #include "scene.h"
 #include "terrain.h"
 #include "render.h"
-#include "water.h"
 #include "rigidbody.h"
 #include "gate.h"
 #include "bvh.h"
@@ -16,11 +17,6 @@ static int ray_world( v3f pos, v3f dir, ray_hit *hit );
 #include "model.h"
 
 #include "traffic.h" /*TODO: -> world_traffic.h */
-#include "world_routes.h"
-#include "world_sfd.h"
-#include "world_audio.h"
-#include "network.h"
-#include "network_msg.h"
 
 #include "shaders/terrain.h"
 #include "shaders/sky.h"
@@ -31,6 +27,10 @@ static int ray_world( v3f pos, v3f dir, ray_hit *hit );
 #include "shaders/fscolour.h"
 #include "shaders/alphatest.h"
 
+enum { k_max_ui_segments = 32 };
+enum { k_route_ui_max_verts = 2000 };
+enum { k_route_ui_max_indices = 3000 };
+
 static struct gworld
 {
    /* gameplay */
@@ -43,8 +43,112 @@ static struct gworld
    spawns[32];
    u32 spawn_count;
 
-   struct subworld_routes routes;
-   struct subworld_sfd sfd;
+   struct subworld_routes
+   {
+      struct route_node
+      {
+         v3f co, right, up, h;
+         u32 next[2];
+
+         u32 special_type, special_id, current_refs, ref_count;
+         u32 route_ids[4];    /* Gates can be linked into up to four routes */
+      }
+      *nodes;
+
+      u32 node_count,
+          node_cap;
+
+      struct route
+      {
+         u32 track_id;
+         v4f colour;
+
+         u32 start;
+         mdl_submesh sm;
+         
+         int active;
+         float factive;
+
+         double best_lap, latest_pass; /* Session */
+
+         struct 
+         {
+            GLuint vao, vbo, ebo;
+
+            u32  indices_head;
+            u32  vertex_head;
+
+            float last_notch;
+
+            struct route_ui_segment
+            {
+               float length;
+               u32 vertex_start, vertex_count,
+                   index_start, index_count;
+            }
+            segments[k_max_ui_segments];
+
+            u32 segment_start, segment_count, fade_start, fade_count;
+            double fade_timer_start;
+            float xpos;
+         }
+         ui;
+
+         m4x3f scoreboard_transform;
+      }
+      *routes;
+
+      u32 route_count,
+          route_cap;
+
+      struct route_gate
+      {
+         teleport_gate gate;
+         
+         u32 node_id;
+
+         struct route_timing
+         {
+            u32 version; /* Incremented on every teleport */
+            double time;
+         } 
+         timing;
+      }
+      *gates;
+
+      struct route_collector
+      {
+         struct route_timing timing;
+      }
+      *collectors;
+
+      u32 gate_count,
+          gate_cap,
+          collector_count,
+          collector_cap;
+
+      u32 active_gate,
+          current_run_version;
+
+      scene scene_lines;
+   }
+   routes;
+
+   struct subworld_sfd
+   {
+      scene mesh;
+      mdl_submesh *sm_module, *sm_card;
+      glmesh temp;
+
+      struct sfd_instance
+      {
+         float *buffer;
+
+         u32 w,h;
+      } 
+      tester;
+   }
+   sfd;
 
    /* Paths */
    traffic_node traffic[128];
@@ -89,51 +193,30 @@ static struct gworld
 }
 world;
 
-static struct subworld_routes *subworld_routes(void) { return &world.routes; }
-static struct subworld_sfd *subworld_sfd(void) { return &world.sfd; }
-
-
-vg_tex2d tex_terrain_colours = { .path = "textures/gradients.qoi",
-                                 .flags = VG_TEXTURE_CLAMP|VG_TEXTURE_NEAREST };
-
-vg_tex2d tex_terrain_noise = { .path = "textures/garbage.qoi",
-                                 .flags = VG_TEXTURE_NEAREST };
-
-vg_tex2d tex_alphatest = { .path = "textures/alphatest.qoi",
-                                 .flags = VG_TEXTURE_NEAREST };
-
-vg_tex2d tex_graffiti = { .path = "textures/graffitibox.qoi",
-                                 .flags = VG_TEXTURE_NEAREST };
-
-static void ray_world_get_tri( ray_hit *hit, v3f tri[3] )
-{
-   for( int i=0; i<3; i++ )
-      v3_copy( world.geo.verts[ hit->tri[i] ].co, tri[i] );
-}
-
-static int ray_world( v3f pos, v3f dir, ray_hit *hit )
-{
-   return scene_raycast( &world.geo, pos, dir, hit );
-}
-
-static int ray_hit_is_terrain( ray_hit *hit )
-{
-   u32 valid_start = 0,
-       valid_end   = world.sm_terrain.vertex_count;
-
-   return (hit->tri[0] >= valid_start) &&
-          (hit->tri[0]  < valid_end);
-}
-
-static int ray_hit_is_ramp( ray_hit *hit )
-{
-   u32 valid_start = world.sm_geo_std.vertex_start,
-       valid_end   = world.sm_geo_vb.vertex_start;
+/*
+ * API
+ */
 
-   return (hit->tri[0] >= valid_start) &&
-          (hit->tri[0]  < valid_end);
-}
+static int ray_hit_is_ramp( ray_hit *hit );
+static int ray_hit_is_terrain( ray_hit *hit );
+static void ray_world_get_tri( ray_hit *hit, v3f tri[3] );
+static int ray_world( v3f pos, v3f dir, ray_hit *hit );
 
+/*
+ * Submodules
+ */
+#include "world_routes.h"
+#include "world_sfd.h"
+#include "world_audio.h"
+#include "world_render.h"
+#include "world_water.h"
+#include "world_gen.h"
+
+/* 
+ * -----------------------------------------------------------------------------
+ *                                    Events
+ * -----------------------------------------------------------------------------
+ */
 static void world_register(void)
 {
    shader_terrain_register();
@@ -145,6 +228,7 @@ static void world_register(void)
 
    world_routes_register();
    world_sfd_register();
+   world_water_register();
 }
 
 static void world_free(void)
@@ -154,373 +238,9 @@ static void world_free(void)
    world_sfd_free();
 }
 
-static void render_world_depth( m4x4f projection, m4x3f camera );
-
-static void add_all_if_material( m4x3f transform, scene *pscene, 
-                                 mdl_header *mdl, u32 id )
-{
-   for( int i=0; i<mdl->node_count; i++ )
-   {
-      mdl_node *pnode = mdl_node_from_id( mdl, i );
-
-      for( int j=0; j<pnode->submesh_count; j++ )
-      {
-         mdl_submesh *sm = mdl_node_submesh( mdl, pnode, j );
-
-         if( sm->material_id == id )
-         {
-            m4x3f transform2;
-            mdl_node_transform( pnode, transform2 );
-            m4x3_mul( transform, transform2, transform2 );
-
-            scene_add_submesh( pscene, mdl, sm, transform2 );
-         }
-      }
-
-      if( pnode->classtype == k_classtype_instance )
-      {
-         if( pnode->sub_uid )
-         {
-            u32 instance_id = pnode->sub_uid -1;
-            struct instance_cache *cache = &world.instance_cache[instance_id];
-            mdl_header *mdl2 = cache->mdl;
-
-            m4x3f transform2;
-            mdl_node_transform( pnode, transform2 );
-            m4x3_mul( transform, transform2, transform2 );
-
-            add_all_if_material( transform2, pscene, mdl2, id );
-         }
-      }
-   }
-}
-
-static void world_apply_procedural_foliage(void)
-{
-   mdl_header *mfoliage = mdl_load("models/rs_foliage.mdl");
-
-   v3f volume;
-   v3_sub( world.geo.bbx[1], world.geo.bbx[0], volume );
-   volume[1] = 1.0f;
-
-   m4x3f transform;
-   mdl_node *mblob = mdl_node_from_name( mfoliage, "blob" );
-   mdl_submesh *sm_blob = mdl_node_submesh( mfoliage, mblob, 0 );
-
-   for( int i=0;i<100000;i++ )
-   {
-      v3f pos;
-      v3_mul( volume, (v3f){ vg_randf(), 1000.0f, vg_randf() }, pos );
-      pos[1] = 1000.0f;
-      v3_add( pos, world.geo.bbx[0], pos );
-      
-      ray_hit hit;
-      hit.dist = INFINITY;
-
-      if( ray_world( pos, (v3f){0.0f,-1.0f,0.0f}, &hit ))
-      {
-         if( (hit.normal[1] > 0.8f) && ray_hit_is_terrain(&hit) &&
-             (hit.pos[1] > water_height()+10.0f) )
-         {
-            v4f qsurface, qrandom;
-            v3f axis;
-
-            v3_cross( (v3f){0.0f,1.0f,0.0f}, hit.normal, axis );
-
-            float angle = v3_dot(hit.normal,(v3f){0.0f,1.0f,0.0f});
-            q_axis_angle( qsurface, axis, angle );
-            q_axis_angle( qrandom, (v3f){0.0f,1.0f,0.0f}, vg_randf()*VG_TAUf );
-            q_mul( qsurface, qrandom, qsurface );
-            q_m3x3( qsurface, transform );
-
-            v3_copy( hit.pos, transform[3] );
-            scene_add_submesh( &world.foliage, mfoliage, sm_blob, transform);
-         }
-      }
-   }
-   free( mfoliage );
-}
-
-static void world_load(void)
-{
-   mdl_header *mworld = mdl_load( "models/mp_dev.mdl" );
-
-   world.spawn_count = 0;
-   world.traffic_count = 0;
-   world.instance_cache = NULL;
-   
-   /*
-    * Process entities
-    */
-   for( int i=0; i<mworld->node_count; i++ )
-   {
-      mdl_node *pnode = mdl_node_from_id( mworld, i );
-      
-      if( pnode->classtype == k_classtype_none )
-      {}
-      else if( pnode->classtype == k_classtype_spawn )
-      {
-         struct respawn_point *rp = &world.spawns[ world.spawn_count ++ ];
-
-         v3_copy( pnode->co, rp->co );
-         v4_copy( pnode->q, rp->q );
-         strcpy( rp->name, mdl_pstr( mworld, pnode->pstr_name ) );
-      }
-      else if( pnode->classtype == k_classtype_water )
-      {
-         if( wrender.enabled )
-         {
-            vg_warn( "Multiple water surfaces in level! ('%s')\n", 
-                  mdl_pstr( mworld, pnode->pstr_name ));
-            continue;
-         }
-
-         mdl_submesh *sm = mdl_node_submesh( mworld, pnode, 0 );
-         
-         if( sm )
-         {
-            glmesh surf;
-            mdl_unpack_submesh( mworld, &surf, sm );
-            water_init();
-            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 ++;
-      }
-      else if( pnode->classtype == k_classtype_instance )
-      {
-         struct classtype_instance *inst = mdl_get_entdata( mworld, pnode );
-         pnode->sub_uid = 0;
-         
-         int cached = 0;
-         for( int i=0; i<world.instance_cache_count; i++ )
-         {
-            struct instance_cache *cache = &world.instance_cache[i];
-            if( inst->pstr_file == cache->pstr_file )
-            {
-               cached = 1;
-               pnode->sub_uid = i+1;
-               break;
-            }
-         }
-
-         if( !cached )
-         {
-            world.instance_cache = buffer_reserve( 
-                            world.instance_cache, world.instance_cache_count,
-                            &world.instance_cache_cap, 1, 
-                            sizeof(struct instance_cache) );
-
-            struct instance_cache *cache = 
-               &world.instance_cache[world.instance_cache_count];
-
-            const char *filename = mdl_pstr(mworld, inst->pstr_file);
-
-            cache->pstr_file = inst->pstr_file;
-            cache->mdl = mdl_load( filename );
-
-            if( cache->mdl )
-            {
-               world.instance_cache_count ++;
-               pnode->sub_uid = world.instance_cache_count;
-               mdl_link_materials( mworld, cache->mdl );
-               vg_success( "Cached %s\n", filename );
-            }
-            else
-            {
-               vg_warn( "Failed to cache %s\n", filename );
-            }
-         }
-      }
-   }
-
-   world.instance_cache = buffer_fix( world.instance_cache, 
-                                      world.instance_cache_count,
-                                     &world.instance_cache_cap,
-                                     sizeof( struct instance_cache ) );
-
-#if 0
-   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)];
-#endif
-
-   /* 
-    * Compile meshes into the world scenes
-    */
-   scene_init( &world.geo );
-
-   u32 mat_surf = 0,
-       mat_surf_oob = 0,
-       mat_vertex_blend = 0,
-       mat_alphatest = 0,
-       mat_graffiti = 0,
-       mat_subworld = 0,
-       mat_terrain = 0;
-
-   for( int i=1; i<mworld->material_count; i++ )
-   {
-      mdl_material *mat = mdl_material_from_id( mworld, i );
-      const char *mat_name = mdl_pstr( mworld, mat->pstr_name );
-
-      if( !strcmp( "surf", mat_name ))
-         mat_surf = i;
-      else if( !strcmp( "surf_oob", mat_name ))
-         mat_surf_oob = i;
-      else if( !strcmp( "vertex_blend", mat_name ))
-         mat_vertex_blend = i;
-      else if( !strcmp( "alphatest", mat_name ))
-         mat_alphatest = i;
-      else if( !strcmp( "graffitibox", mat_name ))
-         mat_graffiti = i;
-      else if( !strcmp( "terrain", mat_name ) )
-         mat_terrain = i;
-   }
-
-   m4x3f midentity;
-   m4x3_identity( midentity );
-
-   if( mat_terrain )
-      add_all_if_material( midentity, &world.geo, mworld, mat_terrain );
-   scene_copy_slice( &world.geo, &world.sm_terrain );
-
-   if( mat_surf_oob )
-      add_all_if_material( midentity, &world.geo, mworld, mat_surf_oob );
-   else
-      vg_warn( "No OOB surface\n" );
-   scene_copy_slice( &world.geo, &world.sm_geo_std_oob );
-
-   if( mat_surf )
-      add_all_if_material( midentity, &world.geo, mworld, mat_surf );
-   scene_copy_slice( &world.geo, &world.sm_geo_std );
-
-   if( mat_vertex_blend )
-      add_all_if_material( midentity, &world.geo, mworld, mat_vertex_blend );
-   scene_copy_slice( &world.geo, &world.sm_geo_vb );
-
-   scene_upload( &world.geo );
-   scene_bh_create( &world.geo );
-
-
-   /* Foliage /nocollide layer.
-    * TODO: Probably should have material traits for this
-    */
-   scene_init( &world.foliage );
-
-   world_apply_procedural_foliage();
-   scene_copy_slice( &world.foliage, &world.sm_foliage_main );
-
-   add_all_if_material( midentity, &world.foliage, mworld, mat_alphatest );
-   scene_copy_slice( &world.foliage, &world.sm_foliage_alphatest );
-
-   add_all_if_material( midentity, &world.foliage, mworld, mat_graffiti );
-   scene_copy_slice( &world.foliage, &world.sm_graffiti );
-
-   scene_upload( &world.foliage );
-   world_routes_loadfrom( mworld );
-
-   for( int i=0; i<world.instance_cache_count; i++ )
-      free( world.instance_cache[i].mdl );
-
-   free( world.instance_cache );
-   free( mworld );
-
-   /* 
-    * Rendering the depth map
-    */
-   m4x4f ortho;
-   m4x3f camera;
-
-   v3f extent;
-   v3_sub( world.geo.bbx[1], world.geo.bbx[0], extent );
-
-   float fl = world.geo.bbx[0][0],
-         fr = world.geo.bbx[1][0],
-         fb = world.geo.bbx[0][2],
-         ft = world.geo.bbx[1][2],
-         rl = 1.0f / (fr-fl),
-         tb = 1.0f / (ft-fb);
-
-   m4x4_zero( ortho );
-       ortho[0][0] = 2.0f * rl;
-       ortho[2][1] = 2.0f * tb;
-   ortho[3][0] = (fr + fl) * -rl;
-   ortho[3][1] = (ft + fb) * -tb;
-   ortho[3][3] = 1.0f;
-   m4x3_identity( camera );
-
-   glViewport( 0, 0, 1024, 1024 );
-   glDisable(GL_DEPTH_TEST);
-   glBindFramebuffer( GL_FRAMEBUFFER, gpipeline.fb_depthmap );
-   shader_fscolour_use();
-   shader_fscolour_uColour( (v4f){-9999.0f,-9999.0f,-9999.0f,-9999.0f} );
-   render_fsquad();
-
-   glEnable(GL_BLEND);
-   glBlendFunc(GL_ONE, GL_ONE);
-   glBlendEquation(GL_MAX);
-   render_world_depth( ortho, camera );
-   glDisable(GL_BLEND);
-   glEnable(GL_DEPTH_TEST);
-
-   /* 
-    * TODO: World settings entity
-    */
-   struct ub_world_lighting *winfo = &gpipeline.ub_world_lighting;
-   v4_copy( wrender.plane, winfo->g_water_plane );
-
-   v4f bounds;
-   bounds[0] = world.geo.bbx[0][0];
-   bounds[1] = world.geo.bbx[0][2];
-   bounds[2] = 1.0f/ (world.geo.bbx[1][0]-world.geo.bbx[0][0]);
-   bounds[3] = 1.0f/ (world.geo.bbx[1][2]-world.geo.bbx[0][2]);
-   v4_copy( bounds, winfo->g_depth_bounds );
-
-   winfo->g_water_fog = 0.04f;
-   render_update_lighting_ub();
-   
-
-   world.mr_ball.type = k_rb_shape_sphere;
-   world.mr_ball.inf.sphere.radius = 2.0f;
-   v3_copy( (v3f){ 0.0f, 110.0f, 0.0f }, world.mr_ball.co );
-
-   q_identity(world.mr_ball.q);
-   rb_init( &world.mr_ball );
-
-   /*
-    * Setup scene collider 
-    */
-   v3_zero( world.rb_geo.co );
-   q_identity( world.rb_geo.q );
-
-   world.rb_geo.type = k_rb_shape_scene;
-   world.rb_geo.inf.scene.pscene = &world.geo;
-   world.rb_geo.is_world = 1;
-   rb_init( &world.rb_geo );
-}
 
 static void world_init(void)
 {
-   vg_tex2d_init( (vg_tex2d *[]){ &tex_terrain_colours, 
-                                  &tex_terrain_noise,
-                                  &tex_alphatest,
-                                  &tex_graffiti }, 4 );
-
    mdl_header *mcars = mdl_load( "models/rs_cars.mdl" );
    mdl_unpack_glmesh( mcars, &world.cars );
    mdl_node *nholden = mdl_node_from_name( mcars, "holden" );
@@ -540,11 +260,12 @@ static void world_init(void)
 
 
    /* Other systems */
+   world_render_init();
    world_sfd_init();
    world_audio_init();
 }
 
-static void world_update(void)
+static void world_update( v3f pos )
 {
    world_routes_update();
    world_routes_debug();
@@ -554,12 +275,11 @@ static void world_update(void)
 
    for( int i=0; i<world.routes.route_count; i++ )
    {
-      float dist = v3_dist2( world.routes.routes[i].scoreboard_transform[3],
-                              player_get_pos() );
+      float d = v3_dist2( world.routes.routes[i].scoreboard_transform[3], pos );
 
-      if( dist < min_dist )
+      if( d < min_dist )
       {
-         min_dist = dist;
+         min_dist = d;
          closest = i;
       }
    }
@@ -568,7 +288,7 @@ static void world_update(void)
    {
       network_scores_updated = 0;
       world.active_route_board = closest;
-      struct subworld_sfd *sfd = subworld_sfd();
+      struct subworld_sfd *sfd = &world.sfd;
 
       struct route *route = &world.routes.routes[closest];
 
@@ -606,225 +326,39 @@ static void world_update(void)
 #endif
 }
 
-/*
- * Rendering
+/* 
+ * -----------------------------------------------------------------------------
+ *                              API implementation
+ * -----------------------------------------------------------------------------
  */
 
-static void bind_terrain_textures(void)
-{
-   vg_tex2d_bind( &tex_terrain_noise, 0 );
-   vg_tex2d_bind( &tex_terrain_colours, 1 );
-}
-
-static void render_world_vb( m4x4f projection, v3f camera )
-{
-   m4x3f identity_matrix;
-   m4x3_identity( identity_matrix );
-
-   shader_vblend_use();
-   shader_vblend_uTexGarbage(0);
-   shader_vblend_uTexGradients(1);
-   shader_link_standard_ub( _shader_vblend.id, 2 );
-   bind_terrain_textures();
-
-   shader_vblend_uPv( projection );
-   shader_vblend_uMdl( identity_matrix );
-   shader_vblend_uCamera( camera );
-
-   scene_bind( &world.geo );
-   mdl_draw_submesh( &world.sm_geo_vb );
-
-   mesh_bind( &world.cars );
-
-#if 0
-   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 );
-   }
-#endif
-}
-
-static void render_world_alphatest( m4x4f projection, v3f camera )
-{
-   m4x3f identity_matrix;
-   m4x3_identity( identity_matrix );
-
-   shader_alphatest_use();
-   shader_alphatest_uTexGarbage(0);
-   shader_alphatest_uTexMain(1);
-   shader_link_standard_ub( _shader_alphatest.id, 2 );
-
-   vg_tex2d_bind( &tex_terrain_noise, 0 );
-   vg_tex2d_bind( &tex_alphatest, 1 );
-
-   shader_alphatest_uPv( projection );
-   shader_alphatest_uMdl( identity_matrix );
-   shader_alphatest_uCamera( camera );
-
-   glDisable(GL_CULL_FACE);
-   scene_bind( &world.foliage );
-   mdl_draw_submesh( &world.sm_foliage_alphatest );
-
-   vg_tex2d_bind( &tex_graffiti, 1 );
-   mdl_draw_submesh( &world.sm_graffiti );
-
-   glEnable(GL_CULL_FACE);
-}
-
-static void render_terrain( m4x4f projection, v3f camera )
-{
-   m4x3f identity_matrix;
-   m4x3_identity( identity_matrix );
-
-   shader_terrain_use();
-   shader_terrain_uTexGarbage(0);
-   shader_terrain_uTexGradients(1);
-   shader_link_standard_ub( _shader_terrain.id, 2 );
-   bind_terrain_textures();
-
-   shader_terrain_uPv( projection );
-   shader_terrain_uMdl( identity_matrix );
-   shader_terrain_uCamera( camera );
-
-   scene_bind( &world.geo );
-   mdl_draw_submesh( &world.sm_terrain );
-   mdl_draw_submesh( &world.sm_geo_std_oob );
-   mdl_draw_submesh( &world.sm_geo_std );
-   mdl_draw_submesh( &world.sm_subworld );
-
-   /* TODO: Dont draw in reflection */
-   glDisable(GL_CULL_FACE);
-   scene_bind( &world.foliage );
-   mdl_draw_submesh( &world.sm_foliage_main );
-   glEnable(GL_CULL_FACE);
-}
-
-static void render_lowerdome( m4x3f camera )
-{
-   m4x4f projection, full;
-   pipeline_projection( projection, 0.4f, 1000.0f );
-
-   m4x3f inverse;
-   m3x3_transpose( camera, inverse );
-   v3_copy((v3f){0.0f,0.0f,0.0f}, inverse[3]);
-   m4x3_expand( inverse, full );
-   m4x4_mul( projection, full, full );
-
-   m4x3f identity_matrix;
-   m4x3_identity( identity_matrix );
-   
-   shader_planeinf_use();
-   shader_planeinf_uMdl(identity_matrix);
-   shader_planeinf_uPv(full);
-   shader_planeinf_uCamera(camera[3]);
-   shader_planeinf_uPlane( (v4f){0.0f,1.0f,0.0f, water_height()} );
-   
-   mdl_draw_submesh( &world.dome_lower );
-}
-
-static void render_sky(m4x3f camera)
+static void ray_world_get_tri( ray_hit *hit, v3f tri[3] )
 {
-   m4x4f projection, full;
-   pipeline_projection( projection, 0.4f, 1000.0f );
-
-   m4x3f inverse;
-   m3x3_transpose( camera, inverse );
-   v3_copy((v3f){0.0f,0.0f,0.0f}, inverse[3]);
-   m4x3_expand( inverse, full );
-   m4x4_mul( projection, full, full );
-
-   m4x3f identity_matrix;
-   m4x3_identity( identity_matrix );
-   
-   shader_sky_use();
-   shader_sky_uMdl(identity_matrix);
-   shader_sky_uPv(full);
-   shader_sky_uTexGarbage(0);
-   shader_sky_uTime( vg_time );
-
-   vg_tex2d_bind( &tex_terrain_noise, 0 );
-
-   glDepthMask( GL_FALSE );
-   glDisable( GL_DEPTH_TEST );
-
-   mesh_bind( &world.skydome );
-   mdl_draw_submesh( &world.dome_upper );
-   
-   glEnable( GL_DEPTH_TEST );
-   glDepthMask( GL_TRUE );
+   for( int i=0; i<3; i++ )
+      v3_copy( world.geo.verts[ hit->tri[i] ].co, tri[i] );
 }
 
-static void render_world_gates( m4x4f projection, v3f playerco, m4x3f camera )
+static int ray_world( v3f pos, v3f dir, ray_hit *hit )
 {
-   float closest = INFINITY;
-   int   id = 0;
-
-   for( int i=0; i<world.routes.gate_count; i++ )
-   {
-      struct route_gate *rg = &world.routes.gates[i];
-      float dist = v3_dist2( rg->gate.co[0], camera[3] );
-
-      if( dist < closest )
-      {
-         closest = dist;
-         id = i;
-      }
-   }
-
-   render_gate( &world.routes.gates[id].gate, playerco, camera );
-   v3_lerp( world.render_gate_pos, 
-            world.routes.gates[id].gate.co[0],
-            1.0f,
-            world.render_gate_pos );
+   return scene_raycast( &world.geo, pos, dir, hit );
 }
 
-static void render_world( m4x4f projection, m4x3f camera )
+static int ray_hit_is_terrain( ray_hit *hit )
 {
-   render_sky( camera );
-   render_world_routes( projection, camera[3] );
-   render_world_vb( projection, camera[3] );
-   render_world_alphatest( projection, camera[3] );
-   render_terrain( projection, camera[3] );
-
-   int closest = 0;
-   float min_dist = INFINITY;
-
-   for( int i=0; i<world.routes.route_count; i++ )
-   {
-      float dist = v3_dist2( world.routes.routes[i].scoreboard_transform[3],
-                              camera[3] );
-
-      if( dist < min_dist )
-      {
-         min_dist = dist;
-         closest = i;
-      }
-   }
+   u32 valid_start = 0,
+       valid_end   = world.sm_terrain.vertex_count;
 
-   sfd_render( &world.sfd.tester, projection, camera[3], 
-         world.routes.routes[closest].scoreboard_transform );
+   return (hit->tri[0] >= valid_start) &&
+          (hit->tri[0]  < valid_end);
 }
 
-static void render_world_depth( m4x4f projection, m4x3f camera )
+static int ray_hit_is_ramp( ray_hit *hit )
 {
-   m4x3f identity_matrix;
-   m4x3_identity( identity_matrix );
-
-   shader_gpos_use();
-   shader_gpos_uCamera( camera[3] );
-   shader_gpos_uPv( projection );
-   shader_gpos_uMdl( identity_matrix );
-   
-   scene_bind( &world.geo );
-   scene_draw( &world.geo );
+   u32 valid_start = world.sm_geo_std.vertex_start,
+       valid_end   = world.sm_geo_vb.vertex_start;
 
-#if 0
-   glDisable(GL_CULL_FACE);
-   scene_bind( &world.foliage );
-   scene_draw( &world.foliage );
-   glEnable(GL_CULL_FACE);
-#endif
+   return (hit->tri[0] >= valid_start) &&
+          (hit->tri[0]  < valid_end);
 }
 
 #endif /* WORLD_H */
diff --git a/world_gen.h b/world_gen.h
new file mode 100644 (file)
index 0000000..74907c6
--- /dev/null
@@ -0,0 +1,365 @@
+#ifndef WORLD_GEN_H
+#define WORLD_GEN_H
+
+#include "world.h"
+
+
+static void world_add_all_if_material( m4x3f transform, scene *pscene, 
+                                       mdl_header *mdl, u32 id )
+{
+   for( int i=0; i<mdl->node_count; i++ )
+   {
+      mdl_node *pnode = mdl_node_from_id( mdl, i );
+
+      for( int j=0; j<pnode->submesh_count; j++ )
+      {
+         mdl_submesh *sm = mdl_node_submesh( mdl, pnode, j );
+
+         if( sm->material_id == id )
+         {
+            m4x3f transform2;
+            mdl_node_transform( pnode, transform2 );
+            m4x3_mul( transform, transform2, transform2 );
+
+            scene_add_submesh( pscene, mdl, sm, transform2 );
+         }
+      }
+
+      if( pnode->classtype == k_classtype_instance )
+      {
+         if( pnode->sub_uid )
+         {
+            u32 instance_id = pnode->sub_uid -1;
+            struct instance_cache *cache = &world.instance_cache[instance_id];
+            mdl_header *mdl2 = cache->mdl;
+
+            m4x3f transform2;
+            mdl_node_transform( pnode, transform2 );
+            m4x3_mul( transform, transform2, transform2 );
+
+            world_add_all_if_material( transform2, pscene, mdl2, id );
+         }
+      }
+   }
+}
+
+static void world_apply_procedural_foliage(void)
+{
+   mdl_header *mfoliage = mdl_load("models/rs_foliage.mdl");
+
+   v3f volume;
+   v3_sub( world.geo.bbx[1], world.geo.bbx[0], volume );
+   volume[1] = 1.0f;
+
+   m4x3f transform;
+   mdl_node *mblob = mdl_node_from_name( mfoliage, "blob" );
+   mdl_submesh *sm_blob = mdl_node_submesh( mfoliage, mblob, 0 );
+
+   for( int i=0;i<100000;i++ )
+   {
+      v3f pos;
+      v3_mul( volume, (v3f){ vg_randf(), 1000.0f, vg_randf() }, pos );
+      pos[1] = 1000.0f;
+      v3_add( pos, world.geo.bbx[0], pos );
+      
+      ray_hit hit;
+      hit.dist = INFINITY;
+
+      if( ray_world( pos, (v3f){0.0f,-1.0f,0.0f}, &hit ))
+      {
+         if( (hit.normal[1] > 0.8f) && ray_hit_is_terrain(&hit) &&
+             (hit.pos[1] > 0.0f+10.0f) )
+         {
+            v4f qsurface, qrandom;
+            v3f axis;
+
+            v3_cross( (v3f){0.0f,1.0f,0.0f}, hit.normal, axis );
+
+            float angle = v3_dot(hit.normal,(v3f){0.0f,1.0f,0.0f});
+            q_axis_angle( qsurface, axis, angle );
+            q_axis_angle( qrandom, (v3f){0.0f,1.0f,0.0f}, vg_randf()*VG_TAUf );
+            q_mul( qsurface, qrandom, qsurface );
+            q_m3x3( qsurface, transform );
+
+            v3_copy( hit.pos, transform[3] );
+            scene_add_submesh( &world.foliage, mfoliage, sm_blob, transform);
+         }
+      }
+   }
+   free( mfoliage );
+}
+
+static void world_load(void)
+{
+   mdl_header *mworld = mdl_load( "models/mp_dev.mdl" );
+
+   world.spawn_count = 0;
+   world.traffic_count = 0;
+   world.instance_cache = NULL;
+   
+   /*
+    * Process entities
+    */
+   for( int i=0; i<mworld->node_count; i++ )
+   {
+      mdl_node *pnode = mdl_node_from_id( mworld, i );
+      
+      if( pnode->classtype == k_classtype_none )
+      {}
+      else if( pnode->classtype == k_classtype_spawn )
+      {
+         struct respawn_point *rp = &world.spawns[ world.spawn_count ++ ];
+
+         v3_copy( pnode->co, rp->co );
+         v4_copy( pnode->q, rp->q );
+         strcpy( rp->name, mdl_pstr( mworld, pnode->pstr_name ) );
+      }
+      else if( pnode->classtype == k_classtype_water )
+      {
+         if( wrender.enabled )
+         {
+            vg_warn( "Multiple water surfaces in level! ('%s')\n", 
+                  mdl_pstr( mworld, pnode->pstr_name ));
+            continue;
+         }
+
+         mdl_submesh *sm = mdl_node_submesh( mworld, pnode, 0 );
+         
+         if( sm )
+         {
+            glmesh surf;
+            mdl_unpack_submesh( mworld, &surf, sm );
+            world_water_init();
+            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 ++;
+      }
+      else if( pnode->classtype == k_classtype_instance )
+      {
+         struct classtype_instance *inst = mdl_get_entdata( mworld, pnode );
+         pnode->sub_uid = 0;
+         
+         int cached = 0;
+         for( int i=0; i<world.instance_cache_count; i++ )
+         {
+            struct instance_cache *cache = &world.instance_cache[i];
+            if( inst->pstr_file == cache->pstr_file )
+            {
+               cached = 1;
+               pnode->sub_uid = i+1;
+               break;
+            }
+         }
+
+         if( !cached )
+         {
+            world.instance_cache = buffer_reserve( 
+                            world.instance_cache, world.instance_cache_count,
+                            &world.instance_cache_cap, 1, 
+                            sizeof(struct instance_cache) );
+
+            struct instance_cache *cache = 
+               &world.instance_cache[world.instance_cache_count];
+
+            const char *filename = mdl_pstr(mworld, inst->pstr_file);
+
+            cache->pstr_file = inst->pstr_file;
+            cache->mdl = mdl_load( filename );
+
+            if( cache->mdl )
+            {
+               world.instance_cache_count ++;
+               pnode->sub_uid = world.instance_cache_count;
+               mdl_link_materials( mworld, cache->mdl );
+               vg_success( "Cached %s\n", filename );
+            }
+            else
+            {
+               vg_warn( "Failed to cache %s\n", filename );
+            }
+         }
+      }
+   }
+
+   world.instance_cache = buffer_fix( world.instance_cache, 
+                                      world.instance_cache_count,
+                                     &world.instance_cache_cap,
+                                     sizeof( struct instance_cache ) );
+
+#if 0
+   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)];
+#endif
+
+   /* 
+    * Compile meshes into the world scenes
+    */
+   scene_init( &world.geo );
+
+   u32 mat_surf = 0,
+       mat_surf_oob = 0,
+       mat_vertex_blend = 0,
+       mat_alphatest = 0,
+       mat_graffiti = 0,
+       mat_subworld = 0,
+       mat_terrain = 0;
+
+   for( int i=1; i<mworld->material_count; i++ )
+   {
+      mdl_material *mat = mdl_material_from_id( mworld, i );
+      const char *mat_name = mdl_pstr( mworld, mat->pstr_name );
+
+      if( !strcmp( "surf", mat_name ))
+         mat_surf = i;
+      else if( !strcmp( "surf_oob", mat_name ))
+         mat_surf_oob = i;
+      else if( !strcmp( "vertex_blend", mat_name ))
+         mat_vertex_blend = i;
+      else if( !strcmp( "alphatest", mat_name ))
+         mat_alphatest = i;
+      else if( !strcmp( "graffitibox", mat_name ))
+         mat_graffiti = i;
+      else if( !strcmp( "terrain", mat_name ) )
+         mat_terrain = i;
+   }
+
+   m4x3f midentity;
+   m4x3_identity( midentity );
+
+   if( mat_terrain )
+      world_add_all_if_material( midentity, &world.geo, mworld, mat_terrain );
+   scene_copy_slice( &world.geo, &world.sm_terrain );
+
+   if( mat_surf_oob )
+      world_add_all_if_material( midentity, &world.geo, mworld, mat_surf_oob );
+   else
+      vg_warn( "No OOB surface\n" );
+   scene_copy_slice( &world.geo, &world.sm_geo_std_oob );
+
+   if( mat_surf )
+      world_add_all_if_material( midentity, &world.geo, mworld, mat_surf );
+   scene_copy_slice( &world.geo, &world.sm_geo_std );
+
+   if( mat_vertex_blend )
+      world_add_all_if_material( midentity, &world.geo,mworld,mat_vertex_blend);
+   scene_copy_slice( &world.geo, &world.sm_geo_vb );
+
+   scene_upload( &world.geo );
+   scene_bh_create( &world.geo );
+
+
+   /* Foliage /nocollide layer.
+    * TODO: Probably should have material traits for this
+    */
+   scene_init( &world.foliage );
+
+   world_apply_procedural_foliage();
+   scene_copy_slice( &world.foliage, &world.sm_foliage_main );
+
+   world_add_all_if_material( midentity, &world.foliage, mworld, mat_alphatest);
+   scene_copy_slice( &world.foliage, &world.sm_foliage_alphatest );
+
+   world_add_all_if_material( midentity, &world.foliage, mworld, mat_graffiti );
+   scene_copy_slice( &world.foliage, &world.sm_graffiti );
+
+   scene_upload( &world.foliage );
+   world_routes_loadfrom( mworld );
+
+   for( int i=0; i<world.instance_cache_count; i++ )
+      free( world.instance_cache[i].mdl );
+
+   free( world.instance_cache );
+   free( mworld );
+
+   /* 
+    * Rendering the depth map
+    */
+   m4x4f ortho;
+   m4x3f camera;
+
+   v3f extent;
+   v3_sub( world.geo.bbx[1], world.geo.bbx[0], extent );
+
+   float fl = world.geo.bbx[0][0],
+         fr = world.geo.bbx[1][0],
+         fb = world.geo.bbx[0][2],
+         ft = world.geo.bbx[1][2],
+         rl = 1.0f / (fr-fl),
+         tb = 1.0f / (ft-fb);
+
+   m4x4_zero( ortho );
+       ortho[0][0] = 2.0f * rl;
+       ortho[2][1] = 2.0f * tb;
+   ortho[3][0] = (fr + fl) * -rl;
+   ortho[3][1] = (ft + fb) * -tb;
+   ortho[3][3] = 1.0f;
+   m4x3_identity( camera );
+
+   glViewport( 0, 0, 1024, 1024 );
+   glDisable(GL_DEPTH_TEST);
+   glBindFramebuffer( GL_FRAMEBUFFER, gpipeline.fb_depthmap );
+   shader_fscolour_use();
+   shader_fscolour_uColour( (v4f){-9999.0f,-9999.0f,-9999.0f,-9999.0f} );
+   render_fsquad();
+
+   glEnable(GL_BLEND);
+   glBlendFunc(GL_ONE, GL_ONE);
+   glBlendEquation(GL_MAX);
+   render_world_depth( ortho, camera );
+   glDisable(GL_BLEND);
+   glEnable(GL_DEPTH_TEST);
+
+   /* 
+    * TODO: World settings entity
+    */
+   struct ub_world_lighting *winfo = &gpipeline.ub_world_lighting;
+   v4_copy( wrender.plane, winfo->g_water_plane );
+
+   v4f bounds;
+   bounds[0] = world.geo.bbx[0][0];
+   bounds[1] = world.geo.bbx[0][2];
+   bounds[2] = 1.0f/ (world.geo.bbx[1][0]-world.geo.bbx[0][0]);
+   bounds[3] = 1.0f/ (world.geo.bbx[1][2]-world.geo.bbx[0][2]);
+   v4_copy( bounds, winfo->g_depth_bounds );
+
+   winfo->g_water_fog = 0.04f;
+   render_update_lighting_ub();
+   
+
+   world.mr_ball.type = k_rb_shape_sphere;
+   world.mr_ball.inf.sphere.radius = 2.0f;
+   v3_copy( (v3f){ 0.0f, 110.0f, 0.0f }, world.mr_ball.co );
+
+   q_identity(world.mr_ball.q);
+   rb_init( &world.mr_ball );
+
+   /*
+    * Setup scene collider 
+    */
+   v3_zero( world.rb_geo.co );
+   q_identity( world.rb_geo.q );
+
+   world.rb_geo.type = k_rb_shape_scene;
+   world.rb_geo.inf.scene.pscene = &world.geo;
+   world.rb_geo.is_world = 1;
+   rb_init( &world.rb_geo );
+}
+
+#endif /* WORLD_GEN_H */
diff --git a/world_render.h b/world_render.h
new file mode 100644 (file)
index 0000000..3662bb1
--- /dev/null
@@ -0,0 +1,254 @@
+#ifndef WORLD_RENDER_H
+#define WORLD_RENDER_H
+
+#include "world.h"
+
+vg_tex2d tex_terrain_colours = { .path = "textures/gradients.qoi",
+                                 .flags = VG_TEXTURE_CLAMP|VG_TEXTURE_NEAREST };
+
+vg_tex2d tex_terrain_noise = { .path = "textures/garbage.qoi",
+                                 .flags = VG_TEXTURE_NEAREST };
+
+vg_tex2d tex_alphatest = { .path = "textures/alphatest.qoi",
+                                 .flags = VG_TEXTURE_NEAREST };
+
+vg_tex2d tex_graffiti = { .path = "textures/graffitibox.qoi",
+                                 .flags = VG_TEXTURE_NEAREST };
+
+static void world_render_init(void)
+{
+   vg_tex2d_init( (vg_tex2d *[]){ &tex_terrain_colours, 
+                                  &tex_terrain_noise,
+                                  &tex_alphatest,
+                                  &tex_graffiti }, 4 );
+}
+
+
+
+static void render_world_depth( m4x4f projection, m4x3f camera );
+
+
+
+
+/*
+ * Rendering
+ */
+
+static void bind_terrain_textures(void)
+{
+   vg_tex2d_bind( &tex_terrain_noise, 0 );
+   vg_tex2d_bind( &tex_terrain_colours, 1 );
+}
+
+static void render_world_vb( m4x4f projection, v3f camera )
+{
+   m4x3f identity_matrix;
+   m4x3_identity( identity_matrix );
+
+   shader_vblend_use();
+   shader_vblend_uTexGarbage(0);
+   shader_vblend_uTexGradients(1);
+   shader_link_standard_ub( _shader_vblend.id, 2 );
+   bind_terrain_textures();
+
+   shader_vblend_uPv( projection );
+   shader_vblend_uMdl( identity_matrix );
+   shader_vblend_uCamera( camera );
+
+   scene_bind( &world.geo );
+   mdl_draw_submesh( &world.sm_geo_vb );
+
+   mesh_bind( &world.cars );
+
+#if 0
+   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 );
+   }
+#endif
+}
+
+static void render_world_alphatest( m4x4f projection, v3f camera )
+{
+   m4x3f identity_matrix;
+   m4x3_identity( identity_matrix );
+
+   shader_alphatest_use();
+   shader_alphatest_uTexGarbage(0);
+   shader_alphatest_uTexMain(1);
+   shader_link_standard_ub( _shader_alphatest.id, 2 );
+
+   vg_tex2d_bind( &tex_terrain_noise, 0 );
+   vg_tex2d_bind( &tex_alphatest, 1 );
+
+   shader_alphatest_uPv( projection );
+   shader_alphatest_uMdl( identity_matrix );
+   shader_alphatest_uCamera( camera );
+
+   glDisable(GL_CULL_FACE);
+   scene_bind( &world.foliage );
+   mdl_draw_submesh( &world.sm_foliage_alphatest );
+
+   vg_tex2d_bind( &tex_graffiti, 1 );
+   mdl_draw_submesh( &world.sm_graffiti );
+
+   glEnable(GL_CULL_FACE);
+}
+
+static void render_terrain( m4x4f projection, v3f camera )
+{
+   m4x3f identity_matrix;
+   m4x3_identity( identity_matrix );
+
+   shader_terrain_use();
+   shader_terrain_uTexGarbage(0);
+   shader_terrain_uTexGradients(1);
+   shader_link_standard_ub( _shader_terrain.id, 2 );
+   bind_terrain_textures();
+
+   shader_terrain_uPv( projection );
+   shader_terrain_uMdl( identity_matrix );
+   shader_terrain_uCamera( camera );
+
+   scene_bind( &world.geo );
+   mdl_draw_submesh( &world.sm_terrain );
+   mdl_draw_submesh( &world.sm_geo_std_oob );
+   mdl_draw_submesh( &world.sm_geo_std );
+   mdl_draw_submesh( &world.sm_subworld );
+
+   /* TODO: Dont draw in reflection */
+   glDisable(GL_CULL_FACE);
+   scene_bind( &world.foliage );
+   mdl_draw_submesh( &world.sm_foliage_main );
+   glEnable(GL_CULL_FACE);
+}
+
+static void render_lowerdome( m4x3f camera )
+{
+   m4x4f projection, full;
+   pipeline_projection( projection, 0.4f, 1000.0f );
+
+   m4x3f inverse;
+   m3x3_transpose( camera, inverse );
+   v3_copy((v3f){0.0f,0.0f,0.0f}, inverse[3]);
+   m4x3_expand( inverse, full );
+   m4x4_mul( projection, full, full );
+
+   m4x3f identity_matrix;
+   m4x3_identity( identity_matrix );
+   
+   shader_planeinf_use();
+   shader_planeinf_uMdl(identity_matrix);
+   shader_planeinf_uPv(full);
+   shader_planeinf_uCamera(camera[3]);
+   shader_planeinf_uPlane( (v4f){0.0f,1.0f,0.0f,0.0f} );
+   
+   mdl_draw_submesh( &world.dome_lower );
+}
+
+static void render_sky(m4x3f camera)
+{
+   m4x4f projection, full;
+   pipeline_projection( projection, 0.4f, 1000.0f );
+
+   m4x3f inverse;
+   m3x3_transpose( camera, inverse );
+   v3_copy((v3f){0.0f,0.0f,0.0f}, inverse[3]);
+   m4x3_expand( inverse, full );
+   m4x4_mul( projection, full, full );
+
+   m4x3f identity_matrix;
+   m4x3_identity( identity_matrix );
+   
+   shader_sky_use();
+   shader_sky_uMdl(identity_matrix);
+   shader_sky_uPv(full);
+   shader_sky_uTexGarbage(0);
+   shader_sky_uTime( vg_time );
+
+   vg_tex2d_bind( &tex_terrain_noise, 0 );
+
+   glDepthMask( GL_FALSE );
+   glDisable( GL_DEPTH_TEST );
+
+   mesh_bind( &world.skydome );
+   mdl_draw_submesh( &world.dome_upper );
+   
+   glEnable( GL_DEPTH_TEST );
+   glDepthMask( GL_TRUE );
+}
+
+static void render_world_gates( m4x4f projection, v3f playerco, m4x3f camera )
+{
+   float closest = INFINITY;
+   int   id = 0;
+
+   for( int i=0; i<world.routes.gate_count; i++ )
+   {
+      struct route_gate *rg = &world.routes.gates[i];
+      float dist = v3_dist2( rg->gate.co[0], camera[3] );
+
+      if( dist < closest )
+      {
+         closest = dist;
+         id = i;
+      }
+   }
+
+   render_gate( &world.routes.gates[id].gate, playerco, camera );
+   v3_lerp( world.render_gate_pos, 
+            world.routes.gates[id].gate.co[0],
+            1.0f,
+            world.render_gate_pos );
+}
+
+static void render_world( m4x4f projection, m4x3f camera )
+{
+   render_sky( camera );
+   render_world_routes( projection, camera[3] );
+   render_world_vb( projection, camera[3] );
+   render_world_alphatest( projection, camera[3] );
+   render_terrain( projection, camera[3] );
+
+   int closest = 0;
+   float min_dist = INFINITY;
+
+   for( int i=0; i<world.routes.route_count; i++ )
+   {
+      float dist = v3_dist2( world.routes.routes[i].scoreboard_transform[3],
+                              camera[3] );
+
+      if( dist < min_dist )
+      {
+         min_dist = dist;
+         closest = i;
+      }
+   }
+
+   sfd_render( &world.sfd.tester, projection, camera[3], 
+         world.routes.routes[closest].scoreboard_transform );
+}
+
+static void render_world_depth( m4x4f projection, m4x3f camera )
+{
+   m4x3f identity_matrix;
+   m4x3_identity( identity_matrix );
+
+   shader_gpos_use();
+   shader_gpos_uCamera( camera[3] );
+   shader_gpos_uPv( projection );
+   shader_gpos_uMdl( identity_matrix );
+   
+   scene_bind( &world.geo );
+   scene_draw( &world.geo );
+
+#if 0
+   glDisable(GL_CULL_FACE);
+   scene_bind( &world.foliage );
+   scene_draw( &world.foliage );
+   glEnable(GL_CULL_FACE);
+#endif
+}
+
+#endif /* WORLD_RENDER_H */
index 9cee0df0d19b38dbd41b5c84042cc9cdaded5b83..7f108aa88d89463ed12121e8db0de1d4116322ec 100644 (file)
@@ -1,11 +1,8 @@
 #ifndef ROUTES_H
 #define ROUTES_H
 
-#include "common.h"
-#include "model.h"
-#include "gate.h"
+#include "world.h"
 #include "world_info.h"
-#include "highscores.h"
 
 #include "shaders/vblend.h"
 #include "shaders/route.h"
@@ -17,102 +14,6 @@ enum route_special_type
    k_route_special_type_collector = 2
 };
 
-enum { k_max_ui_segments = 32 };
-enum { k_route_ui_max_verts = 2000 };
-enum { k_route_ui_max_indices = 3000 };
-
-struct subworld_routes
-{
-   struct route_node
-   {
-      v3f co, right, up, h;
-      u32 next[2];
-
-      u32 special_type, special_id, current_refs, ref_count;
-      u32 route_ids[4];    /* Gates can be linked into up to four routes */
-   }
-   *nodes;
-
-   u32 node_count,
-       node_cap;
-
-   struct route
-   {
-      u32 track_id;
-      v4f colour;
-
-      u32 start;
-      mdl_submesh sm;
-      
-      int active;
-      float factive;
-
-      double best_lap, latest_pass; /* Session */
-
-      struct 
-      {
-         GLuint vao, vbo, ebo;
-
-         u32  indices_head;
-         u32  vertex_head;
-
-         float last_notch;
-
-         struct route_ui_segment
-         {
-            float length;
-            u32 vertex_start, vertex_count,
-                index_start, index_count;
-         }
-         segments[k_max_ui_segments];
-
-         u32 segment_start, segment_count, fade_start, fade_count;
-         double fade_timer_start;
-         float xpos;
-      }
-      ui;
-
-      m4x3f scoreboard_transform;
-   }
-   *routes;
-
-   u32 route_count,
-       route_cap;
-
-   struct route_gate
-   {
-      teleport_gate gate;
-      
-      u32 node_id;
-
-      struct route_timing
-      {
-         u32 version; /* Incremented on every teleport */
-         double time;
-      } 
-      timing;
-   }
-   *gates;
-
-   struct route_collector
-   {
-      struct route_timing timing;
-   }
-   *collectors;
-
-   u32 gate_count,
-       gate_cap,
-       collector_count,
-       collector_cap;
-
-   u32 active_gate,
-       current_run_version;
-
-   scene scene_lines;
-};
-
-static struct subworld_routes *subworld_routes(void);
-
 static void debug_sbpath( struct route_node *rna, struct route_node *rnb,
                           u32 colour, float xoffset )
 {
@@ -144,7 +45,7 @@ static void debug_sbpath( struct route_node *rna, struct route_node *rnb,
  */
 static u32 world_routes_get_path( u32 starter, u32 stack[64] )
 {
-   struct subworld_routes *r = subworld_routes();
+   struct subworld_routes *r = &world.routes;
    u32 stack_i[64];
 
    stack[0] = starter;
@@ -204,7 +105,7 @@ static u32 world_routes_get_path( u32 starter, u32 stack[64] )
  */
 static void world_routes_ui_popfirst( u32 route )
 {
-   struct subworld_routes *r = subworld_routes();
+   struct subworld_routes *r = &world.routes;
    struct route *pr = &r->routes[route];
 
    if( pr->ui.segment_count )
@@ -223,7 +124,7 @@ static void world_routes_ui_popfirst( u32 route )
  */
 static void world_routes_ui_clear( u32 route )
 {
-   struct subworld_routes *r = subworld_routes();
+   struct subworld_routes *r = &world.routes;
    struct route *pr = &r->routes[route];
    pr->ui.segment_start = (pr->ui.segment_start + pr->ui.segment_count) %
                               k_max_ui_segments;
@@ -316,7 +217,7 @@ static struct route_ui_segment *world_routes_ui_curseg( struct route *pr )
  */
 static void world_routes_ui_newseg( u32 route )
 {
-   struct subworld_routes *r = subworld_routes();
+   struct subworld_routes *r = &world.routes;
    struct route *pr = &r->routes[route];
 
    pr->ui.last_notch = 0.0;
@@ -373,7 +274,7 @@ static void world_routes_ui_newseg( u32 route )
  */
 static void world_routes_ui_updatetime( u32 route, float time )
 {
-   struct subworld_routes *r = subworld_routes();
+   struct subworld_routes *r = &world.routes;
    struct route *pr = &r->routes[route];
 
    v2f verts[2];
@@ -396,7 +297,7 @@ static void world_routes_ui_updatetime( u32 route, float time )
  */
 static void world_routes_ui_notch( u32 route, float time )
 {
-   struct subworld_routes *r = subworld_routes();
+   struct subworld_routes *r = &world.routes;
    struct route *pr = &r->routes[route];
 
    if( (time - pr->ui.last_notch) > 1.0 )
@@ -485,7 +386,7 @@ static void world_routes_ui_draw( u32 route, v4f colour, float offset )
    float const k_bar_height = 0.05f,
                k_bar_scale_x = 0.005f;
 
-   struct subworld_routes *r = subworld_routes();
+   struct subworld_routes *r = &world.routes;
    struct route *pr = &r->routes[route];
 
    float cx = pr->ui.xpos;
@@ -558,7 +459,7 @@ static void world_routes_local_set_record( u32 route, double lap_time )
 {
    vg_success( "  NEW LAP TIME: %f\n", lap_time );
 
-   struct subworld_routes *r = subworld_routes();
+   struct subworld_routes *r = &world.routes;
    struct route *pr = &r->routes[route];
 
    if( pr->track_id != 0xffffffff )
@@ -591,7 +492,7 @@ static void world_routes_local_set_record( u32 route, double lap_time )
  */
 static void world_routes_verify_run( u32 route )
 {
-   struct subworld_routes *r = subworld_routes();
+   struct subworld_routes *r = &world.routes;
    struct route *pr = &r->routes[route];
 
    u32 stack[64];
@@ -691,7 +592,7 @@ static void world_routes_verify_run( u32 route )
  */
 static void world_routes_activate_gate( u32 id )
 {
-   struct subworld_routes *r = subworld_routes();
+   struct subworld_routes *r = &world.routes;
    struct route_gate *rg = &r->gates[id];
    struct route_node *pnode = &r->nodes[rg->node_id],
                      *pdest = &r->nodes[pnode->next[0]];
@@ -742,7 +643,7 @@ static void world_routes_activate_gate( u32 id )
  */
 static void world_routes_notify_reset(void)
 {
-   struct subworld_routes *r = subworld_routes();
+   struct subworld_routes *r = &world.routes;
 
    for( int i=0; i<r->route_count; i++ )
    {
@@ -755,7 +656,7 @@ static void world_routes_notify_reset(void)
 
 static void world_routes_debug(void)
 {
-   struct subworld_routes *r = subworld_routes();
+   struct subworld_routes *r = &world.routes;
 
    for( int i=0; i<r->node_count; i++ )
    {
@@ -799,15 +700,6 @@ static void world_routes_debug(void)
    }
 }
 
-static void world_routes_free(void)
-{
-   struct subworld_routes *r = subworld_routes();
-
-   free( r->nodes );
-   free( r->routes );
-   free( r->gates );
-}
-
 static void world_id_fixup( u32 *uid, mdl_header *mdl )
 {
    if( *uid )
@@ -821,7 +713,7 @@ static void world_id_fixup( u32 *uid, mdl_header *mdl )
  */
 static void world_routes_gen_meshes(void)
 {
-   struct subworld_routes *r = subworld_routes();
+   struct subworld_routes *r = &world.routes;
    scene_init( &r->scene_lines );
 
    for( int i=0; i<r->route_count; i++ )
@@ -948,85 +840,10 @@ static void world_routes_gen_meshes(void)
    scene_free_offline_buffers( &r->scene_lines );
 }
 
-static void world_routes_update(void)
-{
-   struct subworld_routes *r = subworld_routes();
-
-   for( int i=0; i<r->route_count; i++ )
-   {
-      struct route *route = &r->routes[i];
-      route->factive = vg_lerpf( route->factive, route->active, 0.01f );
-
-      if( route->active )
-      {
-         world_routes_ui_updatetime( i, vg_time - route->latest_pass );
-      }
-   }
-}
-
-static void bind_terrain_textures(void);
-static void render_world_routes( m4x4f projection, v3f camera )
-{
-   struct subworld_routes *r = subworld_routes();
-
-   m4x3f identity_matrix;
-   m4x3_identity( identity_matrix );
-
-   shader_route_use();
-   shader_route_uTexGarbage(0);
-   shader_link_standard_ub( _shader_route.id, 2 );
-   bind_terrain_textures();
-
-   shader_route_uPv( projection );
-   shader_route_uMdl( identity_matrix );
-   shader_route_uCamera( camera );
-
-   scene_bind( &r->scene_lines );
-
-   for( int i=0; i<r->route_count; i++ )
-   {
-      struct route *route = &r->routes[i];
-
-      v4f colour;
-      v3_lerp( (v3f){0.7f,0.7f,0.7f}, route->colour, route->factive, colour );
-      colour[3] = 1.0f;
-
-      shader_route_uColour( colour );
-      mdl_draw_submesh( &route->sm );
-   }
-}
-
-static void render_world_routes_ui(void)
-{
-   glEnable(GL_BLEND);
-   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-   glBlendEquation(GL_FUNC_ADD);
-
-   struct subworld_routes *r = subworld_routes();
-
-   float active_offset = 0.0f;
-   for( int i=0; i<r->route_count; i++ )
-   {
-      struct route *route = &r->routes[i];
-      world_routes_ui_draw( i, route->colour, active_offset );
-      active_offset += route->factive;
-   }
-
-   glDisable(GL_BLEND);
-}
-
-static void world_routes_register(void)
-{
-   struct subworld_routes *r = subworld_routes();
-   r->current_run_version = 2;
-
-   shader_route_register();
-   shader_routeui_register();
-}
 
 static void world_routes_loadfrom( mdl_header *mdl )
 {
-   struct subworld_routes *r = subworld_routes();
+   struct subworld_routes *r = &world.routes;
    r->nodes = NULL;
    r->node_count = 0;
    r->node_cap = 0;
@@ -1229,4 +1046,95 @@ static void world_routes_loadfrom( mdl_header *mdl )
    world_routes_gen_meshes();
 }
 
+/* 
+ * -----------------------------------------------------------------------------
+ *                                    Events
+ * -----------------------------------------------------------------------------
+ */
+
+static void world_routes_register(void)
+{
+   struct subworld_routes *r = &world.routes;
+   r->current_run_version = 2;
+
+   shader_route_register();
+   shader_routeui_register();
+}
+
+static void world_routes_free(void)
+{
+   struct subworld_routes *r = &world.routes;
+
+   free( r->nodes );
+   free( r->routes );
+   free( r->gates );
+}
+
+static void world_routes_update(void)
+{
+   struct subworld_routes *r = &world.routes;
+
+   for( int i=0; i<r->route_count; i++ )
+   {
+      struct route *route = &r->routes[i];
+      route->factive = vg_lerpf( route->factive, route->active, 0.01f );
+
+      if( route->active )
+      {
+         world_routes_ui_updatetime( i, vg_time - route->latest_pass );
+      }
+   }
+}
+
+static void bind_terrain_textures(void);
+static void render_world_routes( m4x4f projection, v3f camera )
+{
+   struct subworld_routes *r = &world.routes;
+
+   m4x3f identity_matrix;
+   m4x3_identity( identity_matrix );
+
+   shader_route_use();
+   shader_route_uTexGarbage(0);
+   shader_link_standard_ub( _shader_route.id, 2 );
+   bind_terrain_textures();
+
+   shader_route_uPv( projection );
+   shader_route_uMdl( identity_matrix );
+   shader_route_uCamera( camera );
+
+   scene_bind( &r->scene_lines );
+
+   for( int i=0; i<r->route_count; i++ )
+   {
+      struct route *route = &r->routes[i];
+
+      v4f colour;
+      v3_lerp( (v3f){0.7f,0.7f,0.7f}, route->colour, route->factive, colour );
+      colour[3] = 1.0f;
+
+      shader_route_uColour( colour );
+      mdl_draw_submesh( &route->sm );
+   }
+}
+
+static void render_world_routes_ui(void)
+{
+   glEnable(GL_BLEND);
+   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+   glBlendEquation(GL_FUNC_ADD);
+
+   struct subworld_routes *r = &world.routes;
+
+   float active_offset = 0.0f;
+   for( int i=0; i<r->route_count; i++ )
+   {
+      struct route *route = &r->routes[i];
+      world_routes_ui_draw( i, route->colour, active_offset );
+      active_offset += route->factive;
+   }
+
+   glDisable(GL_BLEND);
+}
+
 #endif /* ROUTES_H */
index 515232cf31dc8daea63fe5c6671359f7d8a40be0..c6fa66b0fdb49942485fb85c8fb3ab022fc22132 100644 (file)
@@ -1,8 +1,6 @@
 #ifndef SFD_H
 #define SFD_H
 
-#include "common.h"
-#include "model.h"
 #include "world.h"
 
 #include "shaders/scoretext.h"
@@ -11,25 +9,9 @@
 vg_tex2d tex_scoretext = { .path = "textures/scoretext.qoi",
                            .flags = VG_TEXTURE_CLAMP|VG_TEXTURE_NEAREST };
 
-struct sfd_instance
-{
-   float *buffer;
-
-   u32 w,h;
-};
-
-
-struct subworld_sfd
-{
-   scene mesh;
-   mdl_submesh *sm_module, *sm_card;
-   glmesh temp;
-
-   struct sfd_instance tester;
-};
-
-static struct subworld_sfd *subworld_sfd(void);
-
+/* 
+ * TODO: utf-8 -> ascii
+ */
 
 float sfd_encode_glyph( char c )
 {
@@ -120,7 +102,7 @@ static void sfd_update( struct sfd_instance *display )
 static void sfd_render( struct sfd_instance *display, 
                         m4x4f projection, v3f camera, m4x3f transform )
 {
-   struct subworld_sfd *sfd = subworld_sfd();
+   struct subworld_sfd *sfd = &world.sfd;
    scene_bind( &sfd->mesh );
 
    shader_scoretext_use();
@@ -160,7 +142,7 @@ static void sfd_render( struct sfd_instance *display,
 
 static int world_sfd_test( int argc, const char *argv[] )
 {
-   struct subworld_sfd *sfd = subworld_sfd();
+   struct subworld_sfd *sfd = &world.sfd;
 
    if( argc == 2 )
    {
@@ -173,7 +155,7 @@ static int world_sfd_test( int argc, const char *argv[] )
 
 static void world_sfd_init(void)
 {
-   struct subworld_sfd *sfd = subworld_sfd();
+   struct subworld_sfd *sfd = &world.sfd;
 
        vg_function_push( (struct vg_cmd){
                .name = "sfd",
diff --git a/world_water.h b/world_water.h
new file mode 100644 (file)
index 0000000..85f4b5d
--- /dev/null
@@ -0,0 +1,174 @@
+#ifndef WATER_H
+#define WATER_H
+
+#include "world.h"
+#include "render.h"
+#include "shaders/water.h"
+#include "scene.h"
+
+vg_tex2d tex_water_surf = { .path = "textures/water_surf.qoi" };
+
+static struct
+{
+   struct framebuffer fbreflect, fbdepth;
+   glmesh mdl;
+
+   boxf depthbounds;
+   int depth_computed;
+
+   float height;
+   int enabled;
+   v4f plane;
+}
+wrender =
+{
+   .fbreflect = { .format = GL_RGB,  .div = 3 },
+   .fbdepth   = { .format = GL_RGBA, .div = 4 }
+};
+
+static void world_water_register(void)
+{
+   shader_water_register();
+}
+
+static void world_water_init(void)
+{
+   /* TODO: probably dont do this every time */
+   wrender.enabled = 1;
+   
+   fb_init( &wrender.fbreflect );
+   fb_init( &wrender.fbdepth );
+}
+
+static void water_fb_resize(void)
+{
+   if( !wrender.enabled )
+      return;
+   
+   fb_resize( &wrender.fbreflect );
+   fb_resize( &wrender.fbdepth );
+}
+
+static void water_set_surface( glmesh *surf, float height )
+{
+   wrender.mdl = *surf;
+   wrender.height = height;
+
+   v4_copy( (v4f){ 0.0f, 1.0f, 0.0f, height }, wrender.plane );
+}
+
+static void render_water_texture( m4x3f camera )
+{
+   if( !wrender.enabled )
+      return;
+
+   /* Draw reflection buffa */
+   fb_use( &wrender.fbreflect );
+   glClear( GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT );
+
+   m4x3f new_cam, inverse;
+   v3_copy( camera[3], new_cam[3] );
+   new_cam[3][1] -= 2.0f * (camera[3][1] - wrender.height);
+
+   m3x3f flip;
+   m3x3_identity( flip );
+   flip[1][1] = -1.0f;
+   m3x3_mul( flip, camera, new_cam );
+
+
+   v3f p0;
+   m3x3_mulv( new_cam, (v3f){0.0f,0.0f,-1.0f}, p0 );
+   v3_add( new_cam[3], p0, p0 );
+   vg_line( new_cam[3], p0, 0xffffffff );
+
+   m4x4f view;
+   vg_line_pt3( new_cam[3], 0.3f, 0xff00ffff );
+
+   m4x3_invert_affine( new_cam, inverse );
+   m4x3_expand( inverse, view );
+
+   v4f clippa = { 0.0f, 1.0f, 0.0f, wrender.height-0.1f };
+   m4x3_mulp( inverse, clippa, clippa );
+   clippa[3] *= -1.0f;
+
+   m4x4f projection;
+   m4x4_projection( projection,
+         gpipeline.fov,
+         (float)vg_window_x / (float)vg_window_y, 
+         0.1f, 900.0f );
+   plane_clip_projection( projection, clippa );
+   m4x4_mul( projection, view, projection );
+
+   glCullFace( GL_FRONT );
+   render_world( projection, new_cam );
+   glCullFace( GL_BACK );
+
+
+   /* Draw beneath texture */
+   fb_use( &wrender.fbdepth );
+   glClear( GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT );
+
+   m4x3_invert_affine( camera, inverse );
+   m4x3_expand( inverse, view );
+
+   float bias = -(camera[3][1]-wrender.height)*0.1f;
+   v4f clippb = { 0.0f, -1.0f, 0.0f, -(wrender.height) + bias };
+   m4x3_mulp( inverse, clippb, clippb );
+   clippb[3] *= -1.0f;
+
+   m4x4_projection( projection,
+         gpipeline.fov,
+         (float)vg_window_x / (float)vg_window_y, 
+         0.1f, 900.0f );
+
+   plane_clip_projection( projection, clippb );
+   m4x4_mul( projection, view, projection );
+   render_world_depth( projection, camera );
+   
+   glViewport( 0, 0, vg_window_x, vg_window_y );
+}
+
+static void render_water_surface( m4x4f pv, m4x3f camera )
+{
+   if( !wrender.enabled )
+      return;
+
+   /* Draw surface */
+   shader_water_use();
+   
+   fb_bindtex( &wrender.fbreflect, 0 );
+   shader_water_uTexMain( 0 );
+
+   vg_tex2d_bind( &tex_water_surf, 1 );
+   shader_water_uTexDudv( 1 );
+   shader_water_uInvRes( (v2f){
+         1.0f / (float)vg_window_x,
+         1.0f / (float)vg_window_y });
+
+   shader_link_standard_ub( _shader_water.id, 2 );
+
+   fb_bindtex( &wrender.fbdepth, 3 );
+   shader_water_uTexBack( 3 );
+   shader_water_uTime( vg_time );
+   shader_water_uCamera( camera[3] );
+   shader_water_uSurfaceY( wrender.height );
+
+   shader_water_uPv( pv );
+
+   m4x3f full;
+   m4x3_identity( full );
+   full[3][1] = wrender.height;
+
+   shader_water_uMdl( full );
+
+   glEnable(GL_BLEND);
+   glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
+   glBlendEquation(GL_FUNC_ADD);
+
+   mesh_bind( &wrender.mdl );
+   mesh_draw( &wrender.mdl );
+
+   glDisable(GL_BLEND);
+}
+
+#endif /* WATER_H */