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