i forgo
[carveJwlIkooP6JGAAIwe30JlM.git] / main.c
1 #define VG_3D
2 #define VG_FRAMEBUFFER_RESIZE 1
3 #include "vg/vg.h"
4
5 /* Resources */
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" };
13
14 vg_tex2d *texture_list[] =
15 {
16 &tex_norwey,
17 &tex_gradients,
18 &tex_grid,
19 &tex_sky,
20 &tex_cement,
21 &tex_pallet
22 };
23
24 SHADER_DEFINE( shader_blit,
25 "layout (location=0) in vec2 a_co;"
26 "out vec2 aUv;"
27 ""
28 "void main()"
29 "{"
30 "gl_Position = vec4(a_co*2.0-1.0,0.0,1.0);"
31 "aUv = a_co;"
32 "}",
33
34 /* Fragment */
35 "out vec4 FragColor;"
36 ""
37 "uniform sampler2D uTexMain;"
38 ""
39 "in vec2 aUv;"
40 ""
41 "void main()"
42 "{"
43 "FragColor = texture( uTexMain, aUv );"
44 "}"
45 ,
46 UNIFORMS({ "uTexMain" })
47 )
48
49 /* Convars */
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;
58
59 static m4x3f *replay_buffer = NULL;
60 static int replay_buffer_frame = 0;
61
62 #define REPLAY_LENGTH 120*60
63
64 /* Components */
65 #include "road.h"
66 #include "scene.h"
67 #include "ik.h"
68 #include "character.h"
69 #include "terrain.h"
70 #include "ragdoll.h"
71 #include "rigidbody.h"
72
73 int main( int argc, char *argv[] )
74 {
75 vg_init( argc, argv, "Voyager Game Engine" );
76 }
77
78 m4x3f world_matrix;
79
80 static struct gplayer
81 {
82 /* Physics */
83 v3f co, v, a;
84 v4f rot;
85 float vswitch, slip, slip_last,
86 reverse;
87
88 float iY; /* Yaw inertia */
89 int in_air, is_dead;
90
91 /* Input */
92 v2f joy_l;
93
94 v3f view;
95 v3f follow;
96 v2f look_dir; /* TEMP */
97 v2f board_xy;
98 float grab;
99 float pitch;
100
101 v3f land_target;
102 v3f land_target_log[22];
103 u32 land_target_colours[22];
104 int land_log_count;
105 m3x3f vr;
106
107 m4x3f to_world, to_local;
108
109 struct character mdl;
110
111 v3f handl_target, handr_target,
112 handl, handr;
113 }
114 player;
115
116 static struct gworld
117 {
118 scene geo;
119 submodel sm_road, sm_terrain;
120
121 v3f tutorial;
122 }
123 world;
124
125 static struct grender
126 {
127 GLuint fb_background,
128 rgb_background;
129
130 glmesh fsquad;
131 }
132 render;
133
134 rigidbody mr_box = {
135 .bbx = {{ -1.0f, -0.25f, -0.25f }, { 1.0f, 0.25f, 0.25f }}
136 };
137
138 rigidbody mrs_box = {
139 .bbx = {{ -0.5f, -0.25f, -0.25f }, { 0.5f, 0.25f, 0.25f }}
140 };
141
142 static void player_transform_update(void)
143 {
144 q_normalize( player.rot );
145 q_m3x3( player.rot, player.to_world );
146 v3_copy( player.co, player.to_world[3] );
147
148 m4x3_invert_affine( player.to_world, player.to_local );
149 }
150
151 static int reset_player( int argc, char const *argv[] )
152 {
153 v3_zero( player.co );
154
155 if( argc == 1 )
156 {
157 if( !strcmp( argv[0], "tutorial" ))
158 v3_copy( world.tutorial, player.co );
159 }
160
161 v3_copy( (v3f){ 0.0f, 0.0f, -0.2f }, player.v );
162 q_identity( player.rot );
163 player.vswitch = 1.0f;
164 player.slip_last = 0.0f;
165 player.is_dead = 0;
166 player.in_air = 1;
167 m3x3_identity( player.vr );
168
169 player.mdl.shoes[0] = 1;
170 player.mdl.shoes[1] = 1;
171
172 player_transform_update();
173 return 0;
174 }
175
176 static int playermodel( int argc, char const *argv[] )
177 {
178 if( argc < 1 ) return 0;
179
180 glmesh old_mesh = player.mdl.mesh;
181
182 if( character_load( &player.mdl, argv[0] ) )
183 mesh_free( &old_mesh );
184
185 return 1;
186 }
187
188 void vg_register(void)
189 {
190 scene_register();
191 character_shader_register();
192 SHADER_INIT( shader_blit );
193 }
194
195 void vg_start(void)
196 {
197 replay_buffer = malloc( sizeof(m4x3f) * REPLAY_LENGTH * (PART_COUNT) );
198
199 vg_tex2d_init( texture_list, vg_list_size( texture_list ) );
200
201 rb_init( &mr_box );
202 rb_init( &mrs_box );
203 mrs_box.co[2] += 2.0f;
204
205 vg_convar_push( (struct vg_convar){
206 .name = "frame",
207 .data = &replay_buffer_frame,
208 .data_type = k_convar_dtype_i32,
209 .opt_i32 = { .min=0, .max=REPLAY_LENGTH-1, .clamp=1 },
210 .persistent = 0
211 });
212
213 vg_convar_push( (struct vg_convar){
214 .name = "rec",
215 .data = &replay_record,
216 .data_type = k_convar_dtype_i32,
217 .opt_i32 = { .min=0, .max=1, .clamp=1 },
218 .persistent = 0
219 });
220
221 vg_convar_push( (struct vg_convar){
222 .name = "freecam",
223 .data = &freecam,
224 .data_type = k_convar_dtype_i32,
225 .opt_i32 = { .min=0, .max=1, .clamp=1 },
226 .persistent = 1
227 });
228
229 vg_convar_push( (struct vg_convar){
230 .name = "debugcam",
231 .data = &sv_debugcam,
232 .data_type = k_convar_dtype_i32,
233 .opt_i32 = { .min=0, .max=1, .clamp=0 },
234 .persistent = 1
235 });
236
237 vg_convar_push( (struct vg_convar){
238 .name = "debugview",
239 .data = &debugview,
240 .data_type = k_convar_dtype_i32,
241 .opt_i32 = { .min=0, .max=1, .clamp=0 },
242 .persistent = 1
243 });
244
245 vg_convar_push( (struct vg_convar){
246 .name = "debugsdf",
247 .data = &debugsdf,
248 .data_type = k_convar_dtype_i32,
249 .opt_i32 = { .min=0, .max=1, .clamp=1 },
250 .persistent = 1
251 });
252
253 vg_convar_push( (struct vg_convar){
254 .name = "phys",
255 .data = &sv_phys,
256 .data_type = k_convar_dtype_i32,
257 .opt_i32 = { .min=0, .max=1, .clamp=1 },
258 .persistent = 1
259 });
260
261 vg_convar_push( (struct vg_convar){
262 .name = "thirdperson",
263 .data = &thirdperson,
264 .data_type = k_convar_dtype_i32,
265 .opt_i32 = { .min=0, .max=1, .clamp=1 },
266 .persistent = 1
267 });
268
269 vg_convar_push( (struct vg_convar){
270 .name = "div",
271 .data = &clock_divider,
272 .data_type = k_convar_dtype_i32,
273 .opt_i32 = { .min=0, .max=1, .clamp=0 },
274 .persistent = 1
275 });
276
277 vg_function_push( (struct vg_cmd){
278 .name = "reset",
279 .function = reset_player
280 });
281
282 v3f lightDir = { 0.1f, 0.8f, 0.2f };
283 v3_normalize( lightDir );
284
285 character_load( &player.mdl, "ch_default" );
286 character_init_ragdoll( &player.mdl );
287
288 /* Setup scene */
289 scene_init( &world.geo );
290 model *mworld = vg_asset_read( "models/mp_dev.mdl" );
291
292 scene_add_model( &world.geo, mworld, submodel_get( mworld, "mp_dev" ),
293 (v3f){0.0f,0.0f,0.0f}, 0.0f, 1.0f );
294 scene_copy_slice( &world.geo, &world.sm_road );
295
296 scene_add_model( &world.geo, mworld, submodel_get( mworld, "terrain" ),
297 (v3f){0.0f,0.0f,0.0f}, 0.0f, 1.0f );
298 scene_copy_slice( &world.geo, &world.sm_terrain );
299
300 v3_copy( submodel_get( mworld, "tutorial" )->pivot, world.tutorial );
301
302 free( mworld );
303 scene_upload( &world.geo );
304 bvh_create( &world.geo );
305
306 reset_player( 0, NULL );
307 player_transform_update();
308
309 /* Create framebuffers */
310 glGenFramebuffers( 1, &render.fb_background );
311 glBindFramebuffer( GL_FRAMEBUFFER, render.fb_background );
312
313 glGenTextures( 1, &render.rgb_background );
314 glBindTexture( GL_TEXTURE_2D, render.rgb_background );
315 glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB, vg_window_x, vg_window_y,
316 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
317
318 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
319 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
320 glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
321 GL_TEXTURE_2D,
322 render.rgb_background, 0);
323
324 {
325 float quad[] = { 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f,
326 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f };
327
328 glGenVertexArrays( 1, &render.fsquad.vao );
329 glGenBuffers( 1, &render.fsquad.vbo );
330 glGenBuffers( 1, &render.fsquad.ebo );
331 glBindVertexArray( render.fsquad.vao );
332 glBindBuffer( GL_ARRAY_BUFFER, render.fsquad.vbo );
333 glBufferData( GL_ARRAY_BUFFER, sizeof(quad), quad, GL_STATIC_DRAW );
334 glBindVertexArray( render.fsquad.vao );
335 glVertexAttribPointer( 0, 2, GL_FLOAT, GL_FALSE,
336 sizeof(float)*2, (void*)0 );
337 glEnableVertexAttribArray( 0 );
338 VG_CHECK_GL();
339 }
340 }
341
342 static float ktimestep = 1.0f/60.0f;
343
344 static void player_freecam(void)
345 {
346 m4x3f cam_rot;
347 m4x3_identity( cam_rot );
348 m4x3_rotate_y( cam_rot, -player.look_dir[0] );
349 m4x3_rotate_x( cam_rot, -player.look_dir[1] );
350
351 v3f lookdir = { 0.0f, 0.0f, -1.0f },
352 sidedir = { 1.0f, 0.0f, 0.0f };
353
354 m4x3_mulv( cam_rot, lookdir, lookdir );
355 m4x3_mulv( cam_rot, sidedir, sidedir );
356
357 float movespeed = 5.0f;
358 static v2f mouse_last,
359 view_vel = { 0.0f, 0.0f };
360
361 static v3f move_vel = { 0.0f, 0.0f, 0.0f };
362
363 if( vg_get_button_down( "primary" ) )
364 v2_copy( vg_mouse, mouse_last );
365 else if( vg_get_button( "primary" ) )
366 {
367 v2f delta;
368 v2_sub( vg_mouse, mouse_last, delta );
369 v2_copy( vg_mouse, mouse_last );
370
371 v2_muladds( view_vel, delta, 0.005f, view_vel );
372 }
373
374 v2_muls( view_vel, 0.75f, view_vel );
375 v2_add( view_vel, player.look_dir, player.look_dir );
376 player.look_dir[1] =
377 vg_clampf( player.look_dir[1], -VG_PIf*0.5f, VG_PIf*0.5f );
378
379 if( vg_get_button( "forward" ) )
380 v3_muladds( move_vel, lookdir, ktimestep * movespeed, move_vel );
381 if( vg_get_button( "back" ) )
382 v3_muladds( move_vel, lookdir, ktimestep *-movespeed, move_vel );
383 if( vg_get_button( "left" ) )
384 v3_muladds( move_vel, sidedir, ktimestep *-movespeed, move_vel );
385 if( vg_get_button( "right" ) )
386 v3_muladds( move_vel, sidedir, ktimestep * movespeed, move_vel );
387
388 v3_muls( move_vel, 0.75f, move_vel );
389 v3_add( move_vel, player.view, player.view );
390 }
391
392 static void apply_gravity( v3f vel, float const timestep )
393 {
394 v3f gravity = { 0.0f, -9.6f, 0.0f };
395 v3_muladds( vel, gravity, timestep, vel );
396 }
397
398 static int ray_hit_is_ramp( ray_hit *hit )
399 {
400 return hit->tri[0] < world.sm_road.vertex_count;
401 }
402
403 static void player_start_air(void)
404 {
405 player.in_air = 1;
406
407 float pstep = ktimestep*10.0f;
408
409 float best_velocity_mod = 0.0f,
410 best_velocity_delta = -9999.9f;
411
412 v3f axis, vup;
413 m3x3_mulv( player.to_world, (v3f){0.0f,1.0f,0.0f}, vup );
414 v3_cross( vup, player.v, axis );
415 v3_normalize( axis );
416 player.land_log_count = 0;
417
418 m3x3_identity( player.vr );
419
420 for( int m=-3;m<=12; m++ )
421 {
422 float vmod = ((float)m / 15.0f)*0.09f;
423
424 v3f pco, pco1, pv;
425 v3_copy( player.co, pco );
426 v3_copy( player.v, pv );
427
428 /*
429 * Try different 'rotations' of the velocity to find the best possible
430 * landing normal. This conserves magnitude at the expense of slightly
431 * unrealistic results
432 */
433
434 m3x3f vr;
435 v4f vr_q;
436
437 q_axis_angle( vr_q, axis, vmod );
438 q_m3x3( vr_q, vr );
439
440 for( int i=0; i<50; i++ )
441 {
442 v3_copy( pco, pco1 );
443 apply_gravity( pv, pstep );
444
445 m3x3_mulv( vr, pv, pv );
446 v3_muladds( pco, pv, pstep, pco );
447
448 ray_hit contact;
449 v3f vdir;
450
451 v3_sub( pco, pco1, vdir );
452 contact.dist = v3_length( vdir );
453 v3_divs( vdir, contact.dist, vdir);
454
455 if( bvh_raycast( &world.geo, pco1, vdir, &contact ))
456 {
457 float land_delta = v3_dot( pv, contact.normal );
458 u32 scolour = (u8)(vg_minf(-land_delta * 2.0f, 255.0f));
459
460 /* Bias prediction towords ramps */
461 if( ray_hit_is_ramp( &contact ) )
462 {
463 land_delta *= 0.1f;
464 scolour |= 0x0000a000;
465 }
466
467 if( (land_delta < 0.0f) && (land_delta > best_velocity_delta) )
468 {
469 best_velocity_delta = land_delta;
470 best_velocity_mod = vmod;
471
472 v3_copy( contact.pos, player.land_target );
473
474 q_axis_angle( vr_q, axis, vmod*0.1f );
475 q_m3x3( vr_q, player.vr );
476 }
477
478 v3_copy( contact.pos,
479 player.land_target_log[player.land_log_count] );
480 player.land_target_colours[player.land_log_count] =
481 0xff000000 | scolour;
482
483 player.land_log_count ++;
484
485 break;
486 }
487 }
488 }
489
490 //v3_rotate( player.v, best_velocity_mod, axis, player.v );
491
492 return;
493 v3_muls( player.v, best_velocity_mod, player.v );
494 }
495
496 static int sample_if_resistant( v3f pos )
497 {
498 v3f ground;
499 v3_copy( pos, ground );
500
501 ray_hit hit;
502 if( bvh_scene_sample( &world.geo, ground, &hit ) )
503 {
504 v3f angle;
505 v3_copy( player.v, angle );
506 v3_normalize( angle );
507 float resistance = v3_dot( hit.normal, angle );
508
509 if( resistance < 0.25f )
510 {
511 v3_copy( ground, pos );
512 return 1;
513 }
514 }
515
516 return 0;
517 }
518
519 static float stable_force( float current, float diff )
520 {
521 float new = current + diff;
522
523 if( new * current < 0.0f )
524 return 0.0f;
525
526 return new;
527 }
528
529 static void player_physics_ground(void)
530 {
531 /*
532 * Getting surface collision points,
533 * the contact manifold is a triangle for simplicity.
534 */
535 v3f contact_front, contact_back, contact_norm, vup, vside,
536 axis;
537
538 float klength = 0.65f;
539 m4x3_mulv( player.to_world, (v3f){ 0.15f,0.0f,-klength}, contact_norm );
540 m4x3_mulv( player.to_world, (v3f){-0.15f,0.0f,-klength}, contact_front );
541 m4x3_mulv( player.to_world, (v3f){ 0.00f,0.0f, klength}, contact_back );
542 m3x3_mulv( player.to_world, (v3f){ 0.0f, 1.0f, 0.0f}, vup );
543 m3x3_mulv( player.to_world, (v3f){ 1.0f, 0.0f, 0.0f}, vside );
544
545 v3f cn0, cn1, cn2;
546
547 int contact_count =
548 sample_if_resistant( contact_front ) +
549 sample_if_resistant( contact_back ) +
550 sample_if_resistant( contact_norm );
551
552 if( contact_count < 3 )
553 {
554 player_start_air();
555 return;
556 }
557
558 v3f norm;
559 v3f v0, v1;
560 v3_sub( contact_norm, contact_front, v0 );
561 v3_sub( contact_back, contact_front, v1 );
562 v3_cross( v1, v0, norm );
563 v3_normalize( norm );
564
565 vg_line( contact_norm, contact_front, 0xff00ff00 );
566 vg_line( contact_back, contact_front, 0xff0000ff );
567
568 /* Surface alignment */
569 float angle = v3_dot( vup, norm );
570 v3_cross( vup, norm, axis );
571
572 if( angle < 0.999f )
573 {
574 v4f correction;
575 q_axis_angle( correction, axis, acosf(angle) );
576 q_mul( correction, player.rot, player.rot );
577 }
578
579 float resistance = v3_dot( norm, player.v );
580 if( resistance >= 0.0f )
581 {
582 player_start_air();
583 return;
584 }
585 else
586 {
587 v3_muladds( player.v, norm, -resistance, player.v );
588 }
589
590 /* This is where velocity integration used to be */
591
592 float slip = 0.0f;
593
594 player.co[1] = (contact_front[1]+contact_back[1])*0.5f;
595
596 v3f vel;
597 m3x3_mulv( player.to_local, player.v, vel );
598
599 /* Calculate local forces */
600
601 slip = fabsf(-vel[0] / vel[2]) * vg_signf(vel[0]);
602 if( fabsf( slip ) > 1.2f )
603 slip = vg_signf( slip ) * 1.2f;
604 player.slip = slip;
605 player.reverse = -vg_signf(vel[2]);
606
607 float substep = ktimestep * 0.2f;
608 float fwd_resistance = (vg_get_button( "break" )? 5.0f: 0.02f) * -substep;
609
610 for( int i=0; i<5; i++ )
611 {
612 vel[2] = stable_force( vel[2], vg_signf( vel[2] ) * fwd_resistance );
613 vel[0] = stable_force( vel[0], vg_signf( vel[0] ) * -7.0f *substep );
614 }
615
616 static double start_push = 0.0;
617 if( vg_get_button_down( "push" ) )
618 start_push = vg_time;
619
620 if( !vg_get_button("break") && vg_get_button( "push" ) )
621 {
622 float const k_maxpush = 16.0f,
623 k_pushaccel = 5.0f;
624
625 float cycle_time = vg_time-start_push,
626 amt = k_pushaccel * (sinf( cycle_time * 8.0f )*0.5f+0.5f)*ktimestep,
627 current = v3_length( vel ),
628 new_vel = vg_minf( current + amt, k_maxpush );
629 new_vel -= vg_minf(current, k_maxpush);
630 vel[2] -= new_vel * player.reverse;
631 }
632
633 m3x3_mulv( player.to_world, vel, player.v );
634
635 if( vg_get_button( "yawl" ) )
636 player.iY += 3.6f * ktimestep;
637 if( vg_get_button( "yawr" ) )
638 player.iY -= 3.6f * ktimestep;
639
640 float steer = vg_get_axis( "horizontal" );
641 player.iY -= vg_signf(steer)*powf(steer,2.0f) * 1.5f * ktimestep;
642
643 /* Too much lean and it starts to look like a snowboard here */
644 v2_lerp( player.board_xy, (v2f){ slip*0.25f, 0.0f },
645 ktimestep*5.0f, player.board_xy);
646 }
647
648 static void draw_cross(v3f pos,u32 colour)
649 {
650 v3f p0, p1;
651 v3_add( (v3f){ 1.0f,0.0f,0.0f}, pos, p0 );
652 v3_add( (v3f){-1.0f,0.0f,0.0f}, pos, p1 );
653 vg_line( p0, p1, colour );
654 v3_add( (v3f){0.0f, 1.0f,0.0f}, pos, p0 );
655 v3_add( (v3f){0.0f,-1.0f,0.0f}, pos, p1 );
656 vg_line( p0, p1, colour );
657 v3_add( (v3f){0.0f,0.0f, 1.0f}, pos, p0 );
658 v3_add( (v3f){0.0f,0.0f,-1.0f}, pos, p1 );
659 vg_line( p0, p1, colour );
660 }
661
662 static void player_physics_air(void)
663 {
664 /* Debug prediciton */
665
666 m3x3_mulv( player.vr, player.v, player.v );
667 for( int i=0; i<player.land_log_count; i++ )
668 draw_cross( player.land_target_log[i],
669 player.land_target_colours[i] );
670
671 draw_cross( player.land_target, 0xff0000ff );
672
673 v3f ground_pos;
674 v3_copy( player.co, ground_pos );
675
676 ray_hit hit;
677 if( bvh_scene_sample( &world.geo, ground_pos, &hit ) )
678 {
679 if( ground_pos[1] > player.co[1] )
680 {
681 player.in_air = 0;
682
683 if( !ray_hit_is_ramp( &hit ) )
684 {
685 player.is_dead = 1;
686 m4x3_mulv( player.to_world, player.view, player.follow );
687 character_ragdoll_copypose( &player.mdl, player.v );
688 }
689
690 return;
691 }
692 }
693
694 /* Prediction
695 *
696 * TODO: Find best landing surface and guide player towords it
697 */
698 float pstep = ktimestep*10.0f;
699
700 v3f pco, pco1, pv;
701 v3_copy( player.co, pco );
702 v3_copy( player.v, pv );
703
704 float time_to_impact = 0.0f;
705 float limiter = 1.0f;
706
707 for( int i=0; i<50; i++ )
708 {
709 v3_copy( pco, pco1 );
710 apply_gravity( pv, pstep );
711 v3_muladds( pco, pv, pstep, pco );
712
713 //vg_line( pco, pco1, i&0x1?0xff000000:0xffffffff );
714
715 ray_hit contact;
716 v3f vdir;
717
718 v3_sub( pco, pco1, vdir );
719 contact.dist = v3_length( vdir );
720 v3_divs( vdir, contact.dist, vdir);
721
722 float orig_dist = contact.dist;
723 if( bvh_raycast( &world.geo, pco1, vdir, &contact ))
724 {
725 v3f localup;
726 m3x3_mulv( player.to_world, (v3f){0.0f,1.0f,0.0f}, localup );
727
728 float angle = v3_dot( localup, contact.normal );
729 v3f axis;
730 v3_cross( localup, contact.normal, axis );
731
732 time_to_impact += (contact.dist/orig_dist)*pstep;
733 limiter = vg_minf( 5.0f, time_to_impact )/5.0f;
734 limiter = 1.0f-limiter;
735 limiter *= limiter;
736 limiter = 1.0f-limiter;
737
738 if( angle < 0.99f )
739 {
740 v4f correction;
741 q_axis_angle( correction, axis, acosf(angle)*0.05f*(1.0f-limiter) );
742 q_mul( correction, player.rot, player.rot );
743 }
744
745 draw_cross( contact.pos, 0xffff0000 );
746 break;
747 }
748 time_to_impact += pstep;
749 }
750
751
752 player.iY -= vg_get_axis( "horizontal" ) * 3.6f * ktimestep;
753 {
754
755 float iX = vg_get_axis( "vertical" ) * 3.6f * limiter * ktimestep;
756 static float siX = 0.0f;
757 siX = vg_lerpf( siX, iX, 0.3f );
758
759 v4f rotate;
760 v3f vside;
761
762 m3x3_mulv( player.to_world, (v3f){1.0f,0.0f,0.0f}, vside );
763
764 q_axis_angle( rotate, vside, siX );
765 q_mul( rotate, player.rot, player.rot );
766 }
767
768 v2f target = {0.0f,0.0f};
769 v2_muladds( target, (v2f){ vg_get_axis("h1"), vg_get_axis("v1") },
770 player.grab, target );
771 v2_lerp( player.board_xy, target, ktimestep*3.0f, player.board_xy );
772 }
773
774 static void player_animate(void);
775 static void player_update(void)
776 {
777
778 if( vg_get_axis("grabl")>0.0f)
779 reset_player(0,NULL);
780 if( player.is_dead )
781 return;
782
783 static int clock = 0;
784
785 /* temp */
786 if( freecam )
787 {
788 player_freecam();
789 return;
790 }
791
792 clock ++;
793 if( clock >= clock_divider )
794 clock = 0;
795 else
796 return;
797 float horizontal = vg_get_axis("horizontal"),
798 vertical = vg_get_axis("vertical");
799
800 player.joy_l[0] = vg_signf(horizontal) * powf( horizontal, 2.0f );
801 player.joy_l[1] = vg_signf(vertical) * powf( vertical, 2.0f );
802
803 /* Integrate velocity */
804 if( sv_phys )
805 {
806 apply_gravity( player.v, ktimestep );
807 v3_muladds( player.co, player.v, ktimestep, player.co );
808 }
809
810 /* Integrate inertia */
811 v4f rotate; v3f vup = {0.0f,1.0f,0.0f};
812 m3x3_mulv( player.to_world, vup, vup );
813
814 static float siY = 0.0f;
815
816 float lerpq = player.in_air? 0.04f: 0.3f;
817 siY = vg_lerpf( siY, player.iY, lerpq );
818
819 q_axis_angle( rotate, vup, siY );
820 q_mul( rotate, player.rot, player.rot );
821
822 player.look_dir[0] = atan2f( player.v[0], -player.v[2] );
823 player.look_dir[1] = atan2f( -player.v[1], sqrtf(player.v[0]*player.v[0]+
824 player.v[2]*player.v[2]) ) * 0.3f;
825
826 player.iY = 0.0f; /* temp */
827
828 if( player.in_air )
829 player_physics_air();
830
831 if( !player.in_air )
832 player_physics_ground();
833
834 /* Camera and character */
835
836 player_transform_update();
837 q_normalize(player.rot);
838 player_animate();
839 }
840
841 void vg_update(void)
842 {
843 player_update();
844
845 //rb_torque( &mr_box, (v3f){0.0f,0.0f,1.0f}, 0.01f );
846
847 if( glfwGetKey( vg_window, GLFW_KEY_F ) )
848 character_ragdoll_go( &player.mdl, player.view );
849
850 if( glfwGetKey( vg_window, GLFW_KEY_G ) )
851 {
852 player.is_dead = 1;
853 m4x3_mulv( player.to_world, player.view, player.follow );
854 character_ragdoll_copypose( &player.mdl, player.v );
855 }
856
857 static int clock = 0;
858
859 clock ++;
860 if( clock >= clock_divider )
861 {
862 character_debug_ragdoll( &player.mdl );
863
864 if( player.is_dead )
865 character_ragdoll_iter( &player.mdl, &world.geo );
866
867 rb_build_manifold( &mr_box, &world.geo );
868 rb_build_manifold( &mrs_box, &world.geo );
869 rb_constraint_manifold( &mr_box );
870 rb_constraint_manifold( &mrs_box );
871
872 rb_iter( &mr_box );
873 rb_iter( &mrs_box );
874
875 rb_debug( &mr_box, 0xffffffff );
876 rb_debug( &mrs_box, 0xff00ff00 );
877
878 rb_update_transform( &mr_box );
879 rb_update_transform( &mrs_box );
880
881 clock = 0;
882 }
883
884 }
885
886 static void player_animate(void)
887 {
888 /* Camera position */
889 static v3f last_vel = { 0.0f, 0.0f, 0.0f };
890 static v3f momentum, bob;
891
892 v3_sub( player.v, last_vel, player.a );
893 v3_copy( player.v, last_vel );
894
895 v3_add( momentum, player.a, momentum );
896 v3_lerp( momentum, (v3f){0.0f,0.0f,0.0f}, 0.1f, momentum );
897 v3f target;
898
899 momentum[0] = vg_clampf( momentum[0], -2.0f, 2.0f );
900 momentum[1] = vg_clampf( momentum[1], -0.2f, 5.0f );
901 momentum[2] = vg_clampf( momentum[2], -2.0f, 2.0f );
902 v3_copy( momentum, target );
903 v3_lerp( bob, target, 0.2f, bob );
904
905 /* Head */
906 float lslip = fabsf(player.slip); //vg_minf( 0.4f, slip );
907
908 float grabt = vg_get_axis( "grabr" )*0.5f+0.5f;
909 player.grab = vg_lerpf( player.grab, grabt, 0.04f );
910
911 float kheight = 2.0f,
912 kleg = 0.6f;
913
914 v3f head;
915 head[0] = 0.0f;
916 head[1] = (0.3f+cosf(lslip)*0.5f*(1.0f-player.grab*0.7f)) * kheight;
917 head[2] = 0.0f;
918
919 v3f offset;
920 m3x3_mulv( player.to_local, bob, offset );
921
922 offset[0] *= 0.3333f;
923 offset[1] *= -0.25f;
924 offset[2] *= 0.7f;
925 v3_muladds( head, offset, 0.7f, head );
926 head[1] = vg_clampf( head[1], 0.3f, kheight );
927
928 #if 0
929 if( !freecam )
930 {
931 v3_copy( head, player.view );
932 v3f camoffs = {-0.2f,-0.6f,0.00f};
933 v3_add( player.view, camoffs, player.view );
934 }
935 #endif
936
937
938 /*
939 * Animation blending
940 * ===========================================
941 */
942
943 static float fslide = 0.0f;
944 static float fdirz = 0.0f;
945 static float fdirx = 0.0f;
946 static float fstand = 0.0f;
947 static float ffly = 0.0f;
948
949 float speed = v3_length( player.v );
950
951 fstand = vg_lerpf(fstand, 1.0f-vg_clampf(speed*0.03f,0.0f,1.0f),0.1f);
952 fslide = vg_lerpf(fslide, vg_clampf(lslip+fabsf(offset[0])*0.2f,
953 0.0f,1.0f), 0.04f);
954 fdirz = vg_lerpf(fdirz, player.reverse > 0.0f? 1.0f: 0.0f, 0.04f );
955 fdirx = vg_lerpf(fdirx, player.slip < 0.0f? 1.0f: 0.0f, 0.04f );
956 ffly = vg_lerpf(ffly, player.in_air? 1.0f: 0.0f, 0.04f );
957
958 character_pose_reset( &player.mdl );
959
960 float amt_air = ffly*ffly,
961 amt_ground = 1.0f-amt_air,
962 amt_std = (1.0f-fslide) * amt_ground,
963 amt_stand = amt_std * fstand,
964 amt_aero = amt_std * (1.0f-fstand),
965 amt_slide = amt_ground * fslide;
966
967 character_final_pose( &player.mdl, offset, &pose_stand, amt_stand );
968 character_final_pose( &player.mdl, offset, &pose_aero, amt_aero*fdirz );
969 character_final_pose( &player.mdl, offset,
970 &pose_aero_reverse, amt_aero * (1.0f-fdirz) );
971 character_final_pose( &player.mdl, offset, &pose_slide, amt_slide*fdirx );
972 character_final_pose( &player.mdl, offset,
973 &pose_slide1, amt_slide*(1.0f-fdirx) );
974
975 character_final_pose( &player.mdl, (v3f){0.0f,0.0f,0.0f},
976 &pose_fly, amt_air );
977
978 if( !freecam )
979 {
980 v3_copy( player.mdl.cam_pos, player.view );
981 v3_muladds( player.view, offset, 0.7f, player.view );
982 player.view[1] = vg_clampf( player.view[1], 0.3f, kheight );
983 }
984
985 /*
986 * Additive effects
987 * ==========================
988 */
989 struct ik_basic *arm_l = &player.mdl.ik_arm_l,
990 *arm_r = &player.mdl.ik_arm_r;
991
992 v3f localv;
993 m3x3_mulv( player.to_local, player.v, localv );
994 v3_muladds( arm_l->end, localv, -0.01f, arm_l->end );
995 v3_muladds( arm_r->end, localv, -0.01f, arm_r->end );
996
997 /* New board transformation */
998 v4f board_rotation; v3f board_location;
999
1000 v4f rz, rx;
1001 q_axis_angle( rz, (v3f){ 0.0f, 0.0f, 1.0f }, player.board_xy[0] );
1002 q_axis_angle( rx, (v3f){ 1.0f, 0.0f, 0.0f }, player.board_xy[1] );
1003 q_mul( rx, rz, board_rotation );
1004
1005 v3f *mboard = player.mdl.matrices[k_chpart_board];// player.mboard;
1006 q_m3x3( board_rotation, mboard );
1007 m3x3_mulv( mboard, (v3f){ 0.0f, -0.5f, 0.0f }, board_location );
1008 v3_add( (v3f){0.0f,0.5f,0.0f}, board_location, board_location );
1009 v3_copy( board_location, mboard[3] );
1010
1011
1012 float wheel_r = offset[0]*-0.4f;
1013 v4f qwheel;
1014 q_axis_angle( qwheel, (v3f){0.0f,1.0f,0.0f}, wheel_r );
1015
1016 q_m3x3( qwheel, player.mdl.matrices[k_chpart_wb] );
1017
1018 m3x3_transpose( player.mdl.matrices[k_chpart_wb],
1019 player.mdl.matrices[k_chpart_wf] );
1020 v3_copy( player.mdl.offsets[k_chpart_wb],
1021 player.mdl.matrices[k_chpart_wb][3] );
1022 v3_copy( player.mdl.offsets[k_chpart_wf],
1023 player.mdl.matrices[k_chpart_wf][3] );
1024
1025 m4x3_mul( mboard, player.mdl.matrices[k_chpart_wb],
1026 player.mdl.matrices[k_chpart_wb] );
1027 m4x3_mul( mboard, player.mdl.matrices[k_chpart_wf],
1028 player.mdl.matrices[k_chpart_wf] );
1029
1030 m4x3_mulv( mboard, player.mdl.ik_leg_l.end, player.mdl.ik_leg_l.end );
1031 m4x3_mulv( mboard, player.mdl.ik_leg_r.end, player.mdl.ik_leg_r.end );
1032
1033
1034 v3_copy( player.mdl.ik_arm_l.end, player.handl_target );
1035 v3_copy( player.mdl.ik_arm_r.end, player.handr_target );
1036
1037 if( 1||player.in_air )
1038 {
1039 float tuck = player.board_xy[1],
1040 tuck_amt = fabsf( tuck ) * (1.0f-fabsf(player.board_xy[0]));
1041
1042 float crouch = player.grab*0.3f;
1043 v3_muladds( player.mdl.ik_body.base, (v3f){0.0f,-1.0f,0.0f},
1044 crouch, player.mdl.ik_body.base );
1045 v3_muladds( player.mdl.ik_body.end, (v3f){0.0f,-1.0f,0.0f},
1046 crouch*1.2f, player.mdl.ik_body.end );
1047
1048 if( tuck < 0.0f )
1049 {
1050 //foot_l *= 1.0f-tuck_amt*1.5f;
1051
1052 if( player.grab > 0.1f )
1053 {
1054 m4x3_mulv( mboard, (v3f){0.1f,0.14f,0.6f},
1055 player.handl_target );
1056 }
1057 }
1058 else
1059 {
1060 //foot_r *= 1.0f-tuck_amt*1.4f;
1061
1062 if( player.grab > 0.1f )
1063 {
1064 m4x3_mulv( mboard, (v3f){0.1f,0.14f,-0.6f},
1065 player.handr_target );
1066 }
1067 }
1068 }
1069
1070 v3_lerp( player.handl, player.handl_target, 0.1f, player.handl );
1071 v3_lerp( player.handr, player.handr_target, 0.1f, player.handr );
1072
1073 v3_copy( player.handl, player.mdl.ik_arm_l.end );
1074 v3_copy( player.handr, player.mdl.ik_arm_r.end );
1075
1076 /* Head rotation */
1077
1078 static float rhead = 0.0f;
1079 rhead = vg_lerpf( rhead,
1080 vg_clampf(atan2f( localv[2], -localv[0] ),-1.0f,1.0f), 0.04f );
1081 player.mdl.rhead = rhead;
1082 }
1083
1084 static void draw_player(void)
1085 {
1086 /* Draw */
1087 vg_tex2d_bind( &tex_pallet, 0 );
1088
1089 m4x3_copy( player.to_world, player.mdl.mroot );
1090
1091 if( player.is_dead )
1092 character_mimic_ragdoll( &player.mdl );
1093 else
1094 character_eval( &player.mdl );
1095
1096 character_draw( &player.mdl, (player.is_dead|player.in_air)? 0.0f: 1.0f );
1097 }
1098
1099 static void vg_framebuffer_resize( int w, int h )
1100 {
1101 glBindTexture( GL_TEXTURE_2D, render.rgb_background );
1102 glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB, w, h, 0,
1103 GL_RGB, GL_UNSIGNED_BYTE, NULL );
1104 }
1105
1106 void vg_render(void)
1107 {
1108 glBindFramebuffer( GL_FRAMEBUFFER, 0 );
1109 glViewport( 0,0, vg_window_x, vg_window_y );
1110
1111 glDisable( GL_DEPTH_TEST );
1112 glClearColor( 0.1f, 0.0f, 0.2f, 1.0f );
1113 glClearColor(111.0f/255.0f, 46.0f/255.0f, 45.0f/255.0f,1.0f);
1114
1115 glClearColor( powf(0.066f,1.0f/2.2f),
1116 powf(0.050f,1.0f/2.2f),
1117 powf(0.046f,1.0f/2.2f), 1.0f );
1118 glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
1119
1120 v3f pos_inv;
1121 static v3f cam_lerped = {0.0f,0.0f,0.0f};
1122 v3_lerp( cam_lerped, player.view, 0.08f, cam_lerped );
1123
1124 if( !freecam )
1125 {
1126 m4x3_mulv( player.to_world, cam_lerped, pos_inv );
1127
1128 static float air_blend = 0.0f;
1129
1130 air_blend = vg_lerpf( air_blend, player.in_air, 0.04f );
1131 v3_muladds( pos_inv, player.v, -0.05f*air_blend, pos_inv );
1132 }
1133 else
1134 v3_add( player.co, player.view, pos_inv );
1135 v3_negate( pos_inv, pos_inv );
1136
1137 static float vertical_lerp = 0.0f;
1138 vertical_lerp = vg_lerpf( vertical_lerp, pos_inv[1], 1.0f );
1139 v3f final = { pos_inv[0], vertical_lerp, pos_inv[2] };
1140
1141 float speed = freecam? 0.0f: v3_length( player.v );
1142 v3f shake = { vg_randf()-0.5f, vg_randf()-0.5f, vg_randf()-0.5f };
1143 v3_muls( shake, speed*0.01f, shake );
1144
1145 static v2f cam_lerped_dir;
1146
1147 m4x3_identity( world_matrix );
1148 if( player.is_dead )
1149 {
1150 v3f delta;
1151 v3_sub( player.mdl.ragdoll[k_chpart_head].co, player.follow, delta );
1152 v3_normalize(delta);
1153
1154 v3f follow_pos;
1155 v3_muladds( player.mdl.ragdoll[k_chpart_head].co, delta,
1156 -1.5f, follow_pos );
1157 v3_lerp( player.follow, follow_pos, 0.1f, player.follow );
1158 v3_negate( player.follow, final );
1159
1160
1161 float yaw = atan2f( delta[0], -delta[2] );
1162 float pitch = asinf( delta[1] );
1163 m4x3_rotate_x( world_matrix, -pitch );
1164 m4x3_rotate_y( world_matrix, yaw );
1165 }
1166 else
1167 {
1168 v2_lerp( cam_lerped_dir, player.look_dir, 0.04f, cam_lerped_dir );
1169
1170 m4x3_rotate_x( world_matrix,
1171 freecam?
1172 cam_lerped_dir[1]:
1173 0.6f+shake[1]*0.04f+player.look_dir[1] );
1174
1175 m4x3_rotate_y( world_matrix,
1176 freecam?
1177 cam_lerped_dir[0]:
1178 player.look_dir[0]+shake[0]*0.02f );
1179 }
1180
1181
1182 m4x3_translate( world_matrix, final );
1183
1184 m4x4f world_4x4;
1185 m4x3_expand( world_matrix, world_4x4 );
1186 m4x4_projection( vg_pv,
1187 freecam? 60.0f: 120.0f,
1188 (float)vg_window_x / (float)vg_window_y,
1189 0.01f, 1000.0f );
1190 m4x4_mul( vg_pv, world_4x4, vg_pv );
1191
1192 vg_line( (v3f){ 0.0f, 0.0f, 0.0f }, (v3f){ 1.0f, 0.0f, 0.0f }, 0xffff0000 );
1193 vg_line( (v3f){ 0.0f, 0.0f, 0.0f }, (v3f){ 0.0f, 1.0f, 0.0f }, 0xff00ff00 );
1194 vg_line( (v3f){ 0.0f, 0.0f, 0.0f }, (v3f){ 0.0f, 0.0f, 1.0f }, 0xff0000ff );
1195
1196 glEnable( GL_DEPTH_TEST );
1197
1198 /*
1199 * Draw world
1200 */
1201 SHADER_USE(shader_standard_lit);
1202
1203 m4x3f identity_matrix;
1204 m4x3_identity( identity_matrix );
1205
1206 glUniformMatrix4fv( SHADER_UNIFORM( shader_standard_lit, "uPv" ),
1207 1, GL_FALSE, (float *)vg_pv );
1208 glUniformMatrix4x3fv( SHADER_UNIFORM( shader_standard_lit, "uMdl" ),
1209 1, GL_FALSE, (float *)identity_matrix );
1210
1211 vg_tex2d_bind( &tex_grid, 0 );
1212 glUniform1i( SHADER_UNIFORM( shader_standard_lit, "uTexMain" ), 0 );
1213
1214 glUniform4f( SHADER_UNIFORM(shader_standard_lit,"uColour"),
1215 0.4f,0.4f,0.4f,1.0f );
1216
1217 scene_bind( &world.geo );
1218 scene_draw( &world.geo );
1219
1220 #if 0
1221 if( !replay_record )
1222 {
1223 m4x3f *base = &replay_buffer[(PART_COUNT)*replay_buffer_frame];
1224
1225 for( int i=0; i<PART_COUNT; i++ )
1226 m4x3_copy( base[i], player.mdl.matrices[i] );
1227
1228 replay_buffer_frame ++;
1229
1230 if( replay_buffer_frame == REPLAY_LENGTH )
1231 replay_buffer_frame = 0;
1232
1233 vg_tex2d_bind( &tex_pallet, 0 );
1234 character_draw( &player.mdl, 0.0f );
1235 player_animate();
1236 }
1237 #endif
1238
1239
1240
1241 /* Copy the RGB of what we have into the background buffer */
1242 glBindFramebuffer( GL_READ_FRAMEBUFFER, 0 );
1243 glBindFramebuffer( GL_DRAW_FRAMEBUFFER, render.fb_background );
1244 glBlitFramebuffer( 0,0, vg_window_x, vg_window_y,
1245 0,0, vg_window_x, vg_window_y,
1246 GL_COLOR_BUFFER_BIT,
1247 GL_LINEAR );
1248
1249 /* Clear out the colour buffer, but keep depth */
1250 glBindFramebuffer( GL_FRAMEBUFFER, 0 );
1251 glClearColor( 0.0f, 0.0f, 0.0f, 0.0f );
1252
1253 if( !player.is_dead )
1254 glClear( GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT );
1255 else
1256 glClear( GL_COLOR_BUFFER_BIT );
1257
1258 draw_player();
1259
1260 /* Draw back in the background */
1261 glEnable(GL_BLEND);
1262 glDisable(GL_DEPTH_TEST);
1263 glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_DST_ALPHA);
1264 glBlendEquation(GL_FUNC_ADD);
1265
1266 SHADER_USE( shader_blit );
1267
1268 glUniform1i( SHADER_UNIFORM(shader_blit,"uTexMain"), 0 );
1269 glActiveTexture(GL_TEXTURE0);
1270 glBindTexture( GL_TEXTURE_2D, render.rgb_background );
1271
1272 glBindVertexArray( render.fsquad.vao );
1273 glDrawArrays( GL_TRIANGLES, 0, 6 );
1274
1275 glDisable(GL_BLEND);
1276
1277 /* Other shite */
1278 glDisable( GL_DEPTH_TEST );
1279 vg_lines_drawall( (float *)vg_pv );
1280
1281 /* Debugger camera */
1282 glViewport( 0,0, 800, 800 );
1283 glClearColor( 0.1f, 0.0f, 0.2f, 1.0f );
1284 glClear( GL_DEPTH_BUFFER_BIT );
1285
1286 m4x3_identity( world_matrix );
1287
1288 v3f debugcam;
1289 v3_negate( player.co, debugcam );
1290 debugcam[2] -= 2.0f;
1291 debugcam[1] -= 0.7f;
1292
1293 m4x3_translate( world_matrix, debugcam );
1294 m4x3_expand( world_matrix, world_4x4 );
1295
1296 m4x4_projection( vg_pv,
1297 100.0f,
1298 (float)128.0f / (float)128.0f,
1299 0.01f, 1000.0f );
1300 m4x4_mul( vg_pv, world_4x4, vg_pv );
1301
1302 if(sv_debugcam)
1303 {
1304 glEnable( GL_DEPTH_TEST );
1305 draw_player();
1306 }
1307
1308 glDisable( GL_DEPTH_TEST );
1309 vg_lines_drawall( (float *)vg_pv );
1310
1311 glViewport( 0,0, vg_window_x, vg_window_y );
1312
1313 #if 0
1314 if( replay_record )
1315 {
1316 m4x3f *base = &replay_buffer[(PART_COUNT)*replay_buffer_frame];
1317
1318 for( int i=0; i<PART_COUNT; i++ )
1319 m4x3_copy( player.mdl.matrices[i], base[i] );
1320
1321 replay_buffer_frame ++;
1322
1323 if( replay_buffer_frame == REPLAY_LENGTH )
1324 replay_buffer_frame = 0;
1325 }
1326 #endif
1327 }
1328
1329 void vg_ui(void)
1330 {
1331 char buf[20];
1332
1333 snprintf( buf, 20, "%.2fm/s", v3_length( player.v ) );
1334 gui_text( (ui_px [2]){ 0, 0 }, buf, 1, k_text_align_left );
1335
1336 snprintf( buf, 20, "%.2f %.2f %.2f m/s",
1337 player.a[0], player.a[1], player.a[2] );
1338 gui_text( (ui_px [2]){ 0, 20 }, buf, 1, k_text_align_left );
1339
1340 snprintf( buf, 20, "pos %.2f %.2f %.2f",
1341 player.co[0], player.co[1], player.co[2] );
1342 gui_text( (ui_px [2]){ 0, 40 }, buf, 1, k_text_align_left );
1343
1344 if( vg_gamepad_ready )
1345 {
1346 for( int i=0; i<6; i++ )
1347 {
1348 snprintf( buf, 20, "%.2f", vg_gamepad.axes[i] );
1349 gui_text( (ui_px [2]){ 0, (i+3)*20 }, buf, 1, k_text_align_left );
1350 }
1351 }
1352 else
1353 {
1354 gui_text( (ui_px [2]){ 0, 60 },
1355 "Gamepad not ready", 1, k_text_align_left );
1356 }
1357 }
1358
1359 void vg_free(void){}