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