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