2 * Copyright (C) 2021-2022 Mt.ZERO Software, Harry Godden - All Rights Reserved
5 #ifndef PLAYER_PHYSICS_H
6 #define PLAYER_PHYSICS_H
11 VG_STATIC
void apply_gravity( v3f vel
, float const timestep
)
13 v3f gravity
= { 0.0f
, -9.6f
, 0.0f
};
14 v3_muladds( vel
, gravity
, timestep
, vel
);
18 grind_edge
*player_grind_collect_edge( v3f p0
, v3f p1
,
19 v3f c0
, v3f c1
, float max_dist
)
21 struct player_phys
*phys
= &player
.phys
;
24 bh_iter_init( 0, &it
);
28 box_init_inf( region
);
29 box_addpt( region
, p0
);
30 box_addpt( region
, p1
);
33 v3_add( (v3f
){ k_r
, k_r
, k_r
}, region
[1], region
[1] );
34 v3_add( (v3f
){-k_r
,-k_r
,-k_r
}, region
[0], region
[0] );
36 float closest
= k_r
*k_r
;
37 struct grind_edge
*closest_edge
= NULL
;
40 while( bh_next( world
.grind_bh
, &it
, region
, &idx
) )
42 struct grind_edge
*edge
= &world
.grind_edges
[ idx
];
48 closest_segment_segment( p0
, p1
, edge
->p0
, edge
->p1
, &s
,&t
, pa
, pb
);
63 * Cast a sphere from a to b and see what time it hits
65 VG_STATIC
int spherecast_world( v3f pa
, v3f pb
, float r
, float *t
, v3f n
)
67 struct player_phys
*phys
= &player
.phys
;
70 bh_iter_init( 0, &it
);
73 box_init_inf( region
);
74 box_addpt( region
, pa
);
75 box_addpt( region
, pb
);
77 v3_add( (v3f
){ r
, r
, r
}, region
[1], region
[1] );
78 v3_add( (v3f
){-r
,-r
,-r
}, region
[0], region
[0] );
81 v3_sub( pb
, pa
, dir
);
84 dir_inv
[0] = 1.0f
/dir
[0];
85 dir_inv
[1] = 1.0f
/dir
[1];
86 dir_inv
[2] = 1.0f
/dir
[2];
92 while( bh_next( world
.geo_bh
, &it
, region
, &idx
) )
94 u32
*ptri
= &world
.scene_geo
->arrindices
[ idx
*3 ];
100 for( int j
=0; j
<3; j
++ )
102 v3_copy( world
.scene_geo
->arrvertices
[ptri
[j
]].co
, tri
[j
] );
103 box_addpt( box
, tri
[j
] );
106 v3_add( (v3f
){ r
, r
, r
}, box
[1], box
[1] );
107 v3_add( (v3f
){-r
,-r
,-r
}, box
[0], box
[0] );
109 if( !ray_aabb1( box
, pa
, dir_inv
, 1.0f
) )
114 if( spherecast_triangle( tri
, pa
, dir
, r
, &t
, n1
) )
130 * Trace a path given a velocity rotation.
131 * Closest to 0 is best.
133 VG_STATIC
void player_predict_land( m3x3f vr
,
134 struct land_prediction
*prediction
)
136 struct player_phys
*phys
= &player
.phys
;
138 float pstep
= VG_TIMESTEP_FIXED
* 10.0f
;
139 float k_bias
= 0.96f
;
142 v3_copy( phys
->rb
.co
, pco
);
143 v3_muls( phys
->rb
.v
, k_bias
, pv
);
145 m3x3_mulv( vr
, pv
, pv
);
146 v3_muladds( pco
, pv
, pstep
, pco
);
148 struct grind_edge
*best_grind
= NULL
;
149 float closest_grind
= INFINITY
;
151 float grind_score
= INFINITY
,
152 air_score
= INFINITY
;
154 prediction
->log_length
= 0;
156 for( int i
=0; i
<vg_list_size(prediction
->log
); i
++ )
158 v3_copy( pco
, pco1
);
159 apply_gravity( pv
, pstep
);
161 m3x3_mulv( vr
, pv
, pv
);
162 v3_muladds( pco
, pv
, pstep
, pco
);
166 v3_sub( pco
, pco1
, vdir
);
168 float l
= v3_length( vdir
);
169 v3_muls( vdir
, 1.0f
/l
, vdir
);
172 struct grind_edge
*ge
= player_grind_collect_edge( pco
, pco1
,
175 if( ge
&& (v3_dot((v3f
){0.0f
,1.0f
,0.0f
},vdir
) < -0.2f
) )
177 float d2
= v3_dist2( c0
, c1
);
178 if( d2
< closest_grind
)
182 grind_score
= closest_grind
* 0.05f
;
189 int idx
= spherecast_world( pco1
, pco
, 0.4f
, &t1
, n1
);
192 v3_copy( n1
, prediction
->n
);
193 air_score
= -v3_dot( pv
, n1
);
195 u32 vert_index
= world
.scene_geo
->arrindices
[ idx
*3 ];
196 struct world_material
*mat
= world_tri_index_material( vert_index
);
198 /* Bias prediction towords ramps */
199 if( mat
->info
.flags
& k_material_flag_skate_surface
)
202 v3_lerp( pco1
, pco
, t1
, prediction
->log
[ prediction
->log_length
++ ] );
206 v3_copy( pco
, prediction
->log
[ prediction
->log_length
++ ] );
209 if( grind_score
< air_score
)
211 prediction
->score
= grind_score
;
212 prediction
->type
= k_prediction_grind
;
214 else if( air_score
< INFINITY
)
216 prediction
->score
= air_score
;
217 prediction
->type
= k_prediction_land
;
221 prediction
->score
= INFINITY
;
222 prediction
->type
= k_prediction_none
;
227 * Called when launching into the air to predict and adjust trajectories
229 VG_STATIC
void player_start_air(void)
231 struct player_phys
*phys
= &player
.phys
;
233 float pstep
= VG_TIMESTEP_FIXED
* 10.0f
;
234 float best_velocity_delta
= -9999.9f
;
237 v3_cross( phys
->rb
.up
, phys
->rb
.v
, axis
);
238 v3_normalize( axis
);
239 player
.prediction_count
= 0;
241 m3x3_identity( phys
->vr
);
245 min_score
= INFINITY
,
246 max_score
= -INFINITY
;
249 * Search a broad selection of futures
251 for( int m
=-3;m
<=12; m
++ )
253 struct land_prediction
*p
=
254 &player
.predictions
[ player
.prediction_count
++ ];
256 float vmod
= ((float)m
/ 15.0f
)*0.09f
;
261 q_axis_angle( vr_q
, axis
, vmod
);
264 player_predict_land( vr
, p
);
266 if( p
->type
!= k_prediction_none
)
268 if( p
->score
< min_score
)
270 min_score
= p
->score
;
274 if( p
->score
> max_score
)
275 max_score
= p
->score
;
280 q_axis_angle( vr_q
, axis
, best_vmod
*0.1f
);
281 q_m3x3( vr_q
, phys
->vr
);
283 q_axis_angle( vr_q
, axis
, best_vmod
);
284 q_m3x3( vr_q
, phys
->vr_pstep
);
289 for( int i
=0; i
<player
.prediction_count
; i
++ )
291 struct land_prediction
*p
= &player
.predictions
[i
];
297 vg_error( "negative score! (%f)\n", l
);
301 l
/= (max_score
-min_score
);
307 p
->colour
|= 0xff000000;
312 VG_STATIC
void player_physics_control_passive(void)
314 struct player_phys
*phys
= &player
.phys
;
315 float grabt
= player
.input_grab
->axis
.value
;
319 v2_muladds( phys
->grab_mouse_delta
, vg
.mouse_delta
, 0.02f
,
320 phys
->grab_mouse_delta
);
321 v2_normalize_clamp( phys
->grab_mouse_delta
);
324 v2_zero( phys
->grab_mouse_delta
);
327 v2_zero( phys
->grab_mouse_delta
);
329 phys
->grab
= vg_lerpf( phys
->grab
, grabt
, 0.14f
);
330 player
.phys
.pushing
= 0.0f
;
332 if( !phys
->jump_charge
|| phys
->in_air
)
334 phys
->jump
-= k_jump_charge_speed
* VG_TIMESTEP_FIXED
;
337 phys
->jump_charge
= 0;
338 phys
->jump
= vg_clampf( phys
->jump
, 0.0f
, 1.0f
);
342 * Main friction interface model
344 VG_STATIC
void player_physics_control(void)
346 struct player_phys
*phys
= &player
.phys
;
349 * Computing localized friction forces for controlling the character
350 * Friction across X is significantly more than Z
354 m3x3_mulv( phys
->rb
.to_local
, phys
->rb
.v
, vel
);
357 if( fabsf(vel
[2]) > 0.01f
)
358 slip
= fabsf(-vel
[0] / vel
[2]) * vg_signf(vel
[0]);
360 if( fabsf( slip
) > 1.2f
)
361 slip
= vg_signf( slip
) * 1.2f
;
363 phys
->reverse
= -vg_signf(vel
[2]);
365 float substep
= VG_TIMESTEP_FIXED
;
366 float fwd_resistance
= k_friction_resistance
;
368 vel
[2] = stable_force( vel
[2],vg_signf(vel
[2]) * -fwd_resistance
*substep
);
369 vel
[0] = stable_force( vel
[0],vg_signf(vel
[0]) * -k_friction_lat
*substep
);
371 if( player
.input_jump
->button
.value
)
373 phys
->jump
+= VG_TIMESTEP_FIXED
* k_jump_charge_speed
;
375 if( !phys
->jump_charge
)
376 phys
->jump_dir
= phys
->reverse
> 0.0f
? 1: 0;
378 phys
->jump_charge
= 1;
381 static int push_thresh_last
= 0;
382 float push
= player
.input_push
->button
.value
;
383 int push_thresh
= push
>0.15f
? 1: 0;
385 if( push_thresh
&& !push_thresh_last
)
386 player
.phys
.start_push
= vg
.time
;
388 push_thresh_last
= push_thresh
;
390 if( !player
.input_jump
->button
.value
&& push_thresh
)
392 player
.phys
.pushing
= 1.0f
;
393 player
.phys
.push_time
= vg
.time
- player
.phys
.start_push
;
395 float cycle_time
= player
.phys
.push_time
*k_push_cycle_rate
,
396 amt
= k_push_accel
* (sinf(cycle_time
)*0.5f
+0.5f
)*VG_TIMESTEP_FIXED
,
397 current
= v3_length( vel
),
398 new_vel
= vg_minf( current
+ amt
, k_max_push_speed
);
400 new_vel
-= vg_minf(current
, k_max_push_speed
);
401 vel
[2] -= new_vel
* phys
->reverse
;
404 m3x3_mulv( phys
->rb
.to_world
, vel
, phys
->rb
.v
);
406 float input
= player
.input_js1h
->axis
.value
,
407 grab
= player
.input_grab
->axis
.value
,
408 steer
= input
* (1.0f
-(phys
->jump
+grab
)*0.4f
),
409 steer_scaled
= vg_signf(steer
) * powf(steer
,2.0f
) * k_steer_ground
;
411 phys
->iY
-= steer_scaled
* VG_TIMESTEP_FIXED
;
416 * ===============================================
421 v3_muladds( phys
->rb
.co
, phys
->rb
.up
, 1.0f
-grab
, cog_ideal
);
422 v3_sub( cog_ideal
, phys
->cog
, diff
);
425 if( v3_length2( diff
) > 20.0f
*20.0f
)
426 v3_copy( cog_ideal
, phys
->cog
);
429 float rate_lat
= k_cog_spring_lat
* VG_TIMESTEP_FIXED
,
430 rate_vert
= k_cog_spring_vert
* VG_TIMESTEP_FIXED
,
431 vert_amt
= v3_dot( diff
, phys
->rb
.up
);
433 /* Split into vert/lat springs */
434 v3f diff_vert
, diff_lat
;
435 v3_muladds( diff
, phys
->rb
.up
, -vert_amt
, diff_lat
);
436 v3_muls( phys
->rb
.up
, vert_amt
, diff_vert
);
440 rate_vert
*= k_cog_boost_multiplier
;
442 float ap_a
= k_cog_mass_ratio
,
443 ap_b
= -(1.0f
-k_cog_mass_ratio
);
445 v3_muladds( phys
->cog_v
, diff_lat
, rate_lat
* ap_a
, phys
->cog_v
);
446 v3_muladds( phys
->cog_v
, diff_vert
, rate_vert
* ap_a
, phys
->cog_v
);
448 //v3_muladds( phys->rb.v, diff_lat, rate_lat * ap_b, phys->rb.v );
449 v3_muladds( phys
->rb
.v
, diff_vert
, rate_vert
* ap_b
, phys
->rb
.v
);
452 v3_muls( phys
->cog_v
, 1.0f
-(VG_TIMESTEP_FIXED
*k_cog_damp
), phys
->cog_v
);
455 v3_muladds( phys
->cog
, phys
->cog_v
, VG_TIMESTEP_FIXED
, phys
->cog
);
461 * ===============================================
466 if( !phys
->jump_charge
&& phys
->jump
> 0.2f
)
470 /* Launch more up if alignment is up else improve velocity */
471 float aup
= fabsf(v3_dot( (v3f
){0.0f
,1.0f
,0.0f
}, phys
->rb
.up
)),
473 dir
= mod
+ aup
*(1.0f
-mod
);
475 v3_copy( phys
->rb
.v
, jumpdir
);
476 v3_normalize( jumpdir
);
477 v3_muls( jumpdir
, 1.0f
-dir
, jumpdir
);
478 v3_muladds( jumpdir
, phys
->rb
.up
, dir
, jumpdir
);
479 v3_normalize( jumpdir
);
481 float force
= k_jump_force
*phys
->jump
;
482 v3_muladds( phys
->rb
.v
, jumpdir
, force
, phys
->rb
.v
);
485 player
.jump_time
= vg
.time
;
487 /* TODO: Move to audio file */
489 audio_player_set_flags( &audio_player_extra
, AUDIO_FLAG_SPACIAL_3D
);
490 audio_player_set_position( &audio_player_extra
, phys
->rb
.co
);
491 audio_player_set_vol( &audio_player_extra
, 20.0f
);
492 audio_player_playclip( &audio_player_extra
, &audio_jumps
[rand()%2] );
497 VG_STATIC
void player_physics_control_grind(void)
499 struct player_phys
*phys
= &player
.phys
;
500 v2f steer
= { player
.input_js1h
->axis
.value
,
501 player
.input_js1v
->axis
.value
};
503 float l2
= v2_length2( steer
);
505 v2_muls( steer
, 1.0f
/sqrtf(l2
), steer
);
507 phys
->iY
-= steer
[0] * k_steer_air
* VG_TIMESTEP_FIXED
;
509 float iX
= steer
[1] * phys
->reverse
* k_steer_air
* VG_TIMESTEP_FIXED
;
511 static float siX
= 0.0f
;
512 siX
= vg_lerpf( siX
, iX
, k_steer_air_lerp
);
515 q_axis_angle( rotate
, phys
->rb
.right
, siX
);
516 q_mul( rotate
, phys
->rb
.q
, phys
->rb
.q
);
522 * Air control, no real physics
524 VG_STATIC
void player_physics_control_air(void)
526 struct player_phys
*phys
= &player
.phys
;
528 m3x3_mulv( phys
->vr
, phys
->rb
.v
, phys
->rb
.v
);
529 //vg_line_cross( player.land_target, 0xff0000ff, 0.25f );
536 float pstep
= VG_TIMESTEP_FIXED
* 1.0f
;
537 float k_bias
= 0.98f
;
540 v3_copy( phys
->rb
.co
, pco
);
541 v3_muls( phys
->rb
.v
, 1.0f
, pv
);
543 float time_to_impact
= 0.0f
;
544 float limiter
= 1.0f
;
546 struct grind_edge
*best_grind
= NULL
;
547 float closest_grind
= INFINITY
;
549 v3f target_normal
= { 0.0f
, 1.0f
, 0.0f
};
552 for( int i
=0; i
<250; i
++ )
554 v3_copy( pco
, pco1
);
555 m3x3_mulv( phys
->vr
, pv
, pv
);
556 apply_gravity( pv
, pstep
);
557 v3_muladds( pco
, pv
, pstep
, pco
);
562 v3_sub( pco
, pco1
, vdir
);
563 contact
.dist
= v3_length( vdir
);
564 v3_divs( vdir
, contact
.dist
, vdir
);
567 struct grind_edge
*ge
= player_grind_collect_edge( pco
, pco1
,
570 if( ge
&& (v3_dot((v3f
){0.0f
,1.0f
,0.0f
},vdir
) < -0.2f
) )
572 vg_line( ge
->p0
, ge
->p1
, 0xff0000ff );
573 vg_line_cross( pco
, 0xff0000ff, 0.25f
);
578 float orig_dist
= contact
.dist
;
579 if( ray_world( pco1
, vdir
, &contact
) )
581 v3_copy( contact
.normal
, target_normal
);
583 time_to_impact
+= (contact
.dist
/orig_dist
)*pstep
;
584 vg_line_cross( contact
.pos
, 0xffff0000, 0.25f
);
587 time_to_impact
+= pstep
;
592 float angle
= v3_dot( phys
->rb
.up
, target_normal
);
594 v3_cross( phys
->rb
.up
, target_normal
, axis
);
596 limiter
= vg_minf( 5.0f
, time_to_impact
)/5.0f
;
597 limiter
= 1.0f
-limiter
;
599 limiter
= 1.0f
-limiter
;
601 if( fabsf(angle
) < 0.99f
)
604 q_axis_angle( correction
, axis
,
605 acosf(angle
)*(1.0f
-limiter
)*3.0f
*VG_TIMESTEP_FIXED
);
606 q_mul( correction
, phys
->rb
.q
, phys
->rb
.q
);
610 v2f steer
= { player
.input_js1h
->axis
.value
,
611 player
.input_js1v
->axis
.value
};
613 float l2
= v2_length2( steer
);
615 v2_muls( steer
, 1.0f
/sqrtf(l2
), steer
);
617 phys
->iY
-= steer
[0] * k_steer_air
* VG_TIMESTEP_FIXED
;
619 float iX
= steer
[1] *
620 phys
->reverse
* k_steer_air
* limiter
* VG_TIMESTEP_FIXED
;
622 static float siX
= 0.0f
;
623 siX
= vg_lerpf( siX
, iX
, k_steer_air_lerp
);
626 q_axis_angle( rotate
, phys
->rb
.right
, siX
);
627 q_mul( rotate
, phys
->rb
.q
, phys
->rb
.q
);
630 v2f target
= {0.0f
,0.0f
};
631 v2_muladds( target
, (v2f
){ vg_get_axis("grabh"), vg_get_axis("grabv") },
632 phys
->grab
, target
);
636 VG_STATIC
void player_walk_collider_configuration(void)
638 struct player_phys
*phys
= &player
.phys
;
642 rigidbody
*rbf
= &player
.collide_front
,
643 *rbb
= &player
.collide_back
;
645 v3_add( phys
->rb
.co
, (v3f
){0.0f
,h0
,0.0f
}, rbf
->co
);
646 v3_add( phys
->rb
.co
, (v3f
){0.0f
,h1
,0.0f
}, rbb
->co
);
647 v3_copy( rbf
->co
, rbf
->to_world
[3] );
648 v3_copy( rbb
->co
, rbb
->to_world
[3] );
649 m4x3_invert_affine( rbf
->to_world
, rbf
->to_local
);
650 m4x3_invert_affine( rbb
->to_world
, rbb
->to_local
);
652 rb_update_bounds( rbf
);
653 rb_update_bounds( rbb
);
656 VG_STATIC
void player_regular_collider_configuration(void)
658 struct player_phys
*phys
= &player
.phys
;
660 /* Standard ground configuration */
661 rigidbody
*rbf
= &player
.collide_front
,
662 *rbb
= &player
.collide_back
;
664 m3x3_copy( phys
->rb
.to_world
, player
.collide_front
.to_world
);
665 m3x3_copy( phys
->rb
.to_world
, player
.collide_back
.to_world
);
667 player
.air_blend
= vg_lerpf( player
.air_blend
, phys
->in_air
, 0.1f
);
668 float h
= player
.air_blend
*0.0f
;
670 m4x3_mulv( phys
->rb
.to_world
, (v3f
){0.0f
,h
,-k_board_length
}, rbf
->co
);
671 v3_copy( rbf
->co
, rbf
->to_world
[3] );
672 m4x3_mulv( phys
->rb
.to_world
, (v3f
){0.0f
,h
, k_board_length
}, rbb
->co
);
673 v3_copy( rbb
->co
, rbb
->to_world
[3] );
675 m4x3_invert_affine( rbf
->to_world
, rbf
->to_local
);
676 m4x3_invert_affine( rbb
->to_world
, rbb
->to_local
);
678 rb_update_bounds( rbf
);
679 rb_update_bounds( rbb
);
682 VG_STATIC
void player_integrate(void);
684 VG_STATIC
int player_walk_surface_standable( v3f n
)
686 return v3_dot( n
, (v3f
){0.0f
,1.0f
,0.0f
} ) > 0.5f
;
689 VG_STATIC
void player_walk_stepdown(void)
691 struct player_phys
*phys
= &player
.phys
;
692 float max_dist
= 0.4f
;
695 v3_copy( phys
->rb
.co
, pa
);
698 v3_muladds( pa
, (v3f
){0.01f
,1.0f
,0.01f
}, -max_dist
, pb
);
699 vg_line( pa
, pb
, 0xff000000 );
701 /* TODO: Make #define */
706 if( spherecast_world( pa
, pb
, r
, &t
, n
) != -1 )
708 if( player_walk_surface_standable( n
) )
711 v3_lerp( pa
, pb
, t
+0.001f
, phys
->rb
.co
);
712 phys
->rb
.co
[1] -= 0.3f
;
717 VG_STATIC
int player_update_collision_manifold( rb_ct
*manifold
);
718 VG_STATIC
void player_walk_physics(void)
720 struct player_phys
*phys
= &player
.phys
;
721 rigidbody
*rbf
= &player
.collide_front
,
722 *rbb
= &player
.collide_back
;
724 m3x3_identity( player
.collide_front
.to_world
);
725 m3x3_identity( player
.collide_back
.to_world
);
727 v3_zero( phys
->rb
.w
);
728 q_axis_angle( phys
->rb
.q
, (v3f
){0.0f
,1.0f
,0.0f
}, -player
.angles
[0] );
733 v3f forward_dir
= { sinf(player
.angles
[0]),0.0f
,-cosf(player
.angles
[0]) };
734 v3f right_dir
= { -forward_dir
[2], 0.0f
, forward_dir
[0] };
736 v2f walk
= { player
.input_walkh
->axis
.value
,
737 player
.input_walkv
->axis
.value
};
742 if( v2_length2(walk
) > 0.001f
)
743 v2_normalize_clamp( walk
);
747 player_walk_collider_configuration();
749 /* allow player to accelerate a bit */
751 v3_muls( forward_dir
, walk
[1], walk_3d
);
752 v3_muladds( walk_3d
, right_dir
, walk
[0], walk_3d
);
754 float current_vel
= fabsf(v3_dot( walk_3d
, phys
->rb
.v
)),
755 new_vel
= current_vel
+ VG_TIMESTEP_FIXED
*k_air_accelerate
,
756 clamped_new
= vg_clampf( new_vel
, 0.0f
, k_walkspeed
),
757 vel_diff
= vg_maxf( 0.0f
, clamped_new
- current_vel
);
759 v3_muladds( phys
->rb
.v
, right_dir
, walk
[0] * vel_diff
, phys
->rb
.v
);
760 v3_muladds( phys
->rb
.v
, forward_dir
, walk
[1] * vel_diff
, phys
->rb
.v
);
763 len
= player_update_collision_manifold( manifold
);
764 rb_presolve_contacts( manifold
, len
);
766 for( int i
=0; i
<len
; i
++ )
768 struct contact
*ct
= &manifold
[i
];
769 if( v3_dot( ct
->n
, (v3f
){0.0f
,1.0f
,0.0f
} ) > 0.5f
)
773 for( int j
=0; j
<5; j
++ )
775 for( int i
=0; i
<len
; i
++ )
777 struct contact
*ct
= &manifold
[i
];
780 float vn
= -v3_dot( phys
->rb
.v
, ct
->n
);
783 float temp
= ct
->norm_impulse
;
784 ct
->norm_impulse
= vg_maxf( temp
+ vn
, 0.0f
);
785 vn
= ct
->norm_impulse
- temp
;
788 v3_muls( ct
->n
, vn
, impulse
);
790 v3_add( impulse
, phys
->rb
.v
, phys
->rb
.v
);
793 for( int j
=0; j
<2; j
++ )
795 float f
= k_friction
* ct
->norm_impulse
,
796 vt
= v3_dot( phys
->rb
.v
, ct
->t
[j
] ),
799 float temp
= ct
->tangent_impulse
[j
];
800 ct
->tangent_impulse
[j
] = vg_clampf( temp
+ lambda
, -f
, f
);
801 lambda
= ct
->tangent_impulse
[j
] - temp
;
803 v3_muladds( phys
->rb
.v
, ct
->t
[j
], lambda
, phys
->rb
.v
);
812 player
.walk
= v2_length( walk
);
814 if( player
.input_walk
->button
.value
)
815 v2_muls( walk
, 0.5f
, walk
);
817 v2_muls( walk
, k_walkspeed
* VG_TIMESTEP_FIXED
, walk
);
819 /* Do XY translation */
820 v3f walk_apply
, walk_measured
;
821 v3_zero( walk_apply
);
822 v3_muladds( walk_apply
, right_dir
, walk
[0], walk_apply
);
823 v3_muladds( walk_apply
, forward_dir
, walk
[1], walk_apply
);
824 v3_add( walk_apply
, phys
->rb
.co
, phys
->rb
.co
);
826 /* Directly resolve collisions */
827 player_walk_collider_configuration();
828 len
= player_update_collision_manifold( manifold
);
832 for( int j
=0; j
<8; j
++ )
834 for( int i
=0; i
<len
; i
++ )
836 struct contact
*ct
= &manifold
[i
];
838 float resolved_amt
= v3_dot( ct
->n
, dt
),
839 remaining
= (ct
->p
-k_penetration_slop
) - resolved_amt
,
840 apply
= vg_maxf( remaining
, 0.0f
) * 0.3f
;
842 v3_muladds( dt
, ct
->n
, apply
, dt
);
845 v3_add( dt
, phys
->rb
.co
, phys
->rb
.co
);
847 v3_add( dt
, walk_apply
, walk_measured
);
848 v3_divs( walk_measured
, VG_TIMESTEP_FIXED
, phys
->rb
.v
);
852 struct world_material
*surface_mat
= world_contact_material(manifold
);
853 player
.surface_prop
= surface_mat
->info
.surface_prop
;
857 if( player
.input_jump
->button
.value
)
859 phys
->rb
.v
[1] = 5.0f
;
864 /* Check if grounded by current manifold */
866 for( int i
=0; i
<len
; i
++ )
868 struct contact
*ct
= &manifold
[i
];
869 if( player_walk_surface_standable( ct
->n
) )
875 player_walk_stepdown();
879 VG_STATIC
void player_grind(void)
881 struct player_phys
*phys
= &player
.phys
;
884 int idx
= bh_closest_point( world
.grind_bh
, phys
->rb
.co
, closest
, INFINITY
);
888 struct grind_edge
*edge
= &world
.grind_edges
[ idx
];
890 vg_line( phys
->rb
.co
, closest
, 0xff000000 );
891 vg_line_cross( closest
, 0xff000000, 0.3f
);
892 vg_line( edge
->p0
, edge
->p1
, 0xff000000 );
895 v3_sub( closest
, phys
->rb
.co
, grind_delta
);
897 float p
= v3_dot( phys
->rb
.forward
, grind_delta
);
898 v3_muladds( grind_delta
, phys
->rb
.forward
, -p
, grind_delta
);
900 float a
= vg_maxf( 0.0f
, 4.0f
-v3_dist2( closest
, phys
->rb
.co
) );
901 v3_muladds( phys
->rb
.v
, grind_delta
, a
*0.2f
, phys
->rb
.v
);
904 VG_STATIC
int player_update_grind_collision( rb_ct
*contact
)
906 struct player_phys
*phys
= &player
.phys
;
909 v3_muladds( phys
->rb
.co
, phys
->rb
.forward
, 0.5f
, p0
);
910 v3_muladds( phys
->rb
.co
, phys
->rb
.forward
, -0.5f
, p1
);
911 v3_muladds( p0
, phys
->rb
.up
, 0.125f
-0.15f
, p0
);
912 v3_muladds( p1
, phys
->rb
.up
, 0.125f
-0.15f
, p1
);
914 float const k_r
= 0.25f
;
915 struct grind_edge
*closest_edge
= player_grind_collect_edge( p0
, p1
,
920 vg_line( p0
, p1
, 0xff0000ff );
926 vg_line_cross( c0
, 0xff000000, 0.1f
);
927 vg_line_cross( c1
, 0xff000000, 0.1f
);
928 vg_line( c0
, c1
, 0xff000000 );
932 v3_sub( c1
, c0
, delta
);
934 if( v3_dot( delta
, phys
->rb
.up
) > 0.0001f
)
936 contact
->p
= v3_length( delta
);
937 contact
->type
= k_contact_type_edge
;
938 contact
->element_id
= 0;
939 v3_copy( c1
, contact
->co
);
940 contact
->rba
= &player
.phys
.rb
;
941 contact
->rbb
= &world
.rb_geo
;
943 v3f edge_dir
, axis_dir
;
944 v3_sub( closest_edge
->p1
, closest_edge
->p0
, edge_dir
);
945 v3_normalize( edge_dir
);
946 v3_cross( (v3f
){0.0f
,1.0f
,0.0f
}, edge_dir
, axis_dir
);
947 v3_cross( edge_dir
, axis_dir
, contact
->n
);
950 vg_info( "%f %f\n", v3_length( contact
->n
), contact
->p
);
962 /* Manifold must be able to hold at least 64 elements */
963 VG_STATIC
int player_update_collision_manifold( rb_ct
*manifold
)
965 struct player_phys
*phys
= &player
.phys
;
967 rigidbody
*rbf
= &player
.collide_front
,
968 *rbb
= &player
.collide_back
;
970 rb_debug( rbf
, 0xff00ffff );
971 rb_debug( rbb
, 0xffffff00 );
975 phys
->rise
= vg_lerpf( phys
->rise
, phys
->in_air
? -0.25f
: 0.0f
,
982 len_f
= rb_sphere_scene( rbf
, &world
.rb_geo
, manifold
);
983 rb_manifold_filter_coplanar( manifold
, len_f
, 0.05f
);
986 rb_manifold_filter_backface( manifold
, len_f
);
987 rb_manifold_filter_joint_edges( manifold
, len_f
, 0.05f
);
988 rb_manifold_filter_pairs( manifold
, len_f
, 0.05f
);
990 int new_len_f
= rb_manifold_apply_filtered( manifold
, len_f
);
991 if( len_f
&& !new_len_f
)
996 rb_ct
*man_b
= &manifold
[len_f
];
997 len_b
= rb_sphere_scene( rbb
, &world
.rb_geo
, man_b
);
998 rb_manifold_filter_coplanar( man_b
, len_b
, 0.05f
);
1001 rb_manifold_filter_backface( man_b
, len_b
);
1002 rb_manifold_filter_joint_edges( man_b
, len_b
, 0.05f
);
1003 rb_manifold_filter_pairs( man_b
, len_b
, 0.05f
);
1005 int new_len_b
= rb_manifold_apply_filtered( man_b
, len_b
);
1006 if( len_b
&& !new_len_b
)
1011 return len_f
+ len_b
;
1014 VG_STATIC
void player_adhere_ground( rb_ct
*manifold
, int len
)
1016 struct player_phys
*phys
= &player
.phys
;
1017 int was_in_air
= phys
->in_air
;
1020 v3_zero( surface_avg
);
1025 * ================================================================
1028 player
.normal_pressure
= 0.0f
;
1030 player
.normal_pressure
= v3_dot( phys
->rb
.up
, phys
->rb
.v
);
1037 float mod
= 0.7f
* player
.input_grab
->axis
.value
+ 0.3f
,
1038 spring_k
= mod
* k_spring_force
,
1039 damp_K
= mod
* k_spring_dampener
,
1042 v3_copy( player
.collide_front
.co
, p0_0
);
1043 v3_copy( player
.collide_back
.co
, p1_0
);
1045 v3_muladds( p0_0
, phys
->rb
.up
, -disp_k
, p0_1
);
1046 v3_muladds( p1_0
, phys
->rb
.up
, -disp_k
, p1_1
);
1048 int cast0
= spherecast_world( p0_0
, p0_1
, 0.2f
, &t0
, n0
),
1049 cast1
= spherecast_world( p1_0
, p1_1
, 0.2f
, &t1
, n1
);
1054 m3x3_copy( phys
->rb
.to_world
, temp
);
1057 v3_lerp( p0_0
, p0_1
, t0
, temp
[3] );
1058 v3_copy( temp
[3], animp0
);
1059 debug_sphere( temp
, 0.2f
, VG__PINK
);
1062 v3_sub( p0_0
, phys
->rb
.co
, delta
);
1064 float displacement
= vg_clampf( 1.0f
-t0
, 0.0f
, 1.0f
),
1065 damp
= vg_maxf( 0.0f
, v3_dot( phys
->rb
.up
, phys
->rb
.v
) );
1066 v3_muls( phys
->rb
.up
, displacement
*spring_k
*k_rb_delta
-
1067 damp
*damp_K
*k_rb_delta
, F
);
1069 v3_muladds( phys
->rb
.v
, F
, 1.0f
, phys
->rb
.v
);
1071 /* Angular velocity */
1073 v3_cross( delta
, F
, wa
);
1074 v3_muladds( phys
->rb
.w
, wa
, k_spring_angular
, phys
->rb
.w
);
1077 v3_copy( p0_1
, animp0
);
1081 v3_lerp( p1_0
, p1_1
, t1
, temp
[3] );
1082 v3_copy( temp
[3], animp1
);
1083 debug_sphere( temp
, 0.2f
, VG__PINK
);
1086 v3_sub( p1_0
, phys
->rb
.co
, delta
);
1088 float displacement
= vg_clampf( 1.0f
-t1
, 0.0f
, 1.0f
),
1089 damp
= vg_maxf( 0.0f
, v3_dot( phys
->rb
.up
, phys
->rb
.v
) );
1090 v3_muls( phys
->rb
.up
, displacement
*spring_k
*k_rb_delta
-
1091 damp
*damp_K
*k_rb_delta
, F
);
1093 v3_muladds( phys
->rb
.v
, F
, 1.0f
, phys
->rb
.v
);
1095 /* Angular velocity */
1097 v3_cross( delta
, F
, wa
);
1098 v3_muladds( phys
->rb
.w
, wa
, k_spring_angular
, phys
->rb
.w
);
1101 v3_copy( p1_1
, animp1
);
1103 v3f animavg
, animdelta
;
1104 v3_add( animp0
, animp1
, animavg
);
1105 v3_muls( animavg
, 0.5f
, animavg
);
1107 v3_sub( animp1
, animp0
, animdelta
);
1108 v3_normalize( animdelta
);
1110 m4x3_mulv( phys
->rb
.to_local
, animavg
, player
.board_offset
);
1112 float dx
= -v3_dot( animdelta
, phys
->rb
.forward
),
1113 dy
= v3_dot( animdelta
, phys
->rb
.up
);
1115 float angle
= -atan2f( dy
, dx
);
1116 q_axis_angle( player
.board_rotation
, (v3f
){ 1.0f
, 0.0f
, 0.0f
}, angle
);
1119 * ================================================================
1123 if( len
== 0 && !((cast0
!=-1)&&(cast1
!=-1)) )
1125 phys
->lift_frames
++;
1127 if( phys
->lift_frames
>= 8 )
1132 for( int i
=0; i
<len
; i
++ )
1133 v3_add( surface_avg
, manifold
[i
].n
, surface_avg
);
1134 v3_normalize( surface_avg
);
1136 if( v3_dot( phys
->rb
.v
, surface_avg
) > 0.7f
)
1138 phys
->lift_frames
++;
1140 if( phys
->lift_frames
>= 8 )
1146 phys
->lift_frames
= 0;
1147 v3f projected
, axis
;
1149 float const DOWNFORCE
= -k_downforce
*VG_TIMESTEP_FIXED
;
1150 v3_muladds( phys
->rb
.v
, phys
->rb
.up
, DOWNFORCE
, phys
->rb
.v
);
1152 float d
= v3_dot( phys
->rb
.forward
, surface_avg
);
1153 v3_muladds( surface_avg
, phys
->rb
.forward
, -d
, projected
);
1154 v3_normalize( projected
);
1156 float angle
= v3_dot( phys
->rb
.up
, projected
);
1157 v3_cross( phys
->rb
.up
, projected
, axis
);
1161 v3_add( phys
->rb
.co
, projected
, p0
);
1162 v3_add( phys
->rb
.co
, phys
->rb
.up
, p1
);
1163 vg_line( phys
->rb
.co
, p0
, 0xff00ff00 );
1164 vg_line( phys
->rb
.co
, p1
, 0xff000fff );
1167 if( fabsf(angle
) < 0.999f
)
1170 q_axis_angle( correction
, axis
,
1171 acosf(angle
)*4.0f
*VG_TIMESTEP_FIXED
);
1172 q_mul( correction
, phys
->rb
.q
, phys
->rb
.q
);
1177 if( !was_in_air
&& phys
->in_air
)
1181 VG_STATIC
void player_collision_response( rb_ct
*manifold
, int len
)
1183 struct player_phys
*phys
= &player
.phys
;
1187 * ===============================================
1191 player
.normal_pressure
= v3_dot( phys
->rb
.up
, phys
->rb
.v
);
1194 float ideal
= 1.0f
-player
.input_grab
->axis
.value
,
1195 diff
= phys
->spring
- ideal
,
1196 Fspring
= -k_cog_spring_lat
* diff
,
1197 Fdamp
= -k_cog_damp
* phys
->springv
,
1198 F
= (Fspring
+ Fdamp
) * k_rb_delta
;
1201 phys
->spring
+= phys
->springv
* k_rb_delta
;
1203 if( phys
->springv
> 0.0f
)
1204 v3_muladds( phys
->rb
.v
, phys
->rb
.up
, F
*k_cog_spring_vert
, phys
->rb
.v
);
1207 player
.normal_pressure
= 0.0f
;
1209 player
.normal_pressure
= v3_dot( phys
->rb
.up
, phys
->rb
.v
);
1213 if( player
.input_grab
->axis
.value
> 0.5f
)
1218 v3_muls( phys
->rb
.up
, k_mmthrow_scale
, phys
->throw_v
);
1224 float doty
= v3_dot( phys
->rb
.up
, phys
->throw_v
);
1227 v3_muladds( phys
->throw_v
, phys
->rb
.up
, -doty
, Fl
);
1231 v3_muladds( phys
->rb
.v
, Fl
, k_mmcollect_lat
, phys
->rb
.v
);
1232 v3_muladds( phys
->throw_v
, Fl
, -k_mmcollect_lat
, phys
->throw_v
);
1235 v3_muls( phys
->rb
.up
, -doty
, Fv
);
1236 v3_muladds( phys
->rb
.v
, Fv
, k_mmcollect_vert
, phys
->rb
.v
);
1237 v3_muladds( phys
->throw_v
, Fv
, k_mmcollect_vert
, phys
->throw_v
);
1239 v3_copy( Fl
, player
.debug_mmcollect_lat
);
1240 v3_copy( Fv
, player
.debug_mmcollect_vert
);
1244 if( v3_length2( phys
->throw_v
) > 0.0001f
)
1247 v3_copy( phys
->throw_v
, dir
);
1248 v3_normalize( dir
);
1250 float max
= v3_dot( dir
, phys
->throw_v
),
1251 amt
= vg_minf( k_mmdecay
* k_rb_delta
, max
);
1253 v3_muladds( phys
->throw_v
, dir
, -amt
, phys
->throw_v
);
1257 /* TODO: RElocate */
1260 v3f ideal_cog
, ideal_diff
;
1261 v3_muladds( phys
->rb
.co
, phys
->rb
.up
,
1262 1.0f
-player
.input_grab
->axis
.value
, ideal_cog
);
1263 v3_sub( ideal_cog
, phys
->cog
, ideal_diff
);
1265 /* Apply velocities */
1267 v3_sub( phys
->rb
.v
, phys
->cog_v
, rv
);
1270 v3_muls( ideal_diff
, -k_cog_spring
* k_rb_rate
, F
);
1271 v3_muladds( F
, rv
, -k_cog_damp
* k_rb_rate
, F
);
1273 float ra
= k_cog_mass_ratio
,
1274 rb
= 1.0f
-k_cog_mass_ratio
;
1276 v3_muladds( phys
->cog_v
, F
, -rb
, phys
->cog_v
);
1281 * ===============================================
1284 for( int j
=0; j
<10; j
++ )
1286 for( int i
=0; i
<len
; i
++ )
1288 struct contact
*ct
= &manifold
[i
];
1291 v3_sub( ct
->co
, phys
->rb
.co
, delta
);
1292 v3_cross( phys
->rb
.w
, delta
, dv
);
1293 v3_add( phys
->rb
.v
, dv
, dv
);
1295 float vn
= -v3_dot( dv
, ct
->n
);
1298 float temp
= ct
->norm_impulse
;
1299 ct
->norm_impulse
= vg_maxf( temp
+ vn
, 0.0f
);
1300 vn
= ct
->norm_impulse
- temp
;
1303 v3_muls( ct
->n
, vn
, impulse
);
1305 if( fabsf(v3_dot( impulse
, phys
->rb
.forward
)) > 10.0f
||
1306 fabsf(v3_dot( impulse
, phys
->rb
.up
)) > 50.0f
)
1312 v3_add( impulse
, phys
->rb
.v
, phys
->rb
.v
);
1313 v3_cross( delta
, impulse
, impulse
);
1316 * W Impulses are limited to the Y and X axises, we don't really want
1317 * roll angular velocities being included.
1319 * Can also tweak the resistance of each axis here by scaling the wx,wy
1323 float wy
= v3_dot( phys
->rb
.up
, impulse
) * 0.8f
,
1324 wx
= v3_dot( phys
->rb
.right
, impulse
)*1.0f
;
1326 v3_muladds( phys
->rb
.w
, phys
->rb
.up
, wy
, phys
->rb
.w
);
1327 v3_muladds( phys
->rb
.w
, phys
->rb
.right
, wx
, phys
->rb
.w
);
1331 /* early integrate this */
1332 phys
->cog_v
[1] += -9.8f
* k_rb_delta
;
1333 v3_muladds( phys
->cog
, phys
->cog_v
, k_rb_delta
, phys
->cog
);
1336 VG_STATIC
void player_save_frame(void)
1338 player
.phys_gate_frame
= player
.phys
;
1341 VG_STATIC
void player_restore_frame(void)
1343 player
.phys
= player
.phys_gate_frame
;
1344 rb_update_transform( &player
.phys
.rb
);
1347 VG_STATIC
void player_integrate(void)
1349 struct player_phys
*phys
= &player
.phys
;
1350 v3_sub( phys
->rb
.v
, phys
->v_last
, phys
->a
);
1351 v3_muls( phys
->a
, 1.0f
/VG_TIMESTEP_FIXED
, phys
->a
);
1352 v3_copy( phys
->rb
.v
, phys
->v_last
);
1354 apply_gravity( phys
->rb
.v
, VG_TIMESTEP_FIXED
);
1355 v3_muladds( phys
->rb
.co
, phys
->rb
.v
, VG_TIMESTEP_FIXED
, phys
->rb
.co
);
1358 VG_STATIC
void player_do_motion(void)
1360 struct player_phys
*phys
= &player
.phys
;
1362 if( world
.water
.enabled
)
1364 if( (phys
->rb
.co
[1] < 0.0f
) && !player
.is_dead
)
1367 audio_player_set_flags( &audio_player_extra
, AUDIO_FLAG_SPACIAL_3D
);
1368 audio_player_set_position( &audio_player_extra
, phys
->rb
.co
);
1369 audio_player_set_vol( &audio_player_extra
, 20.0f
);
1370 audio_player_playclip( &audio_player_extra
, &audio_splash
);
1378 v3_copy( phys
->rb
.co
, prevco
);
1380 if( phys
->on_board
)
1384 player_regular_collider_configuration();
1385 int len
= player_update_collision_manifold( manifold
);
1386 int grind_col
= player_update_grind_collision( &manifold
[len
] );
1388 static int _grind_col_pre
= 0;
1393 v3f up
= { 0.0f
, 1.0f
, 0.0f
};
1394 float angle
= v3_dot( phys
->rb
.up
, up
);
1396 if( fabsf(angle
) < 0.99f
)
1399 v3_cross( phys
->rb
.up
, up
, axis
);
1402 q_axis_angle( correction
, axis
,
1403 VG_TIMESTEP_FIXED
* 10.0f
* acosf(angle
) );
1404 q_mul( correction
, phys
->rb
.q
, phys
->rb
.q
);
1407 float const DOWNFORCE
= -k_downforce
*1.2f
*VG_TIMESTEP_FIXED
;
1408 v3_muladds( phys
->rb
.v
, manifold
[len
].n
, DOWNFORCE
, phys
->rb
.v
);
1409 m3x3_identity( phys
->vr
);
1410 m3x3_identity( phys
->vr_pstep
);
1412 if( !_grind_col_pre
)
1415 audio_player_set_flags( &audio_player_extra
,
1416 AUDIO_FLAG_SPACIAL_3D
);
1417 audio_player_set_position( &audio_player_extra
, phys
->rb
.co
);
1418 audio_player_set_vol( &audio_player_extra
, 20.0f
);
1419 audio_player_playclip( &audio_player_extra
, &audio_board
[5] );
1426 player_adhere_ground( manifold
, len
);
1428 if( _grind_col_pre
)
1431 audio_player_set_flags( &audio_player_extra
,
1432 AUDIO_FLAG_SPACIAL_3D
);
1433 audio_player_set_position( &audio_player_extra
, phys
->rb
.co
);
1434 audio_player_set_vol( &audio_player_extra
, 20.0f
);
1435 audio_player_playclip( &audio_player_extra
, &audio_board
[6] );
1440 _grind_col_pre
= grind_col
;
1442 rb_presolve_contacts( manifold
, len
+ VG_MAX(0,grind_col
) );
1443 player_collision_response( manifold
, len
+ VG_MAX(0,grind_col
) );
1445 player_physics_control_passive();
1450 player_physics_control_grind();
1455 player_physics_control_air();
1457 player_physics_control();
1463 player_walk_physics();
1466 /* Real angular velocity integration */
1467 v3_lerp( phys
->rb
.w
, (v3f
){0.0f
,0.0f
,0.0f
}, 0.125f
*0.5f
, phys
->rb
.w
);
1468 if( v3_length2( phys
->rb
.w
) > 0.0f
)
1472 v3_copy( phys
->rb
.w
, axis
);
1474 float mag
= v3_length( axis
);
1475 v3_divs( axis
, mag
, axis
);
1476 q_axis_angle( rotation
, axis
, mag
*k_rb_delta
);
1477 q_mul( rotation
, phys
->rb
.q
, phys
->rb
.q
);
1480 /* Faux angular velocity */
1483 float lerpq
= phys
->in_air
? 0.04f
: 0.3f
;
1484 phys
->siY
= vg_lerpf( phys
->siY
, phys
->iY
, lerpq
);
1486 q_axis_angle( rotate
, phys
->rb
.up
, phys
->siY
);
1487 q_mul( rotate
, phys
->rb
.q
, phys
->rb
.q
);
1491 * Gate intersection, by tracing a line over the gate planes
1493 for( int i
=0; i
<world
.gate_count
; i
++ )
1495 struct route_gate
*rg
= &world
.gates
[i
];
1496 teleport_gate
*gate
= &rg
->gate
;
1498 if( gate_intersect( gate
, phys
->rb
.co
, prevco
) )
1500 m4x3_mulv( gate
->transport
, phys
->rb
.co
, phys
->rb
.co
);
1501 m4x3_mulv( gate
->transport
, phys
->cog
, phys
->cog
);
1502 m3x3_mulv( gate
->transport
, phys
->cog_v
, phys
->cog_v
);
1503 m3x3_mulv( gate
->transport
, phys
->rb
.v
, phys
->rb
.v
);
1504 m3x3_mulv( gate
->transport
, phys
->vl
, phys
->vl
);
1505 m3x3_mulv( gate
->transport
, phys
->v_last
, phys
->v_last
);
1506 m3x3_mulv( gate
->transport
, phys
->m
, phys
->m
);
1507 m3x3_mulv( gate
->transport
, phys
->bob
, phys
->bob
);
1509 /* Pre-emptively edit the camera matrices so that the motion vectors
1513 m4x3_invert_affine( gate
->transport
, transport_i
);
1514 m4x3_expand( transport_i
, transport_4
);
1515 m4x4_mul( main_camera
.mtx
.pv
, transport_4
, main_camera
.mtx
.pv
);
1516 m4x4_mul( main_camera
.mtx
.v
, transport_4
, main_camera
.mtx
.v
);
1518 v4f transport_rotation
;
1519 m3x3_q( gate
->transport
, transport_rotation
);
1520 q_mul( transport_rotation
, phys
->rb
.q
, phys
->rb
.q
);
1522 world_routes_activate_gate( i
);
1524 if( !phys
->on_board
)
1526 v3f fwd_dir
= {cosf(player
.angles
[0]),
1528 sinf(player
.angles
[0])};
1529 m3x3_mulv( gate
->transport
, fwd_dir
, fwd_dir
);
1531 player
.angles
[0] = atan2f( fwd_dir
[2], fwd_dir
[0] );
1534 player
.rewind_length
= 0;
1535 player
.rewind_total_length
= 0.0f
;
1536 player
.rewind_incrementer
= 10000;
1537 player_save_frame();
1540 audio_play_oneshot( &audio_gate_pass
, 1.0f
);
1546 rb_update_transform( &phys
->rb
);
1549 VG_STATIC
void player_freecam(void)
1553 float movespeed
= fc_speed
* VG_TIMESTEP_FIXED
;
1554 v3f lookdir
= { 0.0f
, 0.0f
, -1.0f
},
1555 sidedir
= { 1.0f
, 0.0f
, 0.0f
};
1557 m3x3_mulv( main_camera
.transform
, lookdir
, lookdir
);
1558 m3x3_mulv( main_camera
.transform
, sidedir
, sidedir
);
1560 static v3f move_vel
= { 0.0f
, 0.0f
, 0.0f
};
1562 v2f steer
= { player
.input_js1h
->axis
.value
,
1563 player
.input_js1v
->axis
.value
};
1565 v3_muladds( move_vel
, sidedir
, movespeed
*steer
[0], move_vel
);
1566 v3_muladds( move_vel
, lookdir
, -movespeed
*steer
[1], move_vel
);
1568 v3_muls( move_vel
, 0.7f
, move_vel
);
1569 v3_add( move_vel
, player
.camera_pos
, player
.camera_pos
);
1572 VG_STATIC
int kill_player( int argc
, char const *argv
[] )
1578 VG_STATIC
int reset_player( int argc
, char const *argv
[] )
1580 struct player_phys
*phys
= &player
.phys
;
1581 struct respawn_point
*rp
= NULL
, *r
;
1585 for( int i
=0; i
<world
.spawn_count
; i
++ )
1587 r
= &world
.spawns
[i
];
1588 if( !strcmp( r
->name
, argv
[0] ) )
1596 vg_warn( "No spawn named '%s'\n", argv
[0] );
1601 float min_dist
= INFINITY
;
1603 for( int i
=0; i
<world
.spawn_count
; i
++ )
1605 r
= &world
.spawns
[i
];
1606 float d
= v3_dist2( r
->co
, phys
->rb
.co
);
1608 vg_info( "Dist %s : %f\n", r
->name
, d
);
1619 vg_error( "No spawn found\n" );
1620 vg_info( "Player position: %f %f %f\n", player
.phys
.rb
.co
[0],
1621 player
.phys
.rb
.co
[1],
1622 player
.phys
.rb
.co
[2] );
1623 vg_info( "Player velocity: %f %f %f\n", player
.phys
.rb
.v
[0],
1624 player
.phys
.rb
.v
[1],
1625 player
.phys
.rb
.v
[2] );
1627 if( !world
.spawn_count
)
1630 rp
= &world
.spawns
[0];
1636 q_m3x3( rp
->q
, the_long_way
);
1638 v3f delta
= {1.0f
,0.0f
,0.0f
};
1639 m3x3_mulv( the_long_way
, delta
, delta
);
1643 player
.angles
[0] = atan2f( delta
[0], -delta
[2] );
1644 player
.angles
[1] = -asinf( delta
[1] );
1647 v4_copy( rp
->q
, phys
->rb
.q
);
1648 v3_copy( rp
->co
, phys
->rb
.co
);
1649 v3_zero( phys
->rb
.v
);
1651 phys
->vswitch
= 1.0f
;
1652 phys
->slip_last
= 0.0f
;
1655 m3x3_identity( phys
->vr
);
1657 player
.mdl
.shoes
[0] = 1;
1658 player
.mdl
.shoes
[1] = 1;
1660 rb_update_transform( &phys
->rb
);
1662 v3_add( phys
->rb
.up
, phys
->rb
.co
, phys
->cog
);
1663 v3_zero( phys
->cog_v
);
1665 player_save_frame();
1669 VG_STATIC
void reset_player_poll( int argc
, char const *argv
[] )
1673 for( int i
=0; i
<world
.spawn_count
; i
++ )
1675 struct respawn_point
*r
= &world
.spawns
[i
];
1677 console_suggest_score_text( r
->name
, argv
[argc
-1], 0 );
1682 VG_STATIC
void player_physics_gui(void)
1686 vg_uictx
.cursor
[0] = 0;
1687 vg_uictx
.cursor
[1] = vg
.window_y
- 128;
1688 vg_uictx
.cursor
[3] = 14;
1693 snprintf( buf
, 127, "v: %6.3f %6.3f %6.3f\n", player
.phys
.rb
.v
[0],
1694 player
.phys
.rb
.v
[1],
1695 player
.phys
.rb
.v
[2] );
1697 ui_text( vg_uictx
.cursor
, buf
, 1, 0 );
1698 vg_uictx
.cursor
[1] += 14;
1701 snprintf( buf
, 127, "a: %6.3f %6.3f %6.3f (%6.3f)\n", player
.phys
.a
[0],
1704 v3_length(player
.phys
.a
));
1705 ui_text( vg_uictx
.cursor
, buf
, 1, 0 );
1706 vg_uictx
.cursor
[1] += 14;
1708 float normal_acceleration
= v3_dot( player
.phys
.a
, player
.phys
.rb
.up
);
1709 snprintf( buf
, 127, "Normal acceleration: %6.3f\n", normal_acceleration
);
1711 ui_text( vg_uictx
.cursor
, buf
, 1, 0 );
1712 vg_uictx
.cursor
[1] += 14;
1714 snprintf( buf
, 127, "Normal Pressure: %6.3f\n", player
.normal_pressure
);
1715 ui_text( vg_uictx
.cursor
, buf
, 1, 0 );
1716 vg_uictx
.cursor
[1] += 14;
1721 #endif /* PLAYER_PHYSICS_H */