#ifndef CHARACTER_H
#define CHARACTER_H
-/* TODO: -> Common.h */
-#define VG_3D
-#include "vg/vg.h"
-
+#include "common.h"
#include "model.h"
#include "scene.h"
#include "ik.h"
#include "rigidbody.h"
#include "shaders/character.h"
+vg_tex2d tex_pallet = { .path = "textures/ch_gradient.qoi" };
+
+static void character_register(void)
+{
+ shader_character_register();
+}
+
+static void character_init(void)
+{
+ vg_tex2d_init( (vg_tex2d *[]){ &tex_pallet }, 1 );
+}
+
#define FOREACH_PART(FN) \
FN( foot_l ) \
FN( foot_r ) \
#define ADD_ONE(_) +1
#define PART_COUNT FOREACH_PART(ADD_ONE)
+
enum character_part
{
FOREACH_PART( MAKE_ENUM )
static void character_draw( struct character *ch, float temp )
{
shader_character_use();
-
shader_character_uPv( vg_pv );
+
+ vg_tex2d_bind( &tex_pallet, 0 );
shader_character_uTexMain( 0 );
shader_character_uOpacity( temp );
}
}
-static void character_register(void)
-{
- shader_character_register();
-}
-
-
/*
* Ragdoll Stuff
*/
--- /dev/null
+#ifndef COMMON_H
+#define COMMON_H
+
+#define VG_3D
+#define VG_FRAMEBUFFER_RESIZE 1
+#include "vg/vg.h"
+
+static float ktimestep = 1.0f/60.0f;
+
+#endif /* COMMON_H */
static struct
{
- GLuint fb, rgb, rb, vao, vbo;
+ GLuint fb, rgb, rb;
glmesh mdl;
}
grender;
shader_gate_register();
}
-static void gate_init( void (*newfb)(GLuint*,GLuint*,GLuint*) )
+static void gate_init(void)
{
- newfb( &grender.fb, &grender.rgb, &grender.rb );
- {
- float ksz = 1.0f;
- float quad[] = { -ksz,-ksz,0.0f, ksz, ksz,0.0f, -ksz, ksz,0.0f,
- -ksz,-ksz,0.0f, ksz,-ksz,0.0f, ksz, ksz,0.0f,
- -ksz,-ksz,-0.1f, ksz, ksz,-0.1f, -ksz, ksz,-0.1f,
- -ksz,-ksz,-0.1f, ksz,-ksz,-0.1f, ksz, ksz,-0.1f };
-
- glGenVertexArrays( 1, &grender.vao );
- glGenBuffers( 1, &grender.vbo );
- glBindVertexArray( grender.vao );
- glBindBuffer( GL_ARRAY_BUFFER, grender.vbo );
- glBufferData( GL_ARRAY_BUFFER, sizeof(quad), quad, GL_STATIC_DRAW );
- glBindVertexArray( grender.vao );
- glVertexAttribPointer( 0, 3, GL_FLOAT, GL_FALSE,
- sizeof(float)*3, (void*)0 );
- glEnableVertexAttribArray( 0 );
- VG_CHECK_GL();
- }
+ create_renderbuffer_std( &grender.fb, &grender.rgb, &grender.rb );
model *mgate = vg_asset_read( "models/rs_gate.mdl" );
model_unpack( mgate, &grender.mdl );
mesh_draw( &grender.mdl );
glDisable(GL_BLEND);
- return;
- glBindVertexArray( grender.vao );
- glDrawArrays( GL_TRIANGLES, 0, 12 );
}
static int gate_intersect( teleport_gate *gate, v3f pos, v3f last )
-#define VG_3D
-#define VG_FRAMEBUFFER_RESIZE 1
-#include "vg/vg.h"
+#include "common.h"
/* Resources */
vg_tex2d tex_norwey = { .path = "textures/norway_foliage.qoi" };
vg_tex2d tex_gradients = { .path = "textures/gradients.qoi",
.flags = VG_TEXTURE_CLAMP };
vg_tex2d tex_cement = { .path = "textures/cement512.qoi" };
-vg_tex2d tex_pallet = { .path = "textures/ch_gradient.qoi" };
vg_tex2d tex_water = { .path = "textures/water.qoi" };
/* Convars */
-static int freecam = 0;
static int debugview = 0;
-static int debugsdf = 0;
static int sv_debugcam = 0;
-static int sv_phys = 0;
-static int thirdperson = 0;
-static int clock_divider = 1;
-static int replay_record = 0;
-
-static m4x3f *replay_buffer = NULL;
-static int replay_buffer_frame = 0;
-
-#define REPLAY_LENGTH 120*60
/* Components */
#include "road.h"
#include "gate.h"
#include "water.h"
#include "world.h"
+#include "player.h"
#include "shaders/blit.h"
#include "shaders/standard.h"
&tex_grid,
&tex_sky,
&tex_cement,
- &tex_pallet,
&tex_water,
&tex_water_surf
};
vg_init( argc, argv, "Voyager Game Engine" );
}
-m4x3f world_matrix;
-
-static struct gplayer
-{
- /* Physics */
- v3f co, v, a, v_last, m, bob;
- v4f rot;
- float vswitch, slip, slip_last,
- reverse;
-
- float iY; /* Yaw inertia */
- int in_air, is_dead;
-
- /* Input */
- v2f joy_l;
-
- v3f view;
- v3f follow;
- v2f look_dir; /* TEMP */
- v2f board_xy;
- float grab;
- float pitch;
-
- v3f land_target;
- v3f land_target_log[22];
- u32 land_target_colours[22];
- int land_log_count;
- m3x3f vr;
-
- m4x3f to_world, to_local;
-
- struct character mdl;
-
- v3f handl_target, handr_target,
- handl, handr;
-}
-player;
-
-
-static struct grender
-{
- GLuint fb_background,
- rgb_background;
-
- glmesh fsquad;
-}
-render;
-
#if 0
rigidbody mr_box = {
.bbx = {{ -1.0f, -0.25f, -0.25f }, { 1.0f, 0.25f, 0.25f }}
};
#endif
-static void player_transform_update(void)
-{
- q_normalize( player.rot );
- q_m3x3( player.rot, player.to_world );
- v3_copy( player.co, player.to_world[3] );
-
- m4x3_invert_affine( player.to_world, player.to_local );
-}
-
-static int reset_player( int argc, char const *argv[] )
-{
- v3_zero( player.co );
-
- if( argc == 1 )
- {
- if( !strcmp( argv[0], "tutorial" ))
- v3_copy( world.tutorial, player.co );
- }
-
- v3_copy( (v3f){ 0.0f, 0.0f, -0.2f }, player.v );
- q_identity( player.rot );
- player.vswitch = 1.0f;
- player.slip_last = 0.0f;
- player.is_dead = 0;
- player.in_air = 1;
- m3x3_identity( player.vr );
-
- player.mdl.shoes[0] = 1;
- player.mdl.shoes[1] = 1;
-
- player_transform_update();
- return 0;
-}
-
static int playermodel( int argc, char const *argv[] )
{
if( argc < 1 ) return 0;
return 1;
}
-
void vg_start(void)
{
- replay_buffer = malloc( sizeof(m4x3f) * REPLAY_LENGTH * (PART_COUNT) );
-
- vg_tex2d_init( texture_list, vg_list_size( texture_list ) );
-
-#if 0
- rb_init( &mr_box );
- rb_init( &mrs_box );
- mrs_box.co[2] += 2.0f;
-#endif
-
- vg_convar_push( (struct vg_convar){
- .name = "frame",
- .data = &replay_buffer_frame,
- .data_type = k_convar_dtype_i32,
- .opt_i32 = { .min=0, .max=REPLAY_LENGTH-1, .clamp=1 },
- .persistent = 0
- });
-
- vg_convar_push( (struct vg_convar){
- .name = "rec",
- .data = &replay_record,
- .data_type = k_convar_dtype_i32,
- .opt_i32 = { .min=0, .max=1, .clamp=1 },
- .persistent = 0
- });
-
vg_convar_push( (struct vg_convar){
.name = "freecam",
.data = &freecam,
.persistent = 1
});
- vg_convar_push( (struct vg_convar){
- .name = "debugsdf",
- .data = &debugsdf,
- .data_type = k_convar_dtype_i32,
- .opt_i32 = { .min=0, .max=1, .clamp=1 },
- .persistent = 1
- });
-
- vg_convar_push( (struct vg_convar){
- .name = "phys",
- .data = &sv_phys,
- .data_type = k_convar_dtype_i32,
- .opt_i32 = { .min=0, .max=1, .clamp=1 },
- .persistent = 1
- });
-
- vg_convar_push( (struct vg_convar){
- .name = "thirdperson",
- .data = &thirdperson,
- .data_type = k_convar_dtype_i32,
- .opt_i32 = { .min=0, .max=1, .clamp=1 },
- .persistent = 1
- });
-
- vg_convar_push( (struct vg_convar){
- .name = "div",
- .data = &clock_divider,
- .data_type = k_convar_dtype_i32,
- .opt_i32 = { .min=0, .max=1, .clamp=0 },
- .persistent = 1
- });
-
vg_function_push( (struct vg_cmd){
.name = "reset",
.function = reset_player
});
-
- v3f lightDir = { 0.1f, 0.8f, 0.2f };
- v3_normalize( lightDir );
-
- character_load( &player.mdl, "ch_default" );
- character_init_ragdoll( &player.mdl );
-
- world_init_default();
- reset_player( 1, (const char *[]){ "tutorial" } );
- player_transform_update();
-
- /* Create framebuffers */
- glGenFramebuffers( 1, &render.fb_background );
- glBindFramebuffer( GL_FRAMEBUFFER, render.fb_background );
-
- glGenTextures( 1, &render.rgb_background );
- glBindTexture( GL_TEXTURE_2D, render.rgb_background );
- glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB, vg_window_x, vg_window_y,
- 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
-
- glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
- glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
- glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
- GL_TEXTURE_2D,
- render.rgb_background, 0);
+ vg_tex2d_init( texture_list, vg_list_size( texture_list ) );
- gate_init( create_renderbuffer_std );
+ render_init();
+ gate_init();
terrain_init();
+ character_init();
- {
- float quad[] = { 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f,
- 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f };
-
- glGenVertexArrays( 1, &render.fsquad.vao );
- glGenBuffers( 1, &render.fsquad.vbo );
- glBindVertexArray( render.fsquad.vao );
- glBindBuffer( GL_ARRAY_BUFFER, render.fsquad.vbo );
- glBufferData( GL_ARRAY_BUFFER, sizeof(quad), quad, GL_STATIC_DRAW );
- glBindVertexArray( render.fsquad.vao );
- glVertexAttribPointer( 0, 2, GL_FLOAT, GL_FALSE,
- sizeof(float)*2, (void*)0 );
- glEnableVertexAttribArray( 0 );
- VG_CHECK_GL();
- }
-}
-
-static float ktimestep = 1.0f/60.0f;
-
-static void player_freecam(void)
-{
- m4x3f cam_rot;
- m4x3_identity( cam_rot );
- m4x3_rotate_y( cam_rot, -player.look_dir[0] );
- m4x3_rotate_x( cam_rot, -player.look_dir[1] );
-
- v3f lookdir = { 0.0f, 0.0f, -1.0f },
- sidedir = { 1.0f, 0.0f, 0.0f };
-
- m4x3_mulv( cam_rot, lookdir, lookdir );
- m4x3_mulv( cam_rot, sidedir, sidedir );
-
- float movespeed = 15.0f;
- static v2f mouse_last,
- view_vel = { 0.0f, 0.0f };
-
- static v3f move_vel = { 0.0f, 0.0f, 0.0f };
-
- if( vg_get_button_down( "primary" ) )
- v2_copy( vg_mouse, mouse_last );
- else if( vg_get_button( "primary" ) )
- {
- v2f delta;
- v2_sub( vg_mouse, mouse_last, delta );
- v2_copy( vg_mouse, mouse_last );
-
- v2_muladds( view_vel, delta, 0.005f, view_vel );
- }
-
- v2_muls( view_vel, 0.75f, view_vel );
- v2_add( view_vel, player.look_dir, player.look_dir );
- player.look_dir[1] =
- vg_clampf( player.look_dir[1], -VG_PIf*0.5f, VG_PIf*0.5f );
-
- if( vg_get_button( "forward" ) )
- v3_muladds( move_vel, lookdir, ktimestep * movespeed, move_vel );
- if( vg_get_button( "back" ) )
- v3_muladds( move_vel, lookdir, ktimestep *-movespeed, move_vel );
- if( vg_get_button( "left" ) )
- v3_muladds( move_vel, sidedir, ktimestep *-movespeed, move_vel );
- if( vg_get_button( "right" ) )
- v3_muladds( move_vel, sidedir, ktimestep * movespeed, move_vel );
-
- v3_muls( move_vel, 0.75f, move_vel );
- v3_add( move_vel, player.view, player.view );
-}
-
-static void apply_gravity( v3f vel, float const timestep )
-{
- v3f gravity = { 0.0f, -9.6f, 0.0f };
- v3_muladds( vel, gravity, timestep, vel );
-}
-
-static void player_start_air(void)
-{
- player.in_air = 1;
-
- float pstep = ktimestep*10.0f;
-
- float best_velocity_mod = 0.0f,
- best_velocity_delta = -9999.9f;
-
- v3f axis, vup;
- m3x3_mulv( player.to_world, (v3f){0.0f,1.0f,0.0f}, vup );
- v3_cross( vup, player.v, axis );
- v3_normalize( axis );
- player.land_log_count = 0;
-
- m3x3_identity( player.vr );
-
- for( int m=-3;m<=12; m++ )
- {
- float vmod = ((float)m / 15.0f)*0.09f;
-
- v3f pco, pco1, pv;
- v3_copy( player.co, pco );
- v3_copy( player.v, pv );
- v3_muladds( pco, pv, ktimestep, pco );
-
- /*
- * Try different 'rotations' of the velocity to find the best possible
- * landing normal. This conserves magnitude at the expense of slightly
- * unrealistic results
- */
-
- m3x3f vr;
- v4f vr_q;
-
- q_axis_angle( vr_q, axis, vmod );
- q_m3x3( vr_q, vr );
-
- for( int i=0; i<50; i++ )
- {
- v3_copy( pco, pco1 );
- apply_gravity( pv, pstep );
-
- m3x3_mulv( vr, pv, pv );
- v3_muladds( pco, pv, pstep, pco );
-
- ray_hit contact;
- v3f vdir;
-
- v3_sub( pco, pco1, vdir );
- contact.dist = v3_length( vdir );
- v3_divs( vdir, contact.dist, vdir);
-
- if( ray_world( pco1, vdir, &contact ))
- {
- float land_delta = v3_dot( pv, contact.normal );
- u32 scolour = (u8)(vg_minf(-land_delta * 2.0f, 255.0f));
-
- /* Bias prediction towords ramps */
- if( ray_hit_is_ramp( &contact ) )
- {
- land_delta *= 0.1f;
- scolour |= 0x0000a000;
- }
-
- if( (land_delta < 0.0f) && (land_delta > best_velocity_delta) )
- {
- best_velocity_delta = land_delta;
- best_velocity_mod = vmod;
-
- v3_copy( contact.pos, player.land_target );
-
- q_axis_angle( vr_q, axis, vmod*0.1f );
- q_m3x3( vr_q, player.vr );
- }
-
- v3_copy( contact.pos,
- player.land_target_log[player.land_log_count] );
- player.land_target_colours[player.land_log_count] =
- 0xff000000 | scolour;
-
- player.land_log_count ++;
-
- break;
- }
- }
- }
-
- //v3_rotate( player.v, best_velocity_mod, axis, player.v );
-
- return;
- v3_muls( player.v, best_velocity_mod, player.v );
-}
-
-static int sample_if_resistant( v3f pos )
-{
- v3f ground;
- v3_copy( pos, ground );
- ground[1] += 4.0f;
-
- ray_hit hit;
- hit.dist = INFINITY;
-
- if( ray_world( ground, (v3f){0.0f,-1.0f,0.0f}, &hit ))
- {
- v3f angle;
- v3_copy( player.v, angle );
- v3_normalize( angle );
- float resistance = v3_dot( hit.normal, angle );
-
- if( resistance < 0.25f )
- {
- v3_copy( hit.pos, pos );
- return 1;
- }
- }
-
- return 0;
-}
-
-static float stable_force( float current, float diff )
-{
- float new = current + diff;
-
- if( new * current < 0.0f )
- return 0.0f;
-
- return new;
-}
-
-static void player_physics_ground(void)
-{
- /*
- * Getting surface collision points,
- * the contact manifold is a triangle for simplicity.
- */
- v3f contact_front, contact_back, contact_norm, vup, vside,
- axis;
-
- float klength = 0.65f;
- m4x3_mulv( player.to_world, (v3f){ 0.15f,0.0f,-klength}, contact_norm );
- m4x3_mulv( player.to_world, (v3f){-0.15f,0.0f,-klength}, contact_front );
- m4x3_mulv( player.to_world, (v3f){ 0.00f,0.0f, klength}, contact_back );
- m3x3_mulv( player.to_world, (v3f){ 0.0f, 1.0f, 0.0f}, vup );
- m3x3_mulv( player.to_world, (v3f){ 1.0f, 0.0f, 0.0f}, vside );
-
- v3f cn0, cn1, cn2;
-
- int contact_count =
- sample_if_resistant( contact_front ) +
- sample_if_resistant( contact_back ) +
- sample_if_resistant( contact_norm );
-
- if( contact_count < 3 )
- {
- player_start_air();
- return;
- }
-
- v3f norm;
- v3f v0, v1;
- v3_sub( contact_norm, contact_front, v0 );
- v3_sub( contact_back, contact_front, v1 );
- v3_cross( v1, v0, norm );
- v3_normalize( norm );
-
- vg_line( contact_norm, contact_front, 0xff00ff00 );
- vg_line( contact_back, contact_front, 0xff0000ff );
-
- /* Surface alignment */
- float angle = v3_dot( vup, norm );
- v3_cross( vup, norm, axis );
-
- if( angle < 0.999f )
- {
- v4f correction;
- q_axis_angle( correction, axis, acosf(angle) );
- q_mul( correction, player.rot, player.rot );
- }
-
- float resistance = v3_dot( norm, player.v );
- if( resistance >= 0.0f )
- {
- player_start_air();
- return;
- }
- else
- {
- v3_muladds( player.v, norm, -resistance, player.v );
- }
-
- /* This is where velocity integration used to be */
-
- float slip = 0.0f;
-
- player.co[1] = (contact_front[1]+contact_back[1])*0.5f;
-
- v3f vel;
- m3x3_mulv( player.to_local, player.v, vel );
-
- /* Calculate local forces */
-
- if( fabsf(vel[2]) > 0.01f )
- slip = fabsf(-vel[0] / vel[2]) * vg_signf(vel[0]);
-
- if( fabsf( slip ) > 1.2f )
- slip = vg_signf( slip ) * 1.2f;
- player.slip = slip;
- player.reverse = -vg_signf(vel[2]);
-
- float substep = ktimestep * 0.2f;
- float fwd_resistance = (vg_get_button( "break" )? 5.0f: 0.02f) * -substep;
-
- for( int i=0; i<5; i++ )
- {
- vel[2] = stable_force( vel[2], vg_signf( vel[2] ) * fwd_resistance );
- vel[0] = stable_force( vel[0], vg_signf( vel[0] ) * -7.0f *substep );
- }
-
- static double start_push = 0.0;
- if( vg_get_button_down( "push" ) )
- start_push = vg_time;
-
- if( !vg_get_button("break") && vg_get_button( "push" ) )
- {
- float const k_maxpush = 16.0f,
- k_pushaccel = 5.0f;
-
- float cycle_time = vg_time-start_push,
- amt = k_pushaccel * (sinf( cycle_time * 8.0f )*0.5f+0.5f)*ktimestep,
- current = v3_length( vel ),
- new_vel = vg_minf( current + amt, k_maxpush );
- new_vel -= vg_minf(current, k_maxpush);
- vel[2] -= new_vel * player.reverse;
- }
-
- m3x3_mulv( player.to_world, vel, player.v );
-
- if( vg_get_button( "yawl" ) )
- player.iY += 3.6f * ktimestep;
- if( vg_get_button( "yawr" ) )
- player.iY -= 3.6f * ktimestep;
-
- float steer = vg_get_axis( "horizontal" );
- player.iY -= vg_signf(steer)*powf(steer,2.0f) * 1.5f * ktimestep;
-
- /* Too much lean and it starts to look like a snowboard here */
- v2_lerp( player.board_xy, (v2f){ slip*0.25f, 0.0f },
- ktimestep*5.0f, player.board_xy);
-}
-
-static void draw_cross(v3f pos,u32 colour)
-{
- v3f p0, p1;
- v3_add( (v3f){ 1.0f,0.0f,0.0f}, pos, p0 );
- v3_add( (v3f){-1.0f,0.0f,0.0f}, pos, p1 );
- vg_line( p0, p1, colour );
- v3_add( (v3f){0.0f, 1.0f,0.0f}, pos, p0 );
- v3_add( (v3f){0.0f,-1.0f,0.0f}, pos, p1 );
- vg_line( p0, p1, colour );
- v3_add( (v3f){0.0f,0.0f, 1.0f}, pos, p0 );
- v3_add( (v3f){0.0f,0.0f,-1.0f}, pos, p1 );
- vg_line( p0, p1, colour );
-}
-
-static void player_physics_air(void)
-{
- /* Debug prediciton */
-
- m3x3_mulv( player.vr, player.v, player.v );
- for( int i=0; i<player.land_log_count; i++ )
- draw_cross( player.land_target_log[i],
- player.land_target_colours[i] );
-
- draw_cross( player.land_target, 0xff0000ff );
-
- v3f ground_pos;
- v3_copy( player.co, ground_pos );
- ground_pos[1] += 4.0f;
-
- ray_hit hit;
- hit.dist = INFINITY;
- if( ray_world( ground_pos, (v3f){0.0f,-1.0f,0.0f}, &hit ))
- {
- if( hit.pos[1] > player.co[1] )
- {
- player.in_air = 0;
-
- if( !ray_hit_is_ramp( &hit ) )
- {
- player.is_dead = 1;
- m4x3_mulv( player.to_world, player.view, player.follow );
- character_ragdoll_copypose( &player.mdl, player.v );
- }
-
- return;
- }
- }
-
- /* Prediction
- *
- * TODO: Find best landing surface and guide player towords it
- */
- float pstep = ktimestep*10.0f;
-
- v3f pco, pco1, pv;
- v3_copy( player.co, pco );
- v3_copy( player.v, pv );
-
- float time_to_impact = 0.0f;
- float limiter = 1.0f;
-
- for( int i=0; i<50; i++ )
- {
- v3_copy( pco, pco1 );
- apply_gravity( pv, pstep );
- v3_muladds( pco, pv, pstep, pco );
-
- //vg_line( pco, pco1, i&0x1?0xff000000:0xffffffff );
-
- ray_hit contact;
- v3f vdir;
-
- v3_sub( pco, pco1, vdir );
- contact.dist = v3_length( vdir );
- v3_divs( vdir, contact.dist, vdir);
-
- float orig_dist = contact.dist;
- if( ray_world( pco1, vdir, &contact ))
- {
- v3f localup;
- m3x3_mulv( player.to_world, (v3f){0.0f,1.0f,0.0f}, localup );
-
- float angle = v3_dot( localup, contact.normal );
- v3f axis;
- v3_cross( localup, contact.normal, axis );
-
- time_to_impact += (contact.dist/orig_dist)*pstep;
- limiter = vg_minf( 5.0f, time_to_impact )/5.0f;
- limiter = 1.0f-limiter;
- limiter *= limiter;
- limiter = 1.0f-limiter;
-
- if( angle < 0.99f )
- {
- v4f correction;
- q_axis_angle( correction, axis, acosf(angle)*0.05f*(1.0f-limiter) );
- q_mul( correction, player.rot, player.rot );
- }
-
- draw_cross( contact.pos, 0xffff0000 );
- break;
- }
- time_to_impact += pstep;
- }
-
-
- player.iY -= vg_get_axis( "horizontal" ) * 3.6f * ktimestep;
- {
-
- float iX = vg_get_axis( "vertical" ) * 3.6f * limiter * ktimestep;
- static float siX = 0.0f;
- siX = vg_lerpf( siX, iX, 0.3f );
-
- v4f rotate;
- v3f vside;
-
- m3x3_mulv( player.to_world, (v3f){1.0f,0.0f,0.0f}, vside );
-
- q_axis_angle( rotate, vside, siX );
- q_mul( rotate, player.rot, player.rot );
- }
-
- 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 );
-}
-
-static void player_animate(void);
-static void player_update(void)
-{
-
- if( vg_get_axis("grabl")>0.0f)
- reset_player(0,NULL);
- if( player.is_dead )
- return;
-
- static int clock = 0;
-
- /* temp */
- if( freecam )
- {
- player_freecam();
- return;
- }
-
- clock ++;
- if( clock >= clock_divider )
- clock = 0;
- else
- return;
- float horizontal = vg_get_axis("horizontal"),
- vertical = vg_get_axis("vertical");
-
- player.joy_l[0] = vg_signf(horizontal) * powf( horizontal, 2.0f );
- player.joy_l[1] = vg_signf(vertical) * powf( vertical, 2.0f );
-
- if( player.in_air )
- player_physics_air();
-
- if( !player.in_air )
- player_physics_ground();
-
- /* Integrate velocity */
- v3f prevco;
- v3_copy( player.co, prevco );
- if( sv_phys )
- {
- apply_gravity( player.v, ktimestep );
- v3_muladds( player.co, player.v, ktimestep, player.co );
- }
-
- /* Integrate inertia */
- v4f rotate; v3f vup = {0.0f,1.0f,0.0f};
- m3x3_mulv( player.to_world, vup, vup );
-
- static float siY = 0.0f;
-
- float lerpq = player.in_air? 0.04f: 0.3f;
- siY = vg_lerpf( siY, player.iY, lerpq );
-
- q_axis_angle( rotate, vup, siY );
- q_mul( rotate, player.rot, player.rot );
-
- player.iY = 0.0f; /* temp */
-
-#if 0
- /* GATE COLLISION */
- if( gate_intersect( &gate_a, player.co, prevco ) )
- {
- teleport_gate *gate = &gate_a;
-
- m4x3f transport;
- m4x3_mul( gate->other->to_world, gate->to_local, transport );
- m4x3_mulv( transport, player.co, player.co );
- m3x3_mulv( transport, player.v, player.v );
- m3x3_mulv( transport, player.v_last, player.v_last );
- m3x3_mulv( transport, player.m, player.m );
- m3x3_mulv( transport, player.bob, player.bob );
+ character_load( &player.mdl, "ch_default" );
+ character_init_ragdoll( &player.mdl );
- v4f transport_rotation;
- m3x3_q( transport, transport_rotation );
- q_mul( transport_rotation, player.rot, player.rot );
- }
-#endif
+ world_load();
- /* Camera and character */
+ reset_player( 1, (const char *[]){ "tutorial" } );
player_transform_update();
- player_animate();
-
- player.look_dir[0] = atan2f( player.v[0], -player.v[2] );
- player.look_dir[1] = atan2f( -player.v[1], sqrtf(player.v[0]*player.v[0]+
- player.v[2]*player.v[2]) ) * 0.3f;
}
void vg_update(void)
{
player_update();
-
- //rb_torque( &mr_box, (v3f){0.0f,0.0f,1.0f}, 0.01f );
-
- if( glfwGetKey( vg_window, GLFW_KEY_F ) )
- character_ragdoll_go( &player.mdl, player.view );
-
- if( glfwGetKey( vg_window, GLFW_KEY_G ) )
- {
- player.is_dead = 1;
- m4x3_mulv( player.to_world, player.view, player.follow );
- character_ragdoll_copypose( &player.mdl, player.v );
- }
-
- static int clock = 0;
-
- clock ++;
- if( clock >= clock_divider )
- {
- character_debug_ragdoll( &player.mdl );
-
- if( player.is_dead )
- character_ragdoll_iter( &player.mdl );
-
-#if 0
- rb_build_manifold( &mr_box, &world.geo );
- rb_build_manifold( &mrs_box, &world.geo );
- rb_constraint_manifold( &mr_box );
- rb_constraint_manifold( &mrs_box );
-
- rb_iter( &mr_box );
- rb_iter( &mrs_box );
-
- rb_debug( &mr_box, 0xffffffff );
- rb_debug( &mrs_box, 0xff00ff00 );
-
- rb_update_transform( &mr_box );
- rb_update_transform( &mrs_box );
-#endif
-
- clock = 0;
- }
-
-}
-
-static void player_animate(void)
-{
- /* Camera position */
- v3_sub( player.v, player.v_last, player.a );
- v3_copy( player.v, player.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 );
- v3f target;
-
- player.m[0] = vg_clampf( player.m[0], -2.0f, 2.0f );
- player.m[1] = vg_clampf( player.m[1], -0.2f, 5.0f );
- player.m[2] = vg_clampf( player.m[2], -2.0f, 2.0f );
- v3_copy( player.m, target );
- v3_lerp( player.bob, target, 0.2f, player.bob );
-
- /* Head */
- float lslip = fabsf(player.slip); //vg_minf( 0.4f, slip );
-
- float grabt = vg_get_axis( "grabr" )*0.5f+0.5f;
- player.grab = vg_lerpf( player.grab, grabt, 0.04f );
-
- float kheight = 2.0f,
- kleg = 0.6f;
-
- v3f head;
- head[0] = 0.0f;
- head[1] = (0.3f+cosf(lslip)*0.5f*(1.0f-player.grab*0.7f)) * kheight;
- head[2] = 0.0f;
-
- v3f offset;
- m3x3_mulv( player.to_local, player.bob, offset );
-
- offset[0] *= 0.3333f;
- offset[1] *= -0.25f;
- offset[2] *= 0.7f;
- v3_muladds( head, offset, 0.7f, head );
- head[1] = vg_clampf( head[1], 0.3f, kheight );
-
-#if 0
- if( !freecam )
- {
- v3_copy( head, player.view );
- v3f camoffs = {-0.2f,-0.6f,0.00f};
- v3_add( player.view, camoffs, player.view );
- }
-#endif
-
-
- /*
- * Animation blending
- * ===========================================
- */
-
- static float fslide = 0.0f;
- static float fdirz = 0.0f;
- static float fdirx = 0.0f;
- static float fstand = 0.0f;
- static float ffly = 0.0f;
-
- float speed = v3_length( player.v );
-
- fstand = vg_lerpf(fstand, 1.0f-vg_clampf(speed*0.03f,0.0f,1.0f),0.1f);
- fslide = vg_lerpf(fslide, vg_clampf(lslip+fabsf(offset[0])*0.2f,
- 0.0f,1.0f), 0.04f);
- fdirz = vg_lerpf(fdirz, player.reverse > 0.0f? 1.0f: 0.0f, 0.04f );
- fdirx = vg_lerpf(fdirx, player.slip < 0.0f? 1.0f: 0.0f, 0.04f );
- ffly = vg_lerpf(ffly, player.in_air? 1.0f: 0.0f, 0.04f );
-
- character_pose_reset( &player.mdl );
-
- float amt_air = ffly*ffly,
- amt_ground = 1.0f-amt_air,
- amt_std = (1.0f-fslide) * amt_ground,
- amt_stand = amt_std * fstand,
- amt_aero = amt_std * (1.0f-fstand),
- amt_slide = amt_ground * fslide;
-
- character_final_pose( &player.mdl, offset, &pose_stand, amt_stand );
- character_final_pose( &player.mdl, offset, &pose_aero, amt_aero*fdirz );
- character_final_pose( &player.mdl, offset,
- &pose_aero_reverse, amt_aero * (1.0f-fdirz) );
- character_final_pose( &player.mdl, offset, &pose_slide, amt_slide*fdirx );
- character_final_pose( &player.mdl, offset,
- &pose_slide1, amt_slide*(1.0f-fdirx) );
-
- character_final_pose( &player.mdl, (v3f){0.0f,0.0f,0.0f},
- &pose_fly, amt_air );
-
- if( !freecam )
- {
- v3_copy( player.mdl.cam_pos, player.view );
- v3_muladds( player.view, offset, 0.7f, player.view );
- player.view[1] = vg_clampf( player.view[1], 0.3f, kheight );
- }
-
- /*
- * Additive effects
- * ==========================
- */
- struct ik_basic *arm_l = &player.mdl.ik_arm_l,
- *arm_r = &player.mdl.ik_arm_r;
-
- v3f localv;
- m3x3_mulv( player.to_local, player.v, localv );
- v3_muladds( arm_l->end, localv, -0.01f, arm_l->end );
- v3_muladds( arm_r->end, localv, -0.01f, arm_r->end );
-
- /* New board transformation */
- v4f board_rotation; v3f board_location;
-
- v4f rz, rx;
- q_axis_angle( rz, (v3f){ 0.0f, 0.0f, 1.0f }, player.board_xy[0] );
- q_axis_angle( rx, (v3f){ 1.0f, 0.0f, 0.0f }, player.board_xy[1] );
- q_mul( rx, rz, board_rotation );
-
- v3f *mboard = player.mdl.matrices[k_chpart_board];// player.mboard;
- q_m3x3( board_rotation, mboard );
- m3x3_mulv( mboard, (v3f){ 0.0f, -0.5f, 0.0f }, board_location );
- v3_add( (v3f){0.0f,0.5f,0.0f}, board_location, board_location );
- v3_copy( board_location, mboard[3] );
-
-
- float wheel_r = offset[0]*-0.4f;
- v4f qwheel;
- q_axis_angle( qwheel, (v3f){0.0f,1.0f,0.0f}, wheel_r );
-
- q_m3x3( qwheel, player.mdl.matrices[k_chpart_wb] );
-
- m3x3_transpose( player.mdl.matrices[k_chpart_wb],
- player.mdl.matrices[k_chpart_wf] );
- v3_copy( player.mdl.offsets[k_chpart_wb],
- player.mdl.matrices[k_chpart_wb][3] );
- v3_copy( player.mdl.offsets[k_chpart_wf],
- player.mdl.matrices[k_chpart_wf][3] );
-
- m4x3_mul( mboard, player.mdl.matrices[k_chpart_wb],
- player.mdl.matrices[k_chpart_wb] );
- m4x3_mul( mboard, player.mdl.matrices[k_chpart_wf],
- player.mdl.matrices[k_chpart_wf] );
-
- m4x3_mulv( mboard, player.mdl.ik_leg_l.end, player.mdl.ik_leg_l.end );
- m4x3_mulv( mboard, player.mdl.ik_leg_r.end, player.mdl.ik_leg_r.end );
-
-
- v3_copy( player.mdl.ik_arm_l.end, player.handl_target );
- v3_copy( player.mdl.ik_arm_r.end, player.handr_target );
-
- if( 1||player.in_air )
- {
- float tuck = player.board_xy[1],
- tuck_amt = fabsf( tuck ) * (1.0f-fabsf(player.board_xy[0]));
-
- float crouch = player.grab*0.3f;
- v3_muladds( player.mdl.ik_body.base, (v3f){0.0f,-1.0f,0.0f},
- crouch, player.mdl.ik_body.base );
- v3_muladds( player.mdl.ik_body.end, (v3f){0.0f,-1.0f,0.0f},
- crouch*1.2f, player.mdl.ik_body.end );
-
- if( tuck < 0.0f )
- {
- //foot_l *= 1.0f-tuck_amt*1.5f;
-
- if( player.grab > 0.1f )
- {
- m4x3_mulv( mboard, (v3f){0.1f,0.14f,0.6f},
- player.handl_target );
- }
- }
- else
- {
- //foot_r *= 1.0f-tuck_amt*1.4f;
-
- if( player.grab > 0.1f )
- {
- m4x3_mulv( mboard, (v3f){0.1f,0.14f,-0.6f},
- player.handr_target );
- }
- }
- }
-
- v3_lerp( player.handl, player.handl_target, 0.1f, player.handl );
- v3_lerp( player.handr, player.handr_target, 0.1f, player.handr );
-
- v3_copy( player.handl, player.mdl.ik_arm_l.end );
- v3_copy( player.handr, player.mdl.ik_arm_r.end );
-
- /* Head rotation */
-
- static float rhead = 0.0f;
- rhead = vg_lerpf( rhead,
- vg_clampf(atan2f( localv[2], -localv[0] ),-1.0f,1.0f), 0.04f );
- player.mdl.rhead = rhead;
-}
-
-static void draw_player(void)
-{
- /* Draw */
- vg_tex2d_bind( &tex_pallet, 0 );
-
- m4x3_copy( player.to_world, player.mdl.mroot );
-
- if( player.is_dead )
- character_mimic_ragdoll( &player.mdl );
- else
- character_eval( &player.mdl );
-
- character_draw( &player.mdl, (player.is_dead|player.in_air)? 0.0f: 1.0f );
}
static void vg_framebuffer_resize( int w, int h )
{
- glBindTexture( GL_TEXTURE_2D, render.rgb_background );
- glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB, w, h, 0,
- GL_RGB, GL_UNSIGNED_BYTE, NULL );
-
+ render_fb_resize();
gate_fb_resize();
water_fb_resize();
}
glClearColor( 0.11f, 0.35f, 0.37f, 1.0f );
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
- v3f pos_inv;
- static v3f cam_lerped = {0.0f,0.0f,0.0f};
- v3_lerp( cam_lerped, player.view, 0.08f, cam_lerped );
-
- if( !freecam )
- {
- m4x3_mulv( player.to_world, cam_lerped, pos_inv );
-
- static float air_blend = 0.0f;
-
- air_blend = vg_lerpf( air_blend, player.in_air, 0.04f );
- v3_muladds( pos_inv, player.v, -0.05f*air_blend, pos_inv );
- }
- else
- v3_add( player.co, player.view, pos_inv );
- v3_negate( pos_inv, pos_inv );
-
- static float vertical_lerp = 0.0f;
- vertical_lerp = vg_lerpf( vertical_lerp, pos_inv[1], 1.0f );
- v3f final = { pos_inv[0], vertical_lerp, pos_inv[2] };
-
float speed = freecam? 0.0f: v3_length( player.v );
v3f shake = { vg_randf()-0.5f, vg_randf()-0.5f, vg_randf()-0.5f };
v3_muls( shake, speed*0.01f, shake );
- static v2f cam_lerped_dir;
-
- m4x3_identity( world_matrix );
if( player.is_dead )
{
+#if 0
v3f delta;
v3_sub( player.mdl.ragdoll[k_chpart_head].co, player.follow, delta );
v3_normalize(delta);
float pitch = asinf( delta[1] );
m4x3_rotate_x( world_matrix, -pitch );
m4x3_rotate_y( world_matrix, yaw );
+#endif
}
else
{
- v2_lerp( cam_lerped_dir, player.look_dir, 0.04f, cam_lerped_dir );
-
- m4x3_rotate_x( world_matrix,
- freecam?
- cam_lerped_dir[1]:
- 0.6f+shake[1]*0.04f+player.look_dir[1] );
-
- m4x3_rotate_y( world_matrix,
- freecam?
- cam_lerped_dir[0]:
- player.look_dir[0]+shake[0]*0.02f );
}
-
- m4x3_translate( world_matrix, final );
-
m4x4f world_4x4;
- m4x3_expand( world_matrix, world_4x4 );
+ m4x3_expand( player.camera_inverse, world_4x4 );
gpipeline.fov = freecam? 60.0f: 120.0f;
m4x4_projection( vg_pv, gpipeline.fov,
* Draw world
*/
- m4x3f cam_transform;
- m4x3_invert_affine( world_matrix, cam_transform );
-
- render_world( vg_pv, cam_transform );
- render_water_texture( cam_transform );
+ render_world( vg_pv, player.camera );
+ render_water_texture( player.camera );
glBindFramebuffer( GL_FRAMEBUFFER, 0 );
render_water_surface( vg_pv );
/* Copy the RGB of what we have into the background buffer */
glBindFramebuffer( GL_READ_FRAMEBUFFER, 0 );
- glBindFramebuffer( GL_DRAW_FRAMEBUFFER, render.fb_background );
+ glBindFramebuffer( GL_DRAW_FRAMEBUFFER, gpipeline.fb_background );
glBlitFramebuffer( 0,0, vg_window_x, vg_window_y,
0,0, vg_window_x, vg_window_y,
GL_COLOR_BUFFER_BIT,
shader_blit_use();
shader_blit_uTexMain( 0 );
glActiveTexture(GL_TEXTURE0);
- glBindTexture( GL_TEXTURE_2D, render.rgb_background );
-
- glBindVertexArray( render.fsquad.vao );
- glDrawArrays( GL_TRIANGLES, 0, 6 );
+ glBindTexture( GL_TEXTURE_2D, gpipeline.rgb_background );
+ render_fsquad();
glDisable(GL_BLEND);
/* Other shite */
vg_lines_drawall( (float *)vg_pv );
/* Debugger camera */
+#if 0
glViewport( 0,0, 800, 800 );
glClearColor( 0.1f, 0.0f, 0.2f, 1.0f );
glClear( GL_DEPTH_BUFFER_BIT );
glEnable( GL_DEPTH_TEST );
draw_player();
}
+#endif
glDisable( GL_DEPTH_TEST );
vg_lines_drawall( (float *)vg_pv );
glViewport( 0,0, vg_window_x, vg_window_y );
-
-#if 0
- if( replay_record )
- {
- m4x3f *base = &replay_buffer[(PART_COUNT)*replay_buffer_frame];
-
- for( int i=0; i<PART_COUNT; i++ )
- m4x3_copy( player.mdl.matrices[i], base[i] );
-
- replay_buffer_frame ++;
-
- if( replay_buffer_frame == REPLAY_LENGTH )
- replay_buffer_frame = 0;
- }
-#endif
}
void vg_ui(void)
--- /dev/null
+#ifndef PLAYER_H
+#define PLAYER_H
+
+#include "common.h"
+#include "character.h"
+
+static int freecam = 0;
+
+static struct gplayer
+{
+ /* Physics */
+ v3f co, v, a, v_last, m, bob;
+ v4f rot;
+ float vswitch, slip, slip_last,
+ reverse;
+
+ float iY; /* Yaw inertia */
+ int in_air, is_dead, on_board;
+
+ /* Input */
+ v2f joy_l;
+
+ v2f board_xy;
+ float grab;
+ float pitch;
+
+ v3f land_target;
+ v3f land_target_log[22];
+ u32 land_target_colours[22];
+ int land_log_count;
+ m3x3f vr;
+
+ m4x3f to_world, to_local;
+
+ struct character mdl;
+
+ v3f handl_target, handr_target,
+ handl, handr;
+
+ /* Camera */
+ float air_blend;
+
+ v3f camera_pos, smooth_localcam;
+ v2f angles;
+ m4x3f camera, camera_inverse;
+}
+player;
+
+static void player_transform_update(void)
+{
+ q_normalize( player.rot );
+ q_m3x3( player.rot, player.to_world );
+ v3_copy( player.co, player.to_world[3] );
+
+ m4x3_invert_affine( player.to_world, player.to_local );
+}
+
+static int reset_player( int argc, char const *argv[] )
+{
+ v3_zero( player.co );
+
+ if( argc == 1 )
+ {
+ if( !strcmp( argv[0], "tutorial" ))
+ v3_copy( world.tutorial, player.co );
+ }
+
+ v3_copy( (v3f){ 0.0f, 0.0f, -0.2f }, player.v );
+ q_identity( player.rot );
+ player.vswitch = 1.0f;
+ player.slip_last = 0.0f;
+ player.is_dead = 0;
+ player.in_air = 1;
+ m3x3_identity( player.vr );
+
+ player.mdl.shoes[0] = 1;
+ player.mdl.shoes[1] = 1;
+
+ player_transform_update();
+ return 0;
+}
+
+static void player_mouseview(void)
+{
+ static v2f mouse_last,
+ view_vel = { 0.0f, 0.0f };
+
+ if( vg_get_button_down( "primary" ) )
+ v2_copy( vg_mouse, mouse_last );
+ else if( vg_get_button( "primary" ) )
+ {
+ v2f delta;
+ v2_sub( vg_mouse, mouse_last, delta );
+ v2_copy( vg_mouse, mouse_last );
+
+ v2_muladds( view_vel, delta, 0.005f, view_vel );
+ }
+
+ v2_muls( view_vel, 0.7f, view_vel );
+ v2_add( view_vel, player.angles, player.angles );
+ player.angles[1] = vg_clampf( player.angles[1], -VG_PIf*0.5f, VG_PIf*0.5f );
+
+}
+
+static void player_freecam(void)
+{
+ player_mouseview();
+
+ float movespeed = 25.0f;
+ v3f lookdir = { 0.0f, 0.0f, -1.0f },
+ sidedir = { 1.0f, 0.0f, 0.0f };
+
+ m3x3_mulv( player.camera, lookdir, lookdir );
+ m3x3_mulv( player.camera, sidedir, sidedir );
+
+ static v3f move_vel = { 0.0f, 0.0f, 0.0f };
+ if( vg_get_button( "forward" ) )
+ v3_muladds( move_vel, lookdir, ktimestep * movespeed, move_vel );
+ if( vg_get_button( "back" ) )
+ v3_muladds( move_vel, lookdir, ktimestep *-movespeed, move_vel );
+ if( vg_get_button( "left" ) )
+ v3_muladds( move_vel, sidedir, ktimestep *-movespeed, move_vel );
+ if( vg_get_button( "right" ) )
+ v3_muladds( move_vel, sidedir, ktimestep * movespeed, move_vel );
+
+ v3_muls( move_vel, 0.7f, move_vel );
+ v3_add( move_vel, player.camera_pos, player.camera_pos );
+}
+
+static void apply_gravity( v3f vel, float const timestep )
+{
+ v3f gravity = { 0.0f, -9.6f, 0.0f };
+ v3_muladds( vel, gravity, timestep, vel );
+}
+
+static void player_start_air(void)
+{
+ player.in_air = 1;
+
+ float pstep = ktimestep*10.0f;
+
+ float best_velocity_mod = 0.0f,
+ best_velocity_delta = -9999.9f;
+
+ v3f axis, vup;
+ m3x3_mulv( player.to_world, (v3f){0.0f,1.0f,0.0f}, vup );
+ v3_cross( vup, player.v, axis );
+ v3_normalize( axis );
+ player.land_log_count = 0;
+
+ m3x3_identity( player.vr );
+
+ for( int m=-3;m<=12; m++ )
+ {
+ float vmod = ((float)m / 15.0f)*0.09f;
+
+ v3f pco, pco1, pv;
+ v3_copy( player.co, pco );
+ v3_copy( player.v, pv );
+
+ /*
+ * Try different 'rotations' of the velocity to find the best possible
+ * landing normal. This conserves magnitude at the expense of slightly
+ * unrealistic results
+ */
+
+ m3x3f vr;
+ v4f vr_q;
+
+ q_axis_angle( vr_q, axis, vmod );
+ q_m3x3( vr_q, vr );
+
+ m3x3_mulv( vr, pv, pv );
+ v3_muladds( pco, pv, ktimestep, pco );
+
+ for( int i=0; i<50; i++ )
+ {
+ v3_copy( pco, pco1 );
+ apply_gravity( pv, pstep );
+
+ m3x3_mulv( vr, pv, pv );
+ v3_muladds( pco, pv, pstep, pco );
+
+ ray_hit contact;
+ v3f vdir;
+
+ v3_sub( pco, pco1, vdir );
+ contact.dist = v3_length( vdir );
+ v3_divs( vdir, contact.dist, vdir);
+
+ if( ray_world( pco1, vdir, &contact ))
+ {
+ float land_delta = v3_dot( pv, contact.normal );
+ u32 scolour = (u8)(vg_minf(-land_delta * 2.0f, 255.0f));
+
+ /* Bias prediction towords ramps */
+ if( ray_hit_is_ramp( &contact ) )
+ {
+ land_delta *= 0.1f;
+ scolour |= 0x0000a000;
+ }
+
+ if( (land_delta < 0.0f) && (land_delta > best_velocity_delta) )
+ {
+ best_velocity_delta = land_delta;
+ best_velocity_mod = vmod;
+
+ v3_copy( contact.pos, player.land_target );
+
+ q_axis_angle( vr_q, axis, vmod*0.1f );
+ q_m3x3( vr_q, player.vr );
+ }
+
+ v3_copy( contact.pos,
+ player.land_target_log[player.land_log_count] );
+ player.land_target_colours[player.land_log_count] =
+ 0xff000000 | scolour;
+
+ player.land_log_count ++;
+
+ break;
+ }
+ }
+ }
+
+ //v3_rotate( player.v, best_velocity_mod, axis, player.v );
+
+ return;
+ v3_muls( player.v, best_velocity_mod, player.v );
+}
+
+static int sample_if_resistant( v3f pos )
+{
+ v3f ground;
+ v3_copy( pos, ground );
+ ground[1] += 4.0f;
+
+ ray_hit hit;
+ hit.dist = INFINITY;
+
+ if( ray_world( ground, (v3f){0.0f,-1.0f,0.0f}, &hit ))
+ {
+ v3f angle;
+ v3_copy( player.v, angle );
+ v3_normalize( angle );
+ float resistance = v3_dot( hit.normal, angle );
+
+ if( resistance < 0.25f )
+ {
+ v3_copy( hit.pos, pos );
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static float stable_force( float current, float diff )
+{
+ float new = current + diff;
+
+ if( new * current < 0.0f )
+ return 0.0f;
+
+ return new;
+}
+
+static void player_physics_ground(void)
+{
+ /*
+ * Getting surface collision points,
+ * the contact manifold is a triangle for simplicity.
+ */
+ v3f contact_front, contact_back, contact_norm, vup, vside,
+ axis;
+
+ float klength = 0.65f;
+ m4x3_mulv( player.to_world, (v3f){ 0.15f,0.0f,-klength}, contact_norm );
+ m4x3_mulv( player.to_world, (v3f){-0.15f,0.0f,-klength}, contact_front );
+ m4x3_mulv( player.to_world, (v3f){ 0.00f,0.0f, klength}, contact_back );
+ m3x3_mulv( player.to_world, (v3f){ 0.0f, 1.0f, 0.0f}, vup );
+ m3x3_mulv( player.to_world, (v3f){ 1.0f, 0.0f, 0.0f}, vside );
+
+ v3f cn0, cn1, cn2;
+
+ int contact_count =
+ sample_if_resistant( contact_front ) +
+ sample_if_resistant( contact_back ) +
+ sample_if_resistant( contact_norm );
+
+ if( contact_count < 3 )
+ {
+ player_start_air();
+ return;
+ }
+
+ v3f norm;
+ v3f v0, v1;
+ v3_sub( contact_norm, contact_front, v0 );
+ v3_sub( contact_back, contact_front, v1 );
+ v3_cross( v1, v0, norm );
+ v3_normalize( norm );
+
+ vg_line( contact_norm, contact_front, 0xff00ff00 );
+ vg_line( contact_back, contact_front, 0xff0000ff );
+
+ /* Surface alignment */
+ float angle = v3_dot( vup, norm );
+ v3_cross( vup, norm, axis );
+
+ if( angle < 0.999f )
+ {
+ v4f correction;
+ q_axis_angle( correction, axis, acosf(angle) );
+ q_mul( correction, player.rot, player.rot );
+ }
+
+ float resistance = v3_dot( norm, player.v );
+ if( resistance >= 0.0f )
+ {
+ player_start_air();
+ return;
+ }
+ else
+ {
+ v3_muladds( player.v, norm, -resistance, player.v );
+ }
+
+ /* This is where velocity integration used to be */
+
+ float slip = 0.0f;
+
+ player.co[1] = (contact_front[1]+contact_back[1])*0.5f;
+
+ v3f vel;
+ m3x3_mulv( player.to_local, player.v, vel );
+
+ /* Calculate local forces */
+
+ if( fabsf(vel[2]) > 0.01f )
+ slip = fabsf(-vel[0] / vel[2]) * vg_signf(vel[0]);
+
+ if( fabsf( slip ) > 1.2f )
+ slip = vg_signf( slip ) * 1.2f;
+ player.slip = slip;
+ player.reverse = -vg_signf(vel[2]);
+
+ float substep = ktimestep * 0.2f;
+ float fwd_resistance = (vg_get_button( "break" )? 5.0f: 0.02f) * -substep;
+
+ for( int i=0; i<5; i++ )
+ {
+ vel[2] = stable_force( vel[2], vg_signf( vel[2] ) * fwd_resistance );
+ vel[0] = stable_force( vel[0], vg_signf( vel[0] ) * -7.0f *substep );
+ }
+
+ static double start_push = 0.0;
+ if( vg_get_button_down( "push" ) )
+ start_push = vg_time;
+
+ if( !vg_get_button("break") && vg_get_button( "push" ) )
+ {
+ float const k_maxpush = 16.0f,
+ k_pushaccel = 5.0f;
+
+ float cycle_time = vg_time-start_push,
+ amt = k_pushaccel * (sinf( cycle_time * 8.0f )*0.5f+0.5f)*ktimestep,
+ current = v3_length( vel ),
+ new_vel = vg_minf( current + amt, k_maxpush );
+ new_vel -= vg_minf(current, k_maxpush);
+ vel[2] -= new_vel * player.reverse;
+ }
+
+ m3x3_mulv( player.to_world, vel, player.v );
+
+ if( vg_get_button( "yawl" ) )
+ player.iY += 3.6f * ktimestep;
+ if( vg_get_button( "yawr" ) )
+ player.iY -= 3.6f * ktimestep;
+
+ float steer = vg_get_axis( "horizontal" );
+ player.iY -= vg_signf(steer)*powf(steer,2.0f) * 1.5f * ktimestep;
+
+ /* Too much lean and it starts to look like a snowboard here */
+ v2_lerp( player.board_xy, (v2f){ slip*0.25f, 0.0f },
+ ktimestep*5.0f, player.board_xy);
+}
+
+static void draw_cross(v3f pos,u32 colour, float scale)
+{
+ v3f p0, p1;
+ v3_add( (v3f){ scale,0.0f,0.0f}, pos, p0 );
+ v3_add( (v3f){-scale,0.0f,0.0f}, pos, p1 );
+ vg_line( p0, p1, colour );
+ v3_add( (v3f){0.0f, scale,0.0f}, pos, p0 );
+ v3_add( (v3f){0.0f,-scale,0.0f}, pos, p1 );
+ vg_line( p0, p1, colour );
+ v3_add( (v3f){0.0f,0.0f, scale}, pos, p0 );
+ v3_add( (v3f){0.0f,0.0f,-scale}, pos, p1 );
+ vg_line( p0, p1, colour );
+}
+
+static void player_physics_air(void)
+{
+ m3x3_mulv( player.vr, player.v, player.v );
+ for( int i=0; i<player.land_log_count; i++ )
+ draw_cross( player.land_target_log[i], player.land_target_colours[i], 1);
+
+ draw_cross( player.land_target, 0xff0000ff, 1 );
+
+ v3f ground_pos;
+ v3_copy( player.co, ground_pos );
+ ground_pos[1] += 4.0f;
+
+ ray_hit hit;
+ hit.dist = INFINITY;
+ if( ray_world( ground_pos, (v3f){0.0f,-1.0f,0.0f}, &hit ))
+ {
+ if( hit.pos[1] > player.co[1] )
+ {
+ player.in_air = 0;
+
+ if( !ray_hit_is_ramp( &hit ) )
+ {
+ player.is_dead = 1;
+ character_ragdoll_copypose( &player.mdl, player.v );
+ }
+
+ return;
+ }
+ }
+
+ /* Prediction
+ *
+ * TODO: Find best landing surface and guide player towords it
+ */
+ float pstep = ktimestep*10.0f;
+
+ v3f pco, pco1, pv;
+ v3_copy( player.co, pco );
+ v3_copy( player.v, pv );
+
+ float time_to_impact = 0.0f;
+ float limiter = 1.0f;
+
+ for( int i=0; i<50; i++ )
+ {
+ v3_copy( pco, pco1 );
+ apply_gravity( pv, pstep );
+ v3_muladds( pco, pv, pstep, pco );
+
+ //vg_line( pco, pco1, i&0x1?0xff000000:0xffffffff );
+
+ ray_hit contact;
+ v3f vdir;
+
+ v3_sub( pco, pco1, vdir );
+ contact.dist = v3_length( vdir );
+ v3_divs( vdir, contact.dist, vdir);
+
+ float orig_dist = contact.dist;
+ if( ray_world( pco1, vdir, &contact ))
+ {
+ v3f localup;
+ m3x3_mulv( player.to_world, (v3f){0.0f,1.0f,0.0f}, localup );
+
+ float angle = v3_dot( localup, contact.normal );
+ v3f axis;
+ v3_cross( localup, contact.normal, axis );
+
+ time_to_impact += (contact.dist/orig_dist)*pstep;
+ limiter = vg_minf( 5.0f, time_to_impact )/5.0f;
+ limiter = 1.0f-limiter;
+ limiter *= limiter;
+ limiter = 1.0f-limiter;
+
+ if( angle < 0.99f )
+ {
+ v4f correction;
+ q_axis_angle( correction, axis, acosf(angle)*0.05f*(1.0f-limiter) );
+ q_mul( correction, player.rot, player.rot );
+ }
+
+ draw_cross( contact.pos, 0xffff0000, 1 );
+ break;
+ }
+ time_to_impact += pstep;
+ }
+
+ player.iY -= vg_get_axis( "horizontal" ) * 3.6f * ktimestep;
+ {
+
+ float iX = vg_get_axis( "vertical" ) * 3.6f * limiter * ktimestep;
+ static float siX = 0.0f;
+ siX = vg_lerpf( siX, iX, 0.3f );
+
+ v4f rotate;
+ v3f vside;
+
+ m3x3_mulv( player.to_world, (v3f){1.0f,0.0f,0.0f}, vside );
+
+ q_axis_angle( rotate, vside, siX );
+ q_mul( rotate, player.rot, player.rot );
+ }
+
+ 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 );
+}
+
+static void player_do_motion(void)
+{
+ float horizontal = vg_get_axis("horizontal"),
+ vertical = vg_get_axis("vertical");
+
+ player.joy_l[0] = vg_signf(horizontal) * powf( horizontal, 2.0f );
+ player.joy_l[1] = vg_signf(vertical) * powf( vertical, 2.0f );
+
+ if( player.in_air )
+ player_physics_air();
+
+ if( !player.in_air )
+ player_physics_ground();
+
+ /* Integrate velocity */
+ v3f prevco;
+ v3_copy( player.co, prevco );
+
+ apply_gravity( player.v, ktimestep );
+ v3_muladds( player.co, player.v, ktimestep, player.co );
+
+ /* Integrate inertia */
+ v4f rotate; v3f vup = {0.0f,1.0f,0.0f};
+ m3x3_mulv( player.to_world, vup, vup );
+
+ static float siY = 0.0f;
+
+ float lerpq = player.in_air? 0.04f: 0.3f;
+ siY = vg_lerpf( siY, player.iY, lerpq );
+
+ q_axis_angle( rotate, vup, siY );
+ q_mul( rotate, player.rot, player.rot );
+
+ player.iY = 0.0f; /* temp */
+
+#if 0
+ /* GATE COLLISION */
+ if( gate_intersect( &gate_a, player.co, prevco ) )
+ {
+ teleport_gate *gate = &gate_a;
+
+ m4x3f transport;
+ m4x3_mul( gate->other->to_world, gate->to_local, transport );
+ m4x3_mulv( transport, player.co, player.co );
+ m3x3_mulv( transport, player.v, player.v );
+ m3x3_mulv( transport, player.v_last, player.v_last );
+ m3x3_mulv( transport, player.m, player.m );
+ m3x3_mulv( transport, player.bob, player.bob );
+
+ v4f transport_rotation;
+ m3x3_q( transport, transport_rotation );
+ q_mul( transport_rotation, player.rot, player.rot );
+ }
+#endif
+
+ /* Camera and character */
+ player_transform_update();
+
+ player.angles[0] = atan2f( player.v[0], -player.v[2] );
+ player.angles[1] = atan2f( -player.v[1], sqrtf(player.v[0]*player.v[0]+
+ player.v[2]*player.v[2]) ) * 0.3f;
+
+ player.air_blend = vg_lerpf( player.air_blend, player.in_air, 0.04f );
+ v3_muladds( player.camera_pos, player.v, -0.05f*player.air_blend,
+ player.camera_pos );
+}
+
+static void player_walkgrid(void)
+{
+ float const k_gridscale = 0.5f;
+ float const k_stepheight = 0.5f;
+ float const k_walkspeed = 6.0f;
+ float const k_miny = 0.6f;
+ float const k_height = 1.78f;
+ int const k_gridamt = 8;
+
+ v3f cell;
+ v3_muls( player.co, 1.0f/k_gridscale, cell );
+ v3_floor( cell, cell );
+ v3_muls( cell, k_gridscale, cell );
+
+ struct grid_sample
+ {
+ ray_hit hit;
+ int valid;
+ }
+ samples[ k_gridamt ][ k_gridamt ];
+
+ v3f grid_origin;
+ v3_muladds( cell, (v3f){ -1.0f,0.0f,-1.0f },
+ (float)(k_gridamt/2) * k_gridscale, grid_origin );
+
+ /*
+ * Get sample 'poles'
+ */
+ for( int y=0; y<k_gridamt; y++ )
+ {
+ for( int x=0; x<k_gridamt; x++ )
+ {
+ v3f sample_coord;
+ v3_muladds( grid_origin, (v3f){ x, 0, y }, k_gridscale, sample_coord );
+ sample_coord[1] += k_height;
+
+ struct grid_sample *sample = &samples[y][x];
+ sample->valid = 0;
+ sample->hit.dist = k_stepheight+k_height;
+
+ if( ray_world( sample_coord, (v3f){0.0f,-1.0f,0.0f}, &sample->hit ))
+ {
+ if( sample->hit.normal[1] >= k_miny &&
+ ray_hit_is_ramp( &sample->hit ))
+ {
+ sample->valid = 1;
+ draw_cross( sample->hit.pos, 0xff00ff00, 0.1f );
+ }
+ else
+ draw_cross( sample->hit.pos, 0xff0000ff, 0.1f );
+ }
+ }
+ }
+
+ /*
+ * Clip grid intersections with triangle edges
+ */
+ for( int x=0; x<k_gridamt; x++ )
+ {
+ for( int y=0; y<k_gridamt-1; y++ )
+ {
+ struct grid_sample *sa = &samples[y][x],
+ *sb = &samples[y+1][x];
+
+ if( (sa->valid != sb->valid) && (sa->valid||sb->valid) )
+ {
+ v3f tri[3];
+ ray_world_get_tri( sa->valid? &sa->hit: &sb->hit, tri );
+
+ v3f sample;
+ v3_muladds( grid_origin, (v3f){ x, 0, y },
+ k_gridscale, sample);
+
+ /* Clip triangles until we find an edge inside the cell */
+ int axis = 0;
+ float offset = sample[axis==0?0:2],
+ basis = sample[axis==0?2:0];
+
+ for( int i=0; i<3; i++ )
+ {
+ int ia = i,
+ ib = (i+1)%3;
+ float pa = tri[ia][axis],
+ pb = tri[ib][axis];
+
+ vg_line( tri[ia],tri[ib],0xffaaaaaa );
+
+ if( (pa-offset)*(pb-offset) > 0.0f )
+ continue;
+
+ float d = pb-pa,
+ qa = (offset-pa)/d,
+ h = qa*tri[ib][2] + (1.0f-qa)*tri[ia][2],
+ q = (h-basis)/k_gridscale;
+
+ if( q >= 0.0f && q <= 1.0f )
+ {
+ float height = qa*tri[ia][1] + (1.0f-qa)*tri[ib][1];
+
+ v3f intersection = { offset, height, h };
+ draw_cross( intersection, 0xffff0000, 0.06f );
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ v3f fwd = { -sinf(-player.angles[0]), 0.0f, -cosf(-player.angles[0]) },
+ side = { -fwd[2], 0.0f, fwd[0] };
+
+ /* Temp */
+ if( glfwGetKey( vg_window, GLFW_KEY_W ) )
+ v3_muladds( player.co, fwd, ktimestep*k_walkspeed, player.co );
+ if( glfwGetKey( vg_window, GLFW_KEY_S ) )
+ v3_muladds( player.co, fwd, -ktimestep*k_walkspeed, player.co );
+
+ if( glfwGetKey( vg_window, GLFW_KEY_A ) )
+ v3_muladds( player.co, side, -ktimestep*k_walkspeed, player.co );
+ if( glfwGetKey( vg_window, GLFW_KEY_D ) )
+ v3_muladds( player.co, side, ktimestep*k_walkspeed, player.co );
+
+ m4x3_mulv( player.to_world, (v3f){0.0f,1.8f,0.0f}, player.camera_pos );
+ player_mouseview();
+ player_transform_update();
+}
+
+static void player_animate(void)
+{
+ /* Camera position */
+ v3_sub( player.v, player.v_last, player.a );
+ v3_copy( player.v, player.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 );
+ v3f target;
+
+ player.m[0] = vg_clampf( player.m[0], -2.0f, 2.0f );
+ player.m[1] = vg_clampf( player.m[1], -0.2f, 5.0f );
+ player.m[2] = vg_clampf( player.m[2], -2.0f, 2.0f );
+ v3_copy( player.m, target );
+ v3_lerp( player.bob, target, 0.2f, player.bob );
+
+ /* Head */
+ float lslip = fabsf(player.slip); //vg_minf( 0.4f, slip );
+
+ float grabt = vg_get_axis( "grabr" )*0.5f+0.5f;
+ player.grab = vg_lerpf( player.grab, grabt, 0.04f );
+
+ float kheight = 2.0f,
+ kleg = 0.6f;
+
+ v3f head;
+ head[0] = 0.0f;
+ head[1] = (0.3f+cosf(lslip)*0.5f*(1.0f-player.grab*0.7f)) * kheight;
+ head[2] = 0.0f;
+
+ v3f offset;
+ m3x3_mulv( player.to_local, player.bob, offset );
+
+ offset[0] *= 0.3333f;
+ offset[1] *= -0.25f;
+ offset[2] *= 0.7f;
+ v3_muladds( head, offset, 0.7f, head );
+ head[1] = vg_clampf( head[1], 0.3f, kheight );
+
+#if 0
+ if( !freecam )
+ {
+ v3_copy( head, player.view );
+ v3f camoffs = {-0.2f,-0.6f,0.00f};
+ v3_add( player.view, camoffs, player.view );
+ }
+#endif
+
+ /*
+ * Animation blending
+ * ===========================================
+ */
+
+ static float fslide = 0.0f;
+ static float fdirz = 0.0f;
+ static float fdirx = 0.0f;
+ static float fstand = 0.0f;
+ static float ffly = 0.0f;
+
+ float speed = v3_length( player.v );
+
+ fstand = vg_lerpf(fstand, 1.0f-vg_clampf(speed*0.03f,0.0f,1.0f),0.1f);
+ fslide = vg_lerpf(fslide, vg_clampf(lslip+fabsf(offset[0])*0.2f,
+ 0.0f,1.0f), 0.04f);
+ fdirz = vg_lerpf(fdirz, player.reverse > 0.0f? 1.0f: 0.0f, 0.04f );
+ fdirx = vg_lerpf(fdirx, player.slip < 0.0f? 1.0f: 0.0f, 0.04f );
+ ffly = vg_lerpf(ffly, player.in_air? 1.0f: 0.0f, 0.04f );
+
+ character_pose_reset( &player.mdl );
+
+ float amt_air = ffly*ffly,
+ amt_ground = 1.0f-amt_air,
+ amt_std = (1.0f-fslide) * amt_ground,
+ amt_stand = amt_std * fstand,
+ amt_aero = amt_std * (1.0f-fstand),
+ amt_slide = amt_ground * fslide;
+
+ character_final_pose( &player.mdl, offset, &pose_stand, amt_stand );
+ character_final_pose( &player.mdl, offset, &pose_aero, amt_aero*fdirz );
+ character_final_pose( &player.mdl, offset,
+ &pose_aero_reverse, amt_aero * (1.0f-fdirz) );
+ character_final_pose( &player.mdl, offset, &pose_slide, amt_slide*fdirx );
+ character_final_pose( &player.mdl, offset,
+ &pose_slide1, amt_slide*(1.0f-fdirx) );
+
+ character_final_pose( &player.mdl, (v3f){0.0f,0.0f,0.0f},
+ &pose_fly, amt_air );
+
+ /* Camera position */
+ v3_lerp( player.smooth_localcam, player.mdl.cam_pos, 0.08f,
+ player.smooth_localcam );
+ v3_muladds( player.smooth_localcam, offset, 0.7f, player.camera_pos );
+ player.camera_pos[1] = vg_clampf( player.camera_pos[1], 0.3f, kheight );
+ m4x3_mulv( player.to_world, player.camera_pos, player.camera_pos );
+
+ /*
+ * Additive effects
+ * ==========================
+ */
+ struct ik_basic *arm_l = &player.mdl.ik_arm_l,
+ *arm_r = &player.mdl.ik_arm_r;
+
+ v3f localv;
+ m3x3_mulv( player.to_local, player.v, localv );
+ v3_muladds( arm_l->end, localv, -0.01f, arm_l->end );
+ v3_muladds( arm_r->end, localv, -0.01f, arm_r->end );
+
+ /* New board transformation */
+ v4f board_rotation; v3f board_location;
+
+ v4f rz, rx;
+ q_axis_angle( rz, (v3f){ 0.0f, 0.0f, 1.0f }, player.board_xy[0] );
+ q_axis_angle( rx, (v3f){ 1.0f, 0.0f, 0.0f }, player.board_xy[1] );
+ q_mul( rx, rz, board_rotation );
+
+ v3f *mboard = player.mdl.matrices[k_chpart_board];// player.mboard;
+ q_m3x3( board_rotation, mboard );
+ m3x3_mulv( mboard, (v3f){ 0.0f, -0.5f, 0.0f }, board_location );
+ v3_add( (v3f){0.0f,0.5f,0.0f}, board_location, board_location );
+ v3_copy( board_location, mboard[3] );
+
+
+ float wheel_r = offset[0]*-0.4f;
+ v4f qwheel;
+ q_axis_angle( qwheel, (v3f){0.0f,1.0f,0.0f}, wheel_r );
+
+ q_m3x3( qwheel, player.mdl.matrices[k_chpart_wb] );
+
+ m3x3_transpose( player.mdl.matrices[k_chpart_wb],
+ player.mdl.matrices[k_chpart_wf] );
+ v3_copy( player.mdl.offsets[k_chpart_wb],
+ player.mdl.matrices[k_chpart_wb][3] );
+ v3_copy( player.mdl.offsets[k_chpart_wf],
+ player.mdl.matrices[k_chpart_wf][3] );
+
+ m4x3_mul( mboard, player.mdl.matrices[k_chpart_wb],
+ player.mdl.matrices[k_chpart_wb] );
+ m4x3_mul( mboard, player.mdl.matrices[k_chpart_wf],
+ player.mdl.matrices[k_chpart_wf] );
+
+ m4x3_mulv( mboard, player.mdl.ik_leg_l.end, player.mdl.ik_leg_l.end );
+ m4x3_mulv( mboard, player.mdl.ik_leg_r.end, player.mdl.ik_leg_r.end );
+
+
+ v3_copy( player.mdl.ik_arm_l.end, player.handl_target );
+ v3_copy( player.mdl.ik_arm_r.end, player.handr_target );
+
+ if( 1||player.in_air )
+ {
+ float tuck = player.board_xy[1],
+ tuck_amt = fabsf( tuck ) * (1.0f-fabsf(player.board_xy[0]));
+
+ float crouch = player.grab*0.3f;
+ v3_muladds( player.mdl.ik_body.base, (v3f){0.0f,-1.0f,0.0f},
+ crouch, player.mdl.ik_body.base );
+ v3_muladds( player.mdl.ik_body.end, (v3f){0.0f,-1.0f,0.0f},
+ crouch*1.2f, player.mdl.ik_body.end );
+
+ if( tuck < 0.0f )
+ {
+ //foot_l *= 1.0f-tuck_amt*1.5f;
+
+ if( player.grab > 0.1f )
+ {
+ m4x3_mulv( mboard, (v3f){0.1f,0.14f,0.6f},
+ player.handl_target );
+ }
+ }
+ else
+ {
+ //foot_r *= 1.0f-tuck_amt*1.4f;
+
+ if( player.grab > 0.1f )
+ {
+ m4x3_mulv( mboard, (v3f){0.1f,0.14f,-0.6f},
+ player.handr_target );
+ }
+ }
+ }
+
+ v3_lerp( player.handl, player.handl_target, 0.1f, player.handl );
+ v3_lerp( player.handr, player.handr_target, 0.1f, player.handr );
+
+ v3_copy( player.handl, player.mdl.ik_arm_l.end );
+ v3_copy( player.handr, player.mdl.ik_arm_r.end );
+
+ /* Head rotation */
+
+ static float rhead = 0.0f;
+ rhead = vg_lerpf( rhead,
+ vg_clampf(atan2f( localv[2], -localv[0] ),-1.0f,1.0f), 0.04f );
+ player.mdl.rhead = rhead;
+}
+
+static void player_update(void)
+{
+ if( vg_get_axis("grabl")>0.0f)
+ reset_player(0,NULL);
+
+ if( freecam )
+ {
+ player_freecam();
+ }
+ else
+ {
+ if( player.is_dead )
+ {
+ character_ragdoll_iter( &player.mdl );
+ character_debug_ragdoll( &player.mdl );
+ }
+ else
+ {
+ if( player.on_board )
+ {
+ player_do_motion();
+ player_animate();
+ }
+ else
+ {
+ player_walkgrid();
+ }
+ }
+ }
+
+ /* Update camera matrices */
+ m4x3_identity( player.camera );
+ m4x3_rotate_y( player.camera, -player.angles[0] );
+ m4x3_rotate_x( player.camera, -0.33f -player.angles[1] );
+ v3_copy( player.camera_pos, player.camera[3] );
+ m4x3_invert_affine( player.camera, player.camera_inverse );
+}
+
+static void draw_player(void)
+{
+ /* Draw */
+ m4x3_copy( player.to_world, player.mdl.mroot );
+
+ if( player.is_dead )
+ character_mimic_ragdoll( &player.mdl );
+ else
+ character_eval( &player.mdl );
+
+ character_draw( &player.mdl, (player.is_dead|player.in_air)? 0.0f: 1.0f );
+}
+
+#endif /* PLAYER_H */
#ifndef RENDER_H
#define RENDER_H
-#define VG_3D
-#include "vg/vg.h"
+
+#include "common.h"
+#include "model.h"
static struct pipeline
{
float fov;
+ glmesh fsquad;
+
+ GLuint fb_background,
+ rgb_background;
}
gpipeline;
vg_window_x, vg_window_y );
}
+static void render_fb_resize(void)
+{
+ glBindTexture( GL_TEXTURE_2D, gpipeline.rgb_background );
+ glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB, vg_window_x, vg_window_y, 0,
+ GL_RGB, GL_UNSIGNED_BYTE, NULL );
+}
+
+static void render_init(void)
+{
+ glGenFramebuffers( 1, &gpipeline.fb_background );
+ glBindFramebuffer( GL_FRAMEBUFFER, gpipeline.fb_background );
+
+ glGenTextures( 1, &gpipeline.rgb_background );
+ glBindTexture( GL_TEXTURE_2D, gpipeline.rgb_background );
+ glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB, vg_window_x, vg_window_y,
+ 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
+
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
+ glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ GL_TEXTURE_2D,
+ gpipeline.rgb_background, 0);
+
+ float quad[] = { 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f,
+ 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f };
+
+ glGenVertexArrays( 1, &gpipeline.fsquad.vao );
+ glGenBuffers( 1, &gpipeline.fsquad.vbo );
+ glBindVertexArray( gpipeline.fsquad.vao );
+ glBindBuffer( GL_ARRAY_BUFFER, gpipeline.fsquad.vbo );
+ glBufferData( GL_ARRAY_BUFFER, sizeof(quad), quad, GL_STATIC_DRAW );
+ glBindVertexArray( gpipeline.fsquad.vao );
+ glVertexAttribPointer( 0, 2, GL_FLOAT, GL_FALSE,
+ sizeof(float)*2, (void*)0 );
+ glEnableVertexAttribArray( 0 );
+ VG_CHECK_GL();
+}
+
+static void render_fsquad(void)
+{
+ glBindVertexArray( gpipeline.fsquad.vao );
+ glDrawArrays( GL_TRIANGLES, 0, 6 );
+}
+
#endif /* RENDER_H */
out vec4 FragColor;
uniform vec4 uColour;
+uniform sampler2D uTexGarbage;
+uniform float uTime;
in vec4 aColour;
in vec2 aUv;
vec3 skycolour = vec3(0.4,0.5,0.8);
vec3 diffuse = mix( skycolour, horizon, fblend );
- FragColor = vec4(diffuse,1.0);
+ float fmove = uTime * 0.001;
+ vec2 cloudplane = (aCo.xz / (aCo.y*sign(aNorm.y))) * 0.03;
+ vec4 clouds1 = texture( uTexGarbage, cloudplane + vec2(0.1,0.4)*fmove*2.0 );
+ vec4 clouds2 = texture( uTexGarbage, cloudplane + vec2(0.3,0.1)*fmove );
+
+ float cloud_d = max(clouds1.b*clouds2.r -0.2 - clouds2.g*0.4,0.0);
+ float cloud_e = pow(cloud_d,1.8)*pow(abs(aNorm.y),0.3)*2.0;
+
+ vec3 colour_ocean = vec3( 0.61, 0.84, 0.9 );
+ float fhorizon = step( aNorm.y * 0.5 + 0.5, 0.5 );
+
+ vec3 skycomp = mix(diffuse, vec3(1.0), cloud_e);
+ skycomp = mix(mix(pow(colour_ocean,vec3(6.0))*0.6,skycomp, 0.7),skycomp,fhorizon);
+
+ FragColor = vec4(skycomp, 1.0);
}
"out vec4 FragColor;\n"
"\n"
"uniform vec4 uColour;\n"
+"uniform sampler2D uTexGarbage;\n"
+"uniform float uTime;\n"
"\n"
"in vec4 aColour;\n"
"in vec2 aUv;\n"
" vec3 skycolour = vec3(0.4,0.5,0.8);\n"
" vec3 diffuse = mix( skycolour, horizon, fblend );\n"
"\n"
-" FragColor = vec4(diffuse,1.0);\n"
+" float fmove = uTime * 0.001;\n"
+" vec2 cloudplane = (aCo.xz / (aCo.y*sign(aNorm.y))) * 0.03;\n"
+" vec4 clouds1 = texture( uTexGarbage, cloudplane + vec2(0.1,0.4)*fmove*2.0 );\n"
+" vec4 clouds2 = texture( uTexGarbage, cloudplane + vec2(0.3,0.1)*fmove );\n"
+"\n"
+" float cloud_d = max(clouds1.b*clouds2.r -0.2 - clouds2.g*0.4,0.0);\n"
+" float cloud_e = pow(cloud_d,1.8)*pow(abs(aNorm.y),0.3)*2.0;\n"
+"\n"
+" vec3 colour_ocean = vec3( 0.61, 0.84, 0.9 );\n"
+" float fhorizon = step( aNorm.y * 0.5 + 0.5, 0.5 );\n"
+"\n"
+" vec3 skycomp = mix(diffuse, vec3(1.0), cloud_e);\n"
+" skycomp = mix(mix(pow(colour_ocean,vec3(6.0))*0.6,skycomp, 0.7),skycomp,fhorizon);\n"
+"\n"
+" FragColor = vec4(skycomp, 1.0);\n"
"}\n"
""},
};
static GLuint _uniform_sky_uPv;
static GLuint _uniform_sky_uMdl;
static GLuint _uniform_sky_uColour;
+static GLuint _uniform_sky_uTexGarbage;
+static GLuint _uniform_sky_uTime;
static void shader_sky_uPv(m4x4f m){
glUniformMatrix4fv( _uniform_sky_uPv, 1, GL_FALSE, (float *)m );
}
static void shader_sky_uColour(v4f v){
glUniform4fv( _uniform_sky_uColour, 1, v );
}
+static void shader_sky_uTexGarbage(int i){
+ glUniform1i( _uniform_sky_uTexGarbage, i );
+}
+static void shader_sky_uTime(float f){
+ glUniform1f( _uniform_sky_uTime, f );
+}
static void shader_sky_register(void){
vg_shader_register( &_shader_sky );
}
_uniform_sky_uPv = glGetUniformLocation( _shader_sky.id, "uPv" );
_uniform_sky_uMdl = glGetUniformLocation( _shader_sky.id, "uMdl" );
_uniform_sky_uColour = glGetUniformLocation( _shader_sky.id, "uColour" );
+ _uniform_sky_uTexGarbage = glGetUniformLocation( _shader_sky.id, "uTexGarbage" );
+ _uniform_sky_uTime = glGetUniformLocation( _shader_sky.id, "uTime" );
}
#endif /* SHADER_sky_H */
uniform sampler2D uTexGarbage;
uniform sampler2D uTexGradients;
+uniform vec3 uCamera;
in vec4 aColour;
in vec2 aUv;
vec2 uvgradients = vec2( rgarbage.a, -amtgrass*0.125 ) + aUv;
vec3 diffuse = texture( uTexGradients, uvgradients ).rgb;
+ vec3 lightdir = vec3(0.95,0.0,-0.3);
vec3 shadow = pow(vec3(0.014,0.034,0.084),vec3(1.0/3.2));
- float light1 = 1.0-(dot( vec3(0.95,0.0,-0.3), qnorm )*0.5+0.5);
-
- FragColor = vec4(diffuse*(1.0-light1)+diffuse*shadow*light1, 1.0);
+ float light1 = 1.0-(dot( lightdir, qnorm )*0.5+0.5);
+
+ qnorm = floor(aNorm*8.0)*0.125;
+ vec3 viewdelta = normalize( uCamera - aCo );
+ vec3 specdir = reflect( -lightdir, qnorm );
+ float spec = pow(max(dot(viewdelta,specdir),0.0),10.0) * 0.2*rgarbage.r;
+
+ diffuse = diffuse*(1.0-light1)+diffuse*shadow*light1;
+ FragColor = vec4(diffuse+spec, 1.0);
}
"\n"
"uniform sampler2D uTexGarbage;\n"
"uniform sampler2D uTexGradients;\n"
+"uniform vec3 uCamera;\n"
"\n"
"in vec4 aColour;\n"
"in vec2 aUv;\n"
" vec2 uvgradients = vec2( rgarbage.a, -amtgrass*0.125 ) + aUv;\n"
" vec3 diffuse = texture( uTexGradients, uvgradients ).rgb;\n"
" \n"
+" vec3 lightdir = vec3(0.95,0.0,-0.3);\n"
" vec3 shadow = pow(vec3(0.014,0.034,0.084),vec3(1.0/3.2));\n"
-" float light1 = 1.0-(dot( vec3(0.95,0.0,-0.3), qnorm )*0.5+0.5);\n"
-"\n"
-" FragColor = vec4(diffuse*(1.0-light1)+diffuse*shadow*light1, 1.0);\n"
+" float light1 = 1.0-(dot( lightdir, qnorm )*0.5+0.5);\n"
+" \n"
+" qnorm = floor(aNorm*8.0)*0.125;\n"
+" vec3 viewdelta = normalize( uCamera - aCo );\n"
+" vec3 specdir = reflect( -lightdir, qnorm );\n"
+" float spec = pow(max(dot(viewdelta,specdir),0.0),10.0) * 0.2*rgarbage.r;\n"
+" \n"
+" diffuse = diffuse*(1.0-light1)+diffuse*shadow*light1;\n"
+" FragColor = vec4(diffuse+spec, 1.0);\n"
"}\n"
""},
};
static GLuint _uniform_terrain_uMdl;
static GLuint _uniform_terrain_uTexGarbage;
static GLuint _uniform_terrain_uTexGradients;
+static GLuint _uniform_terrain_uCamera;
static void shader_terrain_uPv(m4x4f m){
glUniformMatrix4fv( _uniform_terrain_uPv, 1, GL_FALSE, (float *)m );
}
static void shader_terrain_uTexGradients(int i){
glUniform1i( _uniform_terrain_uTexGradients, i );
}
+static void shader_terrain_uCamera(v3f v){
+ glUniform3fv( _uniform_terrain_uCamera, 1, v );
+}
static void shader_terrain_register(void){
vg_shader_register( &_shader_terrain );
}
_uniform_terrain_uMdl = glGetUniformLocation( _shader_terrain.id, "uMdl" );
_uniform_terrain_uTexGarbage = glGetUniformLocation( _shader_terrain.id, "uTexGarbage" );
_uniform_terrain_uTexGradients = glGetUniformLocation( _shader_terrain.id, "uTexGradients" );
+ _uniform_terrain_uCamera = glGetUniformLocation( _shader_terrain.id, "uCamera" );
}
#endif /* SHADER_terrain_H */
uniform sampler2D uTexMain;
uniform sampler2D uTexDudv;
+uniform sampler2D uTexDepth;
uniform vec2 uInvRes;
uniform float uTime;
-in vec2 aUv;
+in vec4 aUv;
+in vec3 aCo;
void main()
{
vec2 ssuv = gl_FragCoord.xy*uInvRes;
- vec4 dudva = texture( uTexDudv, aUv + vec2(uTime*0.04f,uTime*0.03f) );
- vec4 dudvb = texture( uTexDudv, aUv - vec2(uTime*0.1,uTime*0.054) );
+ vec4 dudva = texture( uTexDudv, aUv.xy + vec2(uTime*0.04f,uTime*0.03f) );
+ vec4 dudvb = texture( uTexDudv, aUv.xy - vec2(uTime*0.1,uTime*0.054) );
vec2 distortamt = (dudva.rg-0.5) * (dudvb.ba-0.5) * 2.0;
vec4 reflected = texture( uTexMain, ssuv+distortamt );
- FragColor = vec4(reflected.rgb*1.0,reflected.a);
+ float depthvalue = texture( uTexDepth, aUv.zw ).r;
+ float opacity = smoothstep( 0.0, 0.1, depthvalue );
+
+ vec3 colour_shore = vec3( 0.96, 0.98, 0.9 );
+ vec3 colour_ocean = vec3( 0.61, 0.84, 0.9 );
+ vec3 surface_tint = mix( colour_shore, colour_ocean, depthvalue );
+
+ vec3 comp_surf = mix(pow(surface_tint,vec3(6.0))*0.6,reflected.rgb, 0.7);
+
+ float band = fract( aCo.z*0.1+uTime*0.1-depthvalue*10.0 );
+ band = step( band + dudvb.g*0.8, 0.5 ) * max((1.0-depthvalue*4.0),0.0);
+
+ FragColor = vec4(comp_surf + band*0.2,opacity+band*0.2);
}
"\n"
"uniform mat4 uPv;\n"
"uniform mat4x3 uMdl;\n"
+"uniform vec4 uDepthBounds;\n"
"\n"
-"out vec2 aUv;\n"
+"out vec4 aUv;\n"
+"out vec3 aCo;\n"
"\n"
"void main()\n"
"{\n"
" vec3 world_pos = uMdl * vec4( a_co, 1.0 );\n"
" gl_Position = uPv * vec4(world_pos,1.0);\n"
-" aUv = vec2(world_pos[0],world_pos[2])*0.15;\n"
+"\n"
+" vec2 depth_coords = (world_pos.xz-uDepthBounds.xy)*uDepthBounds.zw;\n"
+" aUv = vec4(world_pos.xz*0.1,depth_coords);\n"
+" aCo = world_pos;\n"
"}\n"
""},
.fs =
"\n"
"uniform sampler2D uTexMain;\n"
"uniform sampler2D uTexDudv;\n"
+"uniform sampler2D uTexDepth;\n"
"uniform vec2 uInvRes;\n"
"uniform float uTime;\n"
"\n"
-"in vec2 aUv;\n"
+"in vec4 aUv;\n"
+"in vec3 aCo;\n"
"\n"
"void main()\n"
"{\n"
" vec2 ssuv = gl_FragCoord.xy*uInvRes;\n"
-" vec4 dudva = texture( uTexDudv, aUv + vec2(uTime*0.04f,uTime*0.03f) );\n"
-" vec4 dudvb = texture( uTexDudv, aUv - vec2(uTime*0.1,uTime*0.054) );\n"
+" vec4 dudva = texture( uTexDudv, aUv.xy + vec2(uTime*0.04f,uTime*0.03f) );\n"
+" vec4 dudvb = texture( uTexDudv, aUv.xy - vec2(uTime*0.1,uTime*0.054) );\n"
"\n"
" vec2 distortamt = (dudva.rg-0.5) * (dudvb.ba-0.5) * 2.0;\n"
"\n"
" vec4 reflected = texture( uTexMain, ssuv+distortamt );\n"
-" FragColor = vec4(reflected.rgb*1.0,reflected.a);\n"
+" float depthvalue = texture( uTexDepth, aUv.zw ).r;\n"
+" float opacity = smoothstep( 0.0, 0.1, depthvalue );\n"
+"\n"
+" vec3 colour_shore = vec3( 0.96, 0.98, 0.9 );\n"
+" vec3 colour_ocean = vec3( 0.61, 0.84, 0.9 );\n"
+" vec3 surface_tint = mix( colour_shore, colour_ocean, depthvalue );\n"
+"\n"
+" vec3 comp_surf = mix(pow(surface_tint,vec3(6.0))*0.6,reflected.rgb, 0.7);\n"
+" \n"
+" float band = fract( aCo.z*0.1+uTime*0.1-depthvalue*10.0 );\n"
+" band = step( band + dudvb.g*0.8, 0.5 ) * max((1.0-depthvalue*4.0),0.0);\n"
+"\n"
+" FragColor = vec4(comp_surf + band*0.2,opacity+band*0.2);\n"
"}\n"
""},
};
static GLuint _uniform_water_uPv;
static GLuint _uniform_water_uMdl;
+static GLuint _uniform_water_uDepthBounds;
static GLuint _uniform_water_uTexMain;
static GLuint _uniform_water_uTexDudv;
+static GLuint _uniform_water_uTexDepth;
static GLuint _uniform_water_uInvRes;
static GLuint _uniform_water_uTime;
static void shader_water_uPv(m4x4f m){
static void shader_water_uMdl(m4x3f m){
glUniformMatrix4x3fv( _uniform_water_uMdl, 1, GL_FALSE, (float *)m );
}
+static void shader_water_uDepthBounds(v4f v){
+ glUniform4fv( _uniform_water_uDepthBounds, 1, v );
+}
static void shader_water_uTexMain(int i){
glUniform1i( _uniform_water_uTexMain, i );
}
static void shader_water_uTexDudv(int i){
glUniform1i( _uniform_water_uTexDudv, i );
}
+static void shader_water_uTexDepth(int i){
+ glUniform1i( _uniform_water_uTexDepth, i );
+}
static void shader_water_uInvRes(v2f v){
glUniform2fv( _uniform_water_uInvRes, 1, v );
}
static void shader_water_link(void){
_uniform_water_uPv = glGetUniformLocation( _shader_water.id, "uPv" );
_uniform_water_uMdl = glGetUniformLocation( _shader_water.id, "uMdl" );
+ _uniform_water_uDepthBounds = glGetUniformLocation( _shader_water.id, "uDepthBounds" );
_uniform_water_uTexMain = glGetUniformLocation( _shader_water.id, "uTexMain" );
_uniform_water_uTexDudv = glGetUniformLocation( _shader_water.id, "uTexDudv" );
+ _uniform_water_uTexDepth = glGetUniformLocation( _shader_water.id, "uTexDepth" );
_uniform_water_uInvRes = glGetUniformLocation( _shader_water.id, "uInvRes" );
_uniform_water_uTime = glGetUniformLocation( _shader_water.id, "uTime" );
}
uniform mat4 uPv;
uniform mat4x3 uMdl;
+uniform vec4 uDepthBounds;
-out vec2 aUv;
+out vec4 aUv;
+out vec3 aCo;
void main()
{
vec3 world_pos = uMdl * vec4( a_co, 1.0 );
gl_Position = uPv * vec4(world_pos,1.0);
- aUv = vec2(world_pos[0],world_pos[2])*0.15;
+
+ vec2 depth_coords = (world_pos.xz-uDepthBounds.xy)*uDepthBounds.zw;
+ aUv = vec4(world_pos.xz*0.1,depth_coords);
+ aCo = world_pos;
}
free(msky);
}
-static void render_terrain(m4x4f projection)
+static void render_terrain(m4x4f projection, v3f camera)
{
shader_terrain_use();
shader_terrain_uTexGarbage(0);
m4x3_identity( identity_matrix );
shader_terrain_uPv( projection );
shader_terrain_uMdl( identity_matrix );
+ shader_terrain_uCamera( camera );
}
static void render_sky(m4x3f camera)
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);
#include "model.h"
#include "render.h"
#include "shaders/water.h"
+#include "scene.h"
vg_tex2d tex_water_surf = { .path = "textures/water_surf.qoi" };
GLuint fb, rgb, rb;
glmesh mdl;
+ GLuint depthmap;
+ boxf depthbounds;
+ int depth_computed;
+
float height;
}
wrender;
create_renderbuffer_std( &wrender.fb, &wrender.rgb, &wrender.rb );
}
+static int ray_world( v3f pos, v3f dir, ray_hit *hit );
+
+#ifndef VG_RELEASE
+__attribute__((minsize))
+#endif
+static void water_compute_depth( boxf bounds )
+{
+#ifdef VG_RELEASE
+ int const kres = 512;
+#else
+ int const kres = 64;
+#endif
+
+ vg_info( "Computing depth map\n" );
+ u8 *img = malloc( kres*kres );
+
+ v3f volume;
+ v3_sub( bounds[1], bounds[0], volume );
+ box_copy( bounds, wrender.depthbounds );
+
+ for( int y=0; y<kres; y++ )
+ {
+ for( int x=0; x<kres; x++ )
+ {
+ v3f pos = { x, 0, y };
+ v3_divs( pos, kres, pos );
+ v3_muladd( bounds[0], pos, volume, pos );
+ pos[1] = wrender.height;
+
+ ray_hit hit;
+ hit.dist = INFINITY;
+ u8 *dst = &img[ y*kres+x ];
+
+ if( ray_world( pos, (v3f){0.0f,-1.0f,0.0f}, &hit ))
+ {
+ float h = wrender.height - hit.pos[1];
+ h *= 1.0f/15.0f;
+ h = vg_clampf( h, 0.0f, 1.0f );
+ *dst = (u8)(h*255.0f);
+ }
+ else
+ *dst = 0;
+ }
+ }
+
+ if( wrender.depth_computed )
+ glDeleteTextures( 1, &wrender.depthmap );
+
+ glGenTextures( 1, &wrender.depthmap );
+ glBindTexture( GL_TEXTURE_2D, wrender.depthmap );
+ glTexImage2D( GL_TEXTURE_2D, 0, GL_RED, kres, kres, 0,
+ GL_RED, GL_UNSIGNED_BYTE, img );
+
+ vg_tex2d_mipmap();
+ vg_tex2d_linear_mipmap();
+ vg_tex2d_clamp();
+
+ wrender.depth_computed = 1;
+ free( img );
+ vg_success( "Done.\n" );
+}
+
static void water_set_surface( glmesh *surf, float height )
{
wrender.mdl = *surf;
1.0f / (float)vg_window_x,
1.0f / (float)vg_window_y });
+ glActiveTexture( GL_TEXTURE2 );
+ glBindTexture( GL_TEXTURE_2D, wrender.depthmap );
+ shader_water_uTexDepth( 2 );
+ shader_water_uDepthBounds( (v4f){
+ wrender.depthbounds[0][0],
+ wrender.depthbounds[0][2],
+ 1.0f/ (wrender.depthbounds[1][0]-wrender.depthbounds[0][0]),
+ 1.0f/ (wrender.depthbounds[1][2]-wrender.depthbounds[0][2])} );
+
shader_water_uTime( vg_time );
shader_water_uPv( pv );
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 */
m4x3f identity_matrix;
m4x3_identity( identity_matrix );
- render_terrain( projection );
+ render_terrain( projection, camera[3] );
scene_bind( &world.geo );
scene_draw( &world.geo );
glEnable(GL_CULL_FACE);
}
+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] );
+}
+
+__attribute__((always_inline))
static int ray_world( v3f pos, v3f dir, ray_hit *hit )
{
return bvh_raycast( &world.geo, pos, dir, hit );
return hit->tri[0] < world.sm_road.vertex_count;
}
-static void world_init_default(void)
+static void world_load(void)
{
/* Setup scene */
scene_init( &world.geo );
free( mworld );
scene_upload( &world.geo );
bvh_create( &world.geo );
-
-
+
+ water_compute_depth( world.geo.bbx );
scene_init( &world.foliage );
model *mfoliage = vg_asset_read("models/rs_foliage.mdl");
m4x3f transform;
+ submodel *sm_blob = submodel_get( mfoliage, "blob" ),
+ *sm_tree = submodel_get( mfoliage, "tree" );
+
for( int i=0;i<100000;i++ )
{
v3f pos;
if( ray_world( pos, (v3f){0.0f,-1.0f,0.0f}, &hit ))
{
- if( hit.normal[1] > 0.8f && !ray_hit_is_ramp(&hit) )
+ if( hit.normal[1] > 0.8f && !ray_hit_is_ramp(&hit) &&
+ hit.pos[1] > wrender.height )
{
v4f qsurface, qrandom;
v3f axis;
q_m3x3( qsurface, transform );
v3_copy( hit.pos, transform[3] );
-
- scene_add_foliage( &world.foliage, mfoliage,
- model_get_submodel( mfoliage, 0 ), transform );
+
+ if( vg_randf() < 0.00000006f )
+ {
+ m3x3_identity( transform );
+ scene_add_foliage( &world.foliage, mfoliage, sm_tree, transform );
+ }
+ else
+ scene_add_foliage( &world.foliage, mfoliage, sm_blob, transform );
}
}
}