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 */
#include "render.h"
#include "shaders/gate.h"
#include "shaders/gatelq.h"
-#include "water.h"
+#include "world_water.h"
typedef struct teleport_gate teleport_gate;
#include "anim_test.h"
#include "gate.h"
-#include "water.h"
void vg_register(void)
{
player_register();
world_register();
- water_register();
gate_register();
}
world_load();
reset_player( 1, (const char *[]){ "start" } );
- rb_init( &player.rb );
+ rb_init( &player.phys.rb );
network_init();
}
{
network_update();
player_update();
- world_update();
+ world_update( player.phys.rb.co );
//traffic_visualize( world.traffic, world.traffic_count );
//
/* TEMP */
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 );
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 );
{
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 )
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 =
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;
fairdir,
fsetup,
walk_timer,
+ fjump,
fonboard;
+
int step_phase;
/* player model */
}
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 }
};
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
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;
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;
}
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)
static void player_animate_offboard(void)
{
+ struct player_phys *phys = &player.phys;
+
mdl_keyframe apose[32], bpose[32];
struct skeleton *sk = &player.mdl.sk;
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 );
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 );
*/
/* scalar blending information */
- float speed = v3_length( player.rb.v );
+ float speed = v3_length( phys->rb.v );
/* sliding */
{
/* 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 );
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 );
/* 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;
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 );
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 );
}
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;
/* 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
{
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 );
}
}
*/
static void player_audio(void)
{
+ struct player_phys *phys = &player.phys;
+
static int _ding = 0;
int last = _ding;
static int _air = 0;
int l2 = _air;
- _air = player.in_air;
+ _air = phys->in_air;
static double last_revert = -2000.0;
audio_lock();
double revert_delta = vg_time - last_revert;
- if( player.on_board && (!_air && l2) && (fabsf(player.slip) > 0.5f) &&
+ 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] );
}
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 );
{
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 )
{
}
}
- 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 );
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] );
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;
*/
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
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,
*/
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 )
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;
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" ) )
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;
}
/*
*/
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;
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;
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 );
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;
{
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 );
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 );
}
/*
*/
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 );
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;
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;
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,
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);
}
/*
*/
static void player_physics(void)
{
+ struct player_phys *phys = &player.phys;
/*
* Update collision fronts
*/
rigidbody *rbf = &player.collide_front,
*rbb = &player.collide_back;
- m3x3_copy( player.rb.to_world, player.collide_front.to_world );
- m3x3_copy( player.rb.to_world, player.collide_back.to_world );
+ 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 );
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++ )
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;
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 );
/*
* 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 );
{
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();
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
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,
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 );
}
}
- rb_update_transform( &player.rb );
+ rb_update_transform( &phys->rb );
}
/*
static int reset_player( int argc, char const *argv[] )
{
+ struct player_phys *phys = &player.phys;
struct respawn_point *rp = NULL, *r;
if( argc == 1 )
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 )
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;
}
+++ /dev/null
-#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 */
#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"
#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"
#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 */
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];
}
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();
world_routes_register();
world_sfd_register();
+ world_water_register();
}
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" );
/* 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();
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;
}
}
{
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];
#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 */
--- /dev/null
+#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 */
--- /dev/null
+#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 */
#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"
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 )
{
*/
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;
*/
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 )
*/
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;
*/
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;
*/
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];
*/
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 )
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;
{
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 )
*/
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];
*/
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]];
*/
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++ )
{
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++ )
{
}
}
-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 )
*/
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++ )
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;
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 */
#ifndef SFD_H
#define SFD_H
-#include "common.h"
-#include "model.h"
#include "world.h"
#include "shaders/scoretext.h"
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 )
{
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();
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 )
{
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",
--- /dev/null
+#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 */