whole
[carveJwlIkooP6JGAAIwe30JlM.git] / player.h
1 #ifndef PLAYER_H
2 #define PLAYER_H
3
4 #include "audio.h"
5 #include "common.h"
6 #include "world.h"
7 //#include "character.h"
8 #include "player_model.h"
9 #include "bvh.h"
10
11 /*
12 * Constants
13 */
14
15 static float
16 k_walkspeed = 7.0f, /* no longer used */
17 k_runspeed = 14.0f,
18 k_board_radius = 0.3f,
19 k_board_length = 0.45f,
20 k_board_allowance = 0.04f,
21 k_friction_lat = 8.8f,
22 k_friction_resistance = 0.01f,
23 k_max_push_speed = 16.0f,
24 k_push_accel = 10.0f,
25 k_push_cycle_rate = 8.0f,
26 k_steer_ground = 2.5f,
27 k_steer_air = 3.6f,
28 k_steer_air_lerp = 0.3f,
29 k_pump_force = 0.0f,
30 k_downforce = 5.0f,
31 k_jump_charge_speed = (1.0f/1.0f),
32 k_jump_force = 5.0f,
33 k_pitch_limit = 1.5f,
34 k_look_speed = 2.0f,
35 k_walk_accel = 5.0f,
36 k_walk_friction = 8.0f;
37
38 static int freecam = 0;
39 static int walk_grid_iterations = 1;
40 static float fc_speed = 10.0f;
41
42 static struct gplayer
43 {
44 /* Physics */
45 rigidbody rb, collide_front, collide_back, rb_gate_frame;
46
47 /* TODO: eugh */
48 m3x3f gate_vr_frame, gate_vr_pstep_frame;
49
50 v3f a, v_last, m, bob, vl;
51
52 /* Utility */
53 float vswitch, slip, slip_last,
54 reverse;
55
56 float iY; /* Yaw inertia */
57 int in_air, is_dead, on_board;
58
59 v2f board_xy;
60 float grab;
61 float pitch;
62 float pushing, push_time;
63 float jump;
64 int jump_charge, jump_dir;
65
66 v3f land_target;
67 v3f land_target_log[22];
68 u32 land_target_colours[22];
69 int land_log_count;
70 m3x3f vr,vr_pstep;
71
72 struct character mdl;
73
74 v3f handl_target, handr_target,
75 handl, handr;
76
77 /* Camera */
78 float air_blend;
79
80 v3f camera_pos, smooth_localcam;
81 v2f angles;
82 m4x3f camera, camera_inverse;
83
84 /* animation */
85 double jump_time;
86 float fslide,
87 fdirz, fdirx,
88 fstand,
89 ffly,
90 fpush,
91 fairdir,
92 fsetup,
93 walk_timer,
94 fonboard;
95 }
96 player =
97 {
98 .on_board = 0,
99
100 .collide_front = { .type = k_rb_shape_sphere, .inf.sphere.radius = 0.3f },
101 .collide_back = { .type = k_rb_shape_sphere, .inf.sphere.radius = 0.3f }
102 };
103
104 /*
105 * Player API
106 */
107
108
109 /*
110 * Free camera movement
111 */
112
113 static void player_mouseview(void)
114 {
115 if( gui_want_mouse() )
116 return;
117
118 static v2f mouse_last,
119 view_vel = { 0.0f, 0.0f };
120
121 if( vg_get_button_down( "primary" ) )
122 v2_copy( vg_mouse, mouse_last );
123
124 else if( vg_get_button( "primary" ) )
125 {
126 v2f delta;
127 v2_sub( vg_mouse, mouse_last, delta );
128 v2_copy( vg_mouse, mouse_last );
129
130 v2_muladds( view_vel, delta, 0.001f, view_vel );
131 }
132
133 v2_muladds( view_vel,
134 (v2f){ vg_get_axis("h1"), vg_get_axis("v1") },
135 0.05f, view_vel );
136 v2_muls( view_vel, 0.93f, view_vel );
137 v2_add( view_vel, player.angles, player.angles );
138 player.angles[1] = vg_clampf( player.angles[1], -VG_PIf*0.5f, VG_PIf*0.5f );
139 }
140
141 static void player_freecam(void)
142 {
143 player_mouseview();
144
145 float movespeed = fc_speed;
146 v3f lookdir = { 0.0f, 0.0f, -1.0f },
147 sidedir = { 1.0f, 0.0f, 0.0f };
148
149 m3x3_mulv( player.camera, lookdir, lookdir );
150 m3x3_mulv( player.camera, sidedir, sidedir );
151
152 static v3f move_vel = { 0.0f, 0.0f, 0.0f };
153 if( vg_get_button( "forward" ) )
154 v3_muladds( move_vel, lookdir, ktimestep * movespeed, move_vel );
155 if( vg_get_button( "back" ) )
156 v3_muladds( move_vel, lookdir, ktimestep *-movespeed, move_vel );
157 if( vg_get_button( "left" ) )
158 v3_muladds( move_vel, sidedir, ktimestep *-movespeed, move_vel );
159 if( vg_get_button( "right" ) )
160 v3_muladds( move_vel, sidedir, ktimestep * movespeed, move_vel );
161
162 v3_muls( move_vel, 0.7f, move_vel );
163 v3_add( move_vel, player.camera_pos, player.camera_pos );
164 }
165
166 /*
167 * Player Physics Implementation
168 */
169
170 static void apply_gravity( v3f vel, float const timestep )
171 {
172 v3f gravity = { 0.0f, -9.6f, 0.0f };
173 v3_muladds( vel, gravity, timestep, vel );
174 }
175
176 /*
177 * TODO: The angle bias should become greater when launching from a steeper
178 * angle and skewed towords more 'downwards' angles when launching from
179 * shallower trajectories
180 *
181 * it should also be tweaked by the controller left stick being pushed
182 * up or down
183 */
184 static void player_start_air(void)
185 {
186 if( player.in_air )
187 return;
188
189 player.in_air = 1;
190
191 float pstep = ktimestep*10.0f;
192 float best_velocity_delta = -9999.9f;
193 float k_bias = 0.96f;
194
195 v3f axis;
196 v3_cross( player.rb.up, player.rb.v, axis );
197 v3_normalize( axis );
198 player.land_log_count = 0;
199
200 m3x3_identity( player.vr );
201
202 for( int m=-3;m<=12; m++ )
203 {
204 float vmod = ((float)m / 15.0f)*0.09f;
205
206 v3f pco, pco1, pv;
207 v3_copy( player.rb.co, pco );
208 v3_muls( player.rb.v, k_bias, pv );
209
210 /*
211 * Try different 'rotations' of the velocity to find the best possible
212 * landing normal. This conserves magnitude at the expense of slightly
213 * unrealistic results
214 */
215
216 m3x3f vr;
217 v4f vr_q;
218
219 q_axis_angle( vr_q, axis, vmod );
220 q_m3x3( vr_q, vr );
221
222 m3x3_mulv( vr, pv, pv );
223 v3_muladds( pco, pv, pstep, pco );
224
225 for( int i=0; i<50; i++ )
226 {
227 v3_copy( pco, pco1 );
228 apply_gravity( pv, pstep );
229
230 m3x3_mulv( vr, pv, pv );
231 v3_muladds( pco, pv, pstep, pco );
232
233 ray_hit contact;
234 v3f vdir;
235
236 v3_sub( pco, pco1, vdir );
237 contact.dist = v3_length( vdir );
238 v3_divs( vdir, contact.dist, vdir);
239
240 if( ray_world( pco1, vdir, &contact ))
241 {
242 float land_delta = v3_dot( pv, contact.normal );
243 u32 scolour = (u8)(vg_minf(-land_delta * 2.0f, 255.0f));
244
245 /* Bias prediction towords ramps */
246 if( ray_hit_is_ramp( &contact ) )
247 {
248 land_delta *= 0.1f;
249 scolour |= 0x0000a000;
250 }
251
252 if( (land_delta < 0.0f) && (land_delta > best_velocity_delta) )
253 {
254 best_velocity_delta = land_delta;
255
256 v3_copy( contact.pos, player.land_target );
257
258 m3x3_copy( vr, player.vr_pstep );
259 q_axis_angle( vr_q, axis, vmod*0.1f );
260 q_m3x3( vr_q, player.vr );
261 }
262
263 v3_copy( contact.pos,
264 player.land_target_log[player.land_log_count] );
265 player.land_target_colours[player.land_log_count] =
266 0xff000000 | scolour;
267
268 player.land_log_count ++;
269
270 break;
271 }
272 }
273 }
274 }
275
276 static void draw_cross(v3f pos,u32 colour, float scale)
277 {
278 v3f p0, p1;
279 v3_add( (v3f){ scale,0.0f,0.0f}, pos, p0 );
280 v3_add( (v3f){-scale,0.0f,0.0f}, pos, p1 );
281 vg_line( p0, p1, colour );
282 v3_add( (v3f){0.0f, scale,0.0f}, pos, p0 );
283 v3_add( (v3f){0.0f,-scale,0.0f}, pos, p1 );
284 vg_line( p0, p1, colour );
285 v3_add( (v3f){0.0f,0.0f, scale}, pos, p0 );
286 v3_add( (v3f){0.0f,0.0f,-scale}, pos, p1 );
287 vg_line( p0, p1, colour );
288 }
289
290 static void player_physics_control(void)
291 {
292 /*
293 * Computing localized friction forces for controlling the character
294 * Friction across X is significantly more than Z
295 */
296
297 v3f vel;
298 m3x3_mulv( player.rb.to_local, player.rb.v, vel );
299 float slip = 0.0f;
300
301 if( fabsf(vel[2]) > 0.01f )
302 slip = fabsf(-vel[0] / vel[2]) * vg_signf(vel[0]);
303
304 if( fabsf( slip ) > 1.2f )
305 slip = vg_signf( slip ) * 1.2f;
306 player.slip = slip;
307 player.reverse = -vg_signf(vel[2]);
308
309 float substep = ktimestep * 0.2f;
310 float fwd_resistance = (vg_get_button( "break" )? 5.0f: 0.02f) * -substep;
311
312 for( int i=0; i<5; i++ )
313 {
314 vel[2] = stable_force( vel[2], vg_signf( vel[2] ) * fwd_resistance );
315 vel[0] = stable_force( vel[0],
316 vg_signf( vel[0] ) * -k_friction_lat*substep );
317 }
318
319 static double start_push = 0.0;
320 if( vg_get_button_down( "push" ) )
321 start_push = vg_time;
322
323 if( vg_get_button( "jump" ) )
324 {
325 player.jump += ktimestep * k_jump_charge_speed;
326
327 if( !player.jump_charge )
328 player.jump_dir = player.reverse > 0.0f? 1: 0;
329
330 player.jump_charge = 1;
331 }
332
333 if( !vg_get_button("break") && vg_get_button( "push" ) )
334 {
335 player.pushing = 1.0f;
336 player.push_time = vg_time-start_push;
337
338 float cycle_time = player.push_time*k_push_cycle_rate,
339 amt = k_push_accel * (sinf(cycle_time)*0.5f+0.5f)*ktimestep,
340 current = v3_length( vel ),
341 new_vel = vg_minf( current + amt, k_max_push_speed );
342
343 new_vel -= vg_minf(current, k_max_push_speed);
344 vel[2] -= new_vel * player.reverse;
345 }
346
347 /* Pumping */
348 static float previous = 0.0f;
349 float delta = previous - player.grab,
350 pump = delta * k_pump_force*ktimestep;
351 previous = player.grab;
352
353 v3f p1;
354 v3_muladds( player.rb.co, player.rb.up, pump, p1 );
355 vg_line( player.rb.co, p1, 0xff0000ff );
356
357 vel[1] += pump;
358
359
360 m3x3_mulv( player.rb.to_world, vel, player.rb.v );
361
362 float steer = vg_get_axis( "horizontal" );
363 player.iY -= vg_signf(steer)*powf(steer,2.0f) * k_steer_ground * ktimestep;
364
365 v2_lerp( player.board_xy, (v2f){ slip*0.25f, 0.0f },
366 ktimestep*5.0f, player.board_xy);
367 }
368
369 static void player_physics_control_air(void)
370 {
371 m3x3_mulv( player.vr, player.rb.v, player.rb.v );
372 draw_cross( player.land_target, 0xff0000ff, 0.25f );
373
374 ray_hit hit;
375
376 /*
377 * Prediction
378 */
379 float pstep = ktimestep*10.0f;
380
381 v3f pco, pco1, pv;
382 v3_copy( player.rb.co, pco );
383 v3_copy( player.rb.v, pv );
384
385 float time_to_impact = 0.0f;
386 float limiter = 1.0f;
387
388 for( int i=0; i<50; i++ )
389 {
390 v3_copy( pco, pco1 );
391 m3x3_mulv( player.vr_pstep, pv, pv );
392 apply_gravity( pv, pstep );
393 v3_muladds( pco, pv, pstep, pco );
394
395 //vg_line( pco, pco1, i&0x1?0xff000000:0xffffffff );
396
397 ray_hit contact;
398 v3f vdir;
399
400 v3_sub( pco, pco1, vdir );
401 contact.dist = v3_length( vdir );
402 v3_divs( vdir, contact.dist, vdir);
403
404 float orig_dist = contact.dist;
405 if( ray_world( pco1, vdir, &contact ))
406 {
407 float angle = v3_dot( player.rb.up, contact.normal );
408 v3f axis;
409 v3_cross( player.rb.up, contact.normal, axis );
410
411 time_to_impact += (contact.dist/orig_dist)*pstep;
412 limiter = vg_minf( 5.0f, time_to_impact )/5.0f;
413 limiter = 1.0f-limiter;
414 limiter *= limiter;
415 limiter = 1.0f-limiter;
416
417 if( angle < 0.99f )
418 {
419 v4f correction;
420 q_axis_angle( correction, axis, acosf(angle)*0.05f*(1.0f-limiter) );
421 q_mul( correction, player.rb.q, player.rb.q );
422 }
423
424 draw_cross( contact.pos, 0xffff0000, 0.25f );
425 break;
426 }
427 time_to_impact += pstep;
428 }
429
430 player.iY -= vg_get_axis( "horizontal" ) * k_steer_air * ktimestep;
431 {
432 float iX = vg_get_axis( "vertical" ) *
433 player.reverse * k_steer_air * limiter * ktimestep;
434
435 static float siX = 0.0f;
436 siX = vg_lerpf( siX, iX, k_steer_air_lerp );
437
438 v4f rotate;
439 q_axis_angle( rotate, player.rb.right, siX );
440 q_mul( rotate, player.rb.q, player.rb.q );
441 }
442
443 v2f target = {0.0f,0.0f};
444 v2_muladds( target, (v2f){ vg_get_axis("h1"), vg_get_axis("v1") },
445 player.grab, target );
446 v2_lerp( player.board_xy, target, ktimestep*3.0f, player.board_xy );
447 }
448
449 static void player_init(void)
450 {
451 rb_init( &player.collide_front );
452 rb_init( &player.collide_back );
453 }
454
455 static void player_walk_physics(void)
456 {
457 rigidbody *rbf = &player.collide_front,
458 *rbb = &player.collide_back;
459
460 m3x3_copy( player.rb.to_world, player.collide_front.to_world );
461 m3x3_copy( player.rb.to_world, player.collide_back.to_world );
462
463 float h0 = 0.3f,
464 h1 = 0.9f;
465
466 m4x3_mulv( player.rb.to_world, (v3f){0.0f,h0,0.0f}, rbf->co );
467 v3_copy( rbf->co, rbf->to_world[3] );
468 m4x3_mulv( player.rb.to_world, (v3f){0.0f,h1,0.0f}, rbb->co );
469 v3_copy( rbb->co, rbb->to_world[3] );
470
471 m4x3_invert_affine( rbf->to_world, rbf->to_local );
472 m4x3_invert_affine( rbb->to_world, rbb->to_local );
473
474 rb_update_bounds( rbf );
475 rb_update_bounds( rbb );
476
477 rb_debug( rbf, 0xff0000ff );
478 rb_debug( rbb, 0xff0000ff );
479
480 rb_ct manifold[64];
481 int len = 0;
482
483 len += rb_sphere_scene( rbf, &world.rb_geo, manifold+len );
484 len += rb_sphere_scene( rbb, &world.rb_geo, manifold+len );
485
486 rb_presolve_contacts( manifold, len );
487
488 for( int j=0; j<5; j++ )
489 {
490 for( int i=0; i<len; i++ )
491 {
492 struct contact *ct = &manifold[i];
493
494 /*normal */
495 float vn = -v3_dot( player.rb.v, ct->n );
496 vn += ct->bias;
497
498 float temp = ct->norm_impulse;
499 ct->norm_impulse = vg_maxf( temp + vn, 0.0f );
500 vn = ct->norm_impulse - temp;
501
502 v3f impulse;
503 v3_muls( ct->n, vn, impulse );
504
505 v3_add( impulse, player.rb.v, player.rb.v );
506
507 /* friction */
508 for( int j=0; j<2; j++ )
509 {
510 float f = k_friction * ct->norm_impulse,
511 vt = v3_dot( player.rb.v, ct->t[j] ),
512 lambda = -vt;
513
514 float temp = ct->tangent_impulse[j];
515 ct->tangent_impulse[j] = vg_clampf( temp + lambda, -f, f );
516 lambda = ct->tangent_impulse[j] - temp;
517
518 v3_muladds( player.rb.v, ct->t[j], lambda, player.rb.v );
519 }
520 }
521 }
522
523 v3_zero( player.rb.w );
524 q_axis_angle( player.rb.q, (v3f){0.0f,1.0f,0.0f}, -player.angles[0] );
525
526 v3f forward_dir = { sinf(player.angles[0]),0.0f,-cosf(player.angles[0]) };
527
528 v3f p1;
529 v3_muladds( player.rb.co, forward_dir, 2.0f, p1 );
530 vg_line( player.rb.co, p1, 0xff0000ff );
531
532 float move_dead = 0.1f,
533 move = vg_get_axis("grabr")*0.5f + 0.5f - move_dead;
534
535 if( move > 0.0f )
536 {
537 float move_norm = move * (1.0f/(1.0f-move_dead)),
538 speed = vg_lerpf( 0.1f*k_runspeed, k_runspeed, move_norm ),
539 amt = k_walk_accel * ktimestep,
540 zvel = v3_dot( player.rb.v, forward_dir ),
541 new_vel = vg_minf( zvel + amt, speed ),
542 diff = new_vel - vg_minf( zvel, speed );
543
544 v3_muladds( player.rb.v, forward_dir, diff, player.rb.v );
545
546 /* TODO move */
547 float walk_norm = (float)player.mdl.anim_walk->length / 30.0f,
548 run_norm = (float)player.mdl.anim_run->length / 30.0f;
549
550 player.walk_timer += ktimestep * vg_lerpf( walk_norm,run_norm,move_norm );
551 }
552 else
553 {
554 player.walk_timer = 0.0f;
555 }
556
557 player.rb.v[0] *= 1.0f - (ktimestep*k_walk_friction);
558 player.rb.v[2] *= 1.0f - (ktimestep*k_walk_friction);
559 }
560
561 static void player_physics(void)
562 {
563 /*
564 * Update collision fronts
565 */
566
567 rigidbody *rbf = &player.collide_front,
568 *rbb = &player.collide_back;
569
570 m3x3_copy( player.rb.to_world, player.collide_front.to_world );
571 m3x3_copy( player.rb.to_world, player.collide_back.to_world );
572
573 player.air_blend = vg_lerpf( player.air_blend, player.in_air, 0.1f );
574 float h = player.air_blend*0.2f;
575
576 m4x3_mulv( player.rb.to_world, (v3f){0.0f,h,-k_board_length}, rbf->co );
577 v3_copy( rbf->co, rbf->to_world[3] );
578 m4x3_mulv( player.rb.to_world, (v3f){0.0f,h, k_board_length}, rbb->co );
579 v3_copy( rbb->co, rbb->to_world[3] );
580
581 m4x3_invert_affine( rbf->to_world, rbf->to_local );
582 m4x3_invert_affine( rbb->to_world, rbb->to_local );
583
584 rb_update_bounds( rbf );
585 rb_update_bounds( rbb );
586
587 rb_debug( rbf, 0xff00ffff );
588 rb_debug( rbb, 0xffffff00 );
589
590 rb_ct manifold[64];
591 int len = 0;
592
593 len += rb_sphere_scene( rbf, &world.rb_geo, manifold+len );
594 len += rb_sphere_scene( rbb, &world.rb_geo, manifold+len );
595
596 rb_presolve_contacts( manifold, len );
597 v3f surface_avg = {0.0f, 0.0f, 0.0f};
598
599 if( !len )
600 {
601 player_start_air();
602 }
603 else
604 {
605 for( int i=0; i<len; i++ )
606 {
607 v3_add( manifold[i].n, surface_avg, surface_avg );
608
609 #if 0
610 if( manifold[i].element_id <= world.sm_geo_std_oob.vertex_count )
611 {
612 player.is_dead = 1;
613 character_ragdoll_copypose( &player.mdl, player.rb.v );
614 return;
615 }
616 #endif
617 }
618
619 v3_normalize( surface_avg );
620
621 if( v3_dot( player.rb.v, surface_avg ) > 0.5f )
622 {
623 player_start_air();
624 }
625 else
626 player.in_air = 0;
627 }
628
629 for( int j=0; j<5; j++ )
630 {
631 for( int i=0; i<len; i++ )
632 {
633 struct contact *ct = &manifold[i];
634
635 v3f dv, delta;
636 v3_sub( ct->co, player.rb.co, delta );
637 v3_cross( player.rb.w, delta, dv );
638 v3_add( player.rb.v, dv, dv );
639
640 float vn = -v3_dot( dv, ct->n );
641 vn += ct->bias;
642
643 float temp = ct->norm_impulse;
644 ct->norm_impulse = vg_maxf( temp + vn, 0.0f );
645 vn = ct->norm_impulse - temp;
646
647 v3f impulse;
648 v3_muls( ct->n, vn, impulse );
649
650 if( fabsf(v3_dot( impulse, player.rb.forward )) > 10.0f ||
651 fabsf(v3_dot( impulse, player.rb.up )) > 50.0f )
652 {
653 player.is_dead = 1;
654 character_ragdoll_copypose( &player.mdl, player.rb.v );
655 return;
656 }
657
658 v3_add( impulse, player.rb.v, player.rb.v );
659 v3_cross( delta, impulse, impulse );
660
661 /*
662 * W Impulses are limited to the Y and X axises, we don't really want
663 * roll angular velocities being included.
664 *
665 * Can also tweak the resistance of each axis here by scaling the wx,wy
666 * components.
667 */
668
669 float wy = v3_dot( player.rb.up, impulse ),
670 wx = v3_dot( player.rb.right, impulse )*1.5f;
671
672 v3_muladds( player.rb.w, player.rb.up, wy, player.rb.w );
673 v3_muladds( player.rb.w, player.rb.right, wx, player.rb.w );
674 }
675 }
676
677 float grabt = vg_get_axis( "grabr" )*0.5f+0.5f;
678 player.grab = vg_lerpf( player.grab, grabt, 0.14f );
679 player.pushing = 0.0f;
680
681 if( !player.in_air )
682 {
683 v3f axis;
684 float angle = v3_dot( player.rb.up, surface_avg );
685 v3_cross( player.rb.up, surface_avg, axis );
686
687 //float cz = v3_dot( player.rb.forward, axis );
688 //v3_muls( player.rb.forward, cz, axis );
689
690 if( angle < 0.999f )
691 {
692 v4f correction;
693 q_axis_angle( correction, axis, acosf(angle)*0.3f );
694 q_mul( correction, player.rb.q, player.rb.q );
695 }
696
697 v3_muladds( player.rb.v, player.rb.up,
698 -k_downforce*ktimestep, player.rb.v );
699
700 player_physics_control();
701
702 if( !player.jump_charge && player.jump > 0.2f )
703 {
704 v3f jumpdir;
705
706 /* Launch more up if alignment is up else improve velocity */
707 float aup = fabsf(v3_dot( (v3f){0.0f,1.0f,0.0f}, player.rb.up )),
708 mod = 0.5f,
709 dir = mod + aup*(1.0f-mod);
710
711 v3_copy( player.rb.v, jumpdir );
712 v3_normalize( jumpdir );
713 v3_muls( jumpdir, 1.0f-dir, jumpdir );
714 v3_muladds( jumpdir, player.rb.up, dir, jumpdir );
715 v3_normalize( jumpdir );
716
717 float force = k_jump_force*player.jump;
718 v3_muladds( player.rb.v, jumpdir, force, player.rb.v );
719 player.jump = 0.0f;
720
721 player.jump_time = vg_time;
722 }
723 }
724 else
725 {
726 player_physics_control_air();
727 }
728
729 if( !player.jump_charge )
730 {
731 player.jump -= k_jump_charge_speed * ktimestep;
732 }
733 player.jump_charge = 0;
734 player.jump = vg_clampf( player.jump, 0.0f, 1.0f );
735 }
736
737 static void player_do_motion(void)
738 {
739 float horizontal = vg_get_axis("horizontal"),
740 vertical = vg_get_axis("vertical");
741
742 if( player.on_board )
743 player_physics();
744 else
745 player_walk_physics();
746
747 /* Integrate velocity */
748 v3f prevco;
749 v3_copy( player.rb.co, prevco );
750
751 apply_gravity( player.rb.v, ktimestep );
752 v3_muladds( player.rb.co, player.rb.v, ktimestep, player.rb.co );
753
754 /* Real angular velocity integration */
755 v3_lerp( player.rb.w, (v3f){0.0f,0.0f,0.0f}, 0.125f, player.rb.w );
756 if( v3_length2( player.rb.w ) > 0.0f )
757 {
758 v4f rotation;
759 v3f axis;
760 v3_copy( player.rb.w, axis );
761
762 float mag = v3_length( axis );
763 v3_divs( axis, mag, axis );
764 q_axis_angle( rotation, axis, mag*k_rb_delta );
765 q_mul( rotation, player.rb.q, player.rb.q );
766 }
767
768 /* Faux angular velocity */
769 v4f rotate;
770
771 static float siY = 0.0f;
772 float lerpq = player.in_air? 0.04f: 0.3f;
773 siY = vg_lerpf( siY, player.iY, lerpq );
774
775 q_axis_angle( rotate, player.rb.up, siY );
776 q_mul( rotate, player.rb.q, player.rb.q );
777 player.iY = 0.0f;
778
779 /*
780 * Gate intersection, by tracing a line over the gate planes
781 */
782 for( int i=0; i<world.routes.gate_count; i++ )
783 {
784 struct route_gate *rg = &world.routes.gates[i];
785 teleport_gate *gate = &rg->gate;
786
787 if( gate_intersect( gate, player.rb.co, prevco ) )
788 {
789 m4x3_mulv( gate->transport, player.rb.co, player.rb.co );
790 m3x3_mulv( gate->transport, player.rb.v, player.rb.v );
791 m3x3_mulv( gate->transport, player.vl, player.vl );
792 m3x3_mulv( gate->transport, player.v_last, player.v_last );
793 m3x3_mulv( gate->transport, player.m, player.m );
794 m3x3_mulv( gate->transport, player.bob, player.bob );
795
796 v4f transport_rotation;
797 m3x3_q( gate->transport, transport_rotation );
798 q_mul( transport_rotation, player.rb.q, player.rb.q );
799
800 world_routes_activate_gate( i );
801 player.rb_gate_frame = player.rb;
802
803 m3x3_copy( player.vr, player.gate_vr_frame );
804 m3x3_copy( player.vr_pstep, player.gate_vr_pstep_frame );
805 break;
806 }
807 }
808
809 rb_update_transform( &player.rb );
810 }
811
812 /*
813 * Animation
814 */
815
816 static void player_animate_offboard(void)
817 {
818 mdl_keyframe apose[32], bpose[32];
819 struct skeleton *sk = &player.mdl.sk;
820
821 float walk_norm = 30.0f/(float)player.mdl.anim_walk->length,
822 run_norm = 30.0f/(float)player.mdl.anim_run->length,
823 t = player.walk_timer,
824 l = vg_get_axis("grabr") * 0.5f + 0.5f;
825
826 skeleton_sample_anim( sk, player.mdl.anim_walk, t*walk_norm, apose );
827 skeleton_sample_anim( sk, player.mdl.anim_run, t*run_norm, bpose );
828
829 skeleton_lerp_pose( sk, apose, bpose, l, apose );
830
831 float idle_walk = vg_minf( l * 10.0f, 1.0f);
832
833 skeleton_sample_anim( sk, player.mdl.anim_idle, vg_time*0.1f, bpose );
834 skeleton_lerp_pose( sk, apose, bpose, 1.0f-idle_walk, apose );
835
836 skeleton_apply_pose( &player.mdl.sk, apose, k_anim_apply_defer_ik );
837 skeleton_apply_ik_pass( &player.mdl.sk );
838 skeleton_apply_pose( &player.mdl.sk, apose, k_anim_apply_deffered_only );
839
840 v3_copy( player.mdl.sk.final_mtx[player.mdl.id_head-1][3],
841 player.mdl.cam_pos );
842
843 skeleton_apply_inverses( &player.mdl.sk );
844
845 m4x3f mtx;
846 v4f rot;
847 q_axis_angle( rot, (v3f){0.0f,1.0f,0.0f}, -player.angles[0] - VG_PIf*0.5f );
848 q_m3x3( rot, mtx );
849 v3_copy( player.rb.to_world[3], mtx[3] );
850
851 skeleton_apply_transform( &player.mdl.sk, mtx );
852 skeleton_debug( &player.mdl.sk );
853 }
854
855 static void player_animate(void)
856 {
857 if( !player.on_board )
858 {
859 player_animate_offboard();
860 return;
861 }
862
863 /* Camera position */
864 v3_sub( player.rb.v, player.v_last, player.a );
865 v3_copy( player.rb.v, player.v_last );
866
867 v3_add( player.m, player.a, player.m );
868 v3_lerp( player.m, (v3f){0.0f,0.0f,0.0f}, 0.1f, player.m );
869
870 player.m[0] = vg_clampf( player.m[0], -2.0f, 2.0f );
871 player.m[1] = vg_clampf( player.m[1], -2.0f, 2.0f );
872 player.m[2] = vg_clampf( player.m[2], -2.0f, 2.0f );
873 v3_lerp( player.bob, player.m, 0.2f, player.bob );
874
875 /* Head */
876 float lslip = fabsf(player.slip);
877
878 float kheight = 2.0f,
879 kleg = 0.6f;
880
881 v3f offset;
882 v3_zero( offset );
883 m3x3_mulv( player.rb.to_local, player.bob, offset );
884
885 static float speed_wobble = 0.0f, speed_wobble_2 = 0.0f;
886
887 float kickspeed = vg_clampf(v3_length(player.rb.v)*(1.0f/40.0f), 0.0f, 1.0f);
888 float kicks = (vg_randf()-0.5f)*2.0f*kickspeed;
889 float sign = vg_signf( kicks );
890 speed_wobble = vg_lerpf( speed_wobble, kicks*kicks*sign, 0.1f );
891 speed_wobble_2 = vg_lerpf( speed_wobble_2, speed_wobble, 0.04f );
892
893 offset[0] *= 0.26f;
894 offset[0] += speed_wobble_2*3.0f;
895
896 offset[1] *= -0.3f;
897 offset[2] *= 0.01f;
898
899 offset[0] = vg_clampf( offset[0], -0.8f, 0.8f );
900 offset[1] = vg_clampf( offset[1], -0.5f, 0.0f );
901 offset[1] = 0.0f;
902
903 /*
904 * Animation blending
905 * ===========================================
906 */
907
908 /* scalar blending information */
909 float speed = v3_length( player.rb.v );
910
911 /* sliding */
912 {
913 float desired = vg_clampf( lslip, 0.0f, 1.0f );
914 player.fslide = vg_lerpf( player.fslide, desired, 0.04f );
915 }
916
917 /* movement information */
918 {
919 float dirz = player.reverse > 0.0f? 0.0f: 1.0f,
920 dirx = player.slip < 0.0f? 0.0f: 1.0f,
921 fly = player.in_air? 1.0f: 0.0f;
922
923 player.fdirz = vg_lerpf( player.fdirz, dirz, 0.04f );
924 player.fdirx = vg_lerpf( player.fdirx, dirx, 0.01f );
925 player.ffly = vg_lerpf( player.ffly, fly, 0.04f );
926 }
927
928 struct skeleton *sk = &player.mdl.sk;
929
930 mdl_keyframe apose[32], bpose[32];
931 mdl_keyframe ground_pose[32];
932 {
933 /* when the player is moving fast he will crouch down a little bit */
934 float stand = 1.0f - vg_clampf( speed * 0.03f, 0.0f, 1.0f );
935 player.fstand = vg_lerpf( player.fstand, stand, 0.1f );
936
937 /* stand/crouch */
938 float dir_frame = player.fdirz * (15.0f/30.0f),
939 stand_blend = offset[1]*-2.0f;
940
941 skeleton_sample_anim( sk, player.mdl.anim_stand, dir_frame, apose );
942 skeleton_sample_anim( sk, player.mdl.anim_highg, dir_frame, bpose );
943 skeleton_lerp_pose( sk, apose, bpose, stand_blend, apose );
944
945 /* sliding */
946 float slide_frame = player.fdirx * (15.0f/30.0f);
947 skeleton_sample_anim( sk, player.mdl.anim_slide, slide_frame, bpose );
948 skeleton_lerp_pose( sk, apose, bpose, player.fslide, apose );
949
950 /* pushing */
951 player.fpush = vg_lerpf( player.fpush, player.pushing, 0.1f );
952
953 float pt = player.push_time;
954 if( player.reverse > 0.0f )
955 skeleton_sample_anim( sk, player.mdl.anim_push, pt, bpose );
956 else
957 skeleton_sample_anim( sk, player.mdl.anim_push_reverse, pt, bpose );
958
959 skeleton_lerp_pose( sk, apose, bpose, player.fpush, apose );
960
961 /* trick setup */
962 float jump_start_frame = 14.0f/30.0f;
963 float setup_frame = player.jump * jump_start_frame,
964 setup_blend = vg_minf( player.jump*5.0f, 1.0f );
965
966 float jump_frame = (vg_time - player.jump_time) + jump_start_frame;
967 if( jump_frame >= jump_start_frame && jump_frame <= (40.0f/30.0f) )
968 setup_frame = jump_frame;
969
970 struct skeleton_anim *jump_anim = player.jump_dir?
971 player.mdl.anim_ollie:
972 player.mdl.anim_ollie_reverse;
973
974 skeleton_sample_anim_clamped( sk, jump_anim, setup_frame, bpose );
975 skeleton_lerp_pose( sk, apose, bpose, setup_blend, ground_pose );
976 }
977
978 mdl_keyframe air_pose[32];
979 {
980 float target = -vg_get_axis("horizontal");
981 player.fairdir = vg_lerpf( player.fairdir, target, 0.04f );
982
983 float air_frame = (player.fairdir*0.5f+0.5f) * (15.0f/30.0f);
984
985 skeleton_sample_anim( sk, player.mdl.anim_air, air_frame, apose );
986
987 static v2f grab_choice;
988 v2_lerp( grab_choice, (v2f){ vg_get_axis("h1"), vg_get_axis("v1") },
989 0.04f, grab_choice );
990
991 float ang = atan2f( grab_choice[0], grab_choice[1] ),
992 ang_unit = (ang+VG_PIf) * (1.0f/VG_TAUf),
993 grab_frame = ang_unit * (15.0f/30.0f);
994
995 skeleton_sample_anim( sk, player.mdl.anim_grabs, grab_frame, bpose );
996 skeleton_lerp_pose( sk, apose, bpose, player.grab, air_pose );
997 }
998
999 skeleton_lerp_pose( sk, ground_pose, air_pose, player.ffly, apose );
1000
1001 float add_grab_mod = player.ffly * player.grab;
1002
1003 /* additive effects */
1004 apose[player.mdl.id_hip-1].co[0] += offset[0]*add_grab_mod;
1005 apose[player.mdl.id_hip-1].co[2] += offset[2]*add_grab_mod;
1006 apose[player.mdl.id_ik_hand_l-1].co[0] += offset[0]*add_grab_mod;
1007 apose[player.mdl.id_ik_hand_l-1].co[2] += offset[2]*add_grab_mod;
1008 apose[player.mdl.id_ik_hand_r-1].co[0] += offset[0]*add_grab_mod;
1009 apose[player.mdl.id_ik_hand_r-1].co[2] += offset[2]*add_grab_mod;
1010 apose[player.mdl.id_ik_elbow_l-1].co[0] += offset[0]*add_grab_mod;
1011 apose[player.mdl.id_ik_elbow_l-1].co[2] += offset[2]*add_grab_mod;
1012 apose[player.mdl.id_ik_elbow_r-1].co[0] += offset[0]*add_grab_mod;
1013 apose[player.mdl.id_ik_elbow_r-1].co[2] += offset[2]*add_grab_mod;
1014
1015 skeleton_apply_pose( &player.mdl.sk, apose, k_anim_apply_defer_ik );
1016 skeleton_apply_ik_pass( &player.mdl.sk );
1017 skeleton_apply_pose( &player.mdl.sk, apose, k_anim_apply_deffered_only );
1018
1019 v3_copy( player.mdl.sk.final_mtx[player.mdl.id_head-1][3],
1020 player.mdl.cam_pos );
1021 skeleton_apply_inverses( &player.mdl.sk );
1022 skeleton_apply_transform( &player.mdl.sk, player.rb.to_world );
1023
1024 skeleton_debug( &player.mdl.sk );
1025 }
1026
1027 static void player_camera_update(void)
1028 {
1029 /* Update camera matrices */
1030 m4x3_identity( player.camera );
1031 m4x3_rotate_y( player.camera, -player.angles[0] );
1032 m4x3_rotate_x( player.camera, -player.angles[1] );
1033 v3_copy( player.camera_pos, player.camera[3] );
1034 m4x3_invert_affine( player.camera, player.camera_inverse );
1035 }
1036
1037 static void player_animate_death_cam(void)
1038 {
1039 v3f delta;
1040 v3f head_pos;
1041 v3_copy( player.mdl.ragdoll[0].rb.co, head_pos );
1042
1043 v3_sub( head_pos, player.camera_pos, delta );
1044 v3_normalize( delta );
1045
1046 v3f follow_pos;
1047 v3_muladds( head_pos, delta, -2.5f, follow_pos );
1048 v3_lerp( player.camera_pos, follow_pos, 0.1f, player.camera_pos );
1049
1050 /*
1051 * Make sure the camera stays above the ground
1052 */
1053 v3f min_height = {0.0f,1.0f,0.0f};
1054
1055 v3f sample;
1056 v3_add( player.camera_pos, min_height, sample );
1057 ray_hit hit;
1058 hit.dist = min_height[1]*2.0f;
1059
1060 if( ray_world( sample, (v3f){0.0f,-1.0f,0.0f}, &hit ))
1061 v3_add( hit.pos, min_height, player.camera_pos );
1062
1063 player.camera_pos[1] =
1064 vg_maxf( wrender.height + 2.0f, player.camera_pos[1] );
1065
1066 player.angles[0] = atan2f( delta[0], -delta[2] );
1067 player.angles[1] = -asinf( delta[1] );
1068 }
1069
1070 static void player_animate_camera(void)
1071 {
1072 static v3f lerp_cam = {0.0f,0.0f,0.0f};
1073 v3f cam_pos;
1074
1075 player.fonboard = vg_lerpf(player.fonboard, player.on_board, ktimestep*1.0f);
1076
1077 if( player.on_board )
1078 {
1079 v3f offs = { -0.4f, 0.15f, 0.0f };
1080 v3_lerp( lerp_cam, player.mdl.cam_pos, 0.8f, lerp_cam );
1081 v3_add( lerp_cam, offs, cam_pos );
1082
1083 /* Look angles */
1084 v3_lerp( player.vl, player.rb.v, 0.05f, player.vl );
1085
1086 float yaw = atan2f( player.vl[0], -player.vl[2] ),
1087 pitch = atan2f( -player.vl[1],
1088 sqrtf(
1089 player.vl[0]*player.vl[0] + player.vl[2]*player.vl[2]
1090 )) * 0.7f;
1091
1092 player.angles[0] = yaw;
1093 player.angles[1] = vg_lerpf( player.angles[1], pitch + 0.30f,
1094 player.fonboard );
1095
1096 /* Camera shake */
1097 static v2f shake_damp = {0.0f,0.0f};
1098 v2f shake = { vg_randf()-0.5f, vg_randf()-0.5f };
1099 v2_muls( shake, v3_length(player.rb.v)*0.3f
1100 * (1.0f+fabsf(player.slip)), shake);
1101
1102 v2_lerp( shake_damp, shake, 0.01f, shake_damp );
1103 shake_damp[0] *= 0.2f;
1104
1105 v2_muladds( player.angles, shake_damp, 0.1f, player.angles );
1106 m4x3_mulv( player.rb.to_world, cam_pos, player.camera_pos );
1107 }
1108 else
1109 {
1110 float speed = ktimestep * k_look_speed;
1111 player.angles[0] += vg_get_axis( "horizontal" ) * speed;
1112 player.angles[1] += vg_get_axis( "vertical" ) * speed;
1113
1114 player.angles[1] = vg_clampf( player.angles[1],
1115 -k_pitch_limit, k_pitch_limit );
1116
1117 float s = sinf(player.angles[0]) * 0.2f,
1118 c = -cosf(player.angles[0]) * 0.2f;
1119 v3f forward_dir = { s,0.15f,c };
1120
1121
1122 m4x3f mtx;
1123 v4f rot;
1124 q_axis_angle( rot, (v3f){0.0f,1.0f,0.0f},
1125 -player.angles[0] -VG_PIf*0.5f );
1126 q_m3x3( rot, mtx );
1127 v3_copy( player.rb.to_world[3], mtx[3] );
1128
1129 m4x3_mulv( mtx, player.mdl.cam_pos, cam_pos );
1130 v3_add( cam_pos, forward_dir, player.camera_pos );
1131 v3_lerp( player.vl, player.rb.v, 0.3f, player.vl );
1132 }
1133 }
1134
1135 /*
1136 * Audio
1137 */
1138 static void player_audio(void)
1139 {
1140 float speed = vg_minf(v3_length( player.rb.v )*0.1f,1.0f),
1141 attn = v3_dist( player.rb.co, player.camera[3] )+1.0f;
1142 attn = (1.0f/(attn*attn)) * speed;
1143
1144 static float air = 0.0f;
1145 air = vg_lerpf(air, player.in_air? 1.0f: 0.0f, 0.7f);
1146
1147 v3f ears = { 1.0f,0.0f,0.0f };
1148 v3f delta;
1149
1150 v3_sub( player.rb.co, player.camera[3], delta );
1151 v3_normalize( delta );
1152 m3x3_mulv( player.camera, ears, ears );
1153
1154 float pan = v3_dot( ears, delta );
1155 audio_player0.pan = pan;
1156 audio_player1.pan = pan;
1157 audio_player2.pan = pan;
1158
1159 if( freecam )
1160 {
1161 audio_player0.vol = 0.0f;
1162 audio_player1.vol = 0.0f;
1163 audio_player2.vol = 0.0f;
1164 }
1165 else
1166 {
1167 if( player.is_dead )
1168 {
1169 audio_player0.vol = 0.0f;
1170 audio_player1.vol = 0.0f;
1171 audio_player2.vol = 0.0f;
1172 }
1173 else
1174 {
1175 float slide = vg_clampf( fabsf(player.slip), 0.0f, 1.0f );
1176 audio_player0.vol = (1.0f-air)*attn*(1.0f-slide);
1177 audio_player1.vol = air *attn;
1178 audio_player2.vol = (1.0f-air)*attn*slide;
1179 }
1180 }
1181 }
1182
1183 /*
1184 * Public Endpoints
1185 */
1186 static float *player_cam_pos(void)
1187 {
1188 return player.camera_pos;
1189 }
1190
1191 static int reset_player( int argc, char const *argv[] )
1192 {
1193 struct respawn_point *rp = NULL, *r;
1194
1195 if( argc == 1 )
1196 {
1197 for( int i=0; i<world.spawn_count; i++ )
1198 {
1199 r = &world.spawns[i];
1200 if( !strcmp( r->name, argv[0] ) )
1201 {
1202 rp = r;
1203 break;
1204 }
1205 }
1206
1207 if( !rp )
1208 vg_warn( "No spawn named '%s'\n", argv[0] );
1209 }
1210
1211 if( !rp )
1212 {
1213 float min_dist = INFINITY;
1214
1215 for( int i=0; i<world.spawn_count; i++ )
1216 {
1217 r = &world.spawns[i];
1218 float d = v3_dist2( r->co, player.rb.co );
1219
1220 vg_info( "Dist %s : %f\n", r->name, d );
1221 if( d < min_dist )
1222 {
1223 min_dist = d;
1224 rp = r;
1225 }
1226 }
1227 }
1228
1229 if( !rp )
1230 {
1231 vg_error( "No spawn found\n" );
1232 if( !world.spawn_count )
1233 return 0;
1234
1235 rp = &world.spawns[0];
1236 }
1237
1238 v4_copy( rp->q, player.rb.q );
1239 v3_copy( rp->co, player.rb.co );
1240
1241 player.vswitch = 1.0f;
1242 player.slip_last = 0.0f;
1243 player.is_dead = 0;
1244 player.in_air = 1;
1245 m3x3_identity( player.vr );
1246
1247 player.mdl.shoes[0] = 1;
1248 player.mdl.shoes[1] = 1;
1249
1250 rb_update_transform( &player.rb );
1251 m3x3_mulv( player.rb.to_world, (v3f){ 0.0f, 0.0f, -1.2f }, player.rb.v );
1252 m3x3_identity( player.gate_vr_frame );
1253 m3x3_identity( player.gate_vr_pstep_frame );
1254
1255 player.rb_gate_frame = player.rb;
1256 return 1;
1257 }
1258
1259 static void player_update(void)
1260 {
1261 for( int i=0; i<player.land_log_count; i++ )
1262 draw_cross( player.land_target_log[i],
1263 player.land_target_colours[i], 0.25f);
1264
1265 if( vg_get_axis("grabl")>0.0f)
1266 {
1267 player.rb = player.rb_gate_frame;
1268 m3x3_copy( player.gate_vr_frame, player.vr );
1269 m3x3_copy( player.gate_vr_pstep_frame, player.vr_pstep );
1270 player.is_dead = 0;
1271 player.in_air = 1;
1272 m3x3_identity( player.vr );
1273
1274 player.mdl.shoes[0] = 1;
1275 player.mdl.shoes[1] = 1;
1276
1277 world_routes_notify_reset();
1278 }
1279
1280 if( vg_get_button_down( "switchmode" ) )
1281 {
1282 player.on_board ^= 0x1;
1283 }
1284
1285 if( glfwGetKey( vg_window, GLFW_KEY_O ) )
1286 {
1287 character_ragdoll_copypose( &player.mdl, player.rb.v );
1288 player.is_dead = 1;
1289 }
1290
1291 if( player.is_dead )
1292 {
1293 character_ragdoll_iter( &player.mdl );
1294 character_debug_ragdoll( &player.mdl );
1295
1296 if( !freecam )
1297 player_animate_death_cam();
1298 }
1299 else
1300 {
1301 player_do_motion();
1302 player_animate();
1303
1304 if( !freecam )
1305 player_animate_camera();
1306 }
1307
1308 if( freecam )
1309 player_freecam();
1310
1311 player_camera_update();
1312 player_audio();
1313 }
1314
1315 static void draw_player(void)
1316 {
1317 if( player.is_dead )
1318 character_mimic_ragdoll( &player.mdl );
1319
1320 shader_viewchar_use();
1321 vg_tex2d_bind( &tex_characters, 0 );
1322 shader_viewchar_uTexMain( 0 );
1323 shader_viewchar_uCamera( player.camera[3] );
1324 shader_viewchar_uPv( vg_pv );
1325 shader_link_standard_ub( _shader_viewchar.id, 2 );
1326 glUniformMatrix4x3fv( _uniform_viewchar_uTransforms,
1327 player.mdl.sk.bone_count,
1328 0,
1329 (float *)player.mdl.sk.final_mtx );
1330
1331 mesh_bind( &player.mdl.mesh );
1332 mesh_draw( &player.mdl.mesh );
1333 }
1334
1335 #endif /* PLAYER_H */