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