2 * Copyright (C) 2021-2022 Mt.ZERO Software, Harry Godden - All Rights Reserved
5 #ifndef PLAYER_PHYSICS_H
6 #define PLAYER_PHYSICS_H
10 static void apply_gravity( v3f vel
, float const timestep
)
12 v3f gravity
= { 0.0f
, -9.6f
, 0.0f
};
13 v3_muladds( vel
, gravity
, timestep
, vel
);
17 * Called when launching into the air to predict and adjust trajectories
19 static void player_start_air(void)
21 struct player_phys
*phys
= &player
.phys
;
28 float pstep
= VG_TIMESTEP_FIXED
* 10.0f
;
29 float best_velocity_delta
= -9999.9f
;
33 v3_cross( phys
->rb
.up
, phys
->rb
.v
, axis
);
35 player
.land_log_count
= 0;
37 m3x3_identity( phys
->vr
);
39 for( int m
=-3;m
<=12; m
++ )
41 float vmod
= ((float)m
/ 15.0f
)*0.09f
;
44 v3_copy( phys
->rb
.co
, pco
);
45 v3_muls( phys
->rb
.v
, k_bias
, pv
);
48 * Try different 'rotations' of the velocity to find the best possible
49 * landing normal. This conserves magnitude at the expense of slightly
56 q_axis_angle( vr_q
, axis
, vmod
);
59 m3x3_mulv( vr
, pv
, pv
);
60 v3_muladds( pco
, pv
, pstep
, pco
);
62 for( int i
=0; i
<50; i
++ )
65 apply_gravity( pv
, pstep
);
67 m3x3_mulv( vr
, pv
, pv
);
68 v3_muladds( pco
, pv
, pstep
, pco
);
73 v3_sub( pco
, pco1
, vdir
);
74 contact
.dist
= v3_length( vdir
);
75 v3_divs( vdir
, contact
.dist
, vdir
);
77 if( ray_world( pco1
, vdir
, &contact
))
79 float land_delta
= v3_dot( pv
, contact
.normal
);
80 u32 scolour
= (u8
)(vg_minf(-land_delta
* 2.0f
, 255.0f
));
82 /* Bias prediction towords ramps */
83 if( ray_hit_is_ramp( &contact
) )
86 scolour
|= 0x0000a000;
89 if( (land_delta
< 0.0f
) && (land_delta
> best_velocity_delta
) )
91 best_velocity_delta
= land_delta
;
93 v3_copy( contact
.pos
, player
.land_target
);
95 m3x3_copy( vr
, phys
->vr_pstep
);
96 q_axis_angle( vr_q
, axis
, vmod
*0.1f
);
97 q_m3x3( vr_q
, phys
->vr
);
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
;
105 player
.land_log_count
++;
114 * Main friction interface model
116 static void player_physics_control(void)
118 struct player_phys
*phys
= &player
.phys
;
121 * Computing localized friction forces for controlling the character
122 * Friction across X is significantly more than Z
126 m3x3_mulv( phys
->rb
.to_local
, phys
->rb
.v
, vel
);
129 if( fabsf(vel
[2]) > 0.01f
)
130 slip
= fabsf(-vel
[0] / vel
[2]) * vg_signf(vel
[0]);
132 if( fabsf( slip
) > 1.2f
)
133 slip
= vg_signf( slip
) * 1.2f
;
135 phys
->reverse
= -vg_signf(vel
[2]);
137 float substep
= VG_TIMESTEP_FIXED
* 0.2f
;
138 float fwd_resistance
= (vg_get_button( "break" )? 5.0f
: 0.02f
) * -substep
;
140 for( int i
=0; i
<5; i
++ )
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
);
147 if( vg_get_button( "jump" ) )
149 phys
->jump
+= VG_TIMESTEP_FIXED
* k_jump_charge_speed
;
151 if( !phys
->jump_charge
)
152 phys
->jump_dir
= phys
->reverse
> 0.0f
? 1: 0;
154 phys
->jump_charge
= 1;
157 static int push_button_last
= 0;
158 int push_button
= vg_get_button( "push" );
159 push_button_last
= push_button
;
161 if( push_button
&& !push_button_last
)
162 player
.phys
.start_push
= vg
.time
;
164 if( !vg_get_button("break") && vg_get_button( "push" ) )
166 player
.phys
.pushing
= 1.0f
;
167 player
.phys
.push_time
= vg
.time
- player
.phys
.start_push
;
169 float cycle_time
= player
.phys
.push_time
*k_push_cycle_rate
,
170 amt
= k_push_accel
* (sinf(cycle_time
)*0.5f
+0.5f
)*VG_TIMESTEP_FIXED
,
171 current
= v3_length( vel
),
172 new_vel
= vg_minf( current
+ amt
, k_max_push_speed
);
174 new_vel
-= vg_minf(current
, k_max_push_speed
);
175 vel
[2] -= new_vel
* phys
->reverse
;
179 static float previous
= 0.0f
;
180 float delta
= previous
- phys
->grab
,
181 pump
= delta
* k_pump_force
* VG_TIMESTEP_FIXED
;
182 previous
= phys
->grab
;
185 v3_muladds( phys
->rb
.co
, phys
->rb
.up
, pump
, p1
);
186 vg_line( phys
->rb
.co
, p1
, 0xff0000ff );
191 m3x3_mulv( phys
->rb
.to_world
, vel
, phys
->rb
.v
);
193 float steer
= vg_get_axis( "horizontal" ),
194 steer_scaled
= vg_signf(steer
) * powf(steer
,2.0f
) * k_steer_ground
;
196 phys
->iY
-= steer_scaled
* VG_TIMESTEP_FIXED
;
200 * Air control, no real physics
202 static void player_physics_control_air(void)
204 struct player_phys
*phys
= &player
.phys
;
206 m3x3_mulv( phys
->vr
, phys
->rb
.v
, phys
->rb
.v
);
207 vg_line_cross( player
.land_target
, 0xff0000ff, 0.25f
);
214 float pstep
= VG_TIMESTEP_FIXED
* 10.0f
;
217 v3_copy( phys
->rb
.co
, pco
);
218 v3_copy( phys
->rb
.v
, pv
);
220 float time_to_impact
= 0.0f
;
221 float limiter
= 1.0f
;
223 for( int i
=0; i
<50; i
++ )
225 v3_copy( pco
, pco1
);
226 m3x3_mulv( phys
->vr_pstep
, pv
, pv
);
227 apply_gravity( pv
, pstep
);
228 v3_muladds( pco
, pv
, pstep
, pco
);
233 v3_sub( pco
, pco1
, vdir
);
234 contact
.dist
= v3_length( vdir
);
235 v3_divs( vdir
, contact
.dist
, vdir
);
237 float orig_dist
= contact
.dist
;
238 if( ray_world( pco1
, vdir
, &contact
))
240 float angle
= v3_dot( phys
->rb
.up
, contact
.normal
);
242 v3_cross( phys
->rb
.up
, contact
.normal
, axis
);
244 time_to_impact
+= (contact
.dist
/orig_dist
)*pstep
;
245 limiter
= vg_minf( 5.0f
, time_to_impact
)/5.0f
;
246 limiter
= 1.0f
-limiter
;
248 limiter
= 1.0f
-limiter
;
253 q_axis_angle( correction
, axis
, acosf(angle
)*0.05f
*(1.0f
-limiter
) );
254 q_mul( correction
, phys
->rb
.q
, phys
->rb
.q
);
257 vg_line_cross( contact
.pos
, 0xffff0000, 0.25f
);
260 time_to_impact
+= pstep
;
263 phys
->iY
-= vg_get_axis( "horizontal" ) * k_steer_air
* VG_TIMESTEP_FIXED
;
265 float iX
= vg_get_axis( "vertical" ) *
266 phys
->reverse
* k_steer_air
* limiter
* VG_TIMESTEP_FIXED
;
268 static float siX
= 0.0f
;
269 siX
= vg_lerpf( siX
, iX
, k_steer_air_lerp
);
272 q_axis_angle( rotate
, phys
->rb
.right
, siX
);
273 q_mul( rotate
, phys
->rb
.q
, phys
->rb
.q
);
276 v2f target
= {0.0f
,0.0f
};
277 v2_muladds( target
, (v2f
){ vg_get_axis("h1"), vg_get_axis("v1") },
278 phys
->grab
, target
);
282 * Entire Walking physics model
283 * TODO: sleep when under certain velotiy
285 static void player_walk_physics(void)
287 struct player_phys
*phys
= &player
.phys
;
288 rigidbody
*rbf
= &player
.collide_front
,
289 *rbb
= &player
.collide_back
;
291 m3x3_copy( phys
->rb
.to_world
, player
.collide_front
.to_world
);
292 m3x3_copy( phys
->rb
.to_world
, player
.collide_back
.to_world
);
297 m4x3_mulv( phys
->rb
.to_world
, (v3f
){0.0f
,h0
,0.0f
}, rbf
->co
);
298 v3_copy( rbf
->co
, rbf
->to_world
[3] );
299 m4x3_mulv( phys
->rb
.to_world
, (v3f
){0.0f
,h1
,0.0f
}, rbb
->co
);
300 v3_copy( rbb
->co
, rbb
->to_world
[3] );
302 m4x3_invert_affine( rbf
->to_world
, rbf
->to_local
);
303 m4x3_invert_affine( rbb
->to_world
, rbb
->to_local
);
305 rb_update_bounds( rbf
);
306 rb_update_bounds( rbb
);
308 rb_debug( rbf
, 0xff0000ff );
309 rb_debug( rbb
, 0xff0000ff );
314 len
+= rb_sphere_scene( rbf
, &world
.rb_geo
, manifold
+len
);
315 len
+= rb_sphere_scene( rbb
, &world
.rb_geo
, manifold
+len
);
317 rb_presolve_contacts( manifold
, len
);
319 for( int j
=0; j
<5; j
++ )
321 for( int i
=0; i
<len
; i
++ )
323 struct contact
*ct
= &manifold
[i
];
326 float vn
= -v3_dot( phys
->rb
.v
, ct
->n
);
329 float temp
= ct
->norm_impulse
;
330 ct
->norm_impulse
= vg_maxf( temp
+ vn
, 0.0f
);
331 vn
= ct
->norm_impulse
- temp
;
334 v3_muls( ct
->n
, vn
, impulse
);
336 v3_add( impulse
, phys
->rb
.v
, phys
->rb
.v
);
339 for( int j
=0; j
<2; j
++ )
341 float f
= k_friction
* ct
->norm_impulse
,
342 vt
= v3_dot( phys
->rb
.v
, ct
->t
[j
] ),
345 float temp
= ct
->tangent_impulse
[j
];
346 ct
->tangent_impulse
[j
] = vg_clampf( temp
+ lambda
, -f
, f
);
347 lambda
= ct
->tangent_impulse
[j
] - temp
;
349 v3_muladds( phys
->rb
.v
, ct
->t
[j
], lambda
, phys
->rb
.v
);
354 phys
->in_air
= len
==0?1:0;
358 float const DOWNFORCE
= -k_walk_downforce
*VG_TIMESTEP_FIXED
;
359 v3_muladds( phys
->rb
.v
, (v3f
){0.0f
,-1.0f
,0.0f
}, DOWNFORCE
, phys
->rb
.v
);
361 if( vg_get_button("jump") )
363 phys
->rb
.v
[1] = 5.0f
;
367 v3_zero( phys
->rb
.w
);
368 q_axis_angle( phys
->rb
.q
, (v3f
){0.0f
,1.0f
,0.0f
}, -player
.angles
[0] );
370 v3f forward_dir
= { sinf(player
.angles
[0]),0.0f
,-cosf(player
.angles
[0]) };
373 v3_muladds( phys
->rb
.co
, forward_dir
, 2.0f
, p1
);
374 vg_line( phys
->rb
.co
, p1
, 0xff0000ff );
376 float move_dead
= 0.1f
,
377 move
= vg_get_axis("grabl")*0.5f
+ 0.5f
- move_dead
;
381 float move_norm
= move
* (1.0f
/(1.0f
-move_dead
)),
382 speed
= vg_lerpf( 0.1f
*k_runspeed
, k_runspeed
, move_norm
),
383 amt
= k_walk_accel
* VG_TIMESTEP_FIXED
,
384 zvel
= v3_dot( phys
->rb
.v
, forward_dir
),
385 new_vel
= vg_minf( zvel
+ amt
, speed
),
386 diff
= new_vel
- vg_minf( zvel
, speed
);
390 v3_muladds( phys
->rb
.v
, forward_dir
, diff
, phys
->rb
.v
);
394 float walk_norm
= 30.0f
/(float)player
.mdl
.anim_walk
->length
,
395 run_norm
= 30.0f
/(float)player
.mdl
.anim_run
->length
,
396 walk_adv
= vg_lerpf( walk_norm
,run_norm
,move_norm
);
398 player
.walk_timer
+= walk_adv
* VG_TIMESTEP_FIXED
;
402 player
.walk_timer
= 0.0f
;
407 phys
->rb
.v
[0] *= 1.0f
- (VG_TIMESTEP_FIXED
* k_walk_friction
);
408 phys
->rb
.v
[2] *= 1.0f
- (VG_TIMESTEP_FIXED
* k_walk_friction
);
413 * Physics collision detection, and control
415 static void player_physics(void)
417 struct player_phys
*phys
= &player
.phys
;
419 * Update collision fronts
422 rigidbody
*rbf
= &player
.collide_front
,
423 *rbb
= &player
.collide_back
;
425 m3x3_copy( phys
->rb
.to_world
, player
.collide_front
.to_world
);
426 m3x3_copy( phys
->rb
.to_world
, player
.collide_back
.to_world
);
428 player
.air_blend
= vg_lerpf( player
.air_blend
, phys
->in_air
, 0.1f
);
429 float h
= player
.air_blend
*0.2f
;
431 m4x3_mulv( phys
->rb
.to_world
, (v3f
){0.0f
,h
,-k_board_length
}, rbf
->co
);
432 v3_copy( rbf
->co
, rbf
->to_world
[3] );
433 m4x3_mulv( phys
->rb
.to_world
, (v3f
){0.0f
,h
, k_board_length
}, rbb
->co
);
434 v3_copy( rbb
->co
, rbb
->to_world
[3] );
436 m4x3_invert_affine( rbf
->to_world
, rbf
->to_local
);
437 m4x3_invert_affine( rbb
->to_world
, rbb
->to_local
);
439 rb_update_bounds( rbf
);
440 rb_update_bounds( rbb
);
442 rb_debug( rbf
, 0xff00ffff );
443 rb_debug( rbb
, 0xffffff00 );
448 len
+= rb_sphere_scene( rbf
, &world
.rb_geo
, manifold
+len
);
449 len
+= rb_sphere_scene( rbb
, &world
.rb_geo
, manifold
+len
);
451 rb_presolve_contacts( manifold
, len
);
452 v3f surface_avg
= {0.0f
, 0.0f
, 0.0f
};
460 for( int i
=0; i
<len
; i
++ )
462 v3_add( manifold
[i
].n
, surface_avg
, surface_avg
);
465 v3_normalize( surface_avg
);
467 if( v3_dot( phys
->rb
.v
, surface_avg
) > 0.5f
)
475 for( int j
=0; j
<5; j
++ )
477 for( int i
=0; i
<len
; i
++ )
479 struct contact
*ct
= &manifold
[i
];
482 v3_sub( ct
->co
, phys
->rb
.co
, delta
);
483 v3_cross( phys
->rb
.w
, delta
, dv
);
484 v3_add( phys
->rb
.v
, dv
, dv
);
486 float vn
= -v3_dot( dv
, ct
->n
);
489 float temp
= ct
->norm_impulse
;
490 ct
->norm_impulse
= vg_maxf( temp
+ vn
, 0.0f
);
491 vn
= ct
->norm_impulse
- temp
;
494 v3_muls( ct
->n
, vn
, impulse
);
496 if( fabsf(v3_dot( impulse
, phys
->rb
.forward
)) > 10.0f
||
497 fabsf(v3_dot( impulse
, phys
->rb
.up
)) > 50.0f
)
503 v3_add( impulse
, phys
->rb
.v
, phys
->rb
.v
);
504 v3_cross( delta
, impulse
, impulse
);
507 * W Impulses are limited to the Y and X axises, we don't really want
508 * roll angular velocities being included.
510 * Can also tweak the resistance of each axis here by scaling the wx,wy
514 float wy
= v3_dot( phys
->rb
.up
, impulse
),
515 wx
= v3_dot( phys
->rb
.right
, impulse
)*1.5f
;
517 v3_muladds( phys
->rb
.w
, phys
->rb
.up
, wy
, phys
->rb
.w
);
518 v3_muladds( phys
->rb
.w
, phys
->rb
.right
, wx
, phys
->rb
.w
);
522 float grabt
= vg_get_axis( "grabr" )*0.5f
+0.5f
;
523 phys
->grab
= vg_lerpf( phys
->grab
, grabt
, 0.14f
);
524 player
.phys
.pushing
= 0.0f
;
529 float angle
= v3_dot( phys
->rb
.up
, surface_avg
);
530 v3_cross( phys
->rb
.up
, surface_avg
, axis
);
532 //float cz = v3_dot( player.rb.forward, axis );
533 //v3_muls( player.rb.forward, cz, axis );
538 q_axis_angle( correction
, axis
, acosf(angle
)*0.3f
);
539 q_mul( correction
, phys
->rb
.q
, phys
->rb
.q
);
542 float const DOWNFORCE
= -k_downforce
*VG_TIMESTEP_FIXED
;
543 v3_muladds( phys
->rb
.v
, phys
->rb
.up
, DOWNFORCE
, phys
->rb
.v
);
545 player_physics_control();
547 if( !phys
->jump_charge
&& phys
->jump
> 0.2f
)
551 /* Launch more up if alignment is up else improve velocity */
552 float aup
= fabsf(v3_dot( (v3f
){0.0f
,1.0f
,0.0f
}, phys
->rb
.up
)),
554 dir
= mod
+ aup
*(1.0f
-mod
);
556 v3_copy( phys
->rb
.v
, jumpdir
);
557 v3_normalize( jumpdir
);
558 v3_muls( jumpdir
, 1.0f
-dir
, jumpdir
);
559 v3_muladds( jumpdir
, phys
->rb
.up
, dir
, jumpdir
);
560 v3_normalize( jumpdir
);
562 float force
= k_jump_force
*phys
->jump
;
563 v3_muladds( phys
->rb
.v
, jumpdir
, force
, phys
->rb
.v
);
566 player
.jump_time
= vg
.time
;
568 /* TODO: Move to audio file */
570 audio_player_set_flags( &audio_player_extra
, AUDIO_FLAG_SPACIAL_3D
);
571 audio_player_set_position( &audio_player_extra
, phys
->rb
.co
);
572 audio_player_set_vol( &audio_player_extra
, 20.0f
);
573 audio_player_playclip( &audio_player_extra
, &audio_jumps
[rand()%4] );
579 player_physics_control_air();
582 if( !phys
->jump_charge
)
584 phys
->jump
-= k_jump_charge_speed
* VG_TIMESTEP_FIXED
;
587 phys
->jump_charge
= 0;
588 phys
->jump
= vg_clampf( phys
->jump
, 0.0f
, 1.0f
);
591 static void player_save_frame(void)
593 player
.phys_gate_frame
= player
.phys
;
596 static void player_restore_frame(void)
598 player
.phys
= player
.phys_gate_frame
;
599 rb_update_transform( &player
.phys
.rb
);
602 static void player_do_motion(void)
604 struct player_phys
*phys
= &player
.phys
;
606 float horizontal
= vg_get_axis("horizontal"),
607 vertical
= vg_get_axis("vertical");
609 if( (phys
->rb
.co
[1] < 0.0f
) && !player
.is_dead
)
612 audio_player_set_flags( &audio_player_extra
, AUDIO_FLAG_SPACIAL_3D
);
613 audio_player_set_position( &audio_player_extra
, phys
->rb
.co
);
614 audio_player_set_vol( &audio_player_extra
, 20.0f
);
615 audio_player_playclip( &audio_player_extra
, &audio_splash
);
624 player_walk_physics();
626 /* Integrate velocity */
628 v3_copy( phys
->rb
.co
, prevco
);
630 apply_gravity( phys
->rb
.v
, VG_TIMESTEP_FIXED
);
631 v3_muladds( phys
->rb
.co
, phys
->rb
.v
, VG_TIMESTEP_FIXED
, phys
->rb
.co
);
633 /* Real angular velocity integration */
634 v3_lerp( phys
->rb
.w
, (v3f
){0.0f
,0.0f
,0.0f
}, 0.125f
, phys
->rb
.w
);
635 if( v3_length2( phys
->rb
.w
) > 0.0f
)
639 v3_copy( phys
->rb
.w
, axis
);
641 float mag
= v3_length( axis
);
642 v3_divs( axis
, mag
, axis
);
643 q_axis_angle( rotation
, axis
, mag
*k_rb_delta
);
644 q_mul( rotation
, phys
->rb
.q
, phys
->rb
.q
);
647 /* Faux angular velocity */
650 float lerpq
= phys
->in_air
? 0.04f
: 0.3f
;
651 phys
->siY
= vg_lerpf( phys
->siY
, phys
->iY
, lerpq
);
653 q_axis_angle( rotate
, phys
->rb
.up
, phys
->siY
);
654 q_mul( rotate
, phys
->rb
.q
, phys
->rb
.q
);
658 * Gate intersection, by tracing a line over the gate planes
660 for( int i
=0; i
<world
.routes
.gate_count
; i
++ )
662 struct route_gate
*rg
= &world
.routes
.gates
[i
];
663 teleport_gate
*gate
= &rg
->gate
;
665 if( gate_intersect( gate
, phys
->rb
.co
, prevco
) )
667 m4x3_mulv( gate
->transport
, phys
->rb
.co
, phys
->rb
.co
);
668 m3x3_mulv( gate
->transport
, phys
->rb
.v
, phys
->rb
.v
);
669 m3x3_mulv( gate
->transport
, phys
->vl
, phys
->vl
);
670 m3x3_mulv( gate
->transport
, phys
->v_last
, phys
->v_last
);
671 m3x3_mulv( gate
->transport
, phys
->m
, phys
->m
);
672 m3x3_mulv( gate
->transport
, phys
->bob
, phys
->bob
);
674 v4f transport_rotation
;
675 m3x3_q( gate
->transport
, transport_rotation
);
676 q_mul( transport_rotation
, phys
->rb
.q
, phys
->rb
.q
);
678 world_routes_activate_gate( i
);
680 if( !phys
->on_board
)
682 v3f fwd_dir
= {cosf(player
.angles
[0]),
684 sinf(player
.angles
[0])};
685 m3x3_mulv( gate
->transport
, fwd_dir
, fwd_dir
);
687 player
.angles
[0] = atan2f( fwd_dir
[2], fwd_dir
[0] );
693 audio_play_oneshot( &audio_gate_pass
, 1.0f
);
699 rb_update_transform( &phys
->rb
);
703 * Free camera movement
705 static void player_mouseview(void)
707 if( gui_want_mouse() )
710 static v2f mouse_last
,
711 view_vel
= { 0.0f
, 0.0f
};
713 if( vg_get_button_down( "primary" ) )
714 v2_copy( vg
.mouse
, mouse_last
);
716 else if( vg_get_button( "primary" ) )
719 v2_sub( vg
.mouse
, mouse_last
, delta
);
720 v2_copy( vg
.mouse
, mouse_last
);
722 v2_muladds( view_vel
, delta
, 0.06f
*vg
.time_delta
, view_vel
);
725 v2_muladds( view_vel
, (v2f
){ vg_get_axis("h1"), vg_get_axis("v1") },
726 3.0f
* vg
.time_delta
, view_vel
);
728 v2_muls( view_vel
, 1.0f
-4.2f
*vg
.time_delta
, view_vel
);
729 v2_add( view_vel
, player
.angles
, player
.angles
);
730 player
.angles
[1] = vg_clampf( player
.angles
[1], -VG_PIf
*0.5f
, VG_PIf
*0.5f
);
733 static void player_freecam(void)
737 float movespeed
= fc_speed
;
738 v3f lookdir
= { 0.0f
, 0.0f
, -1.0f
},
739 sidedir
= { 1.0f
, 0.0f
, 0.0f
};
741 m3x3_mulv( player
.camera
, lookdir
, lookdir
);
742 m3x3_mulv( player
.camera
, sidedir
, sidedir
);
744 static v3f move_vel
= { 0.0f
, 0.0f
, 0.0f
};
745 if( vg_get_button( "forward" ) )
746 v3_muladds( move_vel
, lookdir
, VG_TIMESTEP_FIXED
* movespeed
, move_vel
);
747 if( vg_get_button( "back" ) )
748 v3_muladds( move_vel
, lookdir
, VG_TIMESTEP_FIXED
*-movespeed
, move_vel
);
749 if( vg_get_button( "left" ) )
750 v3_muladds( move_vel
, sidedir
, VG_TIMESTEP_FIXED
*-movespeed
, move_vel
);
751 if( vg_get_button( "right" ) )
752 v3_muladds( move_vel
, sidedir
, VG_TIMESTEP_FIXED
* movespeed
, move_vel
);
754 v3_muls( move_vel
, 0.7f
, move_vel
);
755 v3_add( move_vel
, player
.camera_pos
, player
.camera_pos
);
758 static void player_camera_update(void)
760 /* Update camera matrices */
761 v4f qyaw
, qpitch
, qcam
;
762 q_axis_angle( qyaw
, (v3f
){ 0.0f
, 1.0f
, 0.0f
}, -player
.angles
[0] );
763 q_axis_angle( qpitch
, (v3f
){ 1.0f
, 0.0f
, 0.0f
}, -player
.angles
[1] );
765 q_mul( qyaw
, qpitch
, qcam
);
766 q_m3x3( qcam
, player
.camera
);
768 v3_copy( player
.camera_pos
, player
.camera
[3] );
769 m4x3_invert_affine( player
.camera
, player
.camera_inverse
);
772 static int reset_player( int argc
, char const *argv
[] )
774 struct player_phys
*phys
= &player
.phys
;
775 struct respawn_point
*rp
= NULL
, *r
;
779 for( int i
=0; i
<world
.spawn_count
; i
++ )
781 r
= &world
.spawns
[i
];
782 if( !strcmp( r
->name
, argv
[0] ) )
790 vg_warn( "No spawn named '%s'\n", argv
[0] );
795 float min_dist
= INFINITY
;
797 for( int i
=0; i
<world
.spawn_count
; i
++ )
799 r
= &world
.spawns
[i
];
800 float d
= v3_dist2( r
->co
, phys
->rb
.co
);
802 vg_info( "Dist %s : %f\n", r
->name
, d
);
813 vg_error( "No spawn found\n" );
814 if( !world
.spawn_count
)
817 rp
= &world
.spawns
[0];
822 v4_copy( rp
->q
, phys
->rb
.q
);
823 v3_copy( rp
->co
, phys
->rb
.co
);
824 v3_zero( phys
->rb
.v
);
826 phys
->vswitch
= 1.0f
;
827 phys
->slip_last
= 0.0f
;
830 m3x3_identity( phys
->vr
);
832 player
.mdl
.shoes
[0] = 1;
833 player
.mdl
.shoes
[1] = 1;
835 rb_update_transform( &phys
->rb
);
840 #endif /* PLAYER_PHYSICS_H */