In 2017, I wrote an explanation that I never finished/posted.
Overview
Causing the overbounce bug is simple. Stand still on a flat surface and jump repeatedly. It may take 1 to 10 jumps or so. If the player automatically jumps again when they hit the ground, congratulations you’ve experienced the overbounce bug! It shouldn’t matter where the player is but I usually test in the courtyard of the q3dm1 map.
People have found ways to exploit the overbounce bug by falling from certain locations but that’s outside my area of interest. The key point is no horizontal movement when the player hits the ground.
What went wrong
Player walking and running maintains velocity force when going up and down slopes. The following code is from PM_WalkMove() in code/game/bg_pmove.c.
#define OVERCLIP 1.001f
vel = VectorLength(pm->ps->velocity);
// slide along the ground plane
PM_ClipVelocity(pm->ps->velocity,
pml.groundTrace.plane.normal,
pm->ps->velocity,
OVERCLIP);
// don't decrease velocity when going up or down a slope
VectorNormalize(pm->ps->velocity);
VectorScale(pm->ps->velocity, vel, pm->ps->velocity);
The velocity is clipped against the ground plane. The clipped velocity is then normalized to a unit length of 1 and scaled back to the original velocity’s vector length. This allows the original velocity vector length to be maintained in the clipped direction.
The problem is PM_ClipVelocity() can clip too much and incorrectly change the direction. The falling velocity (0, 0, -270) clipped against an upward facing surface (0, 0, 1) becomes velocity (0, 0, 0.27). The velocity is then normalized to (0, 0, 1) and multiplied by the original velocity vector length (270) resulting in final velocity (0, 0, 270). The player is then launched back into the air.
Let’s have a look at PM_ClipVelocity() in code/game/bg_pmove.c.
void PM_ClipVelocity( vec3_t in, vec3_t normal, vec3_t out,
float overbounce ) {
float backoff;
float change;
int i;
backoff = DotProduct (in, normal);
if ( backoff < 0 ) {
backoff *= overbounce;
} else {
backoff /= overbounce;
}
for ( i=0 ; i<3 ; i++ ) {
change = normal[i]*backoff;
out[i] = in[i] - change;
}
}
(I think there should be more written here? 2017 me didn’t write it.)
Solution
My solution for the problem is to only normalize and rescale the velocity if the clipped velocity length is more than 1 unit. It works unless the player’s falling velocity is over 1000 which I don’t think can happen.
if ( pmove_overbounce || VectorLength(pm->ps->velocity) > 1 ) {
// don't decrease velocity when going up or down a slope
VectorNormalize(pm->ps->velocity);
VectorScale(pm->ps->velocity, vel, pm->ps->velocity);
}
Note: Quake III Arena bg_pmove.c source code is available under the Q3SDK end user license agreement and the GNU General Public License version 2 or later.
2023 tl;rd if clipped velocity VectorLength(pm->ps->velocity)
is less than 1, it may of flipped direction and basically impacted directly into the surface normal and velocity should probably be 0 (which trap_SnapVector() probably does).