Optimizing collision engine bottleneck
Posted
by
Vittorio Romeo
on Game Development
See other posts from Game Development
or by Vittorio Romeo
Published on 2013-06-24T19:55:50Z
Indexed on
2013/06/24
22:32 UTC
Read the original article
Hit count: 643
Foreword: I'm aware that optimizing this bottleneck is not a necessity - the engine is already very fast. I, however, for fun and educational purposes, would love to find a way to make the engine even faster.
I'm creating a general-purpose C++ 2D collision detection/response engine, with an emphasis on flexibility and speed.
Here's a very basic diagram of its architecture:

Basically, the main class is World, which owns (manages memory) of a ResolverBase*, a SpatialBase* and a vector<Body*>.
SpatialBase is a pure virtual class which deals with broad-phase collision detection.
ResolverBase is a pure virtual class which deals with collision resolution.
The bodies communicate to the World::SpatialBase* with SpatialInfo objects, owned by the bodies themselves.
There currenly is one spatial class: Grid : SpatialBase, which is a basic fixed 2D grid. It has it's own info class, GridInfo : SpatialInfo.
Here's how its architecture looks:

The Grid class owns a 2D array of Cell*. The Cell class contains two collection of (not owned) Body*: a vector<Body*> which contains all the bodies that are in the cell, and a map<int, vector<Body*>> which contains all the bodies that are in the cell, divided in groups.
Bodies, in fact, have a groupId int that is used for collision groups.
GridInfo objects also contain non-owning pointers to the cells the body is in.
As I previously said, the engine is based on groups.
Body::getGroups()returns avector<int>of all the groups the body is part of.Body::getGroupsToCheck()returns avector<int>of all the groups the body has to check collision against.
Bodies can occupy more than a single cell. GridInfo always stores non-owning pointers to the occupied cells.
After the bodies move, collision detection happens. We assume that all bodies are axis-aligned bounding boxes.
How broad-phase collision detection works:
Part 1: spatial info update
For each Body body:
- Top-leftmost occupied cell and bottom-rightmost occupied cells are calculated.
- If they differ from the previous cells,
body.gridInfo.cellsis cleared, and filled with all the cells the body occupies (2D for loop from the top-leftmost cell to the bottom-rightmost cell).
bodyis now guaranteed to know what cells it occupies. For a performance boost, it stores a pointer to everymap<int, vector<Body*>>of every cell it occupies where theintis a group ofbody->getGroupsToCheck(). These pointers get stored ingridInfo->queries, which is simply avector<map<int, vector<Body*>>*>.bodyis now guaranteed to have a pointer to everyvector<Body*>of bodies of groups it needs to check collision against. These pointers are stored ingridInfo->queries.
Part 2: actual collision checks
For each Body body:
bodyclears and fills avector<Body*> bodiesToCheck, which contains all the bodies it needs to check against. Duplicates are avoided (bodies can belong to more than one group) by checking ifbodiesToCheckalready contains the body we're trying to add.
const vector<Body*>& GridInfo::getBodiesToCheck()
{
bodiesToCheck.clear();
for(const auto& q : queries) for(const auto& b : *q) if(!contains(bodiesToCheck, b)) bodiesToCheck.push_back(b);
return bodiesToCheck;
}
The GridInfo::getBodiesToCheck() method IS THE BOTTLENECK.
The bodiesToCheck vector must be filled for every body update because bodies could have moved meanwhile. It also needs to prevent duplicate collision checks. The contains function simply checks if the vector already contains a body with std::find.
Collision is checked and resolved for every body in
bodiesToCheck.That's it.
So, I've been trying to optimize this broad-phase collision detection for quite a while now. Every time I try something else than the current architecture/setup, something doesn't go as planned or I make assumption about the simulation that later are proven to be false.
My question is: how can I optimize the broad-phase of my collision engine maintaining the grouped bodies approach?
Is there some kind of magic C++ optimization that can be applied here?
Can the architecture be redesigned in order to allow for more performance?
- Actual implementation: SSVSCollsion
- Body.h, Body.cpp
- World.h, World.cpp
- Grid.h, Grid.cpp
- Cell.h, Cell.cpp
- GridInfo.h, GridInfo.cpp
© Game Development or respective owner