I have a 2D game with sprites that come from texture atlases. I'm trying to resolve the sorting of my sprites for rendering.
I want two sprites to be ordered by their order of insertion, or z-order, when they overlap. Otherwise I'd want to sort by their texture index to keep sprites from the same texture together. I use the std::sort function for sorting a vector of queued sprites.
When queueing a sprite I transform its vertices to screen-space vertices on the CPU. The resulting polygon is used in the overlap test, which is based on this post on Stack Overflow about rectangle intersection:
bool Polygon::overlaps(const Polygon &rhs) const
{
return overlapCheck(rhs, *this) && overlapCheck(rhs, rhs);
}
bool Polygon::overlapCheck(const Polygon &rhs, const Polygon ¤t) const
{
bool result = true;
for (int i = 0; i < NUM_VERTICES; ++i)
{
int i2 = (i + 1) % NUM_VERTICES;
glm::vec2 p1 = current.positions[i];
glm::vec2 p2 = current.positions[i2];
glm::vec2 normal(p2.y - p1.y, p1.x - p2.x);
float minA, maxA;
overlapCheckEdges(*this, normal, minA, maxA);
float minB, maxB;
overlapCheckEdges(rhs, normal, minB, maxB);
if ((maxA < minB) || (maxB < minA))
{
result = false;
break;
}
}
return result;
}
void Polygon::overlapCheckEdges(const Polygon &poly, const glm::vec2 &normal, float &min, float &max)
{
bool haveMin = false;
bool haveMax = false;
for (PosIterator it = poly.positions.begin();
it != poly.positions.end(); ++it)
{
float projection = (normal.x * it->x) + (normal.y * it->y);
if (!haveMin || (projection < min))
{
min = projection;
haveMin = true;
}
if (!haveMax || (projection > max))
{
max = projection;
haveMax = true;
}
}
}
My sprite comparison method:
bool Polygon::operator < (const Polygon &rhs) const
{
if (overlaps(rhs))
{
return zOrder < rhs.zOrder;
}
else
{
return textureIndex < rhs.textureIndex;
}
}
The problem, of course, is that something's wrong with the sorting. I render my scene like this:
Transform t;
t.position.x = 33.0f;
t.position.y = 33.0f;
// "wall" and "slug" comes from texture atlas #2
// "altar" comes from texture atlas #1
// Queued sprites are translated to polygons based on given matrix
renderer.queueSprite("wall", projection.getMatrix() * t.getMatrix());
t.position.x = 0.0f;
t.position.y = 0.0f;
renderer.queueSprite("slug", projection.getMatrix() * t.getMatrix());
renderer.queueSprite("altar", projection.getMatrix() * t.getMatrix());
renderer.draw(); // Sprites are sorted then drawn
The sprites have sizes of 32x32 units. This is what I expect to get:

I get this instead:

The pink slug and the white altar are in the wrong order. Oddly enough, if I put the dark wall at (32, 32) instead of (33, 33) so that it's just touching the slug and altar, I get the right order.
I've also noticed that in the comparison method the altar is only ever compared with the wall. In a single sort I see this sequence of comparisons:
slug, wall
slug, wall
altar, wall