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