2 #define VG_FRAMEBUFFER_RESIZE 1
6 vg_tex2d tex_norwey
= { .path
= "textures/norway_foliage.qoi" };
7 vg_tex2d tex_grid
= { .path
= "textures/grid.qoi" };
8 vg_tex2d tex_sky
= { .path
= "textures/sky.qoi" };
9 vg_tex2d tex_gradients
= { .path
= "textures/gradients.qoi",
10 .flags
= VG_TEXTURE_CLAMP
};
11 vg_tex2d tex_cement
= { .path
= "textures/cement512.qoi" };
12 vg_tex2d tex_pallet
= { .path
= "textures/ch_gradient.qoi" };
14 vg_tex2d
*texture_list
[] =
24 SHADER_DEFINE( shader_blit
,
25 "layout (location=0) in vec2 a_co;"
30 "gl_Position = vec4(a_co*2.0-1.0,0.0,1.0);"
37 "uniform sampler2D uTexMain;"
43 "FragColor = texture( uTexMain, aUv );"
46 UNIFORMS({ "uTexMain" })
50 static int freecam
= 0;
51 static int debugview
= 0;
52 static int debugsdf
= 0;
53 static int sv_debugcam
= 0;
54 static int sv_phys
= 0;
55 static int thirdperson
= 0;
56 static int clock_divider
= 1;
57 static int replay_record
= 0;
59 static m4x3f
*replay_buffer
= NULL
;
60 static int replay_buffer_frame
= 0;
62 #define REPLAY_LENGTH 120*60
68 #include "character.h"
71 #include "rigidbody.h"
74 int main( int argc
, char *argv
[] )
76 vg_init( argc
, argv
, "Voyager Game Engine" );
86 float vswitch
, slip
, slip_last
,
89 float iY
; /* Yaw inertia */
97 v2f look_dir
; /* TEMP */
103 v3f land_target_log
[22];
104 u32 land_target_colours
[22];
108 m4x3f to_world
, to_local
;
110 struct character mdl
;
112 v3f handl_target
, handr_target
,
120 submodel sm_road
, sm_terrain
;
126 static struct grender
128 GLuint fb_background
,
136 .bbx
= {{ -1.0f
, -0.25f
, -0.25f
}, { 1.0f
, 0.25f
, 0.25f
}}
139 rigidbody mrs_box
= {
140 .bbx
= {{ -0.5f
, -0.25f
, -0.25f
}, { 0.5f
, 0.25f
, 0.25f
}}
143 teleport_gate gate_a
= {
144 .co
= { 0.0f
, -3.0f
, -15.0f
},
145 .q
= { 0.0f
, 0.0f
, 0.0f
, 1.0f
}
148 .co
= { -8.0f
, -3.0f
, -17.0f
},
149 .q
= { 0.0f
, 0.0f
, 0.0f
, 1.0f
}
152 static void player_transform_update(void)
154 q_normalize( player
.rot
);
155 q_m3x3( player
.rot
, player
.to_world
);
156 v3_copy( player
.co
, player
.to_world
[3] );
158 m4x3_invert_affine( player
.to_world
, player
.to_local
);
161 static int reset_player( int argc
, char const *argv
[] )
163 v3_zero( player
.co
);
167 if( !strcmp( argv
[0], "tutorial" ))
168 v3_copy( world
.tutorial
, player
.co
);
171 v3_copy( (v3f
){ 0.0f
, 0.0f
, -0.2f
}, player
.v
);
172 q_identity( player
.rot
);
173 player
.vswitch
= 1.0f
;
174 player
.slip_last
= 0.0f
;
177 m3x3_identity( player
.vr
);
179 player
.mdl
.shoes
[0] = 1;
180 player
.mdl
.shoes
[1] = 1;
182 player_transform_update();
186 static int playermodel( int argc
, char const *argv
[] )
188 if( argc
< 1 ) return 0;
190 glmesh old_mesh
= player
.mdl
.mesh
;
192 if( character_load( &player
.mdl
, argv
[0] ) )
193 mesh_free( &old_mesh
);
198 void vg_register(void)
202 character_shader_register();
203 SHADER_INIT( shader_blit
);
208 replay_buffer
= malloc( sizeof(m4x3f
) * REPLAY_LENGTH
* (PART_COUNT
) );
210 vg_tex2d_init( texture_list
, vg_list_size( texture_list
) );
214 mrs_box
.co
[2] += 2.0f
;
216 vg_convar_push( (struct vg_convar
){
218 .data
= &replay_buffer_frame
,
219 .data_type
= k_convar_dtype_i32
,
220 .opt_i32
= { .min
=0, .max
=REPLAY_LENGTH
-1, .clamp
=1 },
224 vg_convar_push( (struct vg_convar
){
226 .data
= &replay_record
,
227 .data_type
= k_convar_dtype_i32
,
228 .opt_i32
= { .min
=0, .max
=1, .clamp
=1 },
232 vg_convar_push( (struct vg_convar
){
235 .data_type
= k_convar_dtype_i32
,
236 .opt_i32
= { .min
=0, .max
=1, .clamp
=1 },
240 vg_convar_push( (struct vg_convar
){
242 .data
= &sv_debugcam
,
243 .data_type
= k_convar_dtype_i32
,
244 .opt_i32
= { .min
=0, .max
=1, .clamp
=0 },
248 vg_convar_push( (struct vg_convar
){
251 .data_type
= k_convar_dtype_i32
,
252 .opt_i32
= { .min
=0, .max
=1, .clamp
=0 },
256 vg_convar_push( (struct vg_convar
){
259 .data_type
= k_convar_dtype_i32
,
260 .opt_i32
= { .min
=0, .max
=1, .clamp
=1 },
264 vg_convar_push( (struct vg_convar
){
267 .data_type
= k_convar_dtype_i32
,
268 .opt_i32
= { .min
=0, .max
=1, .clamp
=1 },
272 vg_convar_push( (struct vg_convar
){
273 .name
= "thirdperson",
274 .data
= &thirdperson
,
275 .data_type
= k_convar_dtype_i32
,
276 .opt_i32
= { .min
=0, .max
=1, .clamp
=1 },
280 vg_convar_push( (struct vg_convar
){
282 .data
= &clock_divider
,
283 .data_type
= k_convar_dtype_i32
,
284 .opt_i32
= { .min
=0, .max
=1, .clamp
=0 },
288 vg_function_push( (struct vg_cmd
){
290 .function
= reset_player
293 v3f lightDir
= { 0.1f
, 0.8f
, 0.2f
};
294 v3_normalize( lightDir
);
296 character_load( &player
.mdl
, "ch_default" );
297 character_init_ragdoll( &player
.mdl
);
300 scene_init( &world
.geo
);
301 model
*mworld
= vg_asset_read( "models/mp_dev.mdl" );
303 scene_add_model( &world
.geo
, mworld
, submodel_get( mworld
, "mp_dev" ),
304 (v3f
){0.0f
,0.0f
,0.0f
}, 0.0f
, 1.0f
);
305 scene_copy_slice( &world
.geo
, &world
.sm_road
);
307 scene_add_model( &world
.geo
, mworld
, submodel_get( mworld
, "terrain" ),
308 (v3f
){0.0f
,0.0f
,0.0f
}, 0.0f
, 1.0f
);
309 scene_copy_slice( &world
.geo
, &world
.sm_terrain
);
311 v3_copy( model_marker_get( mworld
, "mp_dev_tutorial" )->co
, world
.tutorial
);
316 model_marker
*ga
= model_marker_get(mworld
,"gate_a"),
317 *gb
= model_marker_get(mworld
,"gate_a_recv");
319 v3_copy( ga
->co
, gate_a
.co
);
320 v3_copy( gb
->co
, gate_b
.co
);
321 v4_copy( ga
->q
, gate_a
.q
);
322 v4_copy( gb
->q
, gate_b
.q
);
323 gate_a
.other
= &gate_b
;
324 gate_b
.other
= &gate_a
;
326 gate_transform_update( &gate_a
);
327 gate_transform_update( &gate_b
);
331 scene_upload( &world
.geo
);
332 bvh_create( &world
.geo
);
334 reset_player( 0, NULL
);
335 player_transform_update();
337 /* Create framebuffers */
338 glGenFramebuffers( 1, &render
.fb_background
);
339 glBindFramebuffer( GL_FRAMEBUFFER
, render
.fb_background
);
341 glGenTextures( 1, &render
.rgb_background
);
342 glBindTexture( GL_TEXTURE_2D
, render
.rgb_background
);
343 glTexImage2D( GL_TEXTURE_2D
, 0, GL_RGB
, vg_window_x
, vg_window_y
,
344 0, GL_RGB
, GL_UNSIGNED_BYTE
, NULL
);
346 glTexParameteri( GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, GL_LINEAR
);
347 glTexParameteri( GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, GL_LINEAR
);
348 glFramebufferTexture2D( GL_FRAMEBUFFER
, GL_COLOR_ATTACHMENT0
,
350 render
.rgb_background
, 0);
355 float quad
[] = { 0.0f
, 0.0f
, 1.0f
, 1.0f
, 0.0f
, 1.0f
,
356 0.0f
, 0.0f
, 1.0f
, 0.0f
, 1.0f
, 1.0f
};
358 glGenVertexArrays( 1, &render
.fsquad
.vao
);
359 glGenBuffers( 1, &render
.fsquad
.vbo
);
360 glBindVertexArray( render
.fsquad
.vao
);
361 glBindBuffer( GL_ARRAY_BUFFER
, render
.fsquad
.vbo
);
362 glBufferData( GL_ARRAY_BUFFER
, sizeof(quad
), quad
, GL_STATIC_DRAW
);
363 glBindVertexArray( render
.fsquad
.vao
);
364 glVertexAttribPointer( 0, 2, GL_FLOAT
, GL_FALSE
,
365 sizeof(float)*2, (void*)0 );
366 glEnableVertexAttribArray( 0 );
371 static float ktimestep
= 1.0f
/60.0f
;
373 static void player_freecam(void)
376 m4x3_identity( cam_rot
);
377 m4x3_rotate_y( cam_rot
, -player
.look_dir
[0] );
378 m4x3_rotate_x( cam_rot
, -player
.look_dir
[1] );
380 v3f lookdir
= { 0.0f
, 0.0f
, -1.0f
},
381 sidedir
= { 1.0f
, 0.0f
, 0.0f
};
383 m4x3_mulv( cam_rot
, lookdir
, lookdir
);
384 m4x3_mulv( cam_rot
, sidedir
, sidedir
);
386 float movespeed
= 5.0f
;
387 static v2f mouse_last
,
388 view_vel
= { 0.0f
, 0.0f
};
390 static v3f move_vel
= { 0.0f
, 0.0f
, 0.0f
};
392 if( vg_get_button_down( "primary" ) )
393 v2_copy( vg_mouse
, mouse_last
);
394 else if( vg_get_button( "primary" ) )
397 v2_sub( vg_mouse
, mouse_last
, delta
);
398 v2_copy( vg_mouse
, mouse_last
);
400 v2_muladds( view_vel
, delta
, 0.005f
, view_vel
);
403 v2_muls( view_vel
, 0.75f
, view_vel
);
404 v2_add( view_vel
, player
.look_dir
, player
.look_dir
);
406 vg_clampf( player
.look_dir
[1], -VG_PIf
*0.5f
, VG_PIf
*0.5f
);
408 if( vg_get_button( "forward" ) )
409 v3_muladds( move_vel
, lookdir
, ktimestep
* movespeed
, move_vel
);
410 if( vg_get_button( "back" ) )
411 v3_muladds( move_vel
, lookdir
, ktimestep
*-movespeed
, move_vel
);
412 if( vg_get_button( "left" ) )
413 v3_muladds( move_vel
, sidedir
, ktimestep
*-movespeed
, move_vel
);
414 if( vg_get_button( "right" ) )
415 v3_muladds( move_vel
, sidedir
, ktimestep
* movespeed
, move_vel
);
417 v3_muls( move_vel
, 0.75f
, move_vel
);
418 v3_add( move_vel
, player
.view
, player
.view
);
421 static void apply_gravity( v3f vel
, float const timestep
)
423 v3f gravity
= { 0.0f
, -9.6f
, 0.0f
};
424 v3_muladds( vel
, gravity
, timestep
, vel
);
427 static int ray_hit_is_ramp( ray_hit
*hit
)
429 return hit
->tri
[0] < world
.sm_road
.vertex_count
;
432 static void player_start_air(void)
436 float pstep
= ktimestep
*10.0f
;
438 float best_velocity_mod
= 0.0f
,
439 best_velocity_delta
= -9999.9f
;
442 m3x3_mulv( player
.to_world
, (v3f
){0.0f
,1.0f
,0.0f
}, vup
);
443 v3_cross( vup
, player
.v
, axis
);
444 v3_normalize( axis
);
445 player
.land_log_count
= 0;
447 m3x3_identity( player
.vr
);
449 for( int m
=-3;m
<=12; m
++ )
451 float vmod
= ((float)m
/ 15.0f
)*0.09f
;
454 v3_copy( player
.co
, pco
);
455 v3_copy( player
.v
, pv
);
456 v3_muladds( pco
, pv
, ktimestep
, pco
);
459 * Try different 'rotations' of the velocity to find the best possible
460 * landing normal. This conserves magnitude at the expense of slightly
461 * unrealistic results
467 q_axis_angle( vr_q
, axis
, vmod
);
470 for( int i
=0; i
<50; i
++ )
472 v3_copy( pco
, pco1
);
473 apply_gravity( pv
, pstep
);
475 m3x3_mulv( vr
, pv
, pv
);
476 v3_muladds( pco
, pv
, pstep
, pco
);
481 v3_sub( pco
, pco1
, vdir
);
482 contact
.dist
= v3_length( vdir
);
483 v3_divs( vdir
, contact
.dist
, vdir
);
485 if( bvh_raycast( &world
.geo
, pco1
, vdir
, &contact
))
487 float land_delta
= v3_dot( pv
, contact
.normal
);
488 u32 scolour
= (u8
)(vg_minf(-land_delta
* 2.0f
, 255.0f
));
490 /* Bias prediction towords ramps */
491 if( ray_hit_is_ramp( &contact
) )
494 scolour
|= 0x0000a000;
497 if( (land_delta
< 0.0f
) && (land_delta
> best_velocity_delta
) )
499 best_velocity_delta
= land_delta
;
500 best_velocity_mod
= vmod
;
502 v3_copy( contact
.pos
, player
.land_target
);
504 q_axis_angle( vr_q
, axis
, vmod
*0.1f
);
505 q_m3x3( vr_q
, player
.vr
);
508 v3_copy( contact
.pos
,
509 player
.land_target_log
[player
.land_log_count
] );
510 player
.land_target_colours
[player
.land_log_count
] =
511 0xff000000 | scolour
;
513 player
.land_log_count
++;
520 //v3_rotate( player.v, best_velocity_mod, axis, player.v );
523 v3_muls( player
.v
, best_velocity_mod
, player
.v
);
526 static int sample_if_resistant( v3f pos
)
529 v3_copy( pos
, ground
);
532 if( bvh_scene_sample( &world
.geo
, ground
, &hit
) )
535 v3_copy( player
.v
, angle
);
536 v3_normalize( angle
);
537 float resistance
= v3_dot( hit
.normal
, angle
);
539 if( resistance
< 0.25f
)
541 v3_copy( ground
, pos
);
549 static float stable_force( float current
, float diff
)
551 float new = current
+ diff
;
553 if( new * current
< 0.0f
)
559 static void player_physics_ground(void)
562 * Getting surface collision points,
563 * the contact manifold is a triangle for simplicity.
565 v3f contact_front
, contact_back
, contact_norm
, vup
, vside
,
568 float klength
= 0.65f
;
569 m4x3_mulv( player
.to_world
, (v3f
){ 0.15f
,0.0f
,-klength
}, contact_norm
);
570 m4x3_mulv( player
.to_world
, (v3f
){-0.15f
,0.0f
,-klength
}, contact_front
);
571 m4x3_mulv( player
.to_world
, (v3f
){ 0.00f
,0.0f
, klength
}, contact_back
);
572 m3x3_mulv( player
.to_world
, (v3f
){ 0.0f
, 1.0f
, 0.0f
}, vup
);
573 m3x3_mulv( player
.to_world
, (v3f
){ 1.0f
, 0.0f
, 0.0f
}, vside
);
578 sample_if_resistant( contact_front
) +
579 sample_if_resistant( contact_back
) +
580 sample_if_resistant( contact_norm
);
582 if( contact_count
< 3 )
590 v3_sub( contact_norm
, contact_front
, v0
);
591 v3_sub( contact_back
, contact_front
, v1
);
592 v3_cross( v1
, v0
, norm
);
593 v3_normalize( norm
);
595 vg_line( contact_norm
, contact_front
, 0xff00ff00 );
596 vg_line( contact_back
, contact_front
, 0xff0000ff );
598 /* Surface alignment */
599 float angle
= v3_dot( vup
, norm
);
600 v3_cross( vup
, norm
, axis
);
605 q_axis_angle( correction
, axis
, acosf(angle
) );
606 q_mul( correction
, player
.rot
, player
.rot
);
609 float resistance
= v3_dot( norm
, player
.v
);
610 if( resistance
>= 0.0f
)
617 v3_muladds( player
.v
, norm
, -resistance
, player
.v
);
620 /* This is where velocity integration used to be */
624 player
.co
[1] = (contact_front
[1]+contact_back
[1])*0.5f
;
627 m3x3_mulv( player
.to_local
, player
.v
, vel
);
629 /* Calculate local forces */
631 slip
= fabsf(-vel
[0] / vel
[2]) * vg_signf(vel
[0]);
632 if( fabsf( slip
) > 1.2f
)
633 slip
= vg_signf( slip
) * 1.2f
;
635 player
.reverse
= -vg_signf(vel
[2]);
637 float substep
= ktimestep
* 0.2f
;
638 float fwd_resistance
= (vg_get_button( "break" )? 5.0f
: 0.02f
) * -substep
;
640 for( int i
=0; i
<5; i
++ )
642 vel
[2] = stable_force( vel
[2], vg_signf( vel
[2] ) * fwd_resistance
);
643 vel
[0] = stable_force( vel
[0], vg_signf( vel
[0] ) * -7.0f
*substep
);
646 static double start_push
= 0.0;
647 if( vg_get_button_down( "push" ) )
648 start_push
= vg_time
;
650 if( !vg_get_button("break") && vg_get_button( "push" ) )
652 float const k_maxpush
= 16.0f
,
655 float cycle_time
= vg_time
-start_push
,
656 amt
= k_pushaccel
* (sinf( cycle_time
* 8.0f
)*0.5f
+0.5f
)*ktimestep
,
657 current
= v3_length( vel
),
658 new_vel
= vg_minf( current
+ amt
, k_maxpush
);
659 new_vel
-= vg_minf(current
, k_maxpush
);
660 vel
[2] -= new_vel
* player
.reverse
;
663 m3x3_mulv( player
.to_world
, vel
, player
.v
);
665 if( vg_get_button( "yawl" ) )
666 player
.iY
+= 3.6f
* ktimestep
;
667 if( vg_get_button( "yawr" ) )
668 player
.iY
-= 3.6f
* ktimestep
;
670 float steer
= vg_get_axis( "horizontal" );
671 player
.iY
-= vg_signf(steer
)*powf(steer
,2.0f
) * 1.5f
* ktimestep
;
673 /* Too much lean and it starts to look like a snowboard here */
674 v2_lerp( player
.board_xy
, (v2f
){ slip
*0.25f
, 0.0f
},
675 ktimestep
*5.0f
, player
.board_xy
);
678 static void draw_cross(v3f pos
,u32 colour
)
681 v3_add( (v3f
){ 1.0f
,0.0f
,0.0f
}, pos
, p0
);
682 v3_add( (v3f
){-1.0f
,0.0f
,0.0f
}, pos
, p1
);
683 vg_line( p0
, p1
, colour
);
684 v3_add( (v3f
){0.0f
, 1.0f
,0.0f
}, pos
, p0
);
685 v3_add( (v3f
){0.0f
,-1.0f
,0.0f
}, pos
, p1
);
686 vg_line( p0
, p1
, colour
);
687 v3_add( (v3f
){0.0f
,0.0f
, 1.0f
}, pos
, p0
);
688 v3_add( (v3f
){0.0f
,0.0f
,-1.0f
}, pos
, p1
);
689 vg_line( p0
, p1
, colour
);
692 static void player_physics_air(void)
694 /* Debug prediciton */
696 m3x3_mulv( player
.vr
, player
.v
, player
.v
);
697 for( int i
=0; i
<player
.land_log_count
; i
++ )
698 draw_cross( player
.land_target_log
[i
],
699 player
.land_target_colours
[i
] );
701 draw_cross( player
.land_target
, 0xff0000ff );
704 v3_copy( player
.co
, ground_pos
);
707 if( bvh_scene_sample( &world
.geo
, ground_pos
, &hit
) )
709 if( ground_pos
[1] > player
.co
[1] )
713 if( !ray_hit_is_ramp( &hit
) )
716 m4x3_mulv( player
.to_world
, player
.view
, player
.follow
);
717 character_ragdoll_copypose( &player
.mdl
, player
.v
);
726 * TODO: Find best landing surface and guide player towords it
728 float pstep
= ktimestep
*10.0f
;
731 v3_copy( player
.co
, pco
);
732 v3_copy( player
.v
, pv
);
734 float time_to_impact
= 0.0f
;
735 float limiter
= 1.0f
;
737 for( int i
=0; i
<50; i
++ )
739 v3_copy( pco
, pco1
);
740 apply_gravity( pv
, pstep
);
741 v3_muladds( pco
, pv
, pstep
, pco
);
743 //vg_line( pco, pco1, i&0x1?0xff000000:0xffffffff );
748 v3_sub( pco
, pco1
, vdir
);
749 contact
.dist
= v3_length( vdir
);
750 v3_divs( vdir
, contact
.dist
, vdir
);
752 float orig_dist
= contact
.dist
;
753 if( bvh_raycast( &world
.geo
, pco1
, vdir
, &contact
))
756 m3x3_mulv( player
.to_world
, (v3f
){0.0f
,1.0f
,0.0f
}, localup
);
758 float angle
= v3_dot( localup
, contact
.normal
);
760 v3_cross( localup
, contact
.normal
, axis
);
762 time_to_impact
+= (contact
.dist
/orig_dist
)*pstep
;
763 limiter
= vg_minf( 5.0f
, time_to_impact
)/5.0f
;
764 limiter
= 1.0f
-limiter
;
766 limiter
= 1.0f
-limiter
;
771 q_axis_angle( correction
, axis
, acosf(angle
)*0.05f
*(1.0f
-limiter
) );
772 q_mul( correction
, player
.rot
, player
.rot
);
775 draw_cross( contact
.pos
, 0xffff0000 );
778 time_to_impact
+= pstep
;
782 player
.iY
-= vg_get_axis( "horizontal" ) * 3.6f
* ktimestep
;
785 float iX
= vg_get_axis( "vertical" ) * 3.6f
* limiter
* ktimestep
;
786 static float siX
= 0.0f
;
787 siX
= vg_lerpf( siX
, iX
, 0.3f
);
792 m3x3_mulv( player
.to_world
, (v3f
){1.0f
,0.0f
,0.0f
}, vside
);
794 q_axis_angle( rotate
, vside
, siX
);
795 q_mul( rotate
, player
.rot
, player
.rot
);
798 v2f target
= {0.0f
,0.0f
};
799 v2_muladds( target
, (v2f
){ vg_get_axis("h1"), vg_get_axis("v1") },
800 player
.grab
, target
);
801 v2_lerp( player
.board_xy
, target
, ktimestep
*3.0f
, player
.board_xy
);
804 static void player_animate(void);
805 static void player_update(void)
808 if( vg_get_axis("grabl")>0.0f
)
809 reset_player(0,NULL
);
813 static int clock
= 0;
823 if( clock
>= clock_divider
)
827 float horizontal
= vg_get_axis("horizontal"),
828 vertical
= vg_get_axis("vertical");
830 player
.joy_l
[0] = vg_signf(horizontal
) * powf( horizontal
, 2.0f
);
831 player
.joy_l
[1] = vg_signf(vertical
) * powf( vertical
, 2.0f
);
834 player_physics_air();
837 player_physics_ground();
839 /* Integrate velocity */
841 v3_copy( player
.co
, prevco
);
844 apply_gravity( player
.v
, ktimestep
);
845 v3_muladds( player
.co
, player
.v
, ktimestep
, player
.co
);
848 /* Integrate inertia */
849 v4f rotate
; v3f vup
= {0.0f
,1.0f
,0.0f
};
850 m3x3_mulv( player
.to_world
, vup
, vup
);
852 static float siY
= 0.0f
;
854 float lerpq
= player
.in_air
? 0.04f
: 0.3f
;
855 siY
= vg_lerpf( siY
, player
.iY
, lerpq
);
857 q_axis_angle( rotate
, vup
, siY
);
858 q_mul( rotate
, player
.rot
, player
.rot
);
860 player
.look_dir
[0] = atan2f( player
.v
[0], -player
.v
[2] );
861 player
.look_dir
[1] = atan2f( -player
.v
[1], sqrtf(player
.v
[0]*player
.v
[0]+
862 player
.v
[2]*player
.v
[2]) ) * 0.3f
;
864 player
.iY
= 0.0f
; /* temp */
867 if( gate_intersect( &gate_a
, player
.co
, prevco
) )
869 teleport_gate
*gate
= &gate_a
;
872 m4x3_mul( gate
->other
->to_world
, gate
->to_local
, transport
);
873 m4x3_mulv( transport
, player
.co
, player
.co
);
874 m3x3_mulv( transport
, player
.v
, player
.v
);
875 m3x3_mulv( transport
, player
.v_last
, player
.v_last
);
877 v4f transport_rotation
;
878 m3x3_q( transport
, transport_rotation
);
879 q_mul( transport_rotation
, player
.rot
, player
.rot
);
882 /* Camera and character */
883 player_transform_update();
884 q_normalize(player
.rot
);
892 //rb_torque( &mr_box, (v3f){0.0f,0.0f,1.0f}, 0.01f );
894 if( glfwGetKey( vg_window
, GLFW_KEY_F
) )
895 character_ragdoll_go( &player
.mdl
, player
.view
);
897 if( glfwGetKey( vg_window
, GLFW_KEY_G
) )
900 m4x3_mulv( player
.to_world
, player
.view
, player
.follow
);
901 character_ragdoll_copypose( &player
.mdl
, player
.v
);
904 static int clock
= 0;
907 if( clock
>= clock_divider
)
909 character_debug_ragdoll( &player
.mdl
);
912 character_ragdoll_iter( &player
.mdl
, &world
.geo
);
914 rb_build_manifold( &mr_box
, &world
.geo
);
915 rb_build_manifold( &mrs_box
, &world
.geo
);
916 rb_constraint_manifold( &mr_box
);
917 rb_constraint_manifold( &mrs_box
);
922 rb_debug( &mr_box
, 0xffffffff );
923 rb_debug( &mrs_box
, 0xff00ff00 );
925 rb_update_transform( &mr_box
);
926 rb_update_transform( &mrs_box
);
933 static void player_animate(void)
935 /* Camera position */
936 static v3f momentum
, bob
;
938 v3_sub( player
.v
, player
.v_last
, player
.a
);
939 v3_copy( player
.v
, player
.v_last
);
941 v3_add( momentum
, player
.a
, momentum
);
942 v3_lerp( momentum
, (v3f
){0.0f
,0.0f
,0.0f
}, 0.1f
, momentum
);
945 momentum
[0] = vg_clampf( momentum
[0], -2.0f
, 2.0f
);
946 momentum
[1] = vg_clampf( momentum
[1], -0.2f
, 5.0f
);
947 momentum
[2] = vg_clampf( momentum
[2], -2.0f
, 2.0f
);
948 v3_copy( momentum
, target
);
949 v3_lerp( bob
, target
, 0.2f
, bob
);
952 float lslip
= fabsf(player
.slip
); //vg_minf( 0.4f, slip );
954 float grabt
= vg_get_axis( "grabr" )*0.5f
+0.5f
;
955 player
.grab
= vg_lerpf( player
.grab
, grabt
, 0.04f
);
957 float kheight
= 2.0f
,
962 head
[1] = (0.3f
+cosf(lslip
)*0.5f
*(1.0f
-player
.grab
*0.7f
)) * kheight
;
966 m3x3_mulv( player
.to_local
, bob
, offset
);
968 offset
[0] *= 0.3333f
;
971 v3_muladds( head
, offset
, 0.7f
, head
);
972 head
[1] = vg_clampf( head
[1], 0.3f
, kheight
);
977 v3_copy( head
, player
.view
);
978 v3f camoffs
= {-0.2f
,-0.6f
,0.00f
};
979 v3_add( player
.view
, camoffs
, player
.view
);
986 * ===========================================
989 static float fslide
= 0.0f
;
990 static float fdirz
= 0.0f
;
991 static float fdirx
= 0.0f
;
992 static float fstand
= 0.0f
;
993 static float ffly
= 0.0f
;
995 float speed
= v3_length( player
.v
);
997 fstand
= vg_lerpf(fstand
, 1.0f
-vg_clampf(speed
*0.03f
,0.0f
,1.0f
),0.1f
);
998 fslide
= vg_lerpf(fslide
, vg_clampf(lslip
+fabsf(offset
[0])*0.2f
,
1000 fdirz
= vg_lerpf(fdirz
, player
.reverse
> 0.0f
? 1.0f
: 0.0f
, 0.04f
);
1001 fdirx
= vg_lerpf(fdirx
, player
.slip
< 0.0f
? 1.0f
: 0.0f
, 0.04f
);
1002 ffly
= vg_lerpf(ffly
, player
.in_air
? 1.0f
: 0.0f
, 0.04f
);
1004 character_pose_reset( &player
.mdl
);
1006 float amt_air
= ffly
*ffly
,
1007 amt_ground
= 1.0f
-amt_air
,
1008 amt_std
= (1.0f
-fslide
) * amt_ground
,
1009 amt_stand
= amt_std
* fstand
,
1010 amt_aero
= amt_std
* (1.0f
-fstand
),
1011 amt_slide
= amt_ground
* fslide
;
1013 character_final_pose( &player
.mdl
, offset
, &pose_stand
, amt_stand
);
1014 character_final_pose( &player
.mdl
, offset
, &pose_aero
, amt_aero
*fdirz
);
1015 character_final_pose( &player
.mdl
, offset
,
1016 &pose_aero_reverse
, amt_aero
* (1.0f
-fdirz
) );
1017 character_final_pose( &player
.mdl
, offset
, &pose_slide
, amt_slide
*fdirx
);
1018 character_final_pose( &player
.mdl
, offset
,
1019 &pose_slide1
, amt_slide
*(1.0f
-fdirx
) );
1021 character_final_pose( &player
.mdl
, (v3f
){0.0f
,0.0f
,0.0f
},
1022 &pose_fly
, amt_air
);
1026 v3_copy( player
.mdl
.cam_pos
, player
.view
);
1027 v3_muladds( player
.view
, offset
, 0.7f
, player
.view
);
1028 player
.view
[1] = vg_clampf( player
.view
[1], 0.3f
, kheight
);
1033 * ==========================
1035 struct ik_basic
*arm_l
= &player
.mdl
.ik_arm_l
,
1036 *arm_r
= &player
.mdl
.ik_arm_r
;
1039 m3x3_mulv( player
.to_local
, player
.v
, localv
);
1040 v3_muladds( arm_l
->end
, localv
, -0.01f
, arm_l
->end
);
1041 v3_muladds( arm_r
->end
, localv
, -0.01f
, arm_r
->end
);
1043 /* New board transformation */
1044 v4f board_rotation
; v3f board_location
;
1047 q_axis_angle( rz
, (v3f
){ 0.0f
, 0.0f
, 1.0f
}, player
.board_xy
[0] );
1048 q_axis_angle( rx
, (v3f
){ 1.0f
, 0.0f
, 0.0f
}, player
.board_xy
[1] );
1049 q_mul( rx
, rz
, board_rotation
);
1051 v3f
*mboard
= player
.mdl
.matrices
[k_chpart_board
];// player.mboard;
1052 q_m3x3( board_rotation
, mboard
);
1053 m3x3_mulv( mboard
, (v3f
){ 0.0f
, -0.5f
, 0.0f
}, board_location
);
1054 v3_add( (v3f
){0.0f
,0.5f
,0.0f
}, board_location
, board_location
);
1055 v3_copy( board_location
, mboard
[3] );
1058 float wheel_r
= offset
[0]*-0.4f
;
1060 q_axis_angle( qwheel
, (v3f
){0.0f
,1.0f
,0.0f
}, wheel_r
);
1062 q_m3x3( qwheel
, player
.mdl
.matrices
[k_chpart_wb
] );
1064 m3x3_transpose( player
.mdl
.matrices
[k_chpart_wb
],
1065 player
.mdl
.matrices
[k_chpart_wf
] );
1066 v3_copy( player
.mdl
.offsets
[k_chpart_wb
],
1067 player
.mdl
.matrices
[k_chpart_wb
][3] );
1068 v3_copy( player
.mdl
.offsets
[k_chpart_wf
],
1069 player
.mdl
.matrices
[k_chpart_wf
][3] );
1071 m4x3_mul( mboard
, player
.mdl
.matrices
[k_chpart_wb
],
1072 player
.mdl
.matrices
[k_chpart_wb
] );
1073 m4x3_mul( mboard
, player
.mdl
.matrices
[k_chpart_wf
],
1074 player
.mdl
.matrices
[k_chpart_wf
] );
1076 m4x3_mulv( mboard
, player
.mdl
.ik_leg_l
.end
, player
.mdl
.ik_leg_l
.end
);
1077 m4x3_mulv( mboard
, player
.mdl
.ik_leg_r
.end
, player
.mdl
.ik_leg_r
.end
);
1080 v3_copy( player
.mdl
.ik_arm_l
.end
, player
.handl_target
);
1081 v3_copy( player
.mdl
.ik_arm_r
.end
, player
.handr_target
);
1083 if( 1||player
.in_air
)
1085 float tuck
= player
.board_xy
[1],
1086 tuck_amt
= fabsf( tuck
) * (1.0f
-fabsf(player
.board_xy
[0]));
1088 float crouch
= player
.grab
*0.3f
;
1089 v3_muladds( player
.mdl
.ik_body
.base
, (v3f
){0.0f
,-1.0f
,0.0f
},
1090 crouch
, player
.mdl
.ik_body
.base
);
1091 v3_muladds( player
.mdl
.ik_body
.end
, (v3f
){0.0f
,-1.0f
,0.0f
},
1092 crouch
*1.2f
, player
.mdl
.ik_body
.end
);
1096 //foot_l *= 1.0f-tuck_amt*1.5f;
1098 if( player
.grab
> 0.1f
)
1100 m4x3_mulv( mboard
, (v3f
){0.1f
,0.14f
,0.6f
},
1101 player
.handl_target
);
1106 //foot_r *= 1.0f-tuck_amt*1.4f;
1108 if( player
.grab
> 0.1f
)
1110 m4x3_mulv( mboard
, (v3f
){0.1f
,0.14f
,-0.6f
},
1111 player
.handr_target
);
1116 v3_lerp( player
.handl
, player
.handl_target
, 0.1f
, player
.handl
);
1117 v3_lerp( player
.handr
, player
.handr_target
, 0.1f
, player
.handr
);
1119 v3_copy( player
.handl
, player
.mdl
.ik_arm_l
.end
);
1120 v3_copy( player
.handr
, player
.mdl
.ik_arm_r
.end
);
1124 static float rhead
= 0.0f
;
1125 rhead
= vg_lerpf( rhead
,
1126 vg_clampf(atan2f( localv
[2], -localv
[0] ),-1.0f
,1.0f
), 0.04f
);
1127 player
.mdl
.rhead
= rhead
;
1130 static void draw_player(void)
1133 vg_tex2d_bind( &tex_pallet
, 0 );
1135 m4x3_copy( player
.to_world
, player
.mdl
.mroot
);
1137 if( player
.is_dead
)
1138 character_mimic_ragdoll( &player
.mdl
);
1140 character_eval( &player
.mdl
);
1142 character_draw( &player
.mdl
, (player
.is_dead
|player
.in_air
)? 0.0f
: 1.0f
);
1145 static void vg_framebuffer_resize( int w
, int h
)
1147 glBindTexture( GL_TEXTURE_2D
, render
.rgb_background
);
1148 glTexImage2D( GL_TEXTURE_2D
, 0, GL_RGB
, w
, h
, 0,
1149 GL_RGB
, GL_UNSIGNED_BYTE
, NULL
);
1152 static void render_world( m4x4f projection
)
1154 SHADER_USE(shader_standard_lit
);
1156 m4x3f identity_matrix
;
1157 m4x3_identity( identity_matrix
);
1159 glUniformMatrix4fv( SHADER_UNIFORM( shader_standard_lit
, "uPv" ),
1160 1, GL_FALSE
, (float *)projection
);
1161 glUniformMatrix4x3fv( SHADER_UNIFORM( shader_standard_lit
, "uMdl" ),
1162 1, GL_FALSE
, (float *)identity_matrix
);
1164 vg_tex2d_bind( &tex_grid
, 0 );
1165 glUniform1i( SHADER_UNIFORM( shader_standard_lit
, "uTexMain" ), 0 );
1166 glUniform4f( SHADER_UNIFORM(shader_standard_lit
,"uColour"),
1167 0.4f
,0.4f
,0.4f
,1.0f
);
1169 scene_bind( &world
.geo
);
1170 scene_draw( &world
.geo
);
1173 void vg_render(void)
1175 glBindFramebuffer( GL_FRAMEBUFFER
, 0 );
1176 glViewport( 0,0, vg_window_x
, vg_window_y
);
1178 glDisable( GL_DEPTH_TEST
);
1179 glClearColor( 0.1f
, 0.0f
, 0.2f
, 1.0f
);
1180 glClearColor(111.0f
/255.0f
, 46.0f
/255.0f
, 45.0f
/255.0f
,1.0f
);
1182 glClearColor( powf(0.066f
,1.0f
/2.2f
),
1183 powf(0.050f
,1.0f
/2.2f
),
1184 powf(0.046f
,1.0f
/2.2f
), 1.0f
);
1185 glClear( GL_COLOR_BUFFER_BIT
| GL_DEPTH_BUFFER_BIT
);
1188 static v3f cam_lerped
= {0.0f
,0.0f
,0.0f
};
1189 v3_lerp( cam_lerped
, player
.view
, 0.08f
, cam_lerped
);
1193 m4x3_mulv( player
.to_world
, cam_lerped
, pos_inv
);
1195 static float air_blend
= 0.0f
;
1197 air_blend
= vg_lerpf( air_blend
, player
.in_air
, 0.04f
);
1198 v3_muladds( pos_inv
, player
.v
, -0.05f
*air_blend
, pos_inv
);
1201 v3_add( player
.co
, player
.view
, pos_inv
);
1202 v3_negate( pos_inv
, pos_inv
);
1204 static float vertical_lerp
= 0.0f
;
1205 vertical_lerp
= vg_lerpf( vertical_lerp
, pos_inv
[1], 1.0f
);
1206 v3f final
= { pos_inv
[0], vertical_lerp
, pos_inv
[2] };
1208 float speed
= freecam
? 0.0f
: v3_length( player
.v
);
1209 v3f shake
= { vg_randf()-0.5f
, vg_randf()-0.5f
, vg_randf()-0.5f
};
1210 v3_muls( shake
, speed
*0.01f
, shake
);
1212 static v2f cam_lerped_dir
;
1214 m4x3_identity( world_matrix
);
1215 if( player
.is_dead
)
1218 v3_sub( player
.mdl
.ragdoll
[k_chpart_head
].co
, player
.follow
, delta
);
1219 v3_normalize(delta
);
1222 v3_muladds( player
.mdl
.ragdoll
[k_chpart_head
].co
, delta
,
1223 -1.5f
, follow_pos
);
1224 v3_lerp( player
.follow
, follow_pos
, 0.1f
, player
.follow
);
1225 v3_negate( player
.follow
, final
);
1228 float yaw
= atan2f( delta
[0], -delta
[2] );
1229 float pitch
= asinf( delta
[1] );
1230 m4x3_rotate_x( world_matrix
, -pitch
);
1231 m4x3_rotate_y( world_matrix
, yaw
);
1235 v2_lerp( cam_lerped_dir
, player
.look_dir
, 0.04f
, cam_lerped_dir
);
1237 m4x3_rotate_x( world_matrix
,
1240 0.6f
+shake
[1]*0.04f
+player
.look_dir
[1] );
1242 m4x3_rotate_y( world_matrix
,
1245 player
.look_dir
[0]+shake
[0]*0.02f
);
1249 m4x3_translate( world_matrix
, final
);
1252 m4x3_expand( world_matrix
, world_4x4
);
1254 float fov
= freecam
? 60.0f
: 120.0f
;
1255 m4x4_projection( vg_pv
, fov
, (float)vg_window_x
/ (float)vg_window_y
,
1258 m4x4_mul( vg_pv
, world_4x4
, vg_pv
);
1260 vg_line( (v3f
){ 0.0f
, 0.0f
, 0.0f
}, (v3f
){ 1.0f
, 0.0f
, 0.0f
}, 0xffff0000 );
1261 vg_line( (v3f
){ 0.0f
, 0.0f
, 0.0f
}, (v3f
){ 0.0f
, 1.0f
, 0.0f
}, 0xff00ff00 );
1262 vg_line( (v3f
){ 0.0f
, 0.0f
, 0.0f
}, (v3f
){ 0.0f
, 0.0f
, 1.0f
}, 0xff0000ff );
1264 glEnable( GL_DEPTH_TEST
);
1269 SHADER_USE(shader_standard_lit
);
1271 m4x3f identity_matrix
;
1272 m4x3_identity( identity_matrix
);
1274 glUniformMatrix4fv( SHADER_UNIFORM( shader_standard_lit
, "uPv" ),
1275 1, GL_FALSE
, (float *)vg_pv
);
1276 glUniformMatrix4x3fv( SHADER_UNIFORM( shader_standard_lit
, "uMdl" ),
1277 1, GL_FALSE
, (float *)identity_matrix
);
1279 vg_tex2d_bind( &tex_grid
, 0 );
1280 glUniform1i( SHADER_UNIFORM( shader_standard_lit
, "uTexMain" ), 0 );
1282 glUniform4f( SHADER_UNIFORM(shader_standard_lit
,"uColour"),
1283 0.4f
,0.4f
,0.4f
,1.0f
);
1285 scene_bind( &world
.geo
);
1286 scene_draw( &world
.geo
);
1289 if( !replay_record
)
1291 m4x3f
*base
= &replay_buffer
[(PART_COUNT
)*replay_buffer_frame
];
1293 for( int i
=0; i
<PART_COUNT
; i
++ )
1294 m4x3_copy( base
[i
], player
.mdl
.matrices
[i
] );
1296 replay_buffer_frame
++;
1298 if( replay_buffer_frame
== REPLAY_LENGTH
)
1299 replay_buffer_frame
= 0;
1301 vg_tex2d_bind( &tex_pallet
, 0 );
1302 character_draw( &player
.mdl
, 0.0f
);
1307 m4x3f cam_transform
;
1308 m4x3_invert_affine( world_matrix
, cam_transform
);
1309 render_gate( &gate_a
, cam_transform
, fov
);
1313 /* Copy the RGB of what we have into the background buffer */
1314 glBindFramebuffer( GL_READ_FRAMEBUFFER
, 0 );
1315 glBindFramebuffer( GL_DRAW_FRAMEBUFFER
, render
.fb_background
);
1316 glBlitFramebuffer( 0,0, vg_window_x
, vg_window_y
,
1317 0,0, vg_window_x
, vg_window_y
,
1318 GL_COLOR_BUFFER_BIT
,
1321 /* Clear out the colour buffer, but keep depth */
1322 glBindFramebuffer( GL_FRAMEBUFFER
, 0 );
1323 glClearColor( 0.0f
, 0.0f
, 0.0f
, 0.0f
);
1325 if( !player
.is_dead
)
1326 glClear( GL_COLOR_BUFFER_BIT
|GL_DEPTH_BUFFER_BIT
);
1328 glClear( GL_COLOR_BUFFER_BIT
);
1332 /* Draw back in the background */
1334 glDisable(GL_DEPTH_TEST
);
1335 glBlendFunc(GL_ONE_MINUS_DST_ALPHA
, GL_DST_ALPHA
);
1336 glBlendEquation(GL_FUNC_ADD
);
1338 SHADER_USE( shader_blit
);
1340 glUniform1i( SHADER_UNIFORM(shader_blit
,"uTexMain"), 0 );
1341 glActiveTexture(GL_TEXTURE0
);
1342 glBindTexture( GL_TEXTURE_2D
, render
.rgb_background
);
1344 glBindVertexArray( render
.fsquad
.vao
);
1345 glDrawArrays( GL_TRIANGLES
, 0, 6 );
1347 glDisable(GL_BLEND
);
1350 glDisable( GL_DEPTH_TEST
);
1351 vg_lines_drawall( (float *)vg_pv
);
1353 /* Debugger camera */
1354 glViewport( 0,0, 800, 800 );
1355 glClearColor( 0.1f
, 0.0f
, 0.2f
, 1.0f
);
1356 glClear( GL_DEPTH_BUFFER_BIT
);
1358 m4x3_identity( world_matrix
);
1361 v3_negate( player
.co
, debugcam
);
1362 debugcam
[2] -= 2.0f
;
1363 debugcam
[1] -= 0.7f
;
1365 m4x3_translate( world_matrix
, debugcam
);
1366 m4x3_expand( world_matrix
, world_4x4
);
1368 m4x4_projection( vg_pv
,
1370 (float)128.0f
/ (float)128.0f
,
1372 m4x4_mul( vg_pv
, world_4x4
, vg_pv
);
1376 glEnable( GL_DEPTH_TEST
);
1380 glDisable( GL_DEPTH_TEST
);
1381 vg_lines_drawall( (float *)vg_pv
);
1383 glViewport( 0,0, vg_window_x
, vg_window_y
);
1388 m4x3f
*base
= &replay_buffer
[(PART_COUNT
)*replay_buffer_frame
];
1390 for( int i
=0; i
<PART_COUNT
; i
++ )
1391 m4x3_copy( player
.mdl
.matrices
[i
], base
[i
] );
1393 replay_buffer_frame
++;
1395 if( replay_buffer_frame
== REPLAY_LENGTH
)
1396 replay_buffer_frame
= 0;
1405 snprintf( buf
, 20, "%.2fm/s", v3_length( player
.v
) );
1406 gui_text( (ui_px
[2]){ 0, 0 }, buf
, 1, k_text_align_left
);
1408 snprintf( buf
, 20, "%.2f %.2f %.2f m/s",
1409 player
.a
[0], player
.a
[1], player
.a
[2] );
1410 gui_text( (ui_px
[2]){ 0, 20 }, buf
, 1, k_text_align_left
);
1412 snprintf( buf
, 20, "pos %.2f %.2f %.2f",
1413 player
.co
[0], player
.co
[1], player
.co
[2] );
1414 gui_text( (ui_px
[2]){ 0, 40 }, buf
, 1, k_text_align_left
);
1416 if( vg_gamepad_ready
)
1418 for( int i
=0; i
<6; i
++ )
1420 snprintf( buf
, 20, "%.2f", vg_gamepad
.axes
[i
] );
1421 gui_text( (ui_px
[2]){ 0, (i
+3)*20 }, buf
, 1, k_text_align_left
);
1426 gui_text( (ui_px
[2]){ 0, 60 },
1427 "Gamepad not ready", 1, k_text_align_left
);
1431 void vg_free(void){}