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