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