stuff
[carveJwlIkooP6JGAAIwe30JlM.git] / player_physics.h
1 /*
2 * Copyright (C) 2021-2022 Mt.ZERO Software, Harry Godden - All Rights Reserved
3 */
4
5 #ifndef PLAYER_PHYSICS_H
6 #define PLAYER_PHYSICS_H
7
8 #include "player.h"
9 #include "camera.h"
10
11 VG_STATIC void apply_gravity( v3f vel, float const timestep )
12 {
13 v3f gravity = { 0.0f, -9.6f, 0.0f };
14 v3_muladds( vel, gravity, timestep, vel );
15 }
16
17 /*
18 * Called when launching into the air to predict and adjust trajectories
19 */
20 VG_STATIC void player_start_air(void)
21 {
22 struct player_phys *phys = &player.phys;
23
24 if( phys->in_air )
25 return;
26
27 phys->in_air = 1;
28
29 float pstep = VG_TIMESTEP_FIXED * 10.0f;
30 float best_velocity_delta = -9999.9f;
31 float k_bias = 0.96f;
32
33 v3f axis;
34 v3_cross( phys->rb.up, phys->rb.v, axis );
35 v3_normalize( axis );
36 player.land_log_count = 0;
37
38 m3x3_identity( phys->vr );
39
40 for( int m=-3;m<=12; m++ )
41 {
42 float vmod = ((float)m / 15.0f)*0.09f;
43
44 v3f pco, pco1, pv;
45 v3_copy( phys->rb.co, pco );
46 v3_muls( phys->rb.v, k_bias, pv );
47
48 /*
49 * Try different 'rotations' of the velocity to find the best possible
50 * landing normal. This conserves magnitude at the expense of slightly
51 * unrealistic results
52 */
53
54 m3x3f vr;
55 v4f vr_q;
56
57 q_axis_angle( vr_q, axis, vmod );
58 q_m3x3( vr_q, vr );
59
60 m3x3_mulv( vr, pv, pv );
61 v3_muladds( pco, pv, pstep, pco );
62
63 for( int i=0; i<50; i++ )
64 {
65 v3_copy( pco, pco1 );
66 apply_gravity( pv, pstep );
67
68 m3x3_mulv( vr, pv, pv );
69 v3_muladds( pco, pv, pstep, pco );
70
71 ray_hit contact;
72 v3f vdir;
73
74 v3_sub( pco, pco1, vdir );
75 contact.dist = v3_length( vdir );
76 v3_divs( vdir, contact.dist, vdir);
77
78 if( ray_world( pco1, vdir, &contact ))
79 {
80 float land_delta = v3_dot( pv, contact.normal );
81 u32 scolour = (u8)(vg_minf(-land_delta * 2.0f, 255.0f));
82
83 /* Bias prediction towords ramps */
84 if( ray_hit_material( &contact )->info.flags
85 & k_material_flag_skate_surface )
86 {
87 land_delta *= 0.1f;
88 scolour |= 0x0000a000;
89 }
90
91 if( (land_delta < 0.0f) && (land_delta > best_velocity_delta) )
92 {
93 best_velocity_delta = land_delta;
94
95 v3_copy( contact.pos, player.land_target );
96
97 m3x3_copy( vr, phys->vr_pstep );
98 q_axis_angle( vr_q, axis, vmod*0.1f );
99 q_m3x3( vr_q, phys->vr );
100 }
101
102 v3_copy( contact.pos,
103 player.land_target_log[player.land_log_count] );
104 player.land_target_colours[player.land_log_count] =
105 0xff000000 | scolour;
106
107 player.land_log_count ++;
108
109 break;
110 }
111 }
112 }
113 }
114
115 /*
116 * Main friction interface model
117 */
118 VG_STATIC void player_physics_control(void)
119 {
120 struct player_phys *phys = &player.phys;
121
122 /*
123 * Computing localized friction forces for controlling the character
124 * Friction across X is significantly more than Z
125 */
126
127 v3f vel;
128 m3x3_mulv( phys->rb.to_local, phys->rb.v, vel );
129 float slip = 0.0f;
130
131 if( fabsf(vel[2]) > 0.01f )
132 slip = fabsf(-vel[0] / vel[2]) * vg_signf(vel[0]);
133
134 if( fabsf( slip ) > 1.2f )
135 slip = vg_signf( slip ) * 1.2f;
136 phys->slip = slip;
137 phys->reverse = -vg_signf(vel[2]);
138
139 float substep = VG_TIMESTEP_FIXED * 0.2f;
140
141 #if 0
142 float fwd_resistance = vg_get_button( "break" )? 5.0f: k_friction_resistance;
143 #else
144 float fwd_resistance = k_friction_resistance;
145 #endif
146
147 for( int i=0; i<5; i++ )
148 {
149 vel[2] = stable_force( vel[2],vg_signf(vel[2]) * -fwd_resistance*substep);
150 vel[0] = stable_force( vel[0],vg_signf(vel[0]) * -k_friction_lat*substep);
151 }
152
153 if( player.input_jump->button.value )
154 {
155 phys->jump += VG_TIMESTEP_FIXED * k_jump_charge_speed;
156
157 if( !phys->jump_charge )
158 phys->jump_dir = phys->reverse > 0.0f? 1: 0;
159
160 phys->jump_charge = 1;
161 }
162
163 static int push_thresh_last = 0;
164 float push = player.input_push->button.value;
165 int push_thresh = push>0.15f? 1: 0;
166
167 if( push_thresh && !push_thresh_last )
168 player.phys.start_push = vg.time;
169
170 push_thresh_last = push_thresh;
171
172 if( !player.input_jump->button.value && push_thresh )
173 {
174 player.phys.pushing = 1.0f;
175 player.phys.push_time = vg.time - player.phys.start_push;
176
177 float cycle_time = player.phys.push_time*k_push_cycle_rate,
178 amt = k_push_accel * (sinf(cycle_time)*0.5f+0.5f)*VG_TIMESTEP_FIXED,
179 current = v3_length( vel ),
180 new_vel = vg_minf( current + amt, k_max_push_speed );
181
182 new_vel -= vg_minf(current, k_max_push_speed);
183 vel[2] -= new_vel * phys->reverse;
184 }
185
186 /* Pumping */
187 static float previous = 0.0f;
188 float delta = previous - phys->grab,
189 pump = delta * k_pump_force * VG_TIMESTEP_FIXED;
190 previous = phys->grab;
191
192 v3f p1;
193 v3_muladds( phys->rb.co, phys->rb.up, pump, p1 );
194 vg_line( phys->rb.co, p1, 0xff0000ff );
195
196 vel[1] += pump;
197
198 m3x3_mulv( phys->rb.to_world, vel, phys->rb.v );
199
200 float input = player.input_js1h->axis.value,
201 grab = player.input_grab->axis.value,
202 steer = input * (1.0f-(phys->jump+grab)*0.4f),
203 steer_scaled = vg_signf(steer) * powf(steer,2.0f) * k_steer_ground;
204
205 phys->iY -= steer_scaled * VG_TIMESTEP_FIXED;
206 }
207
208 /*
209 * Air control, no real physics
210 */
211 VG_STATIC void player_physics_control_air(void)
212 {
213 struct player_phys *phys = &player.phys;
214
215 m3x3_mulv( phys->vr, phys->rb.v, phys->rb.v );
216 vg_line_cross( player.land_target, 0xff0000ff, 0.25f );
217
218 ray_hit hit;
219
220 /*
221 * Prediction
222 */
223 float pstep = VG_TIMESTEP_FIXED * 10.0f;
224
225 v3f pco, pco1, pv;
226 v3_copy( phys->rb.co, pco );
227 v3_copy( phys->rb.v, pv );
228
229 float time_to_impact = 0.0f;
230 float limiter = 1.0f;
231
232 for( int i=0; i<50; i++ )
233 {
234 v3_copy( pco, pco1 );
235 m3x3_mulv( phys->vr_pstep, pv, pv );
236 apply_gravity( pv, pstep );
237 v3_muladds( pco, pv, pstep, pco );
238
239 ray_hit contact;
240 v3f vdir;
241
242 v3_sub( pco, pco1, vdir );
243 contact.dist = v3_length( vdir );
244 v3_divs( vdir, contact.dist, vdir);
245
246 float orig_dist = contact.dist;
247 if( ray_world( pco1, vdir, &contact ))
248 {
249 float angle = v3_dot( phys->rb.up, contact.normal );
250 v3f axis;
251 v3_cross( phys->rb.up, contact.normal, axis );
252
253 time_to_impact += (contact.dist/orig_dist)*pstep;
254 limiter = vg_minf( 5.0f, time_to_impact )/5.0f;
255 limiter = 1.0f-limiter;
256 limiter *= limiter;
257 limiter = 1.0f-limiter;
258
259 if( angle < 0.99f )
260 {
261 v4f correction;
262 q_axis_angle( correction, axis,
263 acosf(angle)*(1.0f-limiter)*3.0f*VG_TIMESTEP_FIXED );
264 q_mul( correction, phys->rb.q, phys->rb.q );
265 }
266
267 vg_line_cross( contact.pos, 0xffff0000, 0.25f );
268 break;
269 }
270 time_to_impact += pstep;
271 }
272
273 v2f steer = { player.input_js1h->axis.value,
274 player.input_js1v->axis.value };
275
276 float l2 = v2_length2( steer );
277 if( l2 > 1.0f )
278 v2_muls( steer, 1.0f/sqrtf(l2), steer );
279
280 phys->iY -= steer[0] * k_steer_air * VG_TIMESTEP_FIXED;
281
282 float iX = steer[1] *
283 phys->reverse * k_steer_air * limiter * VG_TIMESTEP_FIXED;
284
285 static float siX = 0.0f;
286 siX = vg_lerpf( siX, iX, k_steer_air_lerp );
287
288 v4f rotate;
289 q_axis_angle( rotate, phys->rb.right, siX );
290 q_mul( rotate, phys->rb.q, phys->rb.q );
291
292 #if 0
293 v2f target = {0.0f,0.0f};
294 v2_muladds( target, (v2f){ vg_get_axis("grabh"), vg_get_axis("grabv") },
295 phys->grab, target );
296 #endif
297 }
298
299 VG_STATIC void player_walk_update_collision(void)
300 {
301 struct player_phys *phys = &player.phys;
302 float h0 = 0.3f,
303 h1 = 0.9f;
304
305 rigidbody *rbf = &player.collide_front,
306 *rbb = &player.collide_back;
307
308 v3_add( phys->rb.co, (v3f){0.0f,h0,0.0f}, rbf->co );
309 v3_add( phys->rb.co, (v3f){0.0f,h1,0.0f}, rbb->co );
310 v3_copy( rbf->co, rbf->to_world[3] );
311 v3_copy( rbb->co, rbb->to_world[3] );
312 m4x3_invert_affine( rbf->to_world, rbf->to_local );
313 m4x3_invert_affine( rbb->to_world, rbb->to_local );
314
315 rb_update_bounds( rbf );
316 rb_update_bounds( rbb );
317 }
318
319 VG_STATIC void player_integrate(void);
320 /*
321 * Entire Walking physics model
322 * TODO: sleep when under certain velotiy
323 */
324 VG_STATIC void player_walk_physics(void)
325 {
326 struct player_phys *phys = &player.phys;
327 rigidbody *rbf = &player.collide_front,
328 *rbb = &player.collide_back;
329
330 m3x3_identity( player.collide_front.to_world );
331 m3x3_identity( player.collide_back.to_world );
332
333 v3_zero( phys->rb.w );
334 q_axis_angle( phys->rb.q, (v3f){0.0f,1.0f,0.0f}, -player.angles[0] );
335
336 rb_ct manifold[64];
337 int len;
338
339 v3f forward_dir = { sinf(player.angles[0]),0.0f,-cosf(player.angles[0]) };
340 v3f right_dir = { -forward_dir[2], 0.0f, forward_dir[0] };
341
342 if( phys->in_air )
343 {
344 player_walk_update_collision();
345 rb_debug( rbf, 0xff0000ff );
346 rb_debug( rbb, 0xff0000ff );
347
348 len = 0;
349 len += rb_sphere_scene( rbf, &world.rb_geo, manifold+len );
350 len += rb_sphere_scene( rbb, &world.rb_geo, manifold+len );
351 rb_presolve_contacts( manifold, len );
352
353 for( int i=0; i<len; i++ )
354 {
355 struct contact *ct = &manifold[i];
356 if( v3_dot( ct->n, (v3f){0.0f,1.0f,0.0f} ) > 0.5f )
357 phys->in_air = 0;
358 }
359
360 for( int j=0; j<5; j++ )
361 {
362 for( int i=0; i<len; i++ )
363 {
364 struct contact *ct = &manifold[i];
365
366 /*normal */
367 float vn = -v3_dot( phys->rb.v, ct->n );
368 vn += ct->bias;
369
370 float temp = ct->norm_impulse;
371 ct->norm_impulse = vg_maxf( temp + vn, 0.0f );
372 vn = ct->norm_impulse - temp;
373
374 v3f impulse;
375 v3_muls( ct->n, vn, impulse );
376
377 v3_add( impulse, phys->rb.v, phys->rb.v );
378
379 /* friction */
380 for( int j=0; j<2; j++ )
381 {
382 float f = k_friction * ct->norm_impulse,
383 vt = v3_dot( phys->rb.v, ct->t[j] ),
384 lambda = -vt;
385
386 float temp = ct->tangent_impulse[j];
387 ct->tangent_impulse[j] = vg_clampf( temp + lambda, -f, f );
388 lambda = ct->tangent_impulse[j] - temp;
389
390 v3_muladds( phys->rb.v, ct->t[j], lambda, phys->rb.v );
391 }
392 }
393 }
394
395 player_integrate();
396 }
397 else
398 {
399 /* translate player */
400 v2f walk = { player.input_walkh->axis.value,
401 player.input_walkv->axis.value };
402
403 if( v2_length2(walk) > 0.001f )
404 v2_normalize_clamp( walk );
405
406 player.walk = v2_length( walk );
407
408 if( player.input_walk->button.value )
409 v2_muls( walk, 0.5f, walk );
410
411 v2_muls( walk, k_walkspeed * VG_TIMESTEP_FIXED, walk );
412
413 v3f walk_apply;
414 v3_zero( walk_apply );
415
416 /* Do XY translation */
417 v3_muladds( walk_apply, right_dir, walk[0], walk_apply );
418 v3_muladds( walk_apply, forward_dir, walk[1], walk_apply );
419 v3_add( walk_apply, phys->rb.co, phys->rb.co );
420 v3_divs( walk_apply, VG_TIMESTEP_FIXED, phys->rb.v );
421
422 /* Directly resolve collisions */
423 player_walk_update_collision();
424 rb_debug( rbf, 0xffffff00 );
425 rb_debug( rbb, 0xffffff00 );
426
427 len = 0;
428 len += rb_sphere_scene( rbf, &world.rb_geo, manifold+len );
429 len += rb_sphere_scene( rbb, &world.rb_geo, manifold+len );
430
431 v3f dt;
432 v3_zero( dt );
433 for( int j=0; j<3; j++ )
434 {
435 for( int i=0; i<len; i++ )
436 {
437 struct contact *ct = &manifold[i];
438
439 float p = vg_maxf( 0.0f, ct->p - 0.00f ),
440 cur = vg_clampf( v3_dot( ct->n, dt ), 0.0f, p );
441 v3_muladds( dt, ct->n, (p - cur) * 0.333333333f, dt );
442 }
443 }
444 v3_add( dt, phys->rb.co, phys->rb.co );
445
446 /* jump */
447 if( player.input_jump->button.value )
448 {
449 phys->rb.v[1] = 5.0f;
450 phys->in_air = 1;
451 return;
452 }
453
454 /* if we've put us in the air, step down slowly */
455 phys->in_air = 1;
456 float max_dist = 0.3f,
457 start_y = phys->rb.co[1];
458
459 for( int j=0; j<8; j++ )
460 {
461 for( int i=0; i<len; i++ )
462 {
463 struct contact *ct = &manifold[i];
464 if( v3_dot( ct->n, (v3f){0.0f,1.0f,0.0f} ) > 0.5f )
465 {
466 phys->in_air = 0;
467 if( j == 0 )
468 return;
469
470 v3f dt;
471 v3_zero( dt );
472 for( int j=0; j<3; j++ )
473 {
474 for( int i=0; i<len; i++ )
475 {
476 struct contact *ct = &manifold[i];
477
478 float p = vg_maxf( 0.0f, ct->p - 0.0025f ),
479 cur = vg_clampf( v3_dot( ct->n, dt ), 0.0f, p );
480 v3_muladds( dt, ct->n, (p - cur) * 0.333333333f, dt );
481 }
482 }
483 v3_add( dt, phys->rb.co, phys->rb.co );
484 return;
485 }
486 }
487
488 phys->rb.co[1] -= max_dist * 0.125f;
489
490 player_walk_update_collision();
491 len = 0;
492 len += rb_sphere_scene( rbf, &world.rb_geo, manifold+len );
493 len += rb_sphere_scene( rbb, &world.rb_geo, manifold+len );
494 }
495
496 /* Transitioning into air mode */
497 phys->rb.co[1] = start_y;
498 }
499 }
500
501 /*
502 * Physics collision detection, and control
503 */
504 VG_STATIC void player_physics(void)
505 {
506 struct player_phys *phys = &player.phys;
507 /*
508 * Update collision fronts
509 */
510
511 rigidbody *rbf = &player.collide_front,
512 *rbb = &player.collide_back;
513
514 m3x3_copy( phys->rb.to_world, player.collide_front.to_world );
515 m3x3_copy( phys->rb.to_world, player.collide_back.to_world );
516
517 player.air_blend = vg_lerpf( player.air_blend, phys->in_air, 0.1f );
518 float h = player.air_blend*0.2f;
519
520 m4x3_mulv( phys->rb.to_world, (v3f){0.0f,h,-k_board_length}, rbf->co );
521 v3_copy( rbf->co, rbf->to_world[3] );
522 m4x3_mulv( phys->rb.to_world, (v3f){0.0f,h, k_board_length}, rbb->co );
523 v3_copy( rbb->co, rbb->to_world[3] );
524
525 m4x3_invert_affine( rbf->to_world, rbf->to_local );
526 m4x3_invert_affine( rbb->to_world, rbb->to_local );
527
528 rb_update_bounds( rbf );
529 rb_update_bounds( rbb );
530
531 rb_debug( rbf, 0xff00ffff );
532 rb_debug( rbb, 0xffffff00 );
533
534 rb_ct manifold[64];
535 int len = 0;
536
537 len += rb_sphere_scene( rbf, &world.rb_geo, manifold+len );
538 len += rb_sphere_scene( rbb, &world.rb_geo, manifold+len );
539
540 rb_presolve_contacts( manifold, len );
541 v3f surface_avg = {0.0f, 0.0f, 0.0f};
542
543 if( !len )
544 {
545 player_start_air();
546 }
547 else
548 {
549 for( int i=0; i<len; i++ )
550 {
551 v3_add( manifold[i].n, surface_avg, surface_avg );
552 }
553
554 v3_normalize( surface_avg );
555
556 if( v3_dot( phys->rb.v, surface_avg ) > 0.5f )
557 {
558 player_start_air();
559 }
560 else
561 {
562 phys->in_air = 0;
563 }
564 }
565
566 for( int j=0; j<5; j++ )
567 {
568 for( int i=0; i<len; i++ )
569 {
570 struct contact *ct = &manifold[i];
571
572 v3f dv, delta;
573 v3_sub( ct->co, phys->rb.co, delta );
574 v3_cross( phys->rb.w, delta, dv );
575 v3_add( phys->rb.v, dv, dv );
576
577 float vn = -v3_dot( dv, ct->n );
578 vn += ct->bias;
579
580 float temp = ct->norm_impulse;
581 ct->norm_impulse = vg_maxf( temp + vn, 0.0f );
582 vn = ct->norm_impulse - temp;
583
584 v3f impulse;
585 v3_muls( ct->n, vn, impulse );
586
587 if( fabsf(v3_dot( impulse, phys->rb.forward )) > 10.0f ||
588 fabsf(v3_dot( impulse, phys->rb.up )) > 50.0f )
589 {
590 player_kill();
591 return;
592 }
593
594 v3_add( impulse, phys->rb.v, phys->rb.v );
595 v3_cross( delta, impulse, impulse );
596
597 /*
598 * W Impulses are limited to the Y and X axises, we don't really want
599 * roll angular velocities being included.
600 *
601 * Can also tweak the resistance of each axis here by scaling the wx,wy
602 * components.
603 */
604
605 float wy = v3_dot( phys->rb.up, impulse ),
606 wx = v3_dot( phys->rb.right, impulse )*1.5f;
607
608 v3_muladds( phys->rb.w, phys->rb.up, wy, phys->rb.w );
609 v3_muladds( phys->rb.w, phys->rb.right, wx, phys->rb.w );
610 }
611 }
612
613 float grabt = player.input_grab->axis.value;
614
615 if( grabt > 0.5f )
616 {
617 v2_muladds( phys->grab_mouse_delta, vg.mouse_delta, 0.02f,
618 phys->grab_mouse_delta );
619 v2_normalize_clamp( phys->grab_mouse_delta );
620 }
621 else
622 v2_zero( phys->grab_mouse_delta );
623
624 phys->grab = vg_lerpf( phys->grab, grabt, 0.14f );
625 player.phys.pushing = 0.0f;
626
627 if( !phys->in_air )
628 {
629 /* 20/10/22: make this only go axisways instead, may effect velocities. */
630
631 v3f projected, axis;
632
633 float d = v3_dot( phys->rb.forward, surface_avg );
634 v3_muladds( surface_avg, phys->rb.forward, -d, projected );
635 v3_normalize( projected );
636
637 float angle = v3_dot( phys->rb.up, projected );
638 v3_cross( phys->rb.up, projected, axis );
639
640 v3f p0, p1;
641 v3_add( phys->rb.co, projected, p0 );
642 v3_add( phys->rb.co, phys->rb.up, p1 );
643 vg_line( phys->rb.co, p0, 0xff00ff00 );
644 vg_line( phys->rb.co, p1, 0xff000fff );
645
646 if( fabsf(angle) < 0.999f )
647 {
648 v4f correction;
649 q_axis_angle( correction, axis, acosf(angle)*4.0f*VG_TIMESTEP_FIXED );
650 q_mul( correction, phys->rb.q, phys->rb.q );
651 }
652
653 float const DOWNFORCE = -k_downforce*VG_TIMESTEP_FIXED;
654 v3_muladds( phys->rb.v, phys->rb.up, DOWNFORCE, phys->rb.v );
655
656 player_physics_control();
657
658 if( !phys->jump_charge && phys->jump > 0.2f )
659 {
660 v3f jumpdir;
661
662 /* Launch more up if alignment is up else improve velocity */
663 float aup = fabsf(v3_dot( (v3f){0.0f,1.0f,0.0f}, phys->rb.up )),
664 mod = 0.5f,
665 dir = mod + aup*(1.0f-mod);
666
667 v3_copy( phys->rb.v, jumpdir );
668 v3_normalize( jumpdir );
669 v3_muls( jumpdir, 1.0f-dir, jumpdir );
670 v3_muladds( jumpdir, phys->rb.up, dir, jumpdir );
671 v3_normalize( jumpdir );
672
673 float force = k_jump_force*phys->jump;
674 v3_muladds( phys->rb.v, jumpdir, force, phys->rb.v );
675 phys->jump = 0.0f;
676
677 player.jump_time = vg.time;
678
679 /* TODO: Move to audio file */
680 audio_lock();
681 audio_player_set_flags( &audio_player_extra, AUDIO_FLAG_SPACIAL_3D );
682 audio_player_set_position( &audio_player_extra, phys->rb.co );
683 audio_player_set_vol( &audio_player_extra, 20.0f );
684 audio_player_playclip( &audio_player_extra, &audio_jumps[rand()%2] );
685 audio_unlock();
686 }
687 }
688 else
689 {
690 player_physics_control_air();
691 }
692
693 if( !phys->jump_charge )
694 {
695 phys->jump -= k_jump_charge_speed * VG_TIMESTEP_FIXED;
696 }
697
698 phys->jump_charge = 0;
699 phys->jump = vg_clampf( phys->jump, 0.0f, 1.0f );
700 }
701
702 VG_STATIC void player_save_frame(void)
703 {
704 player.phys_gate_frame = player.phys;
705 }
706
707 VG_STATIC void player_restore_frame(void)
708 {
709 player.phys = player.phys_gate_frame;
710 rb_update_transform( &player.phys.rb );
711 }
712
713 VG_STATIC void player_integrate(void)
714 {
715 struct player_phys *phys = &player.phys;
716 apply_gravity( phys->rb.v, VG_TIMESTEP_FIXED );
717 v3_muladds( phys->rb.co, phys->rb.v, VG_TIMESTEP_FIXED, phys->rb.co );
718 }
719
720 VG_STATIC void player_do_motion(void)
721 {
722 struct player_phys *phys = &player.phys;
723
724 if( world.water.enabled )
725 {
726 if( (phys->rb.co[1] < 0.0f) && !player.is_dead )
727 {
728 audio_lock();
729 audio_player_set_flags( &audio_player_extra, AUDIO_FLAG_SPACIAL_3D );
730 audio_player_set_position( &audio_player_extra, phys->rb.co );
731 audio_player_set_vol( &audio_player_extra, 20.0f );
732 audio_player_playclip( &audio_player_extra, &audio_splash );
733 audio_unlock();
734
735 player_kill();
736 }
737 }
738
739
740 v3f prevco;
741 v3_copy( phys->rb.co, prevco );
742
743 if( phys->on_board )
744 {
745 player_physics();
746 player_integrate();
747 }
748 else
749 player_walk_physics();
750
751
752 /* Real angular velocity integration */
753 v3_lerp( phys->rb.w, (v3f){0.0f,0.0f,0.0f}, 0.125f, phys->rb.w );
754 if( v3_length2( phys->rb.w ) > 0.0f )
755 {
756 v4f rotation;
757 v3f axis;
758 v3_copy( phys->rb.w, axis );
759
760 float mag = v3_length( axis );
761 v3_divs( axis, mag, axis );
762 q_axis_angle( rotation, axis, mag*k_rb_delta );
763 q_mul( rotation, phys->rb.q, phys->rb.q );
764 }
765
766 /* Faux angular velocity */
767 v4f rotate;
768
769 float lerpq = phys->in_air? 0.04f: 0.3f;
770 phys->siY = vg_lerpf( phys->siY, phys->iY, lerpq );
771
772 q_axis_angle( rotate, phys->rb.up, phys->siY );
773 q_mul( rotate, phys->rb.q, phys->rb.q );
774 phys->iY = 0.0f;
775
776 /*
777 * Gate intersection, by tracing a line over the gate planes
778 */
779 for( int i=0; i<world.gate_count; i++ )
780 {
781 struct route_gate *rg = &world.gates[i];
782 teleport_gate *gate = &rg->gate;
783
784 if( gate_intersect( gate, phys->rb.co, prevco ) )
785 {
786 m4x3_mulv( gate->transport, phys->rb.co, phys->rb.co );
787 m3x3_mulv( gate->transport, phys->rb.v, phys->rb.v );
788 m3x3_mulv( gate->transport, phys->vl, phys->vl );
789 m3x3_mulv( gate->transport, phys->v_last, phys->v_last );
790 m3x3_mulv( gate->transport, phys->m, phys->m );
791 m3x3_mulv( gate->transport, phys->bob, phys->bob );
792
793 v4f transport_rotation;
794 m3x3_q( gate->transport, transport_rotation );
795 q_mul( transport_rotation, phys->rb.q, phys->rb.q );
796
797 world_routes_activate_gate( i );
798
799 if( !phys->on_board )
800 {
801 v3f fwd_dir = {cosf(player.angles[0]),
802 0.0f,
803 sinf(player.angles[0])};
804 m3x3_mulv( gate->transport, fwd_dir, fwd_dir );
805
806 player.angles[0] = atan2f( fwd_dir[2], fwd_dir[0] );
807 }
808
809 player.rewind_length = 0;
810 player.rewind_total_length = 0.0f;
811 player.rewind_incrementer = 10000;
812 player_save_frame();
813
814 audio_lock();
815 audio_play_oneshot( &audio_gate_pass, 1.0f );
816 audio_unlock();
817 break;
818 }
819 }
820
821 rb_update_transform( &phys->rb );
822 }
823
824 VG_STATIC void player_freecam(void)
825 {
826 player_mouseview();
827
828 float movespeed = fc_speed;
829 v3f lookdir = { 0.0f, 0.0f, -1.0f },
830 sidedir = { 1.0f, 0.0f, 0.0f };
831
832 m3x3_mulv( camera_mtx, lookdir, lookdir );
833 m3x3_mulv( camera_mtx, sidedir, sidedir );
834
835 static v3f move_vel = { 0.0f, 0.0f, 0.0f };
836
837 /* TODO */
838 #if 0
839 if( vg_get_button( "forward" ) )
840 v3_muladds( move_vel, lookdir, VG_TIMESTEP_FIXED * movespeed, move_vel );
841 if( vg_get_button( "back" ) )
842 v3_muladds( move_vel, lookdir, VG_TIMESTEP_FIXED *-movespeed, move_vel );
843 if( vg_get_button( "left" ) )
844 v3_muladds( move_vel, sidedir, VG_TIMESTEP_FIXED *-movespeed, move_vel );
845 if( vg_get_button( "right" ) )
846 v3_muladds( move_vel, sidedir, VG_TIMESTEP_FIXED * movespeed, move_vel );
847 #endif
848
849 v3_muls( move_vel, 0.7f, move_vel );
850 v3_add( move_vel, player.camera_pos, player.camera_pos );
851 }
852
853 VG_STATIC int reset_player( int argc, char const *argv[] )
854 {
855 struct player_phys *phys = &player.phys;
856 struct respawn_point *rp = NULL, *r;
857
858 if( argc == 1 )
859 {
860 for( int i=0; i<world.spawn_count; i++ )
861 {
862 r = &world.spawns[i];
863 if( !strcmp( r->name, argv[0] ) )
864 {
865 rp = r;
866 break;
867 }
868 }
869
870 if( !rp )
871 vg_warn( "No spawn named '%s'\n", argv[0] );
872 }
873
874 if( !rp )
875 {
876 float min_dist = INFINITY;
877
878 for( int i=0; i<world.spawn_count; i++ )
879 {
880 r = &world.spawns[i];
881 float d = v3_dist2( r->co, phys->rb.co );
882
883 vg_info( "Dist %s : %f\n", r->name, d );
884 if( d < min_dist )
885 {
886 min_dist = d;
887 rp = r;
888 }
889 }
890 }
891
892 if( !rp )
893 {
894 vg_error( "No spawn found\n" );
895 if( !world.spawn_count )
896 return 0;
897
898 rp = &world.spawns[0];
899 }
900
901 player.is_dead = 0;
902
903 m3x3f the_long_way;
904 q_m3x3( rp->q, the_long_way );
905
906 v3f delta = {1.0f,0.0f,0.0f};
907 m3x3_mulv( the_long_way, delta, delta );
908
909 player.angles[0] = atan2f( delta[0], -delta[2] );
910 player.angles[1] = -asinf( delta[1] );
911
912
913 v4_copy( rp->q, phys->rb.q );
914 v3_copy( rp->co, phys->rb.co );
915 v3_zero( phys->rb.v );
916
917 phys->vswitch = 1.0f;
918 phys->slip_last = 0.0f;
919 phys->in_air = 1;
920 phys->on_board = 0;
921 m3x3_identity( phys->vr );
922
923 player.mdl.shoes[0] = 1;
924 player.mdl.shoes[1] = 1;
925
926 rb_update_transform( &phys->rb );
927 player_save_frame();
928 return 1;
929 }
930
931 #endif /* PLAYER_PHYSICS_H */