baaa70e919a0328ec6760fc077101c33943c9787
2 * Copyright 2021-2022 (C) Mount0 Software, Harry Godden - All Rights Reserved
3 * -----------------------------------------------------------------------------
5 * Player physics and control submodule
6 * contains main physics models, input, and player control.
8 * -----------------------------------------------------------------------------
11 #ifndef PLAYER_PHYSICS_H
12 #define PLAYER_PHYSICS_H
16 static void apply_gravity( v3f vel
, float const timestep
)
18 v3f gravity
= { 0.0f
, -9.6f
, 0.0f
};
19 v3_muladds( vel
, gravity
, timestep
, vel
);
23 * Called when launching into the air to predict and adjust trajectories
25 static void player_start_air(void)
32 float pstep
= ktimestep
*10.0f
;
33 float best_velocity_delta
= -9999.9f
;
37 v3_cross( player
.rb
.up
, player
.rb
.v
, axis
);
39 player
.land_log_count
= 0;
41 m3x3_identity( player
.vr
);
43 for( int m
=-3;m
<=12; m
++ )
45 float vmod
= ((float)m
/ 15.0f
)*0.09f
;
48 v3_copy( player
.rb
.co
, pco
);
49 v3_muls( player
.rb
.v
, k_bias
, pv
);
52 * Try different 'rotations' of the velocity to find the best possible
53 * landing normal. This conserves magnitude at the expense of slightly
60 q_axis_angle( vr_q
, axis
, vmod
);
63 m3x3_mulv( vr
, pv
, pv
);
64 v3_muladds( pco
, pv
, pstep
, pco
);
66 for( int i
=0; i
<50; i
++ )
69 apply_gravity( pv
, pstep
);
71 m3x3_mulv( vr
, pv
, pv
);
72 v3_muladds( pco
, pv
, pstep
, pco
);
77 v3_sub( pco
, pco1
, vdir
);
78 contact
.dist
= v3_length( vdir
);
79 v3_divs( vdir
, contact
.dist
, vdir
);
81 if( ray_world( pco1
, vdir
, &contact
))
83 float land_delta
= v3_dot( pv
, contact
.normal
);
84 u32 scolour
= (u8
)(vg_minf(-land_delta
* 2.0f
, 255.0f
));
86 /* Bias prediction towords ramps */
87 if( ray_hit_is_ramp( &contact
) )
90 scolour
|= 0x0000a000;
93 if( (land_delta
< 0.0f
) && (land_delta
> best_velocity_delta
) )
95 best_velocity_delta
= land_delta
;
97 v3_copy( contact
.pos
, player
.land_target
);
99 m3x3_copy( vr
, player
.vr_pstep
);
100 q_axis_angle( vr_q
, axis
, vmod
*0.1f
);
101 q_m3x3( vr_q
, player
.vr
);
104 v3_copy( contact
.pos
,
105 player
.land_target_log
[player
.land_log_count
] );
106 player
.land_target_colours
[player
.land_log_count
] =
107 0xff000000 | scolour
;
109 player
.land_log_count
++;
118 * Main friction interface model
120 static void player_physics_control(void)
123 * Computing localized friction forces for controlling the character
124 * Friction across X is significantly more than Z
128 m3x3_mulv( player
.rb
.to_local
, player
.rb
.v
, vel
);
131 if( fabsf(vel
[2]) > 0.01f
)
132 slip
= fabsf(-vel
[0] / vel
[2]) * vg_signf(vel
[0]);
134 if( fabsf( slip
) > 1.2f
)
135 slip
= vg_signf( slip
) * 1.2f
;
137 player
.reverse
= -vg_signf(vel
[2]);
139 float substep
= ktimestep
* 0.2f
;
140 float fwd_resistance
= (vg_get_button( "break" )? 5.0f
: 0.02f
) * -substep
;
142 for( int i
=0; i
<5; i
++ )
144 vel
[2] = stable_force( vel
[2], vg_signf( vel
[2] ) * fwd_resistance
);
145 vel
[0] = stable_force( vel
[0],
146 vg_signf( vel
[0] ) * -k_friction_lat
*substep
);
149 static double start_push
= 0.0;
150 if( vg_get_button_down( "push" ) )
151 start_push
= vg_time
;
153 if( vg_get_button( "jump" ) )
155 player
.jump
+= ktimestep
* k_jump_charge_speed
;
157 if( !player
.jump_charge
)
158 player
.jump_dir
= player
.reverse
> 0.0f
? 1: 0;
160 player
.jump_charge
= 1;
163 if( !vg_get_button("break") && vg_get_button( "push" ) )
165 player
.pushing
= 1.0f
;
166 player
.push_time
= vg_time
-start_push
;
168 float cycle_time
= player
.push_time
*k_push_cycle_rate
,
169 amt
= k_push_accel
* (sinf(cycle_time
)*0.5f
+0.5f
)*ktimestep
,
170 current
= v3_length( vel
),
171 new_vel
= vg_minf( current
+ amt
, k_max_push_speed
);
173 new_vel
-= vg_minf(current
, k_max_push_speed
);
174 vel
[2] -= new_vel
* player
.reverse
;
178 static float previous
= 0.0f
;
179 float delta
= previous
- player
.grab
,
180 pump
= delta
* k_pump_force
*ktimestep
;
181 previous
= player
.grab
;
184 v3_muladds( player
.rb
.co
, player
.rb
.up
, pump
, p1
);
185 vg_line( player
.rb
.co
, p1
, 0xff0000ff );
190 m3x3_mulv( player
.rb
.to_world
, vel
, player
.rb
.v
);
192 float steer
= vg_get_axis( "horizontal" );
193 player
.iY
-= vg_signf(steer
)*powf(steer
,2.0f
) * k_steer_ground
* ktimestep
;
195 v2_lerp( player
.board_xy
, (v2f
){ slip
*0.25f
, 0.0f
},
196 ktimestep
*5.0f
, player
.board_xy
);
200 * Air control, no real physics
202 static void player_physics_control_air(void)
204 m3x3_mulv( player
.vr
, player
.rb
.v
, player
.rb
.v
);
205 vg_line_cross( player
.land_target
, 0xff0000ff, 0.25f
);
212 float pstep
= ktimestep
*10.0f
;
215 v3_copy( player
.rb
.co
, pco
);
216 v3_copy( player
.rb
.v
, pv
);
218 float time_to_impact
= 0.0f
;
219 float limiter
= 1.0f
;
221 for( int i
=0; i
<50; i
++ )
223 v3_copy( pco
, pco1
);
224 m3x3_mulv( player
.vr_pstep
, pv
, pv
);
225 apply_gravity( pv
, pstep
);
226 v3_muladds( pco
, pv
, pstep
, pco
);
231 v3_sub( pco
, pco1
, vdir
);
232 contact
.dist
= v3_length( vdir
);
233 v3_divs( vdir
, contact
.dist
, vdir
);
235 float orig_dist
= contact
.dist
;
236 if( ray_world( pco1
, vdir
, &contact
))
238 float angle
= v3_dot( player
.rb
.up
, contact
.normal
);
240 v3_cross( player
.rb
.up
, contact
.normal
, axis
);
242 time_to_impact
+= (contact
.dist
/orig_dist
)*pstep
;
243 limiter
= vg_minf( 5.0f
, time_to_impact
)/5.0f
;
244 limiter
= 1.0f
-limiter
;
246 limiter
= 1.0f
-limiter
;
251 q_axis_angle( correction
, axis
, acosf(angle
)*0.05f
*(1.0f
-limiter
) );
252 q_mul( correction
, player
.rb
.q
, player
.rb
.q
);
255 vg_line_cross( contact
.pos
, 0xffff0000, 0.25f
);
258 time_to_impact
+= pstep
;
261 player
.iY
-= vg_get_axis( "horizontal" ) * k_steer_air
* ktimestep
;
263 float iX
= vg_get_axis( "vertical" ) *
264 player
.reverse
* k_steer_air
* limiter
* ktimestep
;
266 static float siX
= 0.0f
;
267 siX
= vg_lerpf( siX
, iX
, k_steer_air_lerp
);
270 q_axis_angle( rotate
, player
.rb
.right
, siX
);
271 q_mul( rotate
, player
.rb
.q
, player
.rb
.q
);
274 v2f target
= {0.0f
,0.0f
};
275 v2_muladds( target
, (v2f
){ vg_get_axis("h1"), vg_get_axis("v1") },
276 player
.grab
, target
);
277 v2_lerp( player
.board_xy
, target
, ktimestep
*3.0f
, player
.board_xy
);
281 * Entire Walking physics model
282 * TODO: sleep when under certain velotiy
284 static void player_walk_physics(void)
286 rigidbody
*rbf
= &player
.collide_front
,
287 *rbb
= &player
.collide_back
;
289 m3x3_copy( player
.rb
.to_world
, player
.collide_front
.to_world
);
290 m3x3_copy( player
.rb
.to_world
, player
.collide_back
.to_world
);
295 m4x3_mulv( player
.rb
.to_world
, (v3f
){0.0f
,h0
,0.0f
}, rbf
->co
);
296 v3_copy( rbf
->co
, rbf
->to_world
[3] );
297 m4x3_mulv( player
.rb
.to_world
, (v3f
){0.0f
,h1
,0.0f
}, rbb
->co
);
298 v3_copy( rbb
->co
, rbb
->to_world
[3] );
300 m4x3_invert_affine( rbf
->to_world
, rbf
->to_local
);
301 m4x3_invert_affine( rbb
->to_world
, rbb
->to_local
);
303 rb_update_bounds( rbf
);
304 rb_update_bounds( rbb
);
306 rb_debug( rbf
, 0xff0000ff );
307 rb_debug( rbb
, 0xff0000ff );
312 len
+= rb_sphere_scene( rbf
, &world
.rb_geo
, manifold
+len
);
313 len
+= rb_sphere_scene( rbb
, &world
.rb_geo
, manifold
+len
);
315 rb_presolve_contacts( manifold
, len
);
317 for( int j
=0; j
<5; j
++ )
319 for( int i
=0; i
<len
; i
++ )
321 struct contact
*ct
= &manifold
[i
];
324 float vn
= -v3_dot( player
.rb
.v
, ct
->n
);
327 float temp
= ct
->norm_impulse
;
328 ct
->norm_impulse
= vg_maxf( temp
+ vn
, 0.0f
);
329 vn
= ct
->norm_impulse
- temp
;
332 v3_muls( ct
->n
, vn
, impulse
);
334 v3_add( impulse
, player
.rb
.v
, player
.rb
.v
);
337 for( int j
=0; j
<2; j
++ )
339 float f
= k_friction
* ct
->norm_impulse
,
340 vt
= v3_dot( player
.rb
.v
, ct
->t
[j
] ),
343 float temp
= ct
->tangent_impulse
[j
];
344 ct
->tangent_impulse
[j
] = vg_clampf( temp
+ lambda
, -f
, f
);
345 lambda
= ct
->tangent_impulse
[j
] - temp
;
347 v3_muladds( player
.rb
.v
, ct
->t
[j
], lambda
, player
.rb
.v
);
352 player
.in_air
= len
==0?1:0;
354 v3_zero( player
.rb
.w
);
355 q_axis_angle( player
.rb
.q
, (v3f
){0.0f
,1.0f
,0.0f
}, -player
.angles
[0] );
357 v3f forward_dir
= { sinf(player
.angles
[0]),0.0f
,-cosf(player
.angles
[0]) };
360 v3_muladds( player
.rb
.co
, forward_dir
, 2.0f
, p1
);
361 vg_line( player
.rb
.co
, p1
, 0xff0000ff );
363 float move_dead
= 0.1f
,
364 move
= vg_get_axis("grabr")*0.5f
+ 0.5f
- move_dead
;
368 float move_norm
= move
* (1.0f
/(1.0f
-move_dead
)),
369 speed
= vg_lerpf( 0.1f
*k_runspeed
, k_runspeed
, move_norm
),
370 amt
= k_walk_accel
* ktimestep
,
371 zvel
= v3_dot( player
.rb
.v
, forward_dir
),
372 new_vel
= vg_minf( zvel
+ amt
, speed
),
373 diff
= new_vel
- vg_minf( zvel
, speed
);
375 v3_muladds( player
.rb
.v
, forward_dir
, diff
, player
.rb
.v
);
378 float walk_norm
= 30.0f
/(float)player
.mdl
.anim_walk
->length
,
379 run_norm
= 30.0f
/(float)player
.mdl
.anim_run
->length
;
381 player
.walk_timer
+= ktimestep
* vg_lerpf( walk_norm
,run_norm
,move_norm
);
385 player
.walk_timer
= 0.0f
;
388 player
.rb
.v
[0] *= 1.0f
- (ktimestep
*k_walk_friction
);
389 player
.rb
.v
[2] *= 1.0f
- (ktimestep
*k_walk_friction
);
393 * Physics collision detection, and control
395 static void player_physics(void)
398 * Update collision fronts
401 rigidbody
*rbf
= &player
.collide_front
,
402 *rbb
= &player
.collide_back
;
404 m3x3_copy( player
.rb
.to_world
, player
.collide_front
.to_world
);
405 m3x3_copy( player
.rb
.to_world
, player
.collide_back
.to_world
);
407 player
.air_blend
= vg_lerpf( player
.air_blend
, player
.in_air
, 0.1f
);
408 float h
= player
.air_blend
*0.2f
;
410 m4x3_mulv( player
.rb
.to_world
, (v3f
){0.0f
,h
,-k_board_length
}, rbf
->co
);
411 v3_copy( rbf
->co
, rbf
->to_world
[3] );
412 m4x3_mulv( player
.rb
.to_world
, (v3f
){0.0f
,h
, k_board_length
}, rbb
->co
);
413 v3_copy( rbb
->co
, rbb
->to_world
[3] );
415 m4x3_invert_affine( rbf
->to_world
, rbf
->to_local
);
416 m4x3_invert_affine( rbb
->to_world
, rbb
->to_local
);
418 rb_update_bounds( rbf
);
419 rb_update_bounds( rbb
);
421 rb_debug( rbf
, 0xff00ffff );
422 rb_debug( rbb
, 0xffffff00 );
427 len
+= rb_sphere_scene( rbf
, &world
.rb_geo
, manifold
+len
);
428 len
+= rb_sphere_scene( rbb
, &world
.rb_geo
, manifold
+len
);
430 rb_presolve_contacts( manifold
, len
);
431 v3f surface_avg
= {0.0f
, 0.0f
, 0.0f
};
439 for( int i
=0; i
<len
; i
++ )
441 v3_add( manifold
[i
].n
, surface_avg
, surface_avg
);
444 v3_normalize( surface_avg
);
446 if( v3_dot( player
.rb
.v
, surface_avg
) > 0.5f
)
454 for( int j
=0; j
<5; j
++ )
456 for( int i
=0; i
<len
; i
++ )
458 struct contact
*ct
= &manifold
[i
];
461 v3_sub( ct
->co
, player
.rb
.co
, delta
);
462 v3_cross( player
.rb
.w
, delta
, dv
);
463 v3_add( player
.rb
.v
, dv
, dv
);
465 float vn
= -v3_dot( dv
, ct
->n
);
468 float temp
= ct
->norm_impulse
;
469 ct
->norm_impulse
= vg_maxf( temp
+ vn
, 0.0f
);
470 vn
= ct
->norm_impulse
- temp
;
473 v3_muls( ct
->n
, vn
, impulse
);
475 if( fabsf(v3_dot( impulse
, player
.rb
.forward
)) > 10.0f
||
476 fabsf(v3_dot( impulse
, player
.rb
.up
)) > 50.0f
)
482 v3_add( impulse
, player
.rb
.v
, player
.rb
.v
);
483 v3_cross( delta
, impulse
, impulse
);
486 * W Impulses are limited to the Y and X axises, we don't really want
487 * roll angular velocities being included.
489 * Can also tweak the resistance of each axis here by scaling the wx,wy
493 float wy
= v3_dot( player
.rb
.up
, impulse
),
494 wx
= v3_dot( player
.rb
.right
, impulse
)*1.5f
;
496 v3_muladds( player
.rb
.w
, player
.rb
.up
, wy
, player
.rb
.w
);
497 v3_muladds( player
.rb
.w
, player
.rb
.right
, wx
, player
.rb
.w
);
501 float grabt
= vg_get_axis( "grabr" )*0.5f
+0.5f
;
502 player
.grab
= vg_lerpf( player
.grab
, grabt
, 0.14f
);
503 player
.pushing
= 0.0f
;
508 float angle
= v3_dot( player
.rb
.up
, surface_avg
);
509 v3_cross( player
.rb
.up
, surface_avg
, axis
);
511 //float cz = v3_dot( player.rb.forward, axis );
512 //v3_muls( player.rb.forward, cz, axis );
517 q_axis_angle( correction
, axis
, acosf(angle
)*0.3f
);
518 q_mul( correction
, player
.rb
.q
, player
.rb
.q
);
521 v3_muladds( player
.rb
.v
, player
.rb
.up
,
522 -k_downforce
*ktimestep
, player
.rb
.v
);
524 player_physics_control();
526 if( !player
.jump_charge
&& player
.jump
> 0.2f
)
530 /* Launch more up if alignment is up else improve velocity */
531 float aup
= fabsf(v3_dot( (v3f
){0.0f
,1.0f
,0.0f
}, player
.rb
.up
)),
533 dir
= mod
+ aup
*(1.0f
-mod
);
535 v3_copy( player
.rb
.v
, jumpdir
);
536 v3_normalize( jumpdir
);
537 v3_muls( jumpdir
, 1.0f
-dir
, jumpdir
);
538 v3_muladds( jumpdir
, player
.rb
.up
, dir
, jumpdir
);
539 v3_normalize( jumpdir
);
541 float force
= k_jump_force
*player
.jump
;
542 v3_muladds( player
.rb
.v
, jumpdir
, force
, player
.rb
.v
);
545 player
.jump_time
= vg_time
;
548 audio_player_set_flags( &audio_player_extra
, AUDIO_FLAG_SPACIAL_3D
);
549 audio_player_set_position( &audio_player_extra
, player
.rb
.co
);
550 audio_player_set_vol( &audio_player_extra
, 20.0f
);
551 audio_player_playclip( &audio_player_extra
, &audio_jumps
[rand()%4] );
557 player_physics_control_air();
560 if( !player
.jump_charge
)
562 player
.jump
-= k_jump_charge_speed
* ktimestep
;
564 player
.jump_charge
= 0;
565 player
.jump
= vg_clampf( player
.jump
, 0.0f
, 1.0f
);
568 static void player_do_motion(void)
570 float horizontal
= vg_get_axis("horizontal"),
571 vertical
= vg_get_axis("vertical");
573 if( player
.on_board
)
576 player_walk_physics();
578 /* Integrate velocity */
580 v3_copy( player
.rb
.co
, prevco
);
582 apply_gravity( player
.rb
.v
, ktimestep
);
583 v3_muladds( player
.rb
.co
, player
.rb
.v
, ktimestep
, player
.rb
.co
);
585 /* Real angular velocity integration */
586 v3_lerp( player
.rb
.w
, (v3f
){0.0f
,0.0f
,0.0f
}, 0.125f
, player
.rb
.w
);
587 if( v3_length2( player
.rb
.w
) > 0.0f
)
591 v3_copy( player
.rb
.w
, axis
);
593 float mag
= v3_length( axis
);
594 v3_divs( axis
, mag
, axis
);
595 q_axis_angle( rotation
, axis
, mag
*k_rb_delta
);
596 q_mul( rotation
, player
.rb
.q
, player
.rb
.q
);
599 /* Faux angular velocity */
602 static float siY
= 0.0f
;
603 float lerpq
= player
.in_air
? 0.04f
: 0.3f
;
604 siY
= vg_lerpf( siY
, player
.iY
, lerpq
);
606 q_axis_angle( rotate
, player
.rb
.up
, siY
);
607 q_mul( rotate
, player
.rb
.q
, player
.rb
.q
);
611 * Gate intersection, by tracing a line over the gate planes
613 for( int i
=0; i
<world
.routes
.gate_count
; i
++ )
615 struct route_gate
*rg
= &world
.routes
.gates
[i
];
616 teleport_gate
*gate
= &rg
->gate
;
618 if( gate_intersect( gate
, player
.rb
.co
, prevco
) )
620 m4x3_mulv( gate
->transport
, player
.rb
.co
, player
.rb
.co
);
621 m3x3_mulv( gate
->transport
, player
.rb
.v
, player
.rb
.v
);
622 m3x3_mulv( gate
->transport
, player
.vl
, player
.vl
);
623 m3x3_mulv( gate
->transport
, player
.v_last
, player
.v_last
);
624 m3x3_mulv( gate
->transport
, player
.m
, player
.m
);
625 m3x3_mulv( gate
->transport
, player
.bob
, player
.bob
);
627 v4f transport_rotation
;
628 m3x3_q( gate
->transport
, transport_rotation
);
629 q_mul( transport_rotation
, player
.rb
.q
, player
.rb
.q
);
631 world_routes_activate_gate( i
);
632 player
.rb_gate_frame
= player
.rb
;
633 player
.in_air_frame
= player
.in_air
;
634 player
.on_board_frame
= player
.on_board
;
636 if( !player
.on_board
)
638 v3f fwd_dir
= {cosf(player
.angles
[0]),
640 sinf(player
.angles
[0])};
641 m3x3_mulv( gate
->transport
, fwd_dir
, fwd_dir
);
643 player
.angles
[0] = atan2f( fwd_dir
[2], fwd_dir
[0] );
646 m3x3_copy( player
.vr
, player
.gate_vr_frame
);
647 m3x3_copy( player
.vr_pstep
, player
.gate_vr_pstep_frame
);
650 audio_play_oneshot( &audio_gate_lap
, 1.0f
);
656 rb_update_transform( &player
.rb
);
660 * Free camera movement
662 static void player_mouseview(void)
664 if( gui_want_mouse() )
667 static v2f mouse_last
,
668 view_vel
= { 0.0f
, 0.0f
};
670 if( vg_get_button_down( "primary" ) )
671 v2_copy( vg_mouse
, mouse_last
);
673 else if( vg_get_button( "primary" ) )
676 v2_sub( vg_mouse
, mouse_last
, delta
);
677 v2_copy( vg_mouse
, mouse_last
);
679 v2_muladds( view_vel
, delta
, 0.001f
, view_vel
);
682 v2_muladds( view_vel
,
683 (v2f
){ vg_get_axis("h1"), vg_get_axis("v1") },
685 v2_muls( view_vel
, 0.93f
, view_vel
);
686 v2_add( view_vel
, player
.angles
, player
.angles
);
687 player
.angles
[1] = vg_clampf( player
.angles
[1], -VG_PIf
*0.5f
, VG_PIf
*0.5f
);
690 static void player_freecam(void)
694 float movespeed
= fc_speed
;
695 v3f lookdir
= { 0.0f
, 0.0f
, -1.0f
},
696 sidedir
= { 1.0f
, 0.0f
, 0.0f
};
698 m3x3_mulv( player
.camera
, lookdir
, lookdir
);
699 m3x3_mulv( player
.camera
, sidedir
, sidedir
);
701 static v3f move_vel
= { 0.0f
, 0.0f
, 0.0f
};
702 if( vg_get_button( "forward" ) )
703 v3_muladds( move_vel
, lookdir
, ktimestep
* movespeed
, move_vel
);
704 if( vg_get_button( "back" ) )
705 v3_muladds( move_vel
, lookdir
, ktimestep
*-movespeed
, move_vel
);
706 if( vg_get_button( "left" ) )
707 v3_muladds( move_vel
, sidedir
, ktimestep
*-movespeed
, move_vel
);
708 if( vg_get_button( "right" ) )
709 v3_muladds( move_vel
, sidedir
, ktimestep
* movespeed
, move_vel
);
711 v3_muls( move_vel
, 0.7f
, move_vel
);
712 v3_add( move_vel
, player
.camera_pos
, player
.camera_pos
);
715 static void player_camera_update(void)
717 /* Update camera matrices */
718 v4f qyaw
, qpitch
, qcam
;
719 q_axis_angle( qyaw
, (v3f
){ 0.0f
, 1.0f
, 0.0f
}, -player
.angles
[0] );
720 q_axis_angle( qpitch
, (v3f
){ 1.0f
, 0.0f
, 0.0f
}, -player
.angles
[1] );
722 q_mul( qyaw
, qpitch
, qcam
);
723 q_m3x3( qcam
, player
.camera
);
725 v3_copy( player
.camera_pos
, player
.camera
[3] );
726 m4x3_invert_affine( player
.camera
, player
.camera_inverse
);
729 static int reset_player( int argc
, char const *argv
[] )
731 struct respawn_point
*rp
= NULL
, *r
;
735 for( int i
=0; i
<world
.spawn_count
; i
++ )
737 r
= &world
.spawns
[i
];
738 if( !strcmp( r
->name
, argv
[0] ) )
746 vg_warn( "No spawn named '%s'\n", argv
[0] );
751 float min_dist
= INFINITY
;
753 for( int i
=0; i
<world
.spawn_count
; i
++ )
755 r
= &world
.spawns
[i
];
756 float d
= v3_dist2( r
->co
, player
.rb
.co
);
758 vg_info( "Dist %s : %f\n", r
->name
, d
);
769 vg_error( "No spawn found\n" );
770 if( !world
.spawn_count
)
773 rp
= &world
.spawns
[0];
776 v4_copy( rp
->q
, player
.rb
.q
);
777 v3_copy( rp
->co
, player
.rb
.co
);
779 player
.vswitch
= 1.0f
;
780 player
.slip_last
= 0.0f
;
783 m3x3_identity( player
.vr
);
785 player
.mdl
.shoes
[0] = 1;
786 player
.mdl
.shoes
[1] = 1;
788 rb_update_transform( &player
.rb
);
789 m3x3_mulv( player
.rb
.to_world
, (v3f
){ 0.0f
, 0.0f
, -1.2f
}, player
.rb
.v
);
790 m3x3_identity( player
.gate_vr_frame
);
791 m3x3_identity( player
.gate_vr_pstep_frame
);
793 player
.rb_gate_frame
= player
.rb
;
794 player
.on_board_frame
= player
.on_board
;
795 player
.in_air_frame
= player
.in_air
;
799 #endif /* PLAYER_PHYSICS_H */