void CCollision::MoveBox(vec2 *pInoutPos, vec2 *pInoutVel, vec2 Size, vec2 Elasticity, bool *pGrounded) const
{
// do the move
vec2 Pos = *pInoutPos;
vec2 Vel = *pInoutVel;
float Distance = length(Vel);
int Max = (int)Distance;
if(Distance > 0.00001f)
{
float Fraction = 1.0f / (float)(Max + 1);
float ElasticityX = clamp(Elasticity.x, -1.0f, 1.0f);
float ElasticityY = clamp(Elasticity.y, -1.0f, 1.0f);
for(int i = 0; i <= Max; i++)
{
// Early break as optimization to stop checking for collisions for
// large distances after the obstacles we have already hit reduced
// our speed to exactly 0.
if(Vel == vec2(0, 0))
{
break;
}
vec2 NewPos = Pos + Vel * Fraction; // TODO: this row is not nice
// Fraction can be very small and thus the calculation has no effect, no
// reason to continue calculating.
if(NewPos == Pos)
{
break;
}
if(TestBox(vec2(NewPos.x, NewPos.y), Size))
{
int Hits = 0;
if(TestBox(vec2(Pos.x, NewPos.y), Size))
{
if(pGrounded && ElasticityY > 0 && Vel.y > 0)
*pGrounded = true;
NewPos.y = Pos.y;
Vel.y *= -ElasticityY;
Hits++;
}
if(TestBox(vec2(NewPos.x, Pos.y), Size))
{
NewPos.x = Pos.x;
Vel.x *= -ElasticityX;
Hits++;
}
// neither of the tests got a collision.
// this is a real _corner case_!
if(Hits == 0)
{
if(pGrounded && ElasticityY > 0 && Vel.y > 0)
*pGrounded = true;
NewPos.y = Pos.y;
Vel.y *= -ElasticityY;
NewPos.x = Pos.x;
Vel.x *= -ElasticityX;
}
}
Pos = NewPos;
}
}
*pInoutPos = Pos;
*pInoutVel = Vel;
}