LPR - Walking
[carveJwlIkooP6JGAAIwe30JlM.git] / player.h
1 /*
2 * Copyright (C) 2021-2023 Mt.ZERO Software, Harry Godden - All Rights Reserved
3 */
4
5 #define PLAYER_H
6 #ifndef PLAYER_H
7 #define PLAYER_H
8
9 #define PLAYER_REWIND_FRAMES 60*4
10
11 #include "conf.h"
12 #include "audio.h"
13 #include "common.h"
14 #include "world.h"
15 #include "skeleton.h"
16 #include "bvh.h"
17
18
19 VG_STATIC int freecam = 0;
20 VG_STATIC int walk_grid_iterations = 1;
21 VG_STATIC float fc_speed = 10.0f;
22 VG_STATIC int cl_thirdperson = 0;
23
24 /*
25 * -----------------------------------------------------------------------------
26 * Memory
27 * -----------------------------------------------------------------------------
28 */
29
30 VG_STATIC struct gplayer
31 {
32 rigidbody rb, rb_frame;
33 v3f co, angles; /* used as transfer between controllers */
34
35 enum player_controller
36 {
37 k_player_controller_walk,
38 k_player_controller_skate,
39 k_player_controller_ragdoll,
40 k_player_controller_mountain_skate,
41 k_player_controller_snowboard,
42 k_player_controller_drive
43 }
44 controller,
45 controller_frame;
46
47 m4x3f visual_transform,
48 inv_visual_transform;
49
50 int is_dead, death_tick_allowance, rewinding;
51 int rewind_sound_wait;
52
53
54 v3f handl_target, handr_target,
55 handl, handr;
56
57 /* Input */
58 struct input_binding *input_js1h,
59 *input_js1v,
60 *input_js2h,
61 *input_js2v,
62 *input_jump,
63 *input_push,
64 *input_walk,
65 *input_walkh,
66 *input_walkv,
67 *input_switch_mode,
68 *input_reset,
69 *input_grab;
70
71 /* Camera */
72 float air_time;
73 v3f camera_pos, smooth_localcam;
74
75 struct rewind_frame
76 {
77 v3f pos;
78 v3f ang;
79 }
80 *rewind_buffer;
81 u32 rewind_incrementer,
82 rewind_length;
83
84 float rewind_time, rewind_total_length, rewind_predicted_time;
85 double diag_rewind_start, diag_rewind_time;
86 float dist_accum;
87
88 /* animation */
89 double jump_time;
90 float fslide,
91 fdirz, fdirx,
92 fstand,
93 ffly,
94 fpush,
95 fairdir,
96 fsetup,
97 walk_timer,
98 fjump,
99 fonboard,
100 frun,
101 fgrind;
102
103 v3f board_offset;
104 v4f board_rotation;
105
106 float walk;
107 int step_phase;
108 enum mdl_surface_prop surface_prop;
109
110 /* player model */
111 struct player_model
112 {
113 glmesh player_meshes[3];
114
115 mdl_context meta;
116 struct skeleton sk;
117 struct skeleton_anim *anim_stand,
118 *anim_highg,
119 *anim_slide,
120 *anim_air,
121 *anim_push, *anim_push_reverse,
122 *anim_ollie, *anim_ollie_reverse,
123 *anim_grabs, *anim_stop,
124 *anim_walk, *anim_run, *anim_idle,
125 *anim_jump;
126
127 u32 id_hip,
128 id_ik_hand_l,
129 id_ik_hand_r,
130 id_ik_elbow_l,
131 id_ik_elbow_r,
132 id_head,
133 id_ik_foot_l,
134 id_ik_foot_r,
135 id_board;
136
137 v3f cam_pos;
138
139 struct ragdoll_part
140 {
141 u32 bone_id;
142 //v3f offset;
143
144 /* Collider transform relative to bone */
145 m4x3f collider_mtx,
146 inv_collider_mtx;
147
148 u32 use_limits;
149 v3f limits[2];
150
151 rigidbody rb;
152 u32 parent;
153 u32 colour;
154 }
155 ragdoll[32];
156 u32 ragdoll_count;
157
158 rb_constr_pos position_constraints[32];
159 u32 position_constraints_count;
160
161 rb_constr_swingtwist cone_constraints[32];
162 u32 cone_constraints_count;
163
164 int shoes[2];
165 }
166 mdl;
167 }
168 player__OLD
169
170 #if 0
171 =
172 {
173 .collide_front = { .type = k_rb_shape_sphere, .inf.sphere.radius = 0.3f },
174 .collide_back = { .type = k_rb_shape_sphere, .inf.sphere.radius = 0.3f }
175 }
176 #endif
177
178 ;
179
180 /*
181 * API
182 */
183 VG_STATIC float *player_get_pos(void);
184 VG_STATIC void player_kill(void);
185 VG_STATIC float *player_cam_pos(void);
186 VG_STATIC void player_save_frame(void);
187 VG_STATIC void player_restore_frame(void);
188 VG_STATIC void player_save_rewind_frame(void);
189
190 /*
191 * Submodules
192 */
193 VG_STATIC void player_mouseview(void);
194
195 #include "player_physics.h"
196 #include "player_physics_skate.h"
197 #include "player_physics_walk.h"
198 #include "player_ragdoll.h"
199 #include "player_model.h"
200 #include "player_animation.h"
201 #include "player_audio.h"
202
203 /*
204 * player_physics_<INTERFACE>_<SUB-INTERFACE>
205 *
206 *
207 *
208 */
209
210 /*
211 * -----------------------------------------------------------------------------
212 * Events
213 * -----------------------------------------------------------------------------
214 */
215 VG_STATIC int kill_player( int argc, char const *argv[] );
216 VG_STATIC int reset_player( int argc, char const *argv[] );
217 VG_STATIC void reset_player_poll( int argc, char const *argv[] );
218
219 VG_STATIC void player_init(void) /* 1 */
220 {
221 #if 0
222 player.input_js1h = vg_create_named_input( "steer-h", k_input_type_axis );
223 player.input_js1v = vg_create_named_input( "steer-v", k_input_type_axis );
224 player.input_grab = vg_create_named_input( "grab", k_input_type_axis_norm );
225 player.input_js2h = vg_create_named_input( "grab-h", k_input_type_axis );
226 player.input_js2v = vg_create_named_input( "grab-v", k_input_type_axis );
227 player.input_jump = vg_create_named_input( "jump", k_input_type_button );
228 player.input_push = vg_create_named_input( "push", k_input_type_button );
229 player.input_walk = vg_create_named_input( "walk", k_input_type_button );
230
231 player.input_walkh = vg_create_named_input( "walk-h",
232 k_input_type_axis );
233 player.input_walkv = vg_create_named_input( "walk-v",
234 k_input_type_axis );
235
236
237 player.input_switch_mode = vg_create_named_input( "switch-mode",
238 k_input_type_button );
239 player.input_reset = vg_create_named_input( "reset", k_input_type_button );
240
241 const char *default_cfg[] =
242 {
243 "bind steer-h gp-ls-h",
244 "bind -steer-h a",
245 "bind +steer-h d",
246
247 "bind steer-v gp-ls-v",
248 "bind -steer-v w",
249 "bind +steer-v s",
250
251 "bind grab gp-rt",
252 "bind +grab shift",
253 "bind grab-h gp-rs-h",
254 "bind grab-v gp-rs-v",
255
256 "bind jump space",
257 "bind jump gp-a",
258
259 "bind push gp-b",
260 "bind push w",
261
262 "bind walk shift",
263 "bind walk gp-ls",
264
265 "bind walk-h gp-ls-h",
266 "bind walk-v -gp-ls-v",
267 "bind +walk-h d",
268 "bind -walk-h a",
269 "bind +walk-v w",
270 "bind -walk-v s",
271
272 "bind reset gp-lb",
273 "bind reset r",
274
275 "bind switch-mode gp-y",
276 "bind switch-mode e",
277 };
278
279 for( int i=0; i<vg_list_size(default_cfg); i++ )
280 vg_execute_console_input(default_cfg[i]);
281 #endif
282
283 rb_init( &player.rb );
284
285 VG_VAR_F32( k_walkspeed );
286 VG_VAR_F32( k_stopspeed );
287 VG_VAR_F32( k_airspeed );
288 VG_VAR_F32( k_walk_friction );
289 VG_VAR_F32( k_walk_air_accel );
290 VG_VAR_F32( k_runspeed );
291 VG_VAR_F32( k_walk_accel );
292
293 VG_VAR_I32( freecam );
294 VG_VAR_I32( cl_thirdperson );
295 VG_VAR_F32_PERSISTENT( fc_speed );
296
297 /* TODO: NOT PERSISTENT */
298 VG_VAR_F32( k_ragdoll_limit_scale );
299 VG_VAR_I32( k_ragdoll_div );
300 VG_VAR_I32( k_ragdoll_debug_collider );
301 VG_VAR_I32( k_ragdoll_debug_constraints );
302
303 VG_VAR_F32( k_friction_lat );
304
305 VG_VAR_F32( k_cog_spring );
306 VG_VAR_F32( k_cog_damp );
307
308 VG_VAR_F32( k_cog_mass_ratio );
309 VG_VAR_F32( k_downforce );
310
311 VG_VAR_F32( k_spring_force );
312 VG_VAR_F32( k_spring_dampener );
313 VG_VAR_F32( k_spring_angular );
314
315 VG_VAR_F32( k_mmthrow_scale );
316 VG_VAR_F32( k_mmcollect_lat );
317 VG_VAR_F32( k_mmcollect_vert );
318 VG_VAR_F32( k_mmdecay );
319
320 vg_function_push( (struct vg_cmd){
321 .name = "reset",
322 .function = reset_player,
323 .poll_suggest = reset_player_poll
324 });
325
326 vg_function_push( (struct vg_cmd){
327 .name = "kill",
328 .function = kill_player
329 });
330
331 /* HACK */
332 rb_register_cvar();
333
334 player.rewind_length = 0;
335 player.rewind_buffer =
336 vg_linear_alloc( vg_mem.rtmemory,
337 sizeof(struct rewind_frame) * PLAYER_REWIND_FRAMES );
338
339 player_model_init();
340 }
341
342 VG_STATIC void player_save_rewind_frame(void)
343 {
344 if( player.rewind_length < PLAYER_REWIND_FRAMES )
345 {
346 struct rewind_frame *fr =
347 &player.rewind_buffer[ player.rewind_length ++ ];
348
349 v2_copy( player.angles, fr->ang );
350 v3_copy( player.camera_pos, fr->pos );
351
352 player.rewind_incrementer = 0;
353
354 if( player.rewind_length > 1 )
355 {
356 player.rewind_total_length +=
357 v3_dist( player.rewind_buffer[player.rewind_length-1].pos,
358 player.rewind_buffer[player.rewind_length-2].pos );
359 }
360 }
361 }
362
363
364 /* disaster */
365 VG_STATIC int menu_enabled(void);
366 #include "menu.h"
367
368 VG_STATIC void player_do_motion(void);
369 /*
370 * Free camera movement
371 */
372 VG_STATIC void player_mouseview(void)
373 {
374 if( menu_enabled() )
375 return;
376
377 v2_muladds( player.angles, vg.mouse_delta, 0.0025f, player.angles );
378
379 if( vg_input.controller_should_use_trackpad_look )
380 {
381 static v2f last_input;
382 static v2f vel;
383 static v2f vel_smooth;
384
385 v2f input = { player.input_js2h->axis.value,
386 player.input_js2v->axis.value };
387
388 if( (v2_length2(last_input) > 0.001f) && (v2_length2(input) > 0.001f) )
389 {
390 v2_sub( input, last_input, vel );
391 v2_muls( vel, 1.0f/vg.time_delta, vel );
392 }
393 else
394 {
395 v2_zero( vel );
396 }
397
398 v2_lerp( vel_smooth, vel, vg.time_delta*8.0f, vel_smooth );
399
400 v2_muladds( player.angles, vel_smooth, vg.time_delta, player.angles );
401 v2_copy( input, last_input );
402 }
403 else
404 {
405 player.angles[0] += player.input_js2h->axis.value * vg.time_delta * 4.0f;
406 player.angles[1] += player.input_js2v->axis.value * vg.time_delta * 4.0f;
407 }
408
409 player.angles[1] = vg_clampf( player.angles[1], -VG_PIf*0.5f, VG_PIf*0.5f );
410 }
411
412 /* Deal with input etc */
413 VG_STATIC void player_update_pre(void)
414 {
415
416 {
417 v3f ra, rb, rx;
418 v3_copy( main_camera.pos, ra );
419 v3_muladds( ra, main_camera.transform[2], -10.0f, rb );
420
421 float t;
422 if( spherecast_world( ra, rb, 0.4f, &t, rx ) != -1 )
423 {
424 m4x3f mtx;
425 m3x3_identity( mtx );
426 v3_lerp( ra, rb, t, mtx[3] );
427
428 debug_sphere( mtx, 0.4f, 0xff00ff00 );
429
430 v3f x1;
431 v3_muladds( mtx[3], rx, 0.4f, x1 );
432 vg_line( mtx[3], x1, 0xffffffff );
433 }
434 }
435
436 #if 0
437
438 vg_line_pt3( phys->cog, 0.10f, 0xffffffff );
439 vg_line_pt3( phys->cog, 0.09f, 0xffffffff );
440 vg_line_pt3( phys->cog, 0.08f, 0xffffffff );
441 vg_line( phys->cog, phys->rb.co, 0xff000000 );
442
443 v3f spring_end;
444 v3f throw_end, p0, p1;
445 v3_muladds( phys->rb.co, phys->rb.up, 1.0f, spring_end );
446 v3_muladds( spring_end, phys->throw_v, 1.0f, throw_end );
447 v3_muladds( spring_end, player.debug_mmcollect_lat, 1.0f, p0 );
448 v3_muladds( spring_end, player.debug_mmcollect_vert, 1.0f, p1 );
449 vg_line( spring_end, throw_end, VG__RED );
450 vg_line( spring_end, p0, VG__GREEN );
451 vg_line( spring_end, p1, VG__BLUE );
452 #endif
453
454 if( player.rewinding )
455 return;
456
457 if( vg_input_button_down( player.input_reset ) && !menu_enabled() )
458 {
459 double delta = world.time - world.last_use;
460
461 if( (delta <= RESET_MAX_TIME) && (world.last_use != 0.0) )
462 {
463 player.rewinding = 1;
464 player.rewind_sound_wait = 1;
465 player.rewind_time = (float)player.rewind_length - 0.0001f;
466 player_save_rewind_frame();
467 audio_lock();
468 audio_play_oneshot( &audio_rewind[0], 1.0f );
469 audio_unlock();
470
471 /* based on analytical testing. DONT CHANGE!
472 *
473 * time taken: y = (x^(4/5)) * 74.5
474 * inverse : x = (2/149)^(4/5) * y^(4/5)
475 */
476
477 float constant = powf( 2.0f/149.0f, 4.0f/5.0f ),
478 curve = powf( player.rewind_total_length, 4.0f/5.0f );
479
480 player.rewind_predicted_time = constant * curve;
481 player.diag_rewind_start = vg.time;
482 player.diag_rewind_time = player.rewind_time;
483
484 player.is_dead = 0;
485 player.death_tick_allowance = 30;
486 player_restore_frame();
487
488 if( player.controller == k_player_controller_walk )
489 {
490 player.angles[0] = atan2f( -player.rb.forward[2],
491 -player.rb.forward[0] );
492 }
493
494 player.mdl.shoes[0] = 1;
495 player.mdl.shoes[1] = 1;
496
497 world_routes_notify_reset();
498
499 /* apply 1 frame of movement */
500 player_do_motion();
501 }
502 else
503 {
504 if( player.is_dead )
505 {
506 reset_player( 0, NULL );
507 }
508 else
509 {
510 /* cant do that */
511 audio_lock();
512 audio_play_oneshot( &audio_rewind[4], 1.0f );
513 audio_unlock();
514 }
515 }
516 }
517
518 if( vg_input_button_down( player.input_switch_mode ) && !menu_enabled() )
519 {
520 audio_lock();
521
522 #if 0
523 if( phys->controller == k_player_controller_walk )
524 {
525 phys->controller = k_player_controller_skate;
526
527 v3_muladds( phys->rb.v, phys->rb.forward, 0.2f, phys->rb.v );
528 audio_play_oneshot( &audio_lands[6], 1.0f );
529 }
530 else if( phys->controller == k_player_controller_skate )
531 {
532 phys->controller = k_player_controller_walk;
533
534 audio_play_oneshot( &audio_lands[5], 1.0f );
535 }
536 #endif
537
538 audio_unlock();
539 }
540
541 if( player.controller == k_player_controller_walk )
542 player_mouseview();
543 }
544
545 VG_STATIC void player_update_fixed(void) /* 2 */
546 {
547 if( player.rewinding )
548 return;
549
550 if( player.death_tick_allowance )
551 player.death_tick_allowance --;
552
553 if( player.is_dead )
554 {
555 player_ragdoll_iter();
556 }
557 else
558 {
559 player.rewind_incrementer ++;
560
561 if( player.rewind_incrementer > (u32)(0.25/VG_TIMESTEP_FIXED) )
562 {
563 player_save_rewind_frame();
564 }
565
566 player_do_motion();
567 }
568 }
569
570 VG_STATIC void player_update_post(void)
571 {
572 #if 0
573 for( int i=0; i<player.prediction_count; i++ )
574 {
575 struct land_prediction *p = &player.predictions[i];
576
577 for( int j=0; j<p->log_length - 1; j ++ )
578 vg_line( p->log[j], p->log[j+1], p->colour );
579
580 vg_line_cross( p->log[p->log_length-1], p->colour, 0.25f );
581
582 v3f p1;
583 v3_add( p->log[p->log_length-1], p->n, p1 );
584 vg_line( p->log[p->log_length-1], p1, 0xffffffff );
585 }
586 #endif
587
588 #if 0
589 if( player.is_dead )
590 {
591 player_debug_ragdoll();
592
593 if( !freecam )
594 player_animate_death_cam();
595 }
596 else
597 {
598 player_animate();
599
600 if( !freecam )
601 {
602 if( cl_thirdperson )
603 player_animate_camera_thirdperson();
604 else
605 player_animate_camera();
606 }
607 }
608
609 if( freecam )
610 #endif
611 player_freecam();
612
613 /* CAMERA POSITIONING: LAYER 0 */
614 v2_copy( player.angles, main_camera.angles );
615 v3_copy( player.camera_pos, main_camera.pos );
616
617 #if 0
618 if( player.rewinding )
619 {
620 if( player.rewind_time <= 0.0f )
621 {
622 double taken = vg.time - player.diag_rewind_start;
623 vg_success( "Rewind took (rt, pl, tl): %f, %f, %f\n",
624 taken, player.diag_rewind_time,
625 player.rewind_total_length );
626
627 player.rewinding = 0;
628 player.rewind_length = 1;
629 player.rewind_total_length = 0.0f;
630 player.rewind_incrementer = 0;
631 world.sky_target_rate = 1.0;
632 }
633 else
634 {
635 world.sky_target_rate = -100.0;
636 assert( player.rewind_length > 0 );
637
638 v2f override_angles;
639 v3f override_pos;
640
641 float budget = vg.time_delta,
642 overall_length = player.rewind_length;
643
644 world_routes_rollback_time( player.rewind_time / overall_length );
645
646 for( int i=0; (i<10)&&(player.rewind_time>0.0f)&&(budget>0.0f); i ++ )
647 {
648 /* Interpolate frames */
649 int i0 = floorf( player.rewind_time ),
650 i1 = VG_MIN( i0+1, player.rewind_length-1 );
651
652 struct rewind_frame *fr = &player.rewind_buffer[i0],
653 *fr1 = &player.rewind_buffer[i1];
654
655 float dist = vg_maxf( v3_dist( fr->pos, fr1->pos ), 0.001f ),
656 subl = vg_fractf( player.rewind_time ) + 0.001f,
657
658 sramp= 3.0f-(1.0f/(0.4f+0.4f*player.rewind_time)),
659 speed = sramp*28.0f + 0.5f*player.rewind_time,
660 mod = speed * (budget / dist),
661
662 advl = vg_minf( mod, subl ),
663 advt = (advl / mod) * budget;
664
665 player.dist_accum += speed * advt;
666 player.rewind_time -= advl;
667 budget -= advt;
668 }
669
670 player.rewind_time = vg_maxf( 0.0f, player.rewind_time );
671
672 float current_time = vg.time - player.diag_rewind_start,
673 remaining = player.rewind_predicted_time - current_time;
674
675 if( player.rewind_sound_wait )
676 {
677 if( player.rewind_predicted_time >= 6.5f )
678 {
679 if( remaining <= 6.5f )
680 {
681 audio_lock();
682 audio_play_oneshot( &audio_rewind[3], 1.0f );
683 audio_unlock();
684 player.rewind_sound_wait = 0;
685 }
686 }
687 else if( player.rewind_predicted_time >= 2.5f )
688 {
689 if( remaining <= 2.5f )
690 {
691 audio_lock();
692 audio_play_oneshot( &audio_rewind[2], 1.0f );
693 audio_unlock();
694 player.rewind_sound_wait = 0;
695 }
696 }
697 else if( player.rewind_predicted_time >= 1.5f )
698 {
699 if( remaining <= 1.5f )
700 {
701 audio_lock();
702 audio_play_oneshot( &audio_rewind[1], 1.0f );
703 audio_unlock();
704 player.rewind_sound_wait = 0;
705 }
706 }
707 }
708
709 int i0 = floorf( player.rewind_time ),
710 i1 = VG_MIN( i0+1, player.rewind_length-1 );
711
712 struct rewind_frame *fr = &player.rewind_buffer[i0],
713 *fr1 = &player.rewind_buffer[i1];
714
715 float sub = vg_fractf(player.rewind_time);
716
717 v3_lerp( fr->pos, fr1->pos, sub, override_pos );
718 override_angles[0] = vg_alerpf( fr->ang[0], fr1->ang[0], sub );
719 override_angles[1] = vg_lerpf ( fr->ang[1], fr1->ang[1], sub );
720
721 /* CAMERA POSITIONING: LAYER 1 */
722 float blend = (4.0f-player.rewind_time) * 0.25f,
723 c = vg_clampf( blend, 0.0f, 1.0f );
724
725 main_camera.angles[0] =
726 vg_alerpf(override_angles[0], player.angles[0], c);
727 main_camera.angles[1] =
728 vg_lerpf (override_angles[1], player.angles[1], c);
729 v3_lerp( override_pos, player.camera_pos, c, main_camera.pos );
730 }
731 }
732 #endif
733
734 camera_update_transform( &main_camera );
735 player_audio();
736 }
737
738 VG_STATIC void draw_player( camera *cam )
739 {
740 if( player.is_dead )
741 player_model_copy_ragdoll();
742
743 shader_viewchar_use();
744 vg_tex2d_bind( &tex_characters, 0 );
745 shader_viewchar_uTexMain( 0 );
746 shader_viewchar_uCamera( cam->transform[3] );
747 shader_viewchar_uPv( cam->mtx.pv );
748 shader_link_standard_ub( _shader_viewchar.id, 2 );
749 glUniformMatrix4x3fv( _uniform_viewchar_uTransforms,
750 player.mdl.sk.bone_count,
751 0,
752 (float *)player.mdl.sk.final_mtx );
753
754 mesh_bind( &player.mdl.player_meshes[cl_playermdl_id] );
755 mesh_draw( &player.mdl.player_meshes[cl_playermdl_id] );
756 }
757
758 VG_STATIC void player_do_motion(void)
759 {
760 if( world.water.enabled )
761 {
762 if( (player.rb.co[1] < 0.0f) && !player.is_dead )
763 {
764 audio_lock();
765 audio_player_set_flags( &audio_player_extra, AUDIO_FLAG_SPACIAL_3D );
766 audio_player_set_position( &audio_player_extra, player.rb.co );
767 audio_player_set_vol( &audio_player_extra, 20.0f );
768 audio_player_playclip( &audio_player_extra, &audio_splash );
769 audio_unlock();
770
771 player_kill();
772 }
773 }
774
775 v3f prevco;
776 v3_copy( player.rb.co, prevco );
777
778 if( player.controller == k_player_controller_skate )
779 {
780 #if 0
781 player_skate_update();
782 #endif
783 }
784 else
785 player_walk_physics( &player_walky );
786
787
788 /* Real angular velocity integration */
789 #if 0
790 v3_lerp( phys->rb.w, (v3f){0.0f,0.0f,0.0f}, 0.125f*0.5f, phys->rb.w );
791 if( v3_length2( phys->rb.w ) > 0.0f )
792 {
793 v4f rotation;
794 v3f axis;
795 v3_copy( phys->rb.w, axis );
796
797 float mag = v3_length( axis );
798 v3_divs( axis, mag, axis );
799 q_axis_angle( rotation, axis, mag*k_rb_delta );
800 q_mul( rotation, phys->rb.q, phys->rb.q );
801 }
802
803 /* Faux angular velocity */
804 v4f rotate;
805
806 float lerpq = (player_skate.activity == k_skate_activity_air)? 0.04f: 0.3f;
807 phys->siY = vg_lerpf( phys->siY, phys->iY, lerpq );
808
809 q_axis_angle( rotate, phys->rb.up, phys->siY );
810 q_mul( rotate, phys->rb.q, phys->rb.q );
811 phys->iY = 0.0f;
812 #endif
813
814 /*
815 * Gate intersection, by tracing a line over the gate planes
816 */
817 #if 0
818 for( int i=0; i<world.gate_count; i++ )
819 {
820 struct route_gate *rg = &world.gates[i];
821 teleport_gate *gate = &rg->gate;
822
823 if( gate_intersect( gate, phys->rb.co, prevco ) )
824 {
825 m4x3_mulv( gate->transport, phys->rb.co, phys->rb.co );
826 m4x3_mulv( gate->transport, phys->cog, phys->cog );
827 m3x3_mulv( gate->transport, phys->cog_v, phys->cog_v );
828 m3x3_mulv( gate->transport, phys->rb.v, phys->rb.v );
829 m3x3_mulv( gate->transport, phys->vl, phys->vl );
830 m3x3_mulv( gate->transport, phys->v_last, phys->v_last );
831 m3x3_mulv( gate->transport, phys->m, phys->m );
832 m3x3_mulv( gate->transport, phys->bob, phys->bob );
833
834 /* Pre-emptively edit the camera matrices so that the motion vectors
835 * are correct */
836 m4x3f transport_i;
837 m4x4f transport_4;
838 m4x3_invert_affine( gate->transport, transport_i );
839 m4x3_expand( transport_i, transport_4 );
840 m4x4_mul( main_camera.mtx.pv, transport_4, main_camera.mtx.pv );
841 m4x4_mul( main_camera.mtx.v, transport_4, main_camera.mtx.v );
842
843 v4f transport_rotation;
844 m3x3_q( gate->transport, transport_rotation );
845 q_mul( transport_rotation, phys->rb.q, phys->rb.q );
846
847 world_routes_activate_gate( i );
848
849 if( phys->controller == k_player_controller_walk )
850 {
851 v3f fwd_dir = {cosf(player.angles[0]),
852 0.0f,
853 sinf(player.angles[0])};
854 m3x3_mulv( gate->transport, fwd_dir, fwd_dir );
855
856 player.angles[0] = atan2f( fwd_dir[2], fwd_dir[0] );
857 }
858
859 player.rewind_length = 0;
860 player.rewind_total_length = 0.0f;
861 player.rewind_incrementer = 10000;
862 player_save_frame();
863
864 audio_lock();
865 audio_play_oneshot( &audio_gate_pass, 1.0f );
866 audio_unlock();
867 break;
868 }
869 }
870 #endif
871
872 rb_update_transform( &player.rb );
873 }
874
875 /*
876 * -----------------------------------------------------------------------------
877 * API implementation
878 * -----------------------------------------------------------------------------
879 */
880
881 VG_STATIC float *player_get_pos(void)
882 {
883 return player.rb.co;
884 }
885
886 VG_STATIC void player_kill(void)
887 {
888 if( player.death_tick_allowance == 0 )
889 {
890 player.is_dead = 1;
891 player_ragdoll_copy_model( player.rb.v );
892 }
893 }
894
895 VG_STATIC float *player_cam_pos(void)
896 {
897 return player.camera_pos;
898 }
899
900
901 VG_STATIC void player_save_frame(void)
902 {
903 player.controller_frame = player.controller;
904
905 /* TODO <interface>->save() */
906 }
907
908 VG_STATIC void player_restore_frame(void)
909 {
910 player.controller = player.controller_frame;
911
912 /* TODO <interface>->load() */
913 }
914
915 #endif /* PLAYER_H */