fix geo crash & backflips
[carveJwlIkooP6JGAAIwe30JlM.git] / player_device_skate.h
1 #ifndef PLAYER_DEVICE_SKATE_H
2 #define PLAYER_DEVICE_SKATE_H
3
4 #include "player_interface.h"
5 #include "skeleton.h"
6 #include "player_model.h"
7 #include "player_device_common.h"
8
9 VG_STATIC
10 struct player_device_skate
11 {
12 struct
13 {
14 enum skate_activity
15 {
16 k_skate_activity_air,
17 k_skate_activity_ground,
18 k_skate_activity_grind
19 }
20 activity,
21 activity_prev;
22
23 float steery,
24 steerx,
25 steery_s,
26 steerx_s,
27 reverse,
28 slip;
29
30 v3f flip_axis;
31 float flip_time,
32 flip_rate;
33
34 m3x3f velocity_bias,
35 velocity_bias_pstep;
36 v3f apex;
37
38 int lift_frames;
39
40 v3f throw_v;
41 v3f cog_v, cog;
42
43 float grabbing;
44 v2f grab_mouse_delta;
45
46 int charging_jump, jump_dir;
47 float jump_charge;
48 double jump_time;
49
50 double start_push,
51 cur_push;
52
53 v3f prev_pos;
54
55 /* FIXME: Sensible names */
56 v3f vl, /* 1st */
57 posl, dirl; /* 3rd */
58 }
59 state,
60 state_gate_storage;
61
62 struct land_prediction
63 {
64 v3f log[50];
65 v3f n;
66 v3f apex;
67 u32 log_length;
68 float score,
69 land_dist;
70
71 enum prediction_type
72 {
73 k_prediction_none,
74 k_prediction_land,
75 k_prediction_grind
76 }
77 type;
78
79 u32 colour;
80 }
81 predictions[22];
82 u32 prediction_count;
83 float land_dist;
84 v3f land_normal;
85
86 /* animation */
87 struct skeleton_anim *anim_stand, *anim_highg, *anim_slide,
88 *anim_air,
89 *anim_push, *anim_push_reverse,
90 *anim_ollie, *anim_ollie_reverse,
91 *anim_grabs, *anim_stop;
92 rb_sphere sphere_front, sphere_back;
93 v3f board_offset;
94 v4f board_rotation;
95
96 float blend_slide,
97 blend_z,
98 blend_x,
99 blend_fly,
100 blend_stand,
101 blend_push,
102 blend_jump,
103 blend_airdir;
104
105 v2f wobble;
106
107 float debug_normal_pressure;
108 u32 device_id_walk;
109 }
110 localplayer_device_skate;
111
112 VG_STATIC void player_skate_bind( player_device *dev,
113 player_interface *player )
114 {
115 struct player_device_skate *s = dev->storage;
116 struct player_avatar *av = player->playeravatar;
117 struct skeleton *sk = &av->sk;
118
119 rb_update_transform( &player->rb );
120 s->anim_stand = skeleton_get_anim( sk, "pose_stand" );
121 s->anim_highg = skeleton_get_anim( sk, "pose_highg" );
122 s->anim_air = skeleton_get_anim( sk, "pose_air" );
123 s->anim_slide = skeleton_get_anim( sk, "pose_slide" );
124 s->anim_push = skeleton_get_anim( sk, "push" );
125 s->anim_push_reverse = skeleton_get_anim( sk, "push_reverse" );
126 s->anim_ollie = skeleton_get_anim( sk, "ollie" );
127 s->anim_ollie_reverse = skeleton_get_anim( sk, "ollie_reverse" );
128 s->anim_grabs = skeleton_get_anim( sk, "grabs" );
129
130 s->device_id_walk = player_get_device( player, "walk" );
131 }
132
133 /*
134 * Collision detection routines
135 *
136 *
137 */
138
139 /*
140 * Does collision detection on a sphere vs world, and applies some smoothing
141 * filters to the manifold afterwards
142 */
143 VG_STATIC int skate_collide_smooth( player_interface *player,
144 m4x3f mtx, rb_sphere *sphere,
145 rb_ct *man )
146 {
147 debug_sphere( mtx, sphere->radius, VG__BLACK );
148
149 int len = 0;
150 len = rb_sphere__scene( mtx, sphere, NULL, &world.rb_geo.inf.scene, man );
151
152 for( int i=0; i<len; i++ )
153 {
154 man[i].rba = &player->rb;
155 man[i].rbb = NULL;
156 }
157
158 rb_manifold_filter_coplanar( man, len, 0.05f );
159
160 if( len > 1 )
161 {
162 rb_manifold_filter_backface( man, len );
163 rb_manifold_filter_joint_edges( man, len, 0.05f );
164 rb_manifold_filter_pairs( man, len, 0.05f );
165 }
166 int new_len = rb_manifold_apply_filtered( man, len );
167 if( len && !new_len )
168 len = 1;
169 else
170 len = new_len;
171
172 return len;
173 }
174 /*
175 * Gets the closest grindable edge to the player within max_dist
176 */
177 VG_STATIC struct grind_edge *skate_collect_grind_edge( v3f p0, v3f p1,
178 v3f c0, v3f c1,
179 float max_dist )
180 {
181 bh_iter it;
182 bh_iter_init( 0, &it );
183
184 boxf region;
185
186 box_init_inf( region );
187 box_addpt( region, p0 );
188 box_addpt( region, p1 );
189
190 float k_r = max_dist;
191 v3_add( (v3f){ k_r, k_r, k_r}, region[1], region[1] );
192 v3_add( (v3f){-k_r,-k_r,-k_r}, region[0], region[0] );
193
194 float closest = k_r*k_r;
195 struct grind_edge *closest_edge = NULL;
196
197 int idx;
198 while( bh_next( world.grind_bh, &it, region, &idx ) )
199 {
200 struct grind_edge *edge = &world.grind_edges[ idx ];
201
202 float s,t;
203 v3f pa, pb;
204
205 float d2 =
206 closest_segment_segment( p0, p1, edge->p0, edge->p1, &s,&t, pa, pb );
207
208 if( d2 < closest )
209 {
210 closest = d2;
211 closest_edge = edge;
212 v3_copy( pa, c0 );
213 v3_copy( pb, c1 );
214 }
215 }
216
217 return closest_edge;
218 }
219
220 VG_STATIC int skate_grind_collide( player_device *dev,
221 player_interface *player,
222 rb_ct *contact )
223 {
224 v3f p0, p1, c0, c1;
225 v3_muladds( player->rb.co, player->rb.to_world[2], 0.5f, p0 );
226 v3_muladds( player->rb.co, player->rb.to_world[2], -0.5f, p1 );
227 v3_muladds( p0, player->rb.to_world[1], 0.125f-0.15f, p0 );
228 v3_muladds( p1, player->rb.to_world[1], 0.125f-0.15f, p1 );
229
230 float const k_r = 0.25f;
231 struct grind_edge *closest_edge = skate_collect_grind_edge( p0, p1,
232 c0, c1, k_r );
233
234 if( closest_edge )
235 {
236 v3f delta;
237 v3_sub( c1, c0, delta );
238
239 if( v3_dot( delta, player->rb.to_world[1] ) > 0.0001f )
240 {
241 contact->p = v3_length( delta );
242 contact->type = k_contact_type_edge;
243 contact->element_id = 0;
244 v3_copy( c1, contact->co );
245 contact->rba = NULL;
246 contact->rbb = NULL;
247
248 v3f edge_dir, axis_dir;
249 v3_sub( closest_edge->p1, closest_edge->p0, edge_dir );
250 v3_normalize( edge_dir );
251 v3_cross( (v3f){0.0f,1.0f,0.0f}, edge_dir, axis_dir );
252 v3_cross( edge_dir, axis_dir, contact->n );
253
254 return 1;
255 }
256 else
257 return 0;
258 }
259
260 return 0;
261 }
262
263 /*
264 *
265 * Prediction system
266 *
267 *
268 */
269
270 /*
271 * Trace a path given a velocity rotation.
272 *
273 * TODO: this MIGHT be worth doing RK4 on the gravity field.
274 */
275 VG_STATIC void skate_score_biased_path( v3f co, v3f v, m3x3f vr,
276 struct land_prediction *prediction )
277 {
278 float pstep = VG_TIMESTEP_FIXED * 10.0f;
279 float k_bias = 0.96f;
280
281 v3f pco, pco1, pv;
282 v3_copy( co, pco );
283 v3_muls( v, k_bias, pv );
284
285 m3x3_mulv( vr, pv, pv );
286 v3_muladds( pco, pv, pstep, pco );
287
288 struct grind_edge *best_grind = NULL;
289 float closest_grind = INFINITY;
290
291 float grind_score = INFINITY,
292 air_score = INFINITY,
293 time_to_impact = 0.0f;
294
295 prediction->log_length = 0;
296 v3_copy( pco, prediction->apex );
297
298 for( int i=0; i<vg_list_size(prediction->log); i++ )
299 {
300 v3_copy( pco, pco1 );
301
302 pv[1] += -k_gravity * pstep;
303
304 m3x3_mulv( vr, pv, pv );
305 v3_muladds( pco, pv, pstep, pco );
306
307 if( pco[1] > prediction->apex[1] )
308 v3_copy( pco, prediction->apex );
309
310 v3f vdir;
311
312 v3_sub( pco, pco1, vdir );
313
314 float l = v3_length( vdir );
315 v3_muls( vdir, 1.0f/l, vdir );
316
317 v3f c0, c1;
318 struct grind_edge *ge = skate_collect_grind_edge( pco, pco1,
319 c0, c1, 0.4f );
320
321 if( ge && (v3_dot((v3f){0.0f,1.0f,0.0f},vdir) < -0.2f ) )
322 {
323 float d2 = v3_dist2( c0, c1 );
324 if( d2 < closest_grind )
325 {
326 closest_grind = d2;
327 best_grind = ge;
328 grind_score = closest_grind * 0.05f;
329 }
330 }
331
332 v3f n1;
333
334 float t1;
335 int idx = spherecast_world( pco1, pco, 0.4f, &t1, n1 );
336 if( idx != -1 )
337 {
338 v3_copy( n1, prediction->n );
339 air_score = -v3_dot( pv, n1 );
340
341 u32 vert_index = world.scene_geo->arrindices[ idx*3 ];
342 struct world_material *mat = world_tri_index_material( vert_index );
343
344 /* Bias prediction towords ramps */
345 if( mat->info.flags & k_material_flag_skate_surface )
346 air_score *= 0.1f;
347
348 v3_lerp( pco1, pco, t1, prediction->log[ prediction->log_length ++ ] );
349 time_to_impact += t1 * pstep;
350 break;
351 }
352
353 time_to_impact += pstep;
354 v3_copy( pco, prediction->log[ prediction->log_length ++ ] );
355 }
356
357 if( grind_score < air_score )
358 {
359 prediction->score = grind_score;
360 prediction->type = k_prediction_grind;
361 }
362 else if( air_score < INFINITY )
363 {
364 prediction->score = air_score;
365 prediction->type = k_prediction_land;
366 }
367 else
368 {
369 prediction->score = INFINITY;
370 prediction->type = k_prediction_none;
371 }
372
373 prediction->land_dist = time_to_impact;
374 }
375
376 VG_STATIC
377 void player_approximate_best_trajectory( player_interface *player,
378 struct player_device_skate *s )
379 {
380 float pstep = VG_TIMESTEP_FIXED * 10.0f;
381 float best_velocity_delta = -9999.9f;
382
383 v3f axis;
384 v3_cross( player->rb.to_world[1], player->rb.v, axis );
385 v3_normalize( axis );
386
387 s->prediction_count = 0;
388 m3x3_identity( s->state.velocity_bias );
389
390 float best_vmod = 0.0f,
391 min_score = INFINITY,
392 max_score = -INFINITY;
393
394 v3_zero( s->state.apex );
395 s->land_dist = 0.0f;
396
397 /*
398 * Search a broad selection of futures
399 */
400 for( int m=-3;m<=12; m++ )
401 {
402 struct land_prediction *p = &s->predictions[ s->prediction_count ++ ];
403
404 float vmod = ((float)m / 15.0f)*0.09f;
405
406 m3x3f bias;
407 v4f bias_q;
408
409 q_axis_angle( bias_q, axis, vmod );
410 q_m3x3( bias_q, bias );
411
412 skate_score_biased_path( player->rb.co, player->rb.v, bias, p );
413
414 if( p->type != k_prediction_none )
415 {
416 if( p->score < min_score )
417 {
418 min_score = p->score;
419 best_vmod = vmod;
420 s->land_dist = p->land_dist;
421 v3_copy( p->apex, s->state.apex );
422 }
423
424 if( p->score > max_score )
425 max_score = p->score;
426 }
427 }
428
429 v4f vr_q;
430 q_axis_angle( vr_q, axis, best_vmod*0.1f );
431 q_m3x3( vr_q, s->state.velocity_bias );
432
433 q_axis_angle( vr_q, axis, best_vmod );
434 q_m3x3( vr_q, s->state.velocity_bias_pstep );
435
436 /*
437 * Logging
438 */
439 for( int i=0; i<s->prediction_count; i ++ )
440 {
441 struct land_prediction *p = &s->predictions[i];
442
443 float l = p->score;
444
445 if( l < 0.0f )
446 {
447 vg_error( "negative score! (%f)\n", l );
448 }
449
450 l -= min_score;
451 l /= (max_score-min_score);
452 l = 1.0f - l;
453 l *= 255.0f;
454
455 p->colour = l;
456 p->colour <<= 8;
457 p->colour |= 0xff000000;
458 }
459
460
461 v2f steer = { player->input_js1h->axis.value,
462 player->input_js1v->axis.value };
463 v2_normalize_clamp( steer );
464
465 if( (fabsf(steer[1]) > 0.5f) && (s->land_dist >= 1.0f) )
466 {
467 s->state.flip_rate = (1.0f/s->land_dist) * vg_signf(steer[1]) *
468 s->state.reverse ;
469 s->state.flip_time = 0.0f;
470 v3_copy( player->rb.to_world[0], s->state.flip_axis );
471 }
472 else
473 {
474 s->state.flip_rate = 0.0f;
475 v3_zero( s->state.flip_axis );
476 }
477 }
478
479 /*
480 *
481 * Varius physics models
482 * ------------------------------------------------
483 */
484
485 VG_STATIC void skate_apply_grind_model( player_interface *player,
486 struct player_device_skate *s,
487 rb_ct *manifold, int len )
488 {
489 /* FIXME: Queue audio events instead */
490 if( len == 0 )
491 {
492 if( s->state.activity == k_skate_activity_grind )
493 {
494 #if 0
495 audio_lock();
496 audio_player_set_flags( &audio_player_extra,
497 AUDIO_FLAG_SPACIAL_3D );
498 audio_player_set_position( &audio_player_extra, player.rb.co );
499 audio_player_set_vol( &audio_player_extra, 20.0f );
500 audio_player_playclip( &audio_player_extra, &audio_board[6] );
501 audio_unlock();
502 #endif
503
504 s->state.activity = k_skate_activity_air;
505 }
506 return;
507 }
508
509 v2f steer = { player->input_js1h->axis.value,
510 player->input_js1v->axis.value };
511 v2_normalize_clamp( steer );
512
513 s->state.steery -= steer[0] * k_steer_air * k_rb_delta;
514 s->state.steerx += steer[1] * s->state.reverse * k_steer_air * k_rb_delta;
515
516 #if 0
517 v4f rotate;
518 q_axis_angle( rotate, player->rb.to_world[0], siX );
519 q_mul( rotate, player.rb.q, player.rb.q );
520 #endif
521
522 s->state.slip = 0.0f;
523 s->state.activity = k_skate_activity_grind;
524
525 /* TODO: Compression */
526 v3f up = { 0.0f, 1.0f, 0.0f };
527 float angle = v3_dot( player->rb.to_world[1], up );
528
529 if( fabsf(angle) < 0.99f )
530 {
531 v3f axis;
532 v3_cross( player->rb.to_world[1], up, axis );
533
534 v4f correction;
535 q_axis_angle( correction, axis, k_rb_delta * 10.0f * acosf(angle) );
536 q_mul( correction, player->rb.q, player->rb.q );
537 }
538
539 float const DOWNFORCE = -k_downforce*1.2f*VG_TIMESTEP_FIXED;
540 v3_muladds( player->rb.v, manifold->n, DOWNFORCE, player->rb.v );
541 m3x3_identity( s->state.velocity_bias );
542 m3x3_identity( s->state.velocity_bias_pstep );
543
544 if( s->state.activity_prev != k_skate_activity_grind )
545 {
546 /* FIXME: Queue audio events instead */
547 #if 0
548 audio_lock();
549 audio_player_set_flags( &audio_player_extra,
550 AUDIO_FLAG_SPACIAL_3D );
551 audio_player_set_position( &audio_player_extra, player.rb.co );
552 audio_player_set_vol( &audio_player_extra, 20.0f );
553 audio_player_playclip( &audio_player_extra, &audio_board[5] );
554 audio_unlock();
555 #endif
556 }
557 }
558
559 /*
560 * Air control, no real physics
561 */
562 VG_STATIC void skate_apply_air_model( player_interface *player,
563 struct player_device_skate *s )
564 {
565 if( s->state.activity != k_skate_activity_air )
566 return;
567
568 if( s->state.activity_prev != k_skate_activity_air )
569 player_approximate_best_trajectory( player, s );
570
571 m3x3_mulv( s->state.velocity_bias, player->rb.v, player->rb.v );
572 ray_hit hit;
573
574 /*
575 * Prediction
576 */
577 float pstep = VG_TIMESTEP_FIXED * 1.0f;
578 float k_bias = 0.98f;
579
580 v3f pco, pco1, pv;
581 v3_copy( player->rb.co, pco );
582 v3_muls( player->rb.v, 1.0f, pv );
583
584 float time_to_impact = 0.0f;
585 float limiter = 1.0f;
586
587 struct grind_edge *best_grind = NULL;
588 float closest_grind = INFINITY;
589
590 v3f target_normal = { 0.0f, 1.0f, 0.0f };
591 int has_target = 0;
592
593 for( int i=0; i<250; i++ )
594 {
595 v3_copy( pco, pco1 );
596 m3x3_mulv( s->state.velocity_bias, pv, pv );
597
598 pv[1] += -k_gravity * pstep;
599 v3_muladds( pco, pv, pstep, pco );
600
601 ray_hit contact;
602 v3f vdir;
603
604 v3_sub( pco, pco1, vdir );
605 contact.dist = v3_length( vdir );
606 v3_divs( vdir, contact.dist, vdir);
607
608 v3f c0, c1;
609 struct grind_edge *ge = skate_collect_grind_edge( pco, pco1,
610 c0, c1, 0.4f );
611
612 if( ge && (v3_dot((v3f){0.0f,1.0f,0.0f},vdir) < -0.2f ) )
613 {
614 vg_line( ge->p0, ge->p1, 0xff0000ff );
615 vg_line_cross( pco, 0xff0000ff, 0.25f );
616 has_target = 1;
617 break;
618 }
619
620 float orig_dist = contact.dist;
621 if( ray_world( pco1, vdir, &contact ) )
622 {
623 v3_copy( contact.normal, target_normal );
624 has_target = 1;
625 time_to_impact += (contact.dist/orig_dist)*pstep;
626 vg_line_cross( contact.pos, 0xffff0000, 0.25f );
627 break;
628 }
629 time_to_impact += pstep;
630 }
631
632 if( has_target )
633 {
634 float angle = v3_dot( player->rb.to_world[1], target_normal );
635 v3f axis;
636 v3_cross( player->rb.to_world[1], target_normal, axis );
637
638 limiter = vg_minf( 5.0f, time_to_impact )/5.0f;
639 limiter = 1.0f-limiter;
640 limiter *= limiter;
641 limiter = 1.0f-limiter;
642
643 if( fabsf(angle) < 0.99f )
644 {
645 v4f correction;
646 q_axis_angle( correction, axis,
647 acosf(angle)*(1.0f-limiter)*2.0f*VG_TIMESTEP_FIXED );
648 q_mul( correction, player->rb.q, player->rb.q );
649 }
650 }
651
652 v2f steer = { player->input_js1h->axis.value,
653 player->input_js1v->axis.value };
654 v2_normalize_clamp( steer );
655
656 s->state.steery -= steer[0] * k_steer_air * VG_TIMESTEP_FIXED;
657 s->state.steerx += steer[1] * s->state.reverse * k_steer_air
658 * limiter * k_rb_delta;
659 s->land_dist = time_to_impact;
660 v3_copy( target_normal, s->land_normal );
661 }
662
663 VG_STATIC void skate_get_board_points( player_interface *player,
664 struct player_device_skate *s,
665 v3f front, v3f back )
666 {
667 v3f pos_front = {0.0f,0.0f,-k_board_length},
668 pos_back = {0.0f,0.0f, k_board_length};
669
670 m4x3_mulv( player->rb.to_world, pos_front, front );
671 m4x3_mulv( player->rb.to_world, pos_back, back );
672 }
673
674 /*
675 * Casts and pushes a sphere-spring model into the world
676 */
677 VG_STATIC int skate_simulate_spring( player_interface *player,
678 struct player_device_skate *s,
679 v3f pos )
680 {
681 float mod = 0.7f * player->input_grab->axis.value + 0.3f,
682 spring_k = mod * k_spring_force,
683 damp_k = mod * k_spring_dampener,
684 disp_k = 0.4f;
685
686 v3f start, end;
687 v3_copy( pos, start );
688 v3_muladds( pos, player->rb.to_world[1], -disp_k, end );
689
690 float t;
691 v3f n;
692 int hit_info = spherecast_world( start, end, 0.2f, &t, n );
693
694 if( hit_info != -1 )
695 {
696 v3f F, delta;
697 v3_sub( start, player->rb.co, delta );
698
699 float displacement = vg_clampf( 1.0f-t, 0.0f, 1.0f ),
700 damp =
701 vg_maxf( 0.0f, v3_dot( player->rb.to_world[1], player->rb.v ) );
702
703 v3_muls( player->rb.to_world[1], displacement*spring_k*k_rb_delta -
704 damp*damp_k*k_rb_delta, F );
705
706 v3_muladds( player->rb.v, F, 1.0f, player->rb.v );
707
708 /* Angular velocity */
709 v3f wa;
710 v3_cross( delta, F, wa );
711 v3_muladds( player->rb.w, wa, k_spring_angular, player->rb.w );
712
713 v3_lerp( start, end, t, pos );
714 return 1;
715 }
716 else
717 {
718 v3_copy( end, pos );
719 return 0;
720 }
721 }
722
723
724 /*
725 * Handles connection between the player and the ground
726 */
727 VG_STATIC void skate_apply_interface_model( player_interface *player,
728 struct player_device_skate *s,
729 rb_ct *manifold, int len )
730 {
731 if( !((s->state.activity == k_skate_activity_ground) ||
732 (s->state.activity == k_skate_activity_air )) )
733 return;
734
735 if( s->state.activity == k_skate_activity_air )
736 s->debug_normal_pressure = 0.0f;
737 else
738 s->debug_normal_pressure = v3_dot( player->rb.to_world[1], player->rb.v );
739
740 /* springs */
741 v3f spring0, spring1;
742
743 skate_get_board_points( player, s, spring1, spring0 );
744 int spring_hit0 = 0, //skate_simulate_spring( player, s, spring0 ),
745 spring_hit1 = 0; //skate_simulate_spring( player, s, spring1 );
746
747 v3f animavg, animdelta;
748 v3_add( spring0, spring1, animavg );
749 v3_muls( animavg, 0.5f, animavg );
750
751 v3_sub( spring1, spring0, animdelta );
752 v3_normalize( animdelta );
753
754 m4x3_mulv( player->rb.to_local, animavg, s->board_offset );
755
756 float dx = -v3_dot( animdelta, player->rb.to_world[2] ),
757 dy = v3_dot( animdelta, player->rb.to_world[1] );
758
759 float angle = -atan2f( dy, dx );
760 q_axis_angle( s->board_rotation, (v3f){1.0f,0.0f,0.0f}, angle );
761
762 int lift_frames_limit = 6;
763
764 /* Surface connection */
765 if( len == 0 && !(spring_hit0 && spring_hit1) )
766 {
767 s->state.lift_frames ++;
768
769 if( s->state.lift_frames >= lift_frames_limit )
770 s->state.activity = k_skate_activity_air;
771 }
772 else
773 {
774 v3f surface_avg;
775 v3_zero( surface_avg );
776
777 for( int i=0; i<len; i++ )
778 v3_add( surface_avg, manifold[i].n, surface_avg );
779 v3_normalize( surface_avg );
780
781 if( v3_dot( player->rb.v, surface_avg ) > 0.7f )
782 {
783 s->state.lift_frames ++;
784
785 if( s->state.lift_frames >= lift_frames_limit )
786 s->state.activity = k_skate_activity_air;
787 }
788 else
789 {
790 s->state.activity = k_skate_activity_ground;
791 s->state.lift_frames = 0;
792 v3f projected, axis;
793
794 float const DOWNFORCE = -k_downforce*VG_TIMESTEP_FIXED;
795 v3_muladds( player->rb.v, player->rb.to_world[1],
796 DOWNFORCE, player->rb.v );
797
798 float d = v3_dot( player->rb.to_world[2], surface_avg );
799 v3_muladds( surface_avg, player->rb.to_world[2], -d, projected );
800 v3_normalize( projected );
801
802 float angle = v3_dot( player->rb.to_world[1], projected );
803 v3_cross( player->rb.to_world[1], projected, axis );
804
805 if( fabsf(angle) < 0.9999f )
806 {
807 v4f correction;
808 q_axis_angle( correction, axis,
809 acosf(angle)*4.0f*VG_TIMESTEP_FIXED );
810 q_mul( correction, player->rb.q, player->rb.q );
811 }
812 }
813 }
814 }
815
816 VG_STATIC void skate_apply_grab_model( player_interface *player,
817 struct player_device_skate *s )
818 {
819 float grabt = player->input_grab->axis.value;
820
821 if( grabt > 0.5f )
822 {
823 v2_muladds( s->state.grab_mouse_delta, vg.mouse_delta, 0.02f,
824 s->state.grab_mouse_delta );
825
826 v2_normalize_clamp( s->state.grab_mouse_delta );
827 }
828 else
829 v2_zero( s->state.grab_mouse_delta );
830
831 s->state.grabbing = vg_lerpf( s->state.grabbing, grabt, 8.4f*k_rb_delta );
832 }
833
834 /*
835 * Computes friction and surface interface model
836 */
837 VG_STATIC void skate_apply_friction_model( player_interface *player,
838 struct player_device_skate *s )
839 {
840 if( s->state.activity != k_skate_activity_ground )
841 return;
842
843 /*
844 * Computing localized friction forces for controlling the character
845 * Friction across X is significantly more than Z
846 */
847
848 v3f vel;
849 m3x3_mulv( player->rb.to_local, player->rb.v, vel );
850 float slip = 0.0f;
851
852 if( fabsf(vel[2]) > 0.01f )
853 slip = fabsf(-vel[0] / vel[2]) * vg_signf(vel[0]);
854
855 if( fabsf( slip ) > 1.2f )
856 slip = vg_signf( slip ) * 1.2f;
857
858 s->state.slip = slip;
859 s->state.reverse = -vg_signf(vel[2]);
860
861 vel[0] += vg_cfrictf( vel[0], k_friction_lat * k_rb_delta );
862 vel[2] += vg_cfrictf( vel[2], k_friction_resistance * k_rb_delta );
863
864 /* Pushing additive force */
865
866 if( !player->input_jump->button.value )
867 {
868 if( player->input_push->button.value )
869 {
870 if( (vg.time - s->state.cur_push) > 0.25 )
871 s->state.start_push = vg.time;
872
873 s->state.cur_push = vg.time;
874
875 double push_time = vg.time - s->state.start_push;
876
877 float cycle_time = push_time*k_push_cycle_rate,
878 accel = k_push_accel * (sinf(cycle_time)*0.5f+0.5f),
879 amt = accel * VG_TIMESTEP_FIXED,
880 current = v3_length( vel ),
881 new_vel = vg_minf( current + amt, k_max_push_speed ),
882 delta = new_vel - vg_minf( current, k_max_push_speed );
883
884 vel[2] += delta * -s->state.reverse;
885 }
886 }
887
888 /* Send back to velocity */
889 m3x3_mulv( player->rb.to_world, vel, player->rb.v );
890
891 /* Steering */
892 float input = player->input_js1h->axis.value,
893 grab = player->input_grab->axis.value,
894 steer = input * (1.0f-(s->state.jump_charge+grab)*0.4f),
895 steer_scaled = vg_signf(steer) * powf(steer,2.0f) * k_steer_ground;
896
897 s->state.steery -= steer_scaled * k_rb_delta;
898 }
899
900 VG_STATIC void skate_apply_jump_model( player_interface *player,
901 struct player_device_skate *s )
902 {
903 int charging_jump_prev = s->state.charging_jump;
904 s->state.charging_jump = player->input_jump->button.value;
905
906 /* Cannot charge this in air */
907 if( s->state.activity != k_skate_activity_ground )
908 s->state.charging_jump = 0;
909
910 if( s->state.charging_jump )
911 {
912 s->state.jump_charge += k_rb_delta * k_jump_charge_speed;
913
914 if( !charging_jump_prev )
915 s->state.jump_dir = s->state.reverse>0.0f? 1: 0;
916 }
917 else
918 {
919 s->state.jump_charge -= k_jump_charge_speed * VG_TIMESTEP_FIXED;
920 }
921
922 s->state.jump_charge = vg_clampf( s->state.jump_charge, 0.0f, 1.0f );
923
924 if( s->state.activity == k_skate_activity_air )
925 return;
926
927 /* player let go after charging past 0.2: trigger jump */
928 if( (!s->state.charging_jump) && (s->state.jump_charge > 0.2f) )
929 {
930 v3f jumpdir;
931
932 /* Launch more up if alignment is up else improve velocity */
933 float aup = v3_dot( (v3f){0.0f,1.0f,0.0f}, player->rb.to_world[1] ),
934 mod = 0.5f,
935 dir = mod + fabsf(aup)*(1.0f-mod);
936
937 v3_copy( player->rb.v, jumpdir );
938 v3_normalize( jumpdir );
939 v3_muls( jumpdir, 1.0f-dir, jumpdir );
940 v3_muladds( jumpdir, player->rb.to_world[1], dir, jumpdir );
941 v3_normalize( jumpdir );
942
943 float force = k_jump_force*s->state.jump_charge;
944 v3_muladds( player->rb.v, jumpdir, force, player->rb.v );
945 s->state.jump_charge = 0.0f;
946
947 s->state.jump_time = vg.time;
948
949 v2f steer = { player->input_js1h->axis.value,
950 player->input_js1v->axis.value };
951 v2_normalize_clamp( steer );
952
953 float maxspin = k_steer_air * k_rb_delta * k_spin_boost;
954 s->state.steery_s = -steer[0] * maxspin;
955 s->state.steerx = s->state.steerx_s;
956
957 /* FIXME audio events */
958 #if 0
959 audio_lock();
960 audio_player_set_flags( &audio_player_extra, AUDIO_FLAG_SPACIAL_3D );
961 audio_player_set_position( &audio_player_extra, player.rb.co );
962 audio_player_set_vol( &audio_player_extra, 20.0f );
963 audio_player_playclip( &audio_player_extra, &audio_jumps[rand()%2] );
964 audio_unlock();
965 #endif
966 }
967 }
968
969 VG_STATIC void skate_apply_pump_model( player_interface *player,
970 struct player_device_skate *s )
971 {
972 /* Throw / collect routine
973 *
974 * TODO: Max speed boost
975 */
976 if( player->input_grab->axis.value > 0.5f )
977 {
978 if( s->state.activity == k_skate_activity_ground )
979 {
980 /* Throw */
981 v3_muls( player->rb.to_world[1], k_mmthrow_scale, s->state.throw_v );
982 }
983 }
984 else
985 {
986 /* Collect */
987 float doty = v3_dot( player->rb.to_world[1], s->state.throw_v );
988
989 v3f Fl, Fv;
990 v3_muladds( s->state.throw_v, player->rb.to_world[1], -doty, Fl);
991
992 if( s->state.activity == k_skate_activity_ground )
993 {
994 v3_muladds( player->rb.v, Fl, k_mmcollect_lat, player->rb.v );
995 v3_muladds( s->state.throw_v, Fl, -k_mmcollect_lat, s->state.throw_v );
996 }
997
998 v3_muls( player->rb.to_world[1], -doty, Fv );
999 v3_muladds( player->rb.v, Fv, k_mmcollect_vert, player->rb.v );
1000 v3_muladds( s->state.throw_v, Fv, k_mmcollect_vert, s->state.throw_v );
1001 }
1002
1003 /* Decay */
1004 if( v3_length2( s->state.throw_v ) > 0.0001f )
1005 {
1006 v3f dir;
1007 v3_copy( s->state.throw_v, dir );
1008 v3_normalize( dir );
1009
1010 float max = v3_dot( dir, s->state.throw_v ),
1011 amt = vg_minf( k_mmdecay * k_rb_delta, max );
1012 v3_muladds( s->state.throw_v, dir, -amt, s->state.throw_v );
1013 }
1014 }
1015
1016 VG_STATIC void skate_apply_cog_model( player_interface *player,
1017 struct player_device_skate *s )
1018 {
1019 v3f ideal_cog, ideal_diff;
1020 v3_muladds( player->rb.co, player->rb.to_world[1],
1021 1.0f-player->input_grab->axis.value, ideal_cog );
1022 v3_sub( ideal_cog, s->state.cog, ideal_diff );
1023
1024 /* Apply velocities */
1025 v3f rv;
1026 v3_sub( player->rb.v, s->state.cog_v, rv );
1027
1028 v3f F;
1029 v3_muls( ideal_diff, -k_cog_spring * k_rb_rate, F );
1030 v3_muladds( F, rv, -k_cog_damp * k_rb_rate, F );
1031
1032 float ra = k_cog_mass_ratio,
1033 rb = 1.0f-k_cog_mass_ratio;
1034
1035 /* Apply forces & intergrate */
1036 v3_muladds( s->state.cog_v, F, -rb, s->state.cog_v );
1037 s->state.cog_v[1] += -9.8f * k_rb_delta;
1038 v3_muladds( s->state.cog, s->state.cog_v, k_rb_delta, s->state.cog );
1039 }
1040
1041 VG_STATIC void skate_collision_response( player_interface *player,
1042 struct player_device_skate *s,
1043 rb_ct *manifold, int len )
1044 {
1045 for( int j=0; j<10; j++ )
1046 {
1047 for( int i=0; i<len; i++ )
1048 {
1049 struct contact *ct = &manifold[i];
1050
1051 v3f dv, delta;
1052 v3_sub( ct->co, player->rb.co, delta );
1053 v3_cross( player->rb.w, delta, dv );
1054 v3_add( player->rb.v, dv, dv );
1055
1056 float vn = -v3_dot( dv, ct->n );
1057 vn += ct->bias;
1058
1059 float temp = ct->norm_impulse;
1060 ct->norm_impulse = vg_maxf( temp + vn, 0.0f );
1061 vn = ct->norm_impulse - temp;
1062
1063 v3f impulse;
1064 v3_muls( ct->n, vn, impulse );
1065
1066 if( fabsf(v3_dot( impulse, player->rb.to_world[2] )) > 10.0f ||
1067 fabsf(v3_dot( impulse, player->rb.to_world[1] )) > 50.0f )
1068 {
1069 /* FIXME */
1070 #if 0
1071 player_kill();
1072 return;
1073 #endif
1074 }
1075
1076 v3_add( impulse, player->rb.v, player->rb.v );
1077 v3_cross( delta, impulse, impulse );
1078
1079 /*
1080 * W Impulses are limited to the Y and X axises, we don't really want
1081 * roll angular velocities being included.
1082 *
1083 * Can also tweak the resistance of each axis here by scaling the wx,wy
1084 * components.
1085 */
1086
1087 float wy = v3_dot( player->rb.to_world[1], impulse ) * 0.8f,
1088 wx = v3_dot( player->rb.to_world[0], impulse ) * 1.0f;
1089
1090 v3_muladds( player->rb.w, player->rb.to_world[1], wy, player->rb.w );
1091 v3_muladds( player->rb.w, player->rb.to_world[0], wx, player->rb.w );
1092 }
1093 }
1094 }
1095
1096 VG_STATIC void skate_integrate( player_interface *player,
1097 struct player_device_skate *s )
1098 {
1099 /* integrate rigidbody velocities */
1100 v3f gravity = { 0.0f, -9.6f, 0.0f };
1101 v3_muladds( player->rb.v, gravity, k_rb_delta, player->rb.v );
1102 v3_muladds( player->rb.co, player->rb.v, k_rb_delta, player->rb.co );
1103
1104 float decay_rate = 0.5f*0.125f;
1105
1106 if( s->state.activity == k_skate_activity_air )
1107 {
1108 float dist = 1.0f-(s->land_dist/4.0f);
1109 decay_rate = 0.5f * vg_maxf( dist*dist, 0.0f );
1110 }
1111
1112 v3_lerp( player->rb.w, (v3f){0.0f,0.0f,0.0f}, decay_rate, player->rb.w );
1113
1114 if( v3_length2( player->rb.w ) > 0.0f )
1115 {
1116 v4f rotation;
1117 v3f axis;
1118 v3_copy( player->rb.w, axis );
1119
1120 float mag = v3_length( axis );
1121 v3_divs( axis, mag, axis );
1122 q_axis_angle( rotation, axis, mag*k_rb_delta );
1123 q_mul( rotation, player->rb.q, player->rb.q );
1124 }
1125
1126 /* integrate steering velocities */
1127 v4f rotate;
1128 float l = (s->state.activity == k_skate_activity_air)? 0.04f: 0.24f;
1129
1130 s->state.steery_s = vg_lerpf( s->state.steery_s, s->state.steery, l );
1131 s->state.steerx_s = vg_lerpf( s->state.steerx_s, s->state.steerx, l );
1132
1133 q_axis_angle( rotate, player->rb.to_world[1], s->state.steery_s );
1134 q_mul( rotate, player->rb.q, player->rb.q );
1135
1136 q_axis_angle( rotate, player->rb.to_world[0], s->state.steerx_s );
1137 q_mul( rotate, player->rb.q, player->rb.q );
1138
1139 s->state.steerx = 0.0f;
1140 s->state.steery = 0.0f;
1141
1142 #if 0
1143 v3_sub( player.rb.v, s->phys.v_prev, s->phys.a );
1144 v3_muls( s->phys.a, 1.0f/VG_TIMESTEP_FIXED, s->phys.a );
1145 v3_copy( player.rb.v, s->phys.v_prev );
1146 #endif
1147
1148 s->state.flip_time += s->state.flip_rate * k_rb_delta;
1149 rb_update_transform( &player->rb );
1150 }
1151
1152 VG_STATIC void player_skate_update( player_device *dev,
1153 player_interface *player )
1154 {
1155 struct player_device_skate *s = dev->storage;
1156 v3_copy( player->rb.co, s->state.prev_pos );
1157 s->state.activity_prev = s->state.activity;
1158
1159 /* Setup colliders */
1160 m4x3f mtx_front, mtx_back;
1161 m3x3_identity( mtx_front );
1162 m3x3_identity( mtx_back );
1163
1164 skate_get_board_points( player, s, mtx_front[3], mtx_back[3] );
1165
1166 s->sphere_back.radius = 0.3f;
1167 s->sphere_front.radius = 0.3f;
1168
1169 /* create manifold(s) */
1170 rb_ct manifold[72],
1171 *interface_manifold = NULL,
1172 *grind_manifold = NULL;
1173
1174 int
1175 len_front = skate_collide_smooth( player, mtx_front,
1176 &s->sphere_front, manifold ),
1177 len_back = skate_collide_smooth( player, mtx_back,
1178 &s->sphere_back, &manifold[len_front] ),
1179 interface_len = len_front + len_back;
1180
1181 /* try to slap both wheels onto the ground when landing to prevent mega
1182 * angular velocities being added */
1183 if( (s->state.activity == k_skate_activity_air) && (len_front != len_back) )
1184 {
1185 v3f trace_from, trace_dir;
1186 v3_muls( player->rb.to_world[1], -1.0f, trace_dir );
1187
1188 if( len_front )
1189 v3_copy( mtx_back[3], trace_from );
1190 else
1191 v3_copy( mtx_front[3], trace_from );
1192
1193 ray_hit ray;
1194 ray.dist = 0.6f;
1195
1196 if( ray_world( trace_from, trace_dir, &ray ) )
1197 {
1198 rb_ct *ct = &manifold[ interface_len ];
1199
1200 v3_copy( ray.pos, ct->co );
1201 v3_copy( ray.normal, ct->n );
1202 ct->p = 0.0f;
1203
1204 interface_len ++;
1205 }
1206 }
1207
1208 interface_manifold = manifold;
1209 grind_manifold = manifold + interface_len;
1210
1211 int grind_len = skate_grind_collide( dev, player, grind_manifold );
1212
1213 for( int i=0; i<interface_len+grind_len; i ++ )
1214 {
1215 rb_prepare_contact( &manifold[i] );
1216 rb_debug_contact( &manifold[i] );
1217 }
1218
1219 skate_apply_grind_model( player, s, grind_manifold, grind_len );
1220 skate_apply_interface_model( player, s, manifold, interface_len );
1221
1222 skate_apply_pump_model( player, s );
1223 skate_apply_cog_model( player, s );
1224 skate_collision_response( player, s, manifold, interface_len + grind_len );
1225
1226 skate_apply_grab_model( player, s );
1227 skate_apply_friction_model( player, s );
1228 skate_apply_jump_model( player, s );
1229 skate_apply_air_model( player, s );
1230
1231 skate_integrate( player, s );
1232
1233 vg_line_pt3( s->state.cog, 0.1f, VG__WHITE );
1234 vg_line_pt3( s->state.cog, 0.11f, VG__WHITE );
1235 vg_line_pt3( s->state.cog, 0.12f, VG__WHITE );
1236 vg_line_pt3( s->state.cog, 0.13f, VG__WHITE );
1237 vg_line_pt3( s->state.cog, 0.14f, VG__WHITE );
1238
1239 vg_line( player->rb.co, s->state.cog, VG__RED );
1240
1241
1242 teleport_gate *gate;
1243 if( (gate = world_intersect_gates( player->rb.co, s->state.prev_pos )) )
1244 {
1245 m4x3_mulv( gate->transport, player->rb.co, player->rb.co );
1246 m3x3_mulv( gate->transport, player->rb.v, player->rb.v );
1247 m4x3_mulv( gate->transport, s->state.cog, s->state.cog );
1248 m3x3_mulv( gate->transport, s->state.cog_v, s->state.cog_v );
1249 m3x3_mulv( gate->transport, s->state.throw_v, s->state.throw_v );
1250
1251 /*camera */
1252 m4x3_mulv( gate->transport, s->state.posl, s->state.posl );
1253 m3x3_mulv( gate->transport, s->state.vl, s->state.vl );
1254 m3x3_mulv( gate->transport, s->state.dirl, s->state.dirl );
1255
1256 #if 0
1257 mixedcam_transport( &s->state.cam, gate );
1258 #endif
1259
1260 v4f transport_rotation;
1261 m3x3_q( gate->transport, transport_rotation );
1262 q_mul( transport_rotation, player->rb.q, player->rb.q );
1263 rb_update_transform( &player->rb );
1264
1265 s->state_gate_storage = s->state;
1266 player_pass_gate( player, gate );
1267 }
1268 }
1269
1270 VG_STATIC void player_skate_ui( player_device *dev, player_interface *player )
1271 {
1272 struct player_device_skate *s = dev->storage;
1273
1274 /* FIXME: Compression */
1275 player_debugtext( 1, "V: %5.2f %5.2f %5.2f",player->rb.v[0],
1276 player->rb.v[1],
1277 player->rb.v[2] );
1278 player_debugtext( 1, "CO: %5.2f %5.2f %5.2f",player->rb.co[0],
1279 player->rb.co[1],
1280 player->rb.co[2] );
1281 player_debugtext( 1, "W: %5.2f %5.2f %5.2f",player->rb.w[0],
1282 player->rb.w[1],
1283 player->rb.w[2] );
1284
1285 player_debugtext( 1, "activity: %s\n",
1286 (const char *[]){ "k_skate_activity_air",
1287 "k_skate_activity_ground",
1288 "k_skate_activity_grind }" }
1289 [s->state.activity] );
1290 player_debugtext( 1, "steer_s: %5.2f %5.2f [%.2f %.2f]\n",
1291 s->state.steerx_s, s->state.steery_s,
1292 k_steer_ground, k_steer_air );
1293 player_debugtext( 1, "flip: %.4f %.4f\n", s->state.flip_rate,
1294 s->state.flip_time );
1295 }
1296
1297 VG_STATIC void player_skate_animate( player_device *dev,
1298 player_interface *player )
1299 {
1300 struct player_device_skate *s = dev->storage;
1301 struct player_avatar *av = player->playeravatar;
1302 struct skeleton *sk = &av->sk;
1303
1304 /* Camera position */
1305 /* TODO split up */
1306 /* FIXME */
1307
1308 #if 0
1309 v3_muladds( phys->m, phys->a, VG_TIMESTEP_FIXED, phys->m );
1310 v3_lerp( phys->m, (v3f){0.0f,0.0f,0.0f}, 0.1f, phys->m );
1311
1312 phys->m[0] = vg_clampf( phys->m[0], -2.0f, 2.0f );
1313 phys->m[1] = vg_clampf( phys->m[1], -2.0f, 2.0f );
1314 phys->m[2] = vg_clampf( phys->m[2], -2.0f, 2.0f );
1315 v3_lerp( phys->bob, phys->m, 0.2f, phys->bob );
1316 #endif
1317
1318 /* Head */
1319 float kheight = 2.0f,
1320 kleg = 0.6f;
1321
1322 v3f offset;
1323 v3_zero( offset );
1324
1325 m4x3_mulv( player->rb.to_local, s->state.cog, offset );
1326 v3_muls( offset, -4.0f, offset );
1327
1328 #if 0
1329 m3x3_mulv( player.inv_visual_transform, phys->bob, offset );
1330 #endif
1331
1332 static float speed_wobble = 0.0f, speed_wobble_2 = 0.0f;
1333
1334 float curspeed = v3_length( player->rb.v ),
1335 kickspeed = vg_clampf( curspeed*(1.0f/40.0f), 0.0f, 1.0f ),
1336 kicks = (vg_randf()-0.5f)*2.0f*kickspeed,
1337 sign = vg_signf( kicks );
1338
1339 s->wobble[0] = vg_lerpf( s->wobble[0], kicks*kicks*sign, 6.0f*vg.time_delta);
1340 s->wobble[1] = vg_lerpf( s->wobble[1], speed_wobble, 2.4f*vg.time_delta);
1341
1342 offset[0] *= 0.26f;
1343 offset[0] += speed_wobble_2*3.0f;
1344
1345 offset[1] *= -0.3f;
1346 offset[2] *= 0.01f;
1347
1348 offset[0]=vg_clampf(offset[0],-0.8f,0.8f)*(1.0f-fabsf(s->blend_slide)*0.9f);
1349 offset[1]=vg_clampf(offset[1],-0.5f,0.0f);
1350
1351 /*
1352 * Animation blending
1353 * ===========================================
1354 */
1355
1356 /* sliding */
1357 {
1358 float desired = vg_clampf( fabsf( s->state.slip ), 0.0f, 1.0f );
1359 s->blend_slide = vg_lerpf( s->blend_slide, desired, 2.4f*vg.time_delta);
1360 }
1361
1362 /* movement information */
1363 {
1364 int iair = (s->state.activity == k_skate_activity_air) ||
1365 (s->state.activity == k_skate_activity_grind );
1366
1367 float dirz = s->state.reverse > 0.0f? 0.0f: 1.0f,
1368 dirx = s->state.slip < 0.0f? 0.0f: 1.0f,
1369 fly = iair? 1.0f: 0.0f;
1370
1371 s->blend_z = vg_lerpf( s->blend_z, dirz, 2.4f*vg.time_delta );
1372 s->blend_x = vg_lerpf( s->blend_x, dirx, 0.6f*vg.time_delta );
1373 s->blend_fly = vg_lerpf( s->blend_fly, fly, 2.4f*vg.time_delta );
1374 }
1375
1376 mdl_keyframe apose[32], bpose[32];
1377 mdl_keyframe ground_pose[32];
1378 {
1379 /* when the player is moving fast he will crouch down a little bit */
1380 float stand = 1.0f - vg_clampf( curspeed * 0.03f, 0.0f, 1.0f );
1381 s->blend_stand = vg_lerpf( s->blend_stand, stand, 6.0f*vg.time_delta );
1382
1383 /* stand/crouch */
1384 float dir_frame = s->blend_z * (15.0f/30.0f),
1385 stand_blend = offset[1]*-2.0f;
1386
1387 v3f local_cog;
1388 m4x3_mulv( player->rb.to_local, s->state.cog, local_cog );
1389
1390 stand_blend = vg_clampf( 1.0f-local_cog[1], 0, 1 );
1391
1392 skeleton_sample_anim( sk, s->anim_stand, dir_frame, apose );
1393 skeleton_sample_anim( sk, s->anim_highg, dir_frame, bpose );
1394 skeleton_lerp_pose( sk, apose, bpose, stand_blend, apose );
1395
1396 /* sliding */
1397 float slide_frame = s->blend_x * (15.0f/30.0f);
1398 skeleton_sample_anim( sk, s->anim_slide, slide_frame, bpose );
1399 skeleton_lerp_pose( sk, apose, bpose, s->blend_slide, apose );
1400
1401 /* pushing */
1402 double push_time = vg.time - s->state.start_push;
1403 s->blend_push = vg_lerpf( s->blend_push,
1404 (vg.time - s->state.cur_push) < 0.125,
1405 6.0f*vg.time_delta );
1406
1407 float pt = push_time + vg.accumulator;
1408 if( s->state.reverse > 0.0f )
1409 skeleton_sample_anim( sk, s->anim_push, pt, bpose );
1410 else
1411 skeleton_sample_anim( sk, s->anim_push_reverse, pt, bpose );
1412
1413 skeleton_lerp_pose( sk, apose, bpose, s->blend_push, apose );
1414
1415 /* trick setup */
1416 float jump_start_frame = 14.0f/30.0f;
1417
1418 float charge = s->state.jump_charge;
1419 s->blend_jump = vg_lerpf( s->blend_jump, charge, 8.4f*vg.time_delta );
1420
1421 float setup_frame = charge * jump_start_frame,
1422 setup_blend = vg_minf( s->blend_jump, 1.0f );
1423
1424 float jump_frame = (vg.time - s->state.jump_time) + jump_start_frame;
1425 if( jump_frame >= jump_start_frame && jump_frame <= (40.0f/30.0f) )
1426 setup_frame = jump_frame;
1427
1428 struct skeleton_anim *jump_anim = s->state.jump_dir?
1429 s->anim_ollie:
1430 s->anim_ollie_reverse;
1431
1432 skeleton_sample_anim_clamped( sk, jump_anim, setup_frame, bpose );
1433 skeleton_lerp_pose( sk, apose, bpose, setup_blend, ground_pose );
1434 }
1435
1436 mdl_keyframe air_pose[32];
1437 {
1438 float target = -player->input_js1h->axis.value;
1439 s->blend_airdir = vg_lerpf( s->blend_airdir, target, 2.4f*vg.time_delta );
1440
1441 float air_frame = (s->blend_airdir*0.5f+0.5f) * (15.0f/30.0f);
1442 skeleton_sample_anim( sk, s->anim_air, air_frame, apose );
1443
1444 static v2f grab_choice;
1445
1446 v2f grab_input = { player->input_js2h->axis.value,
1447 player->input_js2v->axis.value };
1448 v2_add( s->state.grab_mouse_delta, grab_input, grab_input );
1449 if( v2_length2( grab_input ) <= 0.001f )
1450 grab_input[0] = -1.0f;
1451 else
1452 v2_normalize_clamp( grab_input );
1453 v2_lerp( grab_choice, grab_input, 2.4f*vg.time_delta, grab_choice );
1454
1455 float ang = atan2f( grab_choice[0], grab_choice[1] ),
1456 ang_unit = (ang+VG_PIf) * (1.0f/VG_TAUf),
1457 grab_frame = ang_unit * (15.0f/30.0f);
1458
1459 skeleton_sample_anim( sk, s->anim_grabs, grab_frame, bpose );
1460 skeleton_lerp_pose( sk, apose, bpose, s->state.grabbing, air_pose );
1461 }
1462
1463 skeleton_lerp_pose( sk, ground_pose, air_pose, s->blend_fly, dev->pose );
1464
1465 float add_grab_mod = 1.0f - s->blend_fly;
1466
1467 /* additive effects */
1468 {
1469 u32 apply_to[] = { av->id_hip,
1470 av->id_ik_hand_l,
1471 av->id_ik_hand_r,
1472 av->id_ik_elbow_l,
1473 av->id_ik_elbow_r };
1474
1475 for( int i=0; i<vg_list_size(apply_to); i ++ )
1476 {
1477 dev->pose[apply_to[i]-1].co[0] += offset[0]*add_grab_mod;
1478 dev->pose[apply_to[i]-1].co[2] += offset[2]*add_grab_mod;
1479 }
1480
1481 mdl_keyframe *kf_board = &dev->pose[av->id_board-1],
1482 *kf_foot_l = &dev->pose[av->id_ik_foot_l-1],
1483 *kf_foot_r = &dev->pose[av->id_ik_foot_r-1];
1484
1485 v3f bo;
1486 v3_muls( s->board_offset, add_grab_mod, bo );
1487
1488 v3_add( bo, kf_board->co, kf_board->co );
1489 v3_add( bo, kf_foot_l->co, kf_foot_l->co );
1490 v3_add( bo, kf_foot_r->co, kf_foot_r->co );
1491
1492 m3x3f c;
1493 q_m3x3( s->board_rotation, c );
1494
1495 v3f d;
1496 v3_sub( kf_foot_l->co, bo, d );
1497 m3x3_mulv( c, d, d );
1498 v3_add( bo, d, kf_foot_l->co );
1499
1500 v3_sub( kf_foot_r->co, bo, d );
1501 m3x3_mulv( c, d, d );
1502 v3_add( bo, d, kf_foot_r->co );
1503
1504 q_mul( s->board_rotation, kf_board->q, kf_board->q );
1505 q_normalize( kf_board->q );
1506 }
1507
1508 /* transform */
1509 rb_extrapolate( &player->rb, dev->pose_root_co, dev->pose_root_q );
1510
1511 v3_muladds( dev->pose_root_co, player->rb.to_world[1], -0.28f,
1512 dev->pose_root_co );
1513
1514 v4f qresy, qresx, qresidual;
1515 m3x3f mtx_residual;
1516 float substep = vg_clampf( vg.accumulator / VG_TIMESTEP_FIXED, 0.0f, 1.0f );
1517 q_axis_angle( qresy, player->rb.to_world[1], s->state.steery_s*substep );
1518 q_axis_angle( qresx, player->rb.to_world[0], s->state.steerx_s*substep );
1519
1520 q_mul( qresy, qresx, qresidual );
1521 q_normalize( qresidual );
1522 q_mul( dev->pose_root_q, qresidual, dev->pose_root_q );
1523 q_normalize( dev->pose_root_q );
1524
1525 v4f qflip;
1526 if( (s->state.activity == k_skate_activity_air) &&
1527 (fabsf(s->state.flip_rate) > 0.01f) )
1528 {
1529 float angle = vg_clampf( s->state.flip_time, -1.0f, 1.0f ) * VG_TAUf,
1530 distm = s->land_dist * fabsf(s->state.flip_rate) * 3.0f,
1531 blend = vg_clampf( 1.0f-distm, 0.0f, 1.0f );
1532
1533 angle = vg_lerpf( angle, vg_signf(s->state.flip_rate) * VG_TAUf, blend );
1534
1535 q_axis_angle( qflip, s->state.flip_axis, angle );
1536 q_mul( qflip, dev->pose_root_q, dev->pose_root_q );
1537 q_normalize( dev->pose_root_q );
1538
1539 v3f rotation_point, rco;
1540 v3_muladds( player->rb.co, player->rb.to_world[1], 0.5f, rotation_point );
1541 v3_sub( dev->pose_root_co, rotation_point, rco );
1542
1543 /* FIXME: q_mul v3 */
1544 m3x3f TEMP;
1545 q_m3x3( qflip, TEMP );
1546 m3x3_mulv( TEMP, rco, rco );
1547 v3_add( rco, rotation_point, dev->pose_root_co );
1548 }
1549
1550 #if 0
1551 if( cl_thirdperson )
1552 {
1553 if( !followcam_will_hit_gate( player, &s->state.cam ) )
1554 {
1555 m4x3f inverse;
1556 m4x3_invert_affine( s->state.cam.gate->transport, inverse );
1557 m4x3_mul( inverse, transform, transform );
1558 }
1559 }
1560 #endif
1561 }
1562
1563 VG_STATIC void skate_camera_vector_look( camera *cam, v3f v, float C, float k )
1564 {
1565 float yaw = atan2f( v[0], -v[2] ),
1566 pitch = atan2f
1567 (
1568 -v[1],
1569 sqrtf
1570 (
1571 v[0]*v[0] + v[2]*v[2]
1572 )
1573 ) * C + k;
1574
1575 cam->angles[0] = yaw;
1576 cam->angles[1] = pitch;
1577 }
1578
1579 VG_STATIC void skate_camera_firstperson( player_device *dev,
1580 player_interface *player )
1581 {
1582 struct player_device_skate *s = dev->storage;
1583 struct player_avatar *av = player->playeravatar;
1584
1585 /* FIXME: viewpoint entity */
1586 v3f vp = {-0.1f,1.8f,0.0f};
1587 m4x3_mulv( av->sk.final_mtx[ av->id_head-1 ], vp, dev->cam_1st.pos );
1588
1589 v3_zero( dev->cam_1st.angles );
1590 dev->cam_1st.fov = 119.0f;
1591
1592 v3f flat_dir,
1593 vel_dir,
1594 look_dir;
1595
1596 v3_copy( player->rb.v, vel_dir );
1597 //v3_normalize( vel_dir );
1598
1599 float tti = s->land_dist;
1600 v3f norm;
1601 v3_copy( s->land_normal, norm );
1602
1603 if( s->state.activity == k_skate_activity_ground )
1604 {
1605 tti = 0.0f;
1606 v3_copy( player->rb.to_world[1], norm );
1607 }
1608
1609 v3_muladds( vel_dir, norm, -v3_dot(vel_dir,norm), flat_dir );
1610 //v3_normalize( flat_dir );
1611
1612 v3_lerp( flat_dir, vel_dir, vg_clampf( tti / 2.0f, 0.4f, 1.0f ), look_dir );
1613 v3_lerp( s->state.vl, look_dir, 4.0f*vg.time_delta, s->state.vl );
1614
1615 skate_camera_vector_look( &dev->cam_1st, s->state.vl, 1.0f, 0.25f );
1616 }
1617
1618 VG_STATIC void skate_camera_thirdperson( player_device *dev,
1619 player_interface *player )
1620 {
1621 struct player_device_skate *s = dev->storage;
1622 struct player_avatar *av = player->playeravatar;
1623
1624 v3f origin, dir, target;
1625 v3_copy( player->rb.co, origin );
1626 v3_add( origin, (v3f){0.0f,1.35f,0.0f}, origin );
1627 v3_sub( origin, s->state.posl, dir );
1628
1629 if( v3_length2( dir ) < 0.1f*0.1f )
1630 v3_copy( (v3f){ 0.0f, 0.0f, 1.0f }, dir ); /* FIXME */
1631 else
1632 v3_normalize( dir );
1633
1634 if( s->state.activity == k_skate_activity_air )
1635 dir[1] *= vg_maxf( 0.0f, 1.0f - (s->land_dist/2.0f) );
1636 dir[1] *= 0.0f;
1637
1638 v3_muladds( origin, dir, -2.0f, target );
1639
1640 v3_lerp( s->state.posl, target, vg.frame_delta * 15.0f, s->state.posl );
1641 v3_lerp( s->state.dirl, dir, 18.0f*vg.time_delta, s->state.dirl );
1642
1643 v3_copy( s->state.posl, dev->cam_3rd.pos );
1644 skate_camera_vector_look( &dev->cam_3rd, s->state.dirl, 1.0f, 0.2f );
1645 }
1646
1647 VG_STATIC void player_skate_post_animate( player_device *dev,
1648 player_interface *player )
1649 {
1650 struct player_device_skate *s = dev->storage;
1651 struct player_avatar *av = player->playeravatar;
1652
1653 v3_zero( dev->cam_1st.pos );
1654 v3_zero( dev->cam_1st.angles );
1655 dev->cam_1st.fov = 90.0f;
1656
1657 skate_camera_thirdperson( dev, player );
1658 skate_camera_firstperson( dev, player );
1659
1660 /* FIXME: Organize this. Its int wrong fucking place */
1661 v3f vp0 = {0.0f,0.1f, 0.6f},
1662 vp1 = {0.0f,0.1f,-0.6f};
1663
1664 m4x3_mulv( av->sk.final_mtx[ av->id_board ], vp0, TEMP_BOARD_0 );
1665 m4x3_mulv( av->sk.final_mtx[ av->id_board ], vp1, TEMP_BOARD_1 );
1666 }
1667
1668 VG_STATIC void player_skate_reset( player_device *dev,
1669 player_interface *player,
1670 struct respawn_point *rp )
1671 {
1672 struct player_device_skate *s = dev->storage;
1673 v3_muladds( player->rb.co, player->rb.to_world[1], 1.0f, s->state.cog );
1674
1675 #if 0
1676 mixedcam_reset( player, &s->state.cam );
1677 #endif
1678 }
1679
1680 VG_STATIC int player_skate_event( player_device *dev, player_interface *player,
1681 enum player_device_event_type ev,
1682 void *data )
1683 {
1684 struct player_device_skate *s = dev->storage;
1685
1686 if( ev == k_player_device_event_bind )
1687 player_skate_bind( dev, player );
1688 else if( ev == k_player_device_event_respawn )
1689 player_skate_reset( dev, player, data );
1690 else if( ev == k_player_device_event_pre_update )
1691 {
1692 if( vg_input_button_down( player->input_use ) )
1693 {
1694 struct device_transition_walk inf;
1695 v3_copy( player->cam.angles, inf.angles );
1696 inf.angles[2] = 0.0f;
1697
1698 player_transition_to_device( player, s->device_id_walk, &inf );
1699 return 1;
1700 }
1701 }
1702 else if( ev == k_player_device_event_custom_transition )
1703 {
1704 /* transition coming in from walking */
1705 struct device_transition_skateboard *inf = data;
1706
1707 q_axis_angle( player->rb.q, (v3f){0.0f,1.0f,0.0f},
1708 atan2f( inf->dir[0], inf->dir[2] ) );
1709 v3_copy( player->cam.pos, s->state.posl );
1710
1711 m3x3f temp;
1712 euler_m3x3( player->cam.angles, temp );
1713 v3_muls( temp[2], -1.0f, s->state.dirl );
1714
1715 rb_update_transform( &player->rb );
1716 v3_muladds( player->rb.co, player->rb.to_world[1], 1.0f, s->state.cog );
1717 v3_copy( player->rb.v, s->state.cog_v );
1718 }
1719 else if( ev == k_player_device_event_update )
1720 {
1721 player_skate_update( dev, player );
1722 }
1723 else if( ev == k_player_device_event_post_update )
1724 {
1725 for( int i=0; i<s->prediction_count; i++ )
1726 {
1727 struct land_prediction *p = &s->predictions[i];
1728
1729 for( int j=0; j<p->log_length - 1; j ++ )
1730 vg_line( p->log[j], p->log[j+1], p->colour );
1731
1732 vg_line_cross( p->log[p->log_length-1], p->colour, 0.25f );
1733
1734 v3f p1;
1735 v3_add( p->log[p->log_length-1], p->n, p1 );
1736 vg_line( p->log[p->log_length-1], p1, 0xffffffff );
1737
1738 vg_line_pt3( p->apex, 0.02f, 0xffffffff );
1739 }
1740
1741 vg_line_pt3( s->state.apex, 0.200f, 0xff0000ff );
1742 vg_line_pt3( s->state.apex, 0.201f, 0xff00ffff );
1743 }
1744 else if( ev == k_player_device_event_animate )
1745 {
1746 player_skate_animate( dev, player );
1747 }
1748 else if( ev == k_player_device_event_post_animate )
1749 {
1750 player_skate_post_animate( dev, player );
1751 }
1752 else if( ev == k_player_device_event_debug_ui )
1753 {
1754 player_skate_ui( dev, player );
1755 }
1756 else
1757 return 0;
1758
1759 return 1;
1760 }
1761
1762 VG_STATIC player_device player_device_skate =
1763 {
1764 .name = "skateboard",
1765 .event = player_skate_event,
1766 .storage = &localplayer_device_skate
1767 };
1768
1769 #endif /* PLAYER_DEVICE_SKATE_H */