gate groundwork
[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 #include "gate.h"
73
74 int main( int argc, char *argv[] )
75 {
76 vg_init( argc, argv, "Voyager Game Engine" );
77 }
78
79 m4x3f world_matrix;
80
81 static struct gplayer
82 {
83 /* Physics */
84 v3f co, v, a, v_last;
85 v4f rot;
86 float vswitch, slip, slip_last,
87 reverse;
88
89 float iY; /* Yaw inertia */
90 int in_air, is_dead;
91
92 /* Input */
93 v2f joy_l;
94
95 v3f view;
96 v3f follow;
97 v2f look_dir; /* TEMP */
98 v2f board_xy;
99 float grab;
100 float pitch;
101
102 v3f land_target;
103 v3f land_target_log[22];
104 u32 land_target_colours[22];
105 int land_log_count;
106 m3x3f vr;
107
108 m4x3f to_world, to_local;
109
110 struct character mdl;
111
112 v3f handl_target, handr_target,
113 handl, handr;
114 }
115 player;
116
117 static struct gworld
118 {
119 scene geo;
120 submodel sm_road, sm_terrain;
121
122 v3f tutorial;
123 }
124 world;
125
126 static struct grender
127 {
128 GLuint fb_background,
129 rgb_background;
130
131 glmesh fsquad;
132 }
133 render;
134
135 rigidbody mr_box = {
136 .bbx = {{ -1.0f, -0.25f, -0.25f }, { 1.0f, 0.25f, 0.25f }}
137 };
138
139 rigidbody mrs_box = {
140 .bbx = {{ -0.5f, -0.25f, -0.25f }, { 0.5f, 0.25f, 0.25f }}
141 };
142
143 teleport_gate gate_a = {
144 .co = { 0.0f, -3.0f, -15.0f },
145 .q = { 0.0f, 0.0f, 0.0f, 1.0f }
146 },
147 gate_b = {
148 .co = { -8.0f, -3.0f, -17.0f },
149 .q = { 0.0f, 0.0f, 0.0f, 1.0f }
150 };
151
152 static void player_transform_update(void)
153 {
154 q_normalize( player.rot );
155 q_m3x3( player.rot, player.to_world );
156 v3_copy( player.co, player.to_world[3] );
157
158 m4x3_invert_affine( player.to_world, player.to_local );
159 }
160
161 static int reset_player( int argc, char const *argv[] )
162 {
163 v3_zero( player.co );
164
165 if( argc == 1 )
166 {
167 if( !strcmp( argv[0], "tutorial" ))
168 v3_copy( world.tutorial, player.co );
169 }
170
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;
175 player.is_dead = 0;
176 player.in_air = 1;
177 m3x3_identity( player.vr );
178
179 player.mdl.shoes[0] = 1;
180 player.mdl.shoes[1] = 1;
181
182 player_transform_update();
183 return 0;
184 }
185
186 static int playermodel( int argc, char const *argv[] )
187 {
188 if( argc < 1 ) return 0;
189
190 glmesh old_mesh = player.mdl.mesh;
191
192 if( character_load( &player.mdl, argv[0] ) )
193 mesh_free( &old_mesh );
194
195 return 1;
196 }
197
198 void vg_register(void)
199 {
200 scene_register();
201 gate_register();
202 character_shader_register();
203 SHADER_INIT( shader_blit );
204 }
205
206 void vg_start(void)
207 {
208 replay_buffer = malloc( sizeof(m4x3f) * REPLAY_LENGTH * (PART_COUNT) );
209
210 vg_tex2d_init( texture_list, vg_list_size( texture_list ) );
211
212 rb_init( &mr_box );
213 rb_init( &mrs_box );
214 mrs_box.co[2] += 2.0f;
215
216 vg_convar_push( (struct vg_convar){
217 .name = "frame",
218 .data = &replay_buffer_frame,
219 .data_type = k_convar_dtype_i32,
220 .opt_i32 = { .min=0, .max=REPLAY_LENGTH-1, .clamp=1 },
221 .persistent = 0
222 });
223
224 vg_convar_push( (struct vg_convar){
225 .name = "rec",
226 .data = &replay_record,
227 .data_type = k_convar_dtype_i32,
228 .opt_i32 = { .min=0, .max=1, .clamp=1 },
229 .persistent = 0
230 });
231
232 vg_convar_push( (struct vg_convar){
233 .name = "freecam",
234 .data = &freecam,
235 .data_type = k_convar_dtype_i32,
236 .opt_i32 = { .min=0, .max=1, .clamp=1 },
237 .persistent = 1
238 });
239
240 vg_convar_push( (struct vg_convar){
241 .name = "debugcam",
242 .data = &sv_debugcam,
243 .data_type = k_convar_dtype_i32,
244 .opt_i32 = { .min=0, .max=1, .clamp=0 },
245 .persistent = 1
246 });
247
248 vg_convar_push( (struct vg_convar){
249 .name = "debugview",
250 .data = &debugview,
251 .data_type = k_convar_dtype_i32,
252 .opt_i32 = { .min=0, .max=1, .clamp=0 },
253 .persistent = 1
254 });
255
256 vg_convar_push( (struct vg_convar){
257 .name = "debugsdf",
258 .data = &debugsdf,
259 .data_type = k_convar_dtype_i32,
260 .opt_i32 = { .min=0, .max=1, .clamp=1 },
261 .persistent = 1
262 });
263
264 vg_convar_push( (struct vg_convar){
265 .name = "phys",
266 .data = &sv_phys,
267 .data_type = k_convar_dtype_i32,
268 .opt_i32 = { .min=0, .max=1, .clamp=1 },
269 .persistent = 1
270 });
271
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 },
277 .persistent = 1
278 });
279
280 vg_convar_push( (struct vg_convar){
281 .name = "div",
282 .data = &clock_divider,
283 .data_type = k_convar_dtype_i32,
284 .opt_i32 = { .min=0, .max=1, .clamp=0 },
285 .persistent = 1
286 });
287
288 vg_function_push( (struct vg_cmd){
289 .name = "reset",
290 .function = reset_player
291 });
292
293 v3f lightDir = { 0.1f, 0.8f, 0.2f };
294 v3_normalize( lightDir );
295
296 character_load( &player.mdl, "ch_default" );
297 character_init_ragdoll( &player.mdl );
298
299 /* Setup scene */
300 scene_init( &world.geo );
301 model *mworld = vg_asset_read( "models/mp_dev.mdl" );
302
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 );
306
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 );
310
311 v3_copy( model_marker_get( mworld, "mp_dev_tutorial" )->co, world.tutorial );
312
313
314 /* GATE DEV */
315 {
316 model_marker *ga = model_marker_get(mworld,"gate_a"),
317 *gb = model_marker_get(mworld,"gate_a_recv");
318
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;
325
326 gate_transform_update( &gate_a );
327 gate_transform_update( &gate_b );
328 }
329
330 free( mworld );
331 scene_upload( &world.geo );
332 bvh_create( &world.geo );
333
334 reset_player( 0, NULL );
335 player_transform_update();
336
337 /* Create framebuffers */
338 glGenFramebuffers( 1, &render.fb_background );
339 glBindFramebuffer( GL_FRAMEBUFFER, render.fb_background );
340
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);
345
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,
349 GL_TEXTURE_2D,
350 render.rgb_background, 0);
351
352 gate_init();
353
354 {
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 };
357
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 );
367 VG_CHECK_GL();
368 }
369 }
370
371 static float ktimestep = 1.0f/60.0f;
372
373 static void player_freecam(void)
374 {
375 m4x3f cam_rot;
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] );
379
380 v3f lookdir = { 0.0f, 0.0f, -1.0f },
381 sidedir = { 1.0f, 0.0f, 0.0f };
382
383 m4x3_mulv( cam_rot, lookdir, lookdir );
384 m4x3_mulv( cam_rot, sidedir, sidedir );
385
386 float movespeed = 5.0f;
387 static v2f mouse_last,
388 view_vel = { 0.0f, 0.0f };
389
390 static v3f move_vel = { 0.0f, 0.0f, 0.0f };
391
392 if( vg_get_button_down( "primary" ) )
393 v2_copy( vg_mouse, mouse_last );
394 else if( vg_get_button( "primary" ) )
395 {
396 v2f delta;
397 v2_sub( vg_mouse, mouse_last, delta );
398 v2_copy( vg_mouse, mouse_last );
399
400 v2_muladds( view_vel, delta, 0.005f, view_vel );
401 }
402
403 v2_muls( view_vel, 0.75f, view_vel );
404 v2_add( view_vel, player.look_dir, player.look_dir );
405 player.look_dir[1] =
406 vg_clampf( player.look_dir[1], -VG_PIf*0.5f, VG_PIf*0.5f );
407
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 );
416
417 v3_muls( move_vel, 0.75f, move_vel );
418 v3_add( move_vel, player.view, player.view );
419 }
420
421 static void apply_gravity( v3f vel, float const timestep )
422 {
423 v3f gravity = { 0.0f, -9.6f, 0.0f };
424 v3_muladds( vel, gravity, timestep, vel );
425 }
426
427 static int ray_hit_is_ramp( ray_hit *hit )
428 {
429 return hit->tri[0] < world.sm_road.vertex_count;
430 }
431
432 static void player_start_air(void)
433 {
434 player.in_air = 1;
435
436 float pstep = ktimestep*10.0f;
437
438 float best_velocity_mod = 0.0f,
439 best_velocity_delta = -9999.9f;
440
441 v3f axis, vup;
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;
446
447 m3x3_identity( player.vr );
448
449 for( int m=-3;m<=12; m++ )
450 {
451 float vmod = ((float)m / 15.0f)*0.09f;
452
453 v3f pco, pco1, pv;
454 v3_copy( player.co, pco );
455 v3_copy( player.v, pv );
456 v3_muladds( pco, pv, ktimestep, pco );
457
458 /*
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
462 */
463
464 m3x3f vr;
465 v4f vr_q;
466
467 q_axis_angle( vr_q, axis, vmod );
468 q_m3x3( vr_q, vr );
469
470 for( int i=0; i<50; i++ )
471 {
472 v3_copy( pco, pco1 );
473 apply_gravity( pv, pstep );
474
475 m3x3_mulv( vr, pv, pv );
476 v3_muladds( pco, pv, pstep, pco );
477
478 ray_hit contact;
479 v3f vdir;
480
481 v3_sub( pco, pco1, vdir );
482 contact.dist = v3_length( vdir );
483 v3_divs( vdir, contact.dist, vdir);
484
485 if( bvh_raycast( &world.geo, pco1, vdir, &contact ))
486 {
487 float land_delta = v3_dot( pv, contact.normal );
488 u32 scolour = (u8)(vg_minf(-land_delta * 2.0f, 255.0f));
489
490 /* Bias prediction towords ramps */
491 if( ray_hit_is_ramp( &contact ) )
492 {
493 land_delta *= 0.1f;
494 scolour |= 0x0000a000;
495 }
496
497 if( (land_delta < 0.0f) && (land_delta > best_velocity_delta) )
498 {
499 best_velocity_delta = land_delta;
500 best_velocity_mod = vmod;
501
502 v3_copy( contact.pos, player.land_target );
503
504 q_axis_angle( vr_q, axis, vmod*0.1f );
505 q_m3x3( vr_q, player.vr );
506 }
507
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;
512
513 player.land_log_count ++;
514
515 break;
516 }
517 }
518 }
519
520 //v3_rotate( player.v, best_velocity_mod, axis, player.v );
521
522 return;
523 v3_muls( player.v, best_velocity_mod, player.v );
524 }
525
526 static int sample_if_resistant( v3f pos )
527 {
528 v3f ground;
529 v3_copy( pos, ground );
530
531 ray_hit hit;
532 if( bvh_scene_sample( &world.geo, ground, &hit ) )
533 {
534 v3f angle;
535 v3_copy( player.v, angle );
536 v3_normalize( angle );
537 float resistance = v3_dot( hit.normal, angle );
538
539 if( resistance < 0.25f )
540 {
541 v3_copy( ground, pos );
542 return 1;
543 }
544 }
545
546 return 0;
547 }
548
549 static float stable_force( float current, float diff )
550 {
551 float new = current + diff;
552
553 if( new * current < 0.0f )
554 return 0.0f;
555
556 return new;
557 }
558
559 static void player_physics_ground(void)
560 {
561 /*
562 * Getting surface collision points,
563 * the contact manifold is a triangle for simplicity.
564 */
565 v3f contact_front, contact_back, contact_norm, vup, vside,
566 axis;
567
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 );
574
575 v3f cn0, cn1, cn2;
576
577 int contact_count =
578 sample_if_resistant( contact_front ) +
579 sample_if_resistant( contact_back ) +
580 sample_if_resistant( contact_norm );
581
582 if( contact_count < 3 )
583 {
584 player_start_air();
585 return;
586 }
587
588 v3f norm;
589 v3f v0, v1;
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 );
594
595 vg_line( contact_norm, contact_front, 0xff00ff00 );
596 vg_line( contact_back, contact_front, 0xff0000ff );
597
598 /* Surface alignment */
599 float angle = v3_dot( vup, norm );
600 v3_cross( vup, norm, axis );
601
602 if( angle < 0.999f )
603 {
604 v4f correction;
605 q_axis_angle( correction, axis, acosf(angle) );
606 q_mul( correction, player.rot, player.rot );
607 }
608
609 float resistance = v3_dot( norm, player.v );
610 if( resistance >= 0.0f )
611 {
612 player_start_air();
613 return;
614 }
615 else
616 {
617 v3_muladds( player.v, norm, -resistance, player.v );
618 }
619
620 /* This is where velocity integration used to be */
621
622 float slip = 0.0f;
623
624 player.co[1] = (contact_front[1]+contact_back[1])*0.5f;
625
626 v3f vel;
627 m3x3_mulv( player.to_local, player.v, vel );
628
629 /* Calculate local forces */
630
631 slip = fabsf(-vel[0] / vel[2]) * vg_signf(vel[0]);
632 if( fabsf( slip ) > 1.2f )
633 slip = vg_signf( slip ) * 1.2f;
634 player.slip = slip;
635 player.reverse = -vg_signf(vel[2]);
636
637 float substep = ktimestep * 0.2f;
638 float fwd_resistance = (vg_get_button( "break" )? 5.0f: 0.02f) * -substep;
639
640 for( int i=0; i<5; i++ )
641 {
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 );
644 }
645
646 static double start_push = 0.0;
647 if( vg_get_button_down( "push" ) )
648 start_push = vg_time;
649
650 if( !vg_get_button("break") && vg_get_button( "push" ) )
651 {
652 float const k_maxpush = 16.0f,
653 k_pushaccel = 5.0f;
654
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;
661 }
662
663 m3x3_mulv( player.to_world, vel, player.v );
664
665 if( vg_get_button( "yawl" ) )
666 player.iY += 3.6f * ktimestep;
667 if( vg_get_button( "yawr" ) )
668 player.iY -= 3.6f * ktimestep;
669
670 float steer = vg_get_axis( "horizontal" );
671 player.iY -= vg_signf(steer)*powf(steer,2.0f) * 1.5f * ktimestep;
672
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);
676 }
677
678 static void draw_cross(v3f pos,u32 colour)
679 {
680 v3f p0, p1;
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 );
690 }
691
692 static void player_physics_air(void)
693 {
694 /* Debug prediciton */
695
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] );
700
701 draw_cross( player.land_target, 0xff0000ff );
702
703 v3f ground_pos;
704 v3_copy( player.co, ground_pos );
705
706 ray_hit hit;
707 if( bvh_scene_sample( &world.geo, ground_pos, &hit ) )
708 {
709 if( ground_pos[1] > player.co[1] )
710 {
711 player.in_air = 0;
712
713 if( !ray_hit_is_ramp( &hit ) )
714 {
715 player.is_dead = 1;
716 m4x3_mulv( player.to_world, player.view, player.follow );
717 character_ragdoll_copypose( &player.mdl, player.v );
718 }
719
720 return;
721 }
722 }
723
724 /* Prediction
725 *
726 * TODO: Find best landing surface and guide player towords it
727 */
728 float pstep = ktimestep*10.0f;
729
730 v3f pco, pco1, pv;
731 v3_copy( player.co, pco );
732 v3_copy( player.v, pv );
733
734 float time_to_impact = 0.0f;
735 float limiter = 1.0f;
736
737 for( int i=0; i<50; i++ )
738 {
739 v3_copy( pco, pco1 );
740 apply_gravity( pv, pstep );
741 v3_muladds( pco, pv, pstep, pco );
742
743 //vg_line( pco, pco1, i&0x1?0xff000000:0xffffffff );
744
745 ray_hit contact;
746 v3f vdir;
747
748 v3_sub( pco, pco1, vdir );
749 contact.dist = v3_length( vdir );
750 v3_divs( vdir, contact.dist, vdir);
751
752 float orig_dist = contact.dist;
753 if( bvh_raycast( &world.geo, pco1, vdir, &contact ))
754 {
755 v3f localup;
756 m3x3_mulv( player.to_world, (v3f){0.0f,1.0f,0.0f}, localup );
757
758 float angle = v3_dot( localup, contact.normal );
759 v3f axis;
760 v3_cross( localup, contact.normal, axis );
761
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;
765 limiter *= limiter;
766 limiter = 1.0f-limiter;
767
768 if( angle < 0.99f )
769 {
770 v4f correction;
771 q_axis_angle( correction, axis, acosf(angle)*0.05f*(1.0f-limiter) );
772 q_mul( correction, player.rot, player.rot );
773 }
774
775 draw_cross( contact.pos, 0xffff0000 );
776 break;
777 }
778 time_to_impact += pstep;
779 }
780
781
782 player.iY -= vg_get_axis( "horizontal" ) * 3.6f * ktimestep;
783 {
784
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 );
788
789 v4f rotate;
790 v3f vside;
791
792 m3x3_mulv( player.to_world, (v3f){1.0f,0.0f,0.0f}, vside );
793
794 q_axis_angle( rotate, vside, siX );
795 q_mul( rotate, player.rot, player.rot );
796 }
797
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 );
802 }
803
804 static void player_animate(void);
805 static void player_update(void)
806 {
807
808 if( vg_get_axis("grabl")>0.0f)
809 reset_player(0,NULL);
810 if( player.is_dead )
811 return;
812
813 static int clock = 0;
814
815 /* temp */
816 if( freecam )
817 {
818 player_freecam();
819 return;
820 }
821
822 clock ++;
823 if( clock >= clock_divider )
824 clock = 0;
825 else
826 return;
827 float horizontal = vg_get_axis("horizontal"),
828 vertical = vg_get_axis("vertical");
829
830 player.joy_l[0] = vg_signf(horizontal) * powf( horizontal, 2.0f );
831 player.joy_l[1] = vg_signf(vertical) * powf( vertical, 2.0f );
832
833 if( player.in_air )
834 player_physics_air();
835
836 if( !player.in_air )
837 player_physics_ground();
838
839 /* Integrate velocity */
840 v3f prevco;
841 v3_copy( player.co, prevco );
842 if( sv_phys )
843 {
844 apply_gravity( player.v, ktimestep );
845 v3_muladds( player.co, player.v, ktimestep, player.co );
846 }
847
848 /* Integrate inertia */
849 v4f rotate; v3f vup = {0.0f,1.0f,0.0f};
850 m3x3_mulv( player.to_world, vup, vup );
851
852 static float siY = 0.0f;
853
854 float lerpq = player.in_air? 0.04f: 0.3f;
855 siY = vg_lerpf( siY, player.iY, lerpq );
856
857 q_axis_angle( rotate, vup, siY );
858 q_mul( rotate, player.rot, player.rot );
859
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;
863
864 player.iY = 0.0f; /* temp */
865
866 /* GATE COLLISION */
867 if( gate_intersect( &gate_a, player.co, prevco ) )
868 {
869 teleport_gate *gate = &gate_a;
870
871 m4x3f transport;
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 );
876
877 v4f transport_rotation;
878 m3x3_q( transport, transport_rotation );
879 q_mul( transport_rotation, player.rot, player.rot );
880 }
881
882 /* Camera and character */
883 player_transform_update();
884 q_normalize(player.rot);
885 player_animate();
886 }
887
888 void vg_update(void)
889 {
890 player_update();
891
892 //rb_torque( &mr_box, (v3f){0.0f,0.0f,1.0f}, 0.01f );
893
894 if( glfwGetKey( vg_window, GLFW_KEY_F ) )
895 character_ragdoll_go( &player.mdl, player.view );
896
897 if( glfwGetKey( vg_window, GLFW_KEY_G ) )
898 {
899 player.is_dead = 1;
900 m4x3_mulv( player.to_world, player.view, player.follow );
901 character_ragdoll_copypose( &player.mdl, player.v );
902 }
903
904 static int clock = 0;
905
906 clock ++;
907 if( clock >= clock_divider )
908 {
909 character_debug_ragdoll( &player.mdl );
910
911 if( player.is_dead )
912 character_ragdoll_iter( &player.mdl, &world.geo );
913
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 );
918
919 rb_iter( &mr_box );
920 rb_iter( &mrs_box );
921
922 rb_debug( &mr_box, 0xffffffff );
923 rb_debug( &mrs_box, 0xff00ff00 );
924
925 rb_update_transform( &mr_box );
926 rb_update_transform( &mrs_box );
927
928 clock = 0;
929 }
930
931 }
932
933 static void player_animate(void)
934 {
935 /* Camera position */
936 static v3f momentum, bob;
937
938 v3_sub( player.v, player.v_last, player.a );
939 v3_copy( player.v, player.v_last );
940
941 v3_add( momentum, player.a, momentum );
942 v3_lerp( momentum, (v3f){0.0f,0.0f,0.0f}, 0.1f, momentum );
943 v3f target;
944
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 );
950
951 /* Head */
952 float lslip = fabsf(player.slip); //vg_minf( 0.4f, slip );
953
954 float grabt = vg_get_axis( "grabr" )*0.5f+0.5f;
955 player.grab = vg_lerpf( player.grab, grabt, 0.04f );
956
957 float kheight = 2.0f,
958 kleg = 0.6f;
959
960 v3f head;
961 head[0] = 0.0f;
962 head[1] = (0.3f+cosf(lslip)*0.5f*(1.0f-player.grab*0.7f)) * kheight;
963 head[2] = 0.0f;
964
965 v3f offset;
966 m3x3_mulv( player.to_local, bob, offset );
967
968 offset[0] *= 0.3333f;
969 offset[1] *= -0.25f;
970 offset[2] *= 0.7f;
971 v3_muladds( head, offset, 0.7f, head );
972 head[1] = vg_clampf( head[1], 0.3f, kheight );
973
974 #if 0
975 if( !freecam )
976 {
977 v3_copy( head, player.view );
978 v3f camoffs = {-0.2f,-0.6f,0.00f};
979 v3_add( player.view, camoffs, player.view );
980 }
981 #endif
982
983
984 /*
985 * Animation blending
986 * ===========================================
987 */
988
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;
994
995 float speed = v3_length( player.v );
996
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,
999 0.0f,1.0f), 0.04f);
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 );
1003
1004 character_pose_reset( &player.mdl );
1005
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;
1012
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) );
1020
1021 character_final_pose( &player.mdl, (v3f){0.0f,0.0f,0.0f},
1022 &pose_fly, amt_air );
1023
1024 if( !freecam )
1025 {
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 );
1029 }
1030
1031 /*
1032 * Additive effects
1033 * ==========================
1034 */
1035 struct ik_basic *arm_l = &player.mdl.ik_arm_l,
1036 *arm_r = &player.mdl.ik_arm_r;
1037
1038 v3f localv;
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 );
1042
1043 /* New board transformation */
1044 v4f board_rotation; v3f board_location;
1045
1046 v4f rz, rx;
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 );
1050
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] );
1056
1057
1058 float wheel_r = offset[0]*-0.4f;
1059 v4f qwheel;
1060 q_axis_angle( qwheel, (v3f){0.0f,1.0f,0.0f}, wheel_r );
1061
1062 q_m3x3( qwheel, player.mdl.matrices[k_chpart_wb] );
1063
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] );
1070
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] );
1075
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 );
1078
1079
1080 v3_copy( player.mdl.ik_arm_l.end, player.handl_target );
1081 v3_copy( player.mdl.ik_arm_r.end, player.handr_target );
1082
1083 if( 1||player.in_air )
1084 {
1085 float tuck = player.board_xy[1],
1086 tuck_amt = fabsf( tuck ) * (1.0f-fabsf(player.board_xy[0]));
1087
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 );
1093
1094 if( tuck < 0.0f )
1095 {
1096 //foot_l *= 1.0f-tuck_amt*1.5f;
1097
1098 if( player.grab > 0.1f )
1099 {
1100 m4x3_mulv( mboard, (v3f){0.1f,0.14f,0.6f},
1101 player.handl_target );
1102 }
1103 }
1104 else
1105 {
1106 //foot_r *= 1.0f-tuck_amt*1.4f;
1107
1108 if( player.grab > 0.1f )
1109 {
1110 m4x3_mulv( mboard, (v3f){0.1f,0.14f,-0.6f},
1111 player.handr_target );
1112 }
1113 }
1114 }
1115
1116 v3_lerp( player.handl, player.handl_target, 0.1f, player.handl );
1117 v3_lerp( player.handr, player.handr_target, 0.1f, player.handr );
1118
1119 v3_copy( player.handl, player.mdl.ik_arm_l.end );
1120 v3_copy( player.handr, player.mdl.ik_arm_r.end );
1121
1122 /* Head rotation */
1123
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;
1128 }
1129
1130 static void draw_player(void)
1131 {
1132 /* Draw */
1133 vg_tex2d_bind( &tex_pallet, 0 );
1134
1135 m4x3_copy( player.to_world, player.mdl.mroot );
1136
1137 if( player.is_dead )
1138 character_mimic_ragdoll( &player.mdl );
1139 else
1140 character_eval( &player.mdl );
1141
1142 character_draw( &player.mdl, (player.is_dead|player.in_air)? 0.0f: 1.0f );
1143 }
1144
1145 static void vg_framebuffer_resize( int w, int h )
1146 {
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 );
1150 }
1151
1152 static void render_world( m4x4f projection )
1153 {
1154 SHADER_USE(shader_standard_lit);
1155
1156 m4x3f identity_matrix;
1157 m4x3_identity( identity_matrix );
1158
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 );
1163
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 );
1168
1169 scene_bind( &world.geo );
1170 scene_draw( &world.geo );
1171 }
1172
1173 void vg_render(void)
1174 {
1175 glBindFramebuffer( GL_FRAMEBUFFER, 0 );
1176 glViewport( 0,0, vg_window_x, vg_window_y );
1177
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);
1181
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 );
1186
1187 v3f pos_inv;
1188 static v3f cam_lerped = {0.0f,0.0f,0.0f};
1189 v3_lerp( cam_lerped, player.view, 0.08f, cam_lerped );
1190
1191 if( !freecam )
1192 {
1193 m4x3_mulv( player.to_world, cam_lerped, pos_inv );
1194
1195 static float air_blend = 0.0f;
1196
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 );
1199 }
1200 else
1201 v3_add( player.co, player.view, pos_inv );
1202 v3_negate( pos_inv, pos_inv );
1203
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] };
1207
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 );
1211
1212 static v2f cam_lerped_dir;
1213
1214 m4x3_identity( world_matrix );
1215 if( player.is_dead )
1216 {
1217 v3f delta;
1218 v3_sub( player.mdl.ragdoll[k_chpart_head].co, player.follow, delta );
1219 v3_normalize(delta);
1220
1221 v3f follow_pos;
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 );
1226
1227
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 );
1232 }
1233 else
1234 {
1235 v2_lerp( cam_lerped_dir, player.look_dir, 0.04f, cam_lerped_dir );
1236
1237 m4x3_rotate_x( world_matrix,
1238 freecam?
1239 cam_lerped_dir[1]:
1240 0.6f+shake[1]*0.04f+player.look_dir[1] );
1241
1242 m4x3_rotate_y( world_matrix,
1243 freecam?
1244 cam_lerped_dir[0]:
1245 player.look_dir[0]+shake[0]*0.02f );
1246 }
1247
1248
1249 m4x3_translate( world_matrix, final );
1250
1251 m4x4f world_4x4;
1252 m4x3_expand( world_matrix, world_4x4 );
1253
1254 float fov = freecam? 60.0f: 120.0f;
1255 m4x4_projection( vg_pv, fov, (float)vg_window_x / (float)vg_window_y,
1256 0.01f, 1000.0f );
1257
1258 m4x4_mul( vg_pv, world_4x4, vg_pv );
1259
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 );
1263
1264 glEnable( GL_DEPTH_TEST );
1265
1266 /*
1267 * Draw world
1268 */
1269 SHADER_USE(shader_standard_lit);
1270
1271 m4x3f identity_matrix;
1272 m4x3_identity( identity_matrix );
1273
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 );
1278
1279 vg_tex2d_bind( &tex_grid, 0 );
1280 glUniform1i( SHADER_UNIFORM( shader_standard_lit, "uTexMain" ), 0 );
1281
1282 glUniform4f( SHADER_UNIFORM(shader_standard_lit,"uColour"),
1283 0.4f,0.4f,0.4f,1.0f );
1284
1285 scene_bind( &world.geo );
1286 scene_draw( &world.geo );
1287
1288 #if 0
1289 if( !replay_record )
1290 {
1291 m4x3f *base = &replay_buffer[(PART_COUNT)*replay_buffer_frame];
1292
1293 for( int i=0; i<PART_COUNT; i++ )
1294 m4x3_copy( base[i], player.mdl.matrices[i] );
1295
1296 replay_buffer_frame ++;
1297
1298 if( replay_buffer_frame == REPLAY_LENGTH )
1299 replay_buffer_frame = 0;
1300
1301 vg_tex2d_bind( &tex_pallet, 0 );
1302 character_draw( &player.mdl, 0.0f );
1303 player_animate();
1304 }
1305 #endif
1306
1307 m4x3f cam_transform;
1308 m4x3_invert_affine( world_matrix, cam_transform );
1309 render_gate( &gate_a, cam_transform, fov );
1310
1311
1312
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,
1319 GL_LINEAR );
1320
1321 /* Clear out the colour buffer, but keep depth */
1322 glBindFramebuffer( GL_FRAMEBUFFER, 0 );
1323 glClearColor( 0.0f, 0.0f, 0.0f, 0.0f );
1324
1325 if( !player.is_dead )
1326 glClear( GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT );
1327 else
1328 glClear( GL_COLOR_BUFFER_BIT );
1329
1330 draw_player();
1331
1332 /* Draw back in the background */
1333 glEnable(GL_BLEND);
1334 glDisable(GL_DEPTH_TEST);
1335 glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_DST_ALPHA);
1336 glBlendEquation(GL_FUNC_ADD);
1337
1338 SHADER_USE( shader_blit );
1339
1340 glUniform1i( SHADER_UNIFORM(shader_blit,"uTexMain"), 0 );
1341 glActiveTexture(GL_TEXTURE0);
1342 glBindTexture( GL_TEXTURE_2D, render.rgb_background );
1343
1344 glBindVertexArray( render.fsquad.vao );
1345 glDrawArrays( GL_TRIANGLES, 0, 6 );
1346
1347 glDisable(GL_BLEND);
1348
1349 /* Other shite */
1350 glDisable( GL_DEPTH_TEST );
1351 vg_lines_drawall( (float *)vg_pv );
1352
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 );
1357
1358 m4x3_identity( world_matrix );
1359
1360 v3f debugcam;
1361 v3_negate( player.co, debugcam );
1362 debugcam[2] -= 2.0f;
1363 debugcam[1] -= 0.7f;
1364
1365 m4x3_translate( world_matrix, debugcam );
1366 m4x3_expand( world_matrix, world_4x4 );
1367
1368 m4x4_projection( vg_pv,
1369 100.0f,
1370 (float)128.0f / (float)128.0f,
1371 0.01f, 1000.0f );
1372 m4x4_mul( vg_pv, world_4x4, vg_pv );
1373
1374 if(sv_debugcam)
1375 {
1376 glEnable( GL_DEPTH_TEST );
1377 draw_player();
1378 }
1379
1380 glDisable( GL_DEPTH_TEST );
1381 vg_lines_drawall( (float *)vg_pv );
1382
1383 glViewport( 0,0, vg_window_x, vg_window_y );
1384
1385 #if 0
1386 if( replay_record )
1387 {
1388 m4x3f *base = &replay_buffer[(PART_COUNT)*replay_buffer_frame];
1389
1390 for( int i=0; i<PART_COUNT; i++ )
1391 m4x3_copy( player.mdl.matrices[i], base[i] );
1392
1393 replay_buffer_frame ++;
1394
1395 if( replay_buffer_frame == REPLAY_LENGTH )
1396 replay_buffer_frame = 0;
1397 }
1398 #endif
1399 }
1400
1401 void vg_ui(void)
1402 {
1403 char buf[20];
1404
1405 snprintf( buf, 20, "%.2fm/s", v3_length( player.v ) );
1406 gui_text( (ui_px [2]){ 0, 0 }, buf, 1, k_text_align_left );
1407
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 );
1411
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 );
1415
1416 if( vg_gamepad_ready )
1417 {
1418 for( int i=0; i<6; i++ )
1419 {
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 );
1422 }
1423 }
1424 else
1425 {
1426 gui_text( (ui_px [2]){ 0, 60 },
1427 "Gamepad not ready", 1, k_text_align_left );
1428 }
1429 }
1430
1431 void vg_free(void){}