Ordering occurs via a object.zindex variable. But table sorting being a heavy operation I told myself: "Hey, why not remove every object that is not in my scene before sorting the queue ?". It's known as frustum culling when it is about 3D games. I call it screenSpace culling for 2d as I have no better word to describe it.
RenderQueue also performs a test to know wherever I have to change love.graphics.setColor() and camera:attach() or camera:detach(). The goal being, minimizing the calls to theses function. (Feel free to tell me if it's a stupid idea ).
I made a test where I draw a few thousand images randomly positioned in and out of the screen space. It appears that activating the culling makes it slightly SLOWER than a standard call. I'll provide a .love demonstrating it soon if I'm asked to. By the time here is the code I use in my RenderQueue object:
Code: Select all
------------------------------------------------------------------------
-- @class RenderQueue
-- Renders a list of drawable to the screen
------------------------------------------------------------------------
class "RenderQueue"{
}
function RenderQueue:__init__()
self.queue = {}
self.camera = nil
self.cameraAABB = nil
self.attached = false
self.screenAABB = AABB(0, 0, love.graphics.getWidth(), love.graphics.getHeight() )
self.debug = {queueSize=0, renderTime=0, culledItems=0}
end
function RenderQueue:addDrawableObject( p_element )
assert( p_element )
assert( isinstance(p_element, DrawableObject) or issubclass(p_element, DrawableObject) )
table.insert( self.queue, p_element )
end
function RenderQueue:registerCamera( p_camera )
assert( p_camera )
self.camera = p_camera
end
function RenderQueue:sort()
table.sort( self.queue, function(a,b) return a.zindex<b.zindex end )
end
function RenderQueue:setCurrentColor( p_color )
assert( p_color )
love.graphics.setColor( p_color.r, p_color.r, p_color.b, p_color.a )
self.currentColor = p_color
end
function RenderQueue:draw()
self.debug.queueSize = #self.queue
local startTime = love.timer.getMicroTime()
self:screenSpaceCulling()
self.debug.culledItems = (self.debug.queueSize - #self.queue)
self:sort()
for i, v in ipairs( self.queue ) do
if not ( self.currentColor == v.color ) then
self:setCurrentColor( v.color )
end
if not ( self.attached == v.attached ) then
self:setCameraAttached( v.attached )
end
v:draw()
end
self:clear()
local endTime = love.timer.getMicroTime()
self.debug.renderTime = (endTime - startTime)
end
function RenderQueue:registerCamera( p_camera )
self.camera = p_camera
end
function RenderQueue:setCameraAttached( p_bool )
assert( self.camera )
if p_bool then
self.camera:attach()
self.attached = true
else
self.camera:detach()
self.attached = false
end
end
function RenderQueue:clear()
self.queue = {}
if self.attached then
self:setCameraAttached( false )
end
end
function RenderQueue:getCameraAABB()
if self.camera then
if self.camera.rot == 0 then --already aligned, quick AABB
local tlx, tly = self.camera:worldCoords( 0, 0 )
local brx, bry = self.camera:worldCoords( love.graphics.getWidth(), love.graphics.getHeight() )
return AABB( tlx, tly, brx, bry )
else
local tlx, tly = self.camera:worldCoords( 0, 0 )
local brx, bry = self.camera:worldCoords( love.graphics.getWidth(), love.graphics.getHeight() )
return AABB( math.min(tlx, brx), math.min(tly,bry), math.max(tlx, brx), math.max(tly,bry) )
end
end
end
function RenderQueue:screenSpaceCulling()
--Removes elements that are not in the screen or in its projection
local camBox = self:getCameraAABB()
--Reverse loop idea from:
--http://stackoverflow.com/questions/12394841/safely-remove-items-from-an-array-table-while-iterating
--Consider using the 3 loops answers for more perfs
local tremove = {}
for i=#self.queue,1,-1 do
if self.queue[i].attached then
if not camBox:crosses( self.queue[i].boundingBox ) then
table.remove( self.queue, i )
end
else
if not v.boundingBox:crosses( screenAABB ) then
table.remove( self.queue, i )
end
end
end
end
function RenderQueue:getDebugInfo()
return "Queue size: "..self.debug.queueSize.."\n"
..self.debug.culledItems.." items culled".."\n"
.."in "..self.debug.renderTime
end