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