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 * Called when launching into the air to predict and adjust trajectories
20 VG_STATIC
void player_start_air(void)
22 struct player_phys
*phys
= &player
.phys
;
29 float pstep
= VG_TIMESTEP_FIXED
* 10.0f
;
30 float best_velocity_delta
= -9999.9f
;
34 v3_cross( phys
->rb
.up
, phys
->rb
.v
, axis
);
36 player
.land_log_count
= 0;
38 m3x3_identity( phys
->vr
);
40 for( int m
=-3;m
<=12; m
++ )
42 float vmod
= ((float)m
/ 15.0f
)*0.09f
;
45 v3_copy( phys
->rb
.co
, pco
);
46 v3_muls( phys
->rb
.v
, k_bias
, pv
);
49 * Try different 'rotations' of the velocity to find the best possible
50 * landing normal. This conserves magnitude at the expense of slightly
57 q_axis_angle( vr_q
, axis
, vmod
);
60 m3x3_mulv( vr
, pv
, pv
);
61 v3_muladds( pco
, pv
, pstep
, pco
);
63 for( int i
=0; i
<50; i
++ )
66 apply_gravity( pv
, pstep
);
68 m3x3_mulv( vr
, pv
, pv
);
69 v3_muladds( pco
, pv
, pstep
, pco
);
74 v3_sub( pco
, pco1
, vdir
);
75 contact
.dist
= v3_length( vdir
);
76 v3_divs( vdir
, contact
.dist
, vdir
);
78 if( ray_world( pco1
, vdir
, &contact
))
80 float land_delta
= v3_dot( pv
, contact
.normal
);
81 u32 scolour
= (u8
)(vg_minf(-land_delta
* 2.0f
, 255.0f
));
83 /* Bias prediction towords ramps */
84 if( ray_hit_material( &contact
)->info
.flags
85 & k_material_flag_skate_surface
)
88 scolour
|= 0x0000a000;
91 if( (land_delta
< 0.0f
) && (land_delta
> best_velocity_delta
) )
93 best_velocity_delta
= land_delta
;
95 v3_copy( contact
.pos
, player
.land_target
);
97 m3x3_copy( vr
, phys
->vr_pstep
);
98 q_axis_angle( vr_q
, axis
, vmod
*0.1f
);
99 q_m3x3( vr_q
, phys
->vr
);
102 v3_copy( contact
.pos
,
103 player
.land_target_log
[player
.land_log_count
] );
104 player
.land_target_colours
[player
.land_log_count
] =
105 0xff000000 | scolour
;
107 player
.land_log_count
++;
116 * Main friction interface model
118 VG_STATIC
void player_physics_control(void)
120 struct player_phys
*phys
= &player
.phys
;
123 * Computing localized friction forces for controlling the character
124 * Friction across X is significantly more than Z
128 m3x3_mulv( phys
->rb
.to_local
, phys
->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 phys
->reverse
= -vg_signf(vel
[2]);
139 float substep
= VG_TIMESTEP_FIXED
* 0.2f
;
140 float fwd_resistance
= k_friction_resistance
;
142 for( int i
=0; i
<5; i
++ )
144 vel
[2] = stable_force( vel
[2],vg_signf(vel
[2]) * -fwd_resistance
*substep
);
145 vel
[0] = stable_force( vel
[0],vg_signf(vel
[0]) * -k_friction_lat
*substep
);
148 if( player
.input_jump
->button
.value
)
150 phys
->jump
+= VG_TIMESTEP_FIXED
* k_jump_charge_speed
;
152 if( !phys
->jump_charge
)
153 phys
->jump_dir
= phys
->reverse
> 0.0f
? 1: 0;
155 phys
->jump_charge
= 1;
158 static int push_thresh_last
= 0;
159 float push
= player
.input_push
->button
.value
;
160 int push_thresh
= push
>0.15f
? 1: 0;
162 if( push_thresh
&& !push_thresh_last
)
163 player
.phys
.start_push
= vg
.time
;
165 push_thresh_last
= push_thresh
;
167 if( !player
.input_jump
->button
.value
&& push_thresh
)
169 player
.phys
.pushing
= 1.0f
;
170 player
.phys
.push_time
= vg
.time
- player
.phys
.start_push
;
172 float cycle_time
= player
.phys
.push_time
*k_push_cycle_rate
,
173 amt
= k_push_accel
* (sinf(cycle_time
)*0.5f
+0.5f
)*VG_TIMESTEP_FIXED
,
174 current
= v3_length( vel
),
175 new_vel
= vg_minf( current
+ amt
, k_max_push_speed
);
177 new_vel
-= vg_minf(current
, k_max_push_speed
);
178 vel
[2] -= new_vel
* phys
->reverse
;
181 m3x3_mulv( phys
->rb
.to_world
, vel
, phys
->rb
.v
);
183 float input
= player
.input_js1h
->axis
.value
,
184 grab
= player
.input_grab
->axis
.value
,
185 steer
= input
* (1.0f
-(phys
->jump
+grab
)*0.4f
),
186 steer_scaled
= vg_signf(steer
) * powf(steer
,2.0f
) * k_steer_ground
;
188 phys
->iY
-= steer_scaled
* VG_TIMESTEP_FIXED
;
192 * Air control, no real physics
194 VG_STATIC
void player_physics_control_air(void)
196 struct player_phys
*phys
= &player
.phys
;
198 m3x3_mulv( phys
->vr
, phys
->rb
.v
, phys
->rb
.v
);
199 vg_line_cross( player
.land_target
, 0xff0000ff, 0.25f
);
206 float pstep
= VG_TIMESTEP_FIXED
* 10.0f
;
209 v3_copy( phys
->rb
.co
, pco
);
210 v3_copy( phys
->rb
.v
, pv
);
212 float time_to_impact
= 0.0f
;
213 float limiter
= 1.0f
;
215 for( int i
=0; i
<50; i
++ )
217 v3_copy( pco
, pco1
);
218 m3x3_mulv( phys
->vr_pstep
, pv
, pv
);
219 apply_gravity( pv
, pstep
);
220 v3_muladds( pco
, pv
, pstep
, pco
);
225 v3_sub( pco
, pco1
, vdir
);
226 contact
.dist
= v3_length( vdir
);
227 v3_divs( vdir
, contact
.dist
, vdir
);
229 float orig_dist
= contact
.dist
;
230 if( ray_world( pco1
, vdir
, &contact
))
232 float angle
= v3_dot( phys
->rb
.up
, contact
.normal
);
234 v3_cross( phys
->rb
.up
, contact
.normal
, axis
);
236 time_to_impact
+= (contact
.dist
/orig_dist
)*pstep
;
237 limiter
= vg_minf( 5.0f
, time_to_impact
)/5.0f
;
238 limiter
= 1.0f
-limiter
;
240 limiter
= 1.0f
-limiter
;
245 q_axis_angle( correction
, axis
,
246 acosf(angle
)*(1.0f
-limiter
)*3.0f
*VG_TIMESTEP_FIXED
);
247 q_mul( correction
, phys
->rb
.q
, phys
->rb
.q
);
250 vg_line_cross( contact
.pos
, 0xffff0000, 0.25f
);
253 time_to_impact
+= pstep
;
256 v2f steer
= { player
.input_js1h
->axis
.value
,
257 player
.input_js1v
->axis
.value
};
259 float l2
= v2_length2( steer
);
261 v2_muls( steer
, 1.0f
/sqrtf(l2
), steer
);
263 phys
->iY
-= steer
[0] * k_steer_air
* VG_TIMESTEP_FIXED
;
265 float iX
= steer
[1] *
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("grabh"), vg_get_axis("grabv") },
278 phys
->grab
, target
);
282 VG_STATIC
void player_walk_update_collision(void)
284 struct player_phys
*phys
= &player
.phys
;
288 rigidbody
*rbf
= &player
.collide_front
,
289 *rbb
= &player
.collide_back
;
291 v3_add( phys
->rb
.co
, (v3f
){0.0f
,h0
,0.0f
}, rbf
->co
);
292 v3_add( phys
->rb
.co
, (v3f
){0.0f
,h1
,0.0f
}, rbb
->co
);
293 v3_copy( rbf
->co
, rbf
->to_world
[3] );
294 v3_copy( rbb
->co
, rbb
->to_world
[3] );
295 m4x3_invert_affine( rbf
->to_world
, rbf
->to_local
);
296 m4x3_invert_affine( rbb
->to_world
, rbb
->to_local
);
298 rb_update_bounds( rbf
);
299 rb_update_bounds( rbb
);
302 VG_STATIC
void player_integrate(void);
304 * Entire Walking physics model
305 * TODO: sleep when under certain velotiy
307 VG_STATIC
void player_walk_physics(void)
309 struct player_phys
*phys
= &player
.phys
;
310 rigidbody
*rbf
= &player
.collide_front
,
311 *rbb
= &player
.collide_back
;
313 m3x3_identity( player
.collide_front
.to_world
);
314 m3x3_identity( player
.collide_back
.to_world
);
316 v3_zero( phys
->rb
.w
);
317 q_axis_angle( phys
->rb
.q
, (v3f
){0.0f
,1.0f
,0.0f
}, -player
.angles
[0] );
322 v3f forward_dir
= { sinf(player
.angles
[0]),0.0f
,-cosf(player
.angles
[0]) };
323 v3f right_dir
= { -forward_dir
[2], 0.0f
, forward_dir
[0] };
325 v2f walk
= { player
.input_walkh
->axis
.value
,
326 player
.input_walkv
->axis
.value
};
328 if( v2_length2(walk
) > 0.001f
)
329 v2_normalize_clamp( walk
);
333 player_walk_update_collision();
334 rb_debug( rbf
, 0xff0000ff );
335 rb_debug( rbb
, 0xff0000ff );
337 /* allow player to accelerate a bit */
339 v3_muls( forward_dir
, walk
[1], walk_3d
);
340 v3_muladds( walk_3d
, right_dir
, walk
[0], walk_3d
);
342 float current_vel
= fabsf(v3_dot( walk_3d
, phys
->rb
.v
)),
343 new_vel
= current_vel
+ VG_TIMESTEP_FIXED
*k_air_accelerate
,
344 clamped_new
= vg_clampf( new_vel
, 0.0f
, k_walkspeed
),
345 vel_diff
= vg_maxf( 0.0f
, clamped_new
- current_vel
);
347 v3_muladds( phys
->rb
.v
, right_dir
, walk
[0] * vel_diff
, phys
->rb
.v
);
348 v3_muladds( phys
->rb
.v
, forward_dir
, walk
[1] * vel_diff
, phys
->rb
.v
);
352 len
+= rb_sphere_scene( rbf
, &world
.rb_geo
, manifold
+len
);
353 len
+= rb_sphere_scene( rbb
, &world
.rb_geo
, manifold
+len
);
354 rb_presolve_contacts( manifold
, len
);
356 for( int i
=0; i
<len
; i
++ )
358 struct contact
*ct
= &manifold
[i
];
359 if( v3_dot( ct
->n
, (v3f
){0.0f
,1.0f
,0.0f
} ) > 0.5f
)
363 for( int j
=0; j
<5; j
++ )
365 for( int i
=0; i
<len
; i
++ )
367 struct contact
*ct
= &manifold
[i
];
370 float vn
= -v3_dot( phys
->rb
.v
, ct
->n
);
373 float temp
= ct
->norm_impulse
;
374 ct
->norm_impulse
= vg_maxf( temp
+ vn
, 0.0f
);
375 vn
= ct
->norm_impulse
- temp
;
378 v3_muls( ct
->n
, vn
, impulse
);
380 v3_add( impulse
, phys
->rb
.v
, phys
->rb
.v
);
383 for( int j
=0; j
<2; j
++ )
385 float f
= k_friction
* ct
->norm_impulse
,
386 vt
= v3_dot( phys
->rb
.v
, ct
->t
[j
] ),
389 float temp
= ct
->tangent_impulse
[j
];
390 ct
->tangent_impulse
[j
] = vg_clampf( temp
+ lambda
, -f
, f
);
391 lambda
= ct
->tangent_impulse
[j
] - temp
;
393 v3_muladds( phys
->rb
.v
, ct
->t
[j
], lambda
, phys
->rb
.v
);
402 player
.walk
= v2_length( walk
);
404 if( player
.input_walk
->button
.value
)
405 v2_muls( walk
, 0.5f
, walk
);
407 v2_muls( walk
, k_walkspeed
* VG_TIMESTEP_FIXED
, walk
);
410 v3_zero( walk_apply
);
412 /* Do XY translation */
413 v3_muladds( walk_apply
, right_dir
, walk
[0], walk_apply
);
414 v3_muladds( walk_apply
, forward_dir
, walk
[1], walk_apply
);
415 v3_add( walk_apply
, phys
->rb
.co
, phys
->rb
.co
);
416 v3_divs( walk_apply
, VG_TIMESTEP_FIXED
, phys
->rb
.v
);
418 /* Directly resolve collisions */
419 player_walk_update_collision();
420 rb_debug( rbf
, 0xffffff00 );
421 rb_debug( rbb
, 0xffffff00 );
424 len
+= rb_sphere_scene( rbf
, &world
.rb_geo
, manifold
+len
);
425 len
+= rb_sphere_scene( rbb
, &world
.rb_geo
, manifold
+len
);
429 for( int j
=0; j
<3; j
++ )
431 for( int i
=0; i
<len
; i
++ )
433 struct contact
*ct
= &manifold
[i
];
435 float p
= vg_maxf( 0.0f
, ct
->p
- 0.00f
),
436 cur
= vg_clampf( v3_dot( ct
->n
, dt
), 0.0f
, p
);
437 v3_muladds( dt
, ct
->n
, (p
- cur
) * 0.333333333f
, dt
);
440 v3_add( dt
, phys
->rb
.co
, phys
->rb
.co
);
444 struct world_material
*surface_mat
= world_contact_material(manifold
);
445 player
.surface_prop
= surface_mat
->info
.surface_prop
;
449 if( player
.input_jump
->button
.value
)
451 phys
->rb
.v
[1] = 5.0f
;
456 /* if we've put us in the air, step down slowly */
458 float max_dist
= 0.3f
,
459 start_y
= phys
->rb
.co
[1];
461 for( int j
=0; j
<8; j
++ )
463 for( int i
=0; i
<len
; i
++ )
465 struct contact
*ct
= &manifold
[i
];
466 if( v3_dot( ct
->n
, (v3f
){0.0f
,1.0f
,0.0f
} ) > 0.5f
)
474 for( int j
=0; j
<3; j
++ )
476 for( int i
=0; i
<len
; i
++ )
478 struct contact
*ct
= &manifold
[i
];
480 float p
= vg_maxf( 0.0f
, ct
->p
- 0.0025f
),
481 cur
= vg_clampf( v3_dot( ct
->n
, dt
), 0.0f
, p
);
482 v3_muladds( dt
, ct
->n
, (p
- cur
) * 0.333333333f
, dt
);
485 v3_add( dt
, phys
->rb
.co
, phys
->rb
.co
);
490 phys
->rb
.co
[1] -= max_dist
* 0.125f
;
492 player_walk_update_collision();
494 len
+= rb_sphere_scene( rbf
, &world
.rb_geo
, manifold
+len
);
495 len
+= rb_sphere_scene( rbb
, &world
.rb_geo
, manifold
+len
);
498 /* Transitioning into air mode */
499 phys
->rb
.co
[1] = start_y
;
503 VG_STATIC
void player_grind(void)
505 struct player_phys
*phys
= &player
.phys
;
508 int idx
= bh_closest_point( world
.grind_bh
, phys
->rb
.co
, closest
, INFINITY
);
512 struct grind_edge
*edge
= &world
.grind_edges
[ idx
];
514 vg_line( phys
->rb
.co
, closest
, 0xff000000 );
515 vg_line_cross( closest
, 0xff000000, 0.3f
);
516 vg_line( edge
->p0
, edge
->p1
, 0xff000000 );
520 idx
= bh_closest_point( world
.geo_bh
, phys
->rb
.co
, closest
, INFINITY
);
521 vg_line( phys
->rb
.co
, closest
, 0xff000000 );
522 vg_line_cross( closest
, 0xff000000, 0.3f
);
524 idx
= world
.scene_geo
->arrindices
[ idx
* 3 ];
525 struct world_material
*mat
= world_tri_index_material( idx
);
527 if( mat
->info
.flags
& k_material_flag_grind_surface
)
530 v3_sub( closest
, phys
->rb
.co
, grind_delta
);
532 float p
= v3_dot( phys
->rb
.forward
, grind_delta
);
533 v3_muladds( grind_delta
, phys
->rb
.forward
, -p
, grind_delta
);
535 float a
= vg_maxf( 0.0f
, 4.0f
-v3_dist2( closest
, phys
->rb
.co
) );
536 v3_muladds( phys
->rb
.v
, grind_delta
, a
*0.2f
, phys
->rb
.v
);
541 * Physics collision detection, and control
543 VG_STATIC
void player_physics(void)
545 struct player_phys
*phys
= &player
.phys
;
547 * Update collision fronts
550 rigidbody
*rbf
= &player
.collide_front
,
551 *rbb
= &player
.collide_back
;
553 m3x3_copy( phys
->rb
.to_world
, player
.collide_front
.to_world
);
554 m3x3_copy( phys
->rb
.to_world
, player
.collide_back
.to_world
);
556 player
.air_blend
= vg_lerpf( player
.air_blend
, phys
->in_air
, 0.1f
);
557 float h
= player
.air_blend
*0.2f
;
559 m4x3_mulv( phys
->rb
.to_world
, (v3f
){0.0f
,h
,-k_board_length
}, rbf
->co
);
560 v3_copy( rbf
->co
, rbf
->to_world
[3] );
561 m4x3_mulv( phys
->rb
.to_world
, (v3f
){0.0f
,h
, k_board_length
}, rbb
->co
);
562 v3_copy( rbb
->co
, rbb
->to_world
[3] );
564 m4x3_invert_affine( rbf
->to_world
, rbf
->to_local
);
565 m4x3_invert_affine( rbb
->to_world
, rbb
->to_local
);
567 rb_update_bounds( rbf
);
568 rb_update_bounds( rbb
);
570 rb_debug( rbf
, 0xff00ffff );
571 rb_debug( rbb
, 0xffffff00 );
577 len_f
= rb_sphere_scene( rbf
, &world
.rb_geo
, manifold
);
578 rb_manifold_filter_coplanar( manifold
, len_f
, 0.05f
);
581 rb_manifold_filter_backface( manifold
, len_f
);
582 rb_manifold_filter_joint_edges( manifold
, len_f
, 0.05f
);
583 rb_manifold_filter_pairs( manifold
, len_f
, 0.05f
);
585 len_f
= rb_manifold_apply_filtered( manifold
, len_f
);
587 rb_ct
*man_b
= &manifold
[len_f
];
588 len_b
= rb_sphere_scene( rbb
, &world
.rb_geo
, man_b
);
589 rb_manifold_filter_coplanar( man_b
, len_b
, 0.05f
);
592 rb_manifold_filter_backface( man_b
, len_b
);
593 rb_manifold_filter_joint_edges( man_b
, len_b
, 0.05f
);
594 rb_manifold_filter_pairs( man_b
, len_b
, 0.05f
);
596 len_b
= rb_manifold_apply_filtered( man_b
, len_b
);
598 int len
= len_f
+len_b
;
603 v3_sub( phys
->rb
.co
, (v3f
){2.0f
,2.0f
,2.0f
}, bax
[0] );
604 v3_add( phys
->rb
.co
, (v3f
){2.0f
,2.0f
,2.0f
}, bax
[1] );
607 * Preprocess collision points, and create a surface picture.
608 * we want contacts that are within our 'capsule's internal line to be
609 * clamped so that they face the line and do not oppose, to stop the
610 * player hanging up on stuff
612 for( int i
=0; i
<len
; i
++ )
615 v3_sub( manifold
[i
].co
, rbf
->co
, dfront
);
616 v3_sub( manifold
[i
].co
, rbb
->co
, dback
);
618 if( (v3_dot( dfront
, phys
->rb
.forward
) < -0.02f
) &&
619 (v3_dot( dback
, phys
->rb
.forward
) > 0.02f
))
621 float p
= v3_dot( manifold
[i
].n
, phys
->rb
.forward
);
622 v3_muladds( manifold
[i
].n
, phys
->rb
.forward
, -p
, manifold
[i
].n
);
623 v3_normalize( manifold
[i
].n
);
627 rb_presolve_contacts( manifold
, len
);
628 v3f surface_avg
= {0.0f
, 0.0f
, 0.0f
};
636 for( int i
=0; i
<len
; i
++ )
638 v3_add( manifold
[i
].n
, surface_avg
, surface_avg
);
641 v3_normalize( surface_avg
);
643 if( v3_dot( phys
->rb
.v
, surface_avg
) > 0.5f
)
653 for( int j
=0; j
<5; j
++ )
655 for( int i
=0; i
<len
; i
++ )
657 struct contact
*ct
= &manifold
[i
];
660 v3_sub( ct
->co
, phys
->rb
.co
, delta
);
661 v3_cross( phys
->rb
.w
, delta
, dv
);
662 v3_add( phys
->rb
.v
, dv
, dv
);
664 float vn
= -v3_dot( dv
, ct
->n
);
667 float temp
= ct
->norm_impulse
;
668 ct
->norm_impulse
= vg_maxf( temp
+ vn
, 0.0f
);
669 vn
= ct
->norm_impulse
- temp
;
672 v3_muls( ct
->n
, vn
, impulse
);
674 if( fabsf(v3_dot( impulse
, phys
->rb
.forward
)) > 10.0f
||
675 fabsf(v3_dot( impulse
, phys
->rb
.up
)) > 50.0f
)
681 v3_add( impulse
, phys
->rb
.v
, phys
->rb
.v
);
682 v3_cross( delta
, impulse
, impulse
);
685 * W Impulses are limited to the Y and X axises, we don't really want
686 * roll angular velocities being included.
688 * Can also tweak the resistance of each axis here by scaling the wx,wy
692 float wy
= v3_dot( phys
->rb
.up
, impulse
),
693 wx
= v3_dot( phys
->rb
.right
, impulse
)*1.5f
;
695 v3_muladds( phys
->rb
.w
, phys
->rb
.up
, wy
, phys
->rb
.w
);
696 v3_muladds( phys
->rb
.w
, phys
->rb
.right
, wx
, phys
->rb
.w
);
700 float grabt
= player
.input_grab
->axis
.value
;
704 v2_muladds( phys
->grab_mouse_delta
, vg
.mouse_delta
, 0.02f
,
705 phys
->grab_mouse_delta
);
706 v2_normalize_clamp( phys
->grab_mouse_delta
);
709 v2_zero( phys
->grab_mouse_delta
);
711 phys
->grab
= vg_lerpf( phys
->grab
, grabt
, 0.14f
);
712 player
.phys
.pushing
= 0.0f
;
718 float d
= v3_dot( phys
->rb
.forward
, surface_avg
);
719 v3_muladds( surface_avg
, phys
->rb
.forward
, -d
, projected
);
720 v3_normalize( projected
);
722 float angle
= v3_dot( phys
->rb
.up
, projected
);
723 v3_cross( phys
->rb
.up
, projected
, axis
);
726 v3_add( phys
->rb
.co
, projected
, p0
);
727 v3_add( phys
->rb
.co
, phys
->rb
.up
, p1
);
728 vg_line( phys
->rb
.co
, p0
, 0xff00ff00 );
729 vg_line( phys
->rb
.co
, p1
, 0xff000fff );
731 if( fabsf(angle
) < 0.999f
)
734 q_axis_angle( correction
, axis
, acosf(angle
)*4.0f
*VG_TIMESTEP_FIXED
);
735 q_mul( correction
, phys
->rb
.q
, phys
->rb
.q
);
738 float const DOWNFORCE
= -k_downforce
*VG_TIMESTEP_FIXED
;
739 v3_muladds( phys
->rb
.v
, phys
->rb
.up
, DOWNFORCE
, phys
->rb
.v
);
741 player_physics_control();
743 if( !phys
->jump_charge
&& phys
->jump
> 0.2f
)
747 /* Launch more up if alignment is up else improve velocity */
748 float aup
= fabsf(v3_dot( (v3f
){0.0f
,1.0f
,0.0f
}, phys
->rb
.up
)),
750 dir
= mod
+ aup
*(1.0f
-mod
);
752 v3_copy( phys
->rb
.v
, jumpdir
);
753 v3_normalize( jumpdir
);
754 v3_muls( jumpdir
, 1.0f
-dir
, jumpdir
);
755 v3_muladds( jumpdir
, phys
->rb
.up
, dir
, jumpdir
);
756 v3_normalize( jumpdir
);
758 float force
= k_jump_force
*phys
->jump
;
759 v3_muladds( phys
->rb
.v
, jumpdir
, force
, phys
->rb
.v
);
762 player
.jump_time
= vg
.time
;
764 /* TODO: Move to audio file */
766 audio_player_set_flags( &audio_player_extra
, AUDIO_FLAG_SPACIAL_3D
);
767 audio_player_set_position( &audio_player_extra
, phys
->rb
.co
);
768 audio_player_set_vol( &audio_player_extra
, 20.0f
);
769 audio_player_playclip( &audio_player_extra
, &audio_jumps
[rand()%2] );
775 player_physics_control_air();
778 if( !phys
->jump_charge
)
780 phys
->jump
-= k_jump_charge_speed
* VG_TIMESTEP_FIXED
;
783 phys
->jump_charge
= 0;
784 phys
->jump
= vg_clampf( phys
->jump
, 0.0f
, 1.0f
);
787 VG_STATIC
void player_save_frame(void)
789 player
.phys_gate_frame
= player
.phys
;
792 VG_STATIC
void player_restore_frame(void)
794 player
.phys
= player
.phys_gate_frame
;
795 rb_update_transform( &player
.phys
.rb
);
798 VG_STATIC
void player_integrate(void)
800 struct player_phys
*phys
= &player
.phys
;
801 apply_gravity( phys
->rb
.v
, VG_TIMESTEP_FIXED
);
802 v3_muladds( phys
->rb
.co
, phys
->rb
.v
, VG_TIMESTEP_FIXED
, phys
->rb
.co
);
805 VG_STATIC
void player_do_motion(void)
807 struct player_phys
*phys
= &player
.phys
;
809 if( world
.water
.enabled
)
811 if( (phys
->rb
.co
[1] < 0.0f
) && !player
.is_dead
)
814 audio_player_set_flags( &audio_player_extra
, AUDIO_FLAG_SPACIAL_3D
);
815 audio_player_set_position( &audio_player_extra
, phys
->rb
.co
);
816 audio_player_set_vol( &audio_player_extra
, 20.0f
);
817 audio_player_playclip( &audio_player_extra
, &audio_splash
);
826 v3_copy( phys
->rb
.co
, prevco
);
834 player_walk_physics();
837 /* Real angular velocity integration */
838 v3_lerp( phys
->rb
.w
, (v3f
){0.0f
,0.0f
,0.0f
}, 0.125f
, phys
->rb
.w
);
839 if( v3_length2( phys
->rb
.w
) > 0.0f
)
843 v3_copy( phys
->rb
.w
, axis
);
845 float mag
= v3_length( axis
);
846 v3_divs( axis
, mag
, axis
);
847 q_axis_angle( rotation
, axis
, mag
*k_rb_delta
);
848 q_mul( rotation
, phys
->rb
.q
, phys
->rb
.q
);
851 /* Faux angular velocity */
854 float lerpq
= phys
->in_air
? 0.04f
: 0.3f
;
855 phys
->siY
= vg_lerpf( phys
->siY
, phys
->iY
, lerpq
);
857 q_axis_angle( rotate
, phys
->rb
.up
, phys
->siY
);
858 q_mul( rotate
, phys
->rb
.q
, phys
->rb
.q
);
862 * Gate intersection, by tracing a line over the gate planes
864 for( int i
=0; i
<world
.gate_count
; i
++ )
866 struct route_gate
*rg
= &world
.gates
[i
];
867 teleport_gate
*gate
= &rg
->gate
;
869 if( gate_intersect( gate
, phys
->rb
.co
, prevco
) )
871 m4x3_mulv( gate
->transport
, phys
->rb
.co
, phys
->rb
.co
);
872 m3x3_mulv( gate
->transport
, phys
->rb
.v
, phys
->rb
.v
);
873 m3x3_mulv( gate
->transport
, phys
->vl
, phys
->vl
);
874 m3x3_mulv( gate
->transport
, phys
->v_last
, phys
->v_last
);
875 m3x3_mulv( gate
->transport
, phys
->m
, phys
->m
);
876 m3x3_mulv( gate
->transport
, phys
->bob
, phys
->bob
);
878 v4f transport_rotation
;
879 m3x3_q( gate
->transport
, transport_rotation
);
880 q_mul( transport_rotation
, phys
->rb
.q
, phys
->rb
.q
);
882 world_routes_activate_gate( i
);
884 if( !phys
->on_board
)
886 v3f fwd_dir
= {cosf(player
.angles
[0]),
888 sinf(player
.angles
[0])};
889 m3x3_mulv( gate
->transport
, fwd_dir
, fwd_dir
);
891 player
.angles
[0] = atan2f( fwd_dir
[2], fwd_dir
[0] );
894 player
.rewind_length
= 0;
895 player
.rewind_total_length
= 0.0f
;
896 player
.rewind_incrementer
= 10000;
900 audio_play_oneshot( &audio_gate_pass
, 1.0f
);
906 rb_update_transform( &phys
->rb
);
909 VG_STATIC
void player_freecam(void)
913 float movespeed
= fc_speed
;
914 v3f lookdir
= { 0.0f
, 0.0f
, -1.0f
},
915 sidedir
= { 1.0f
, 0.0f
, 0.0f
};
917 m3x3_mulv( camera_mtx
, lookdir
, lookdir
);
918 m3x3_mulv( camera_mtx
, sidedir
, sidedir
);
920 static v3f move_vel
= { 0.0f
, 0.0f
, 0.0f
};
924 if( vg_get_button( "forward" ) )
925 v3_muladds( move_vel
, lookdir
, VG_TIMESTEP_FIXED
* movespeed
, move_vel
);
926 if( vg_get_button( "back" ) )
927 v3_muladds( move_vel
, lookdir
, VG_TIMESTEP_FIXED
*-movespeed
, move_vel
);
928 if( vg_get_button( "left" ) )
929 v3_muladds( move_vel
, sidedir
, VG_TIMESTEP_FIXED
*-movespeed
, move_vel
);
930 if( vg_get_button( "right" ) )
931 v3_muladds( move_vel
, sidedir
, VG_TIMESTEP_FIXED
* movespeed
, move_vel
);
934 v3_muls( move_vel
, 0.7f
, move_vel
);
935 v3_add( move_vel
, player
.camera_pos
, player
.camera_pos
);
938 VG_STATIC
int reset_player( int argc
, char const *argv
[] )
940 struct player_phys
*phys
= &player
.phys
;
941 struct respawn_point
*rp
= NULL
, *r
;
945 for( int i
=0; i
<world
.spawn_count
; i
++ )
947 r
= &world
.spawns
[i
];
948 if( !strcmp( r
->name
, argv
[0] ) )
956 vg_warn( "No spawn named '%s'\n", argv
[0] );
961 float min_dist
= INFINITY
;
963 for( int i
=0; i
<world
.spawn_count
; i
++ )
965 r
= &world
.spawns
[i
];
966 float d
= v3_dist2( r
->co
, phys
->rb
.co
);
968 vg_info( "Dist %s : %f\n", r
->name
, d
);
979 vg_error( "No spawn found\n" );
980 if( !world
.spawn_count
)
983 rp
= &world
.spawns
[0];
989 q_m3x3( rp
->q
, the_long_way
);
991 v3f delta
= {1.0f
,0.0f
,0.0f
};
992 m3x3_mulv( the_long_way
, delta
, delta
);
994 player
.angles
[0] = atan2f( delta
[0], -delta
[2] );
995 player
.angles
[1] = -asinf( delta
[1] );
998 v4_copy( rp
->q
, phys
->rb
.q
);
999 v3_copy( rp
->co
, phys
->rb
.co
);
1000 v3_zero( phys
->rb
.v
);
1002 phys
->vswitch
= 1.0f
;
1003 phys
->slip_last
= 0.0f
;
1006 m3x3_identity( phys
->vr
);
1008 player
.mdl
.shoes
[0] = 1;
1009 player
.mdl
.shoes
[1] = 1;
1011 rb_update_transform( &phys
->rb
);
1012 player_save_frame();
1016 #endif /* PLAYER_PHYSICS_H */