Hello everybody.
I have implemented a 2D raycasting algorithm in my game engine but I must be doing something very wrong, since my game is pretty much unplayable if I cast around 10 rays each frame (it runs fine at around 5-6 casts per frame). I tried profiling but, as you can see from the two pictures, I ended up on STL territory, which tells me that the performance issues arise from my approach to the problem.
I'm using the Sweep-and-Prune algorithm for my broadphase detection, which divides my entire map into sections, and colliders inside a section are only checked for collisions with other colliders from that section. This approach is used in my raycasting algorithm as well. Initially, I get a list of all colliders inside the raycast's radius and then I check the ray against every collider in that list. As far as I understand it the problem lies not in the Ray vs. Shape checks, but in the preparation work before that. Any tips or guidelines on how to correct my approach, or specifics of the algorithms, are most welcome.
Thank you in advance.
Raycast
bool MCollisionEngine::Raycast(const glm::vec2& Origin, const glm::vec2& Direction, float MaxDistance, std::string LayerMask, bool bHitTriggers) const
{
std::vector<PCollider*> PossibleColliders = GetCollidersInRadius(Origin, MaxDistance, LayerMask, bHitTriggers);
const std::bitset<32> CollisionMask = std::bitset<32>(LayerMask);
for (PCollider* const Collider : PossibleColliders)
{
const PCollisionLayer& ColliderLayer = Layers.at(Collider->LayerKey);
if (CollisionMask.test(ColliderLayer.Index))
{
SShape* Shape = Collider->Shape.get();
switch (Shape->GetType())
{
case EShapeType::AABB:
if (RayIntersectsAABB(Origin, Direction, MaxDistance, static_cast<SAABB*>(Shape)))
{
return true;
}
break;
case EShapeType::Circle:
if (RayIntersectsCircle(Origin, Direction, MaxDistance, static_cast<SCircle*>(Shape)))
{
return true;
}
break;
case EShapeType::OBB:
if (RayIntersectsOBB(Origin, Direction, MaxDistance, static_cast<SOBB*>(Shape)))
{
return true;
}
break;
}
}
}
return false;
}
BroadCircleToOBB
bool MCollisionEngine::BroadCircleToOBB(const SCircle& Circle, const SOBB* const OBB)
{
const std::vector<glm::vec2>& Normals = OBB->GetNormals();
for (int i = 0; i < 2; i++)
{
const glm::vec2& Normal = Normals[i];
Line CircleProj = Circle.ProjectInto(Normal);
Line OBBProj = OBB->ProjectInto(Normal);
if (glm::length(CircleProj.CalculatePenetrationVector(OBBProj)) < Utils::EPSILON)
{
return false;
}
}
return true;
}
SOBB::GetSupportPoint
glm::vec2 SOBB::GetSupportPoint(const glm::vec2& Direction) const
{
const std::vector<glm::vec2>& Vertices = GetVertices();
float BestProjection = -std::numeric_limits<float>::max();
glm::vec2 BestVertex;
for (const glm::vec2& Vertex : Vertices)
{
float Projection = glm::dot(Vertex, Direction);
if (Projection > BestProjection)
{
BestProjection = Projection;
BestVertex = Vertex;
}
}
return BestVertex + Position;
}