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