257 lines
6.0 KiB
Lua
257 lines
6.0 KiB
Lua
local atlas = {}
|
|
atlas.__index = atlas
|
|
---@class atlases
|
|
local atlases ={}
|
|
atlases.__index = atlases
|
|
local BLOCK_SIZE = 5
|
|
local coefficient = 1.5
|
|
local selfRenderTime = false
|
|
local sw, sh = love.graphics.getDimensions()
|
|
|
|
function atlases.create()
|
|
local self = {
|
|
atlases = {}
|
|
}
|
|
self.atlases[1] = atlas.new(sw*1.10, sh*1.10)
|
|
self.atlases[2] = atlas.new(sw*1.10, sh*1.10)
|
|
|
|
return setmetatable(self, atlases)
|
|
end
|
|
|
|
function atlases.setBench(time)
|
|
selfRenderTime = time
|
|
end
|
|
|
|
function atlases:getRatio(index)
|
|
return self.atlases[index].taken_area/self.atlases[index].ideal_area
|
|
end
|
|
|
|
function atlases:getFreeArea(index)
|
|
return self.atlases[index].ideal_area - self.atlases[index].taken_area
|
|
end
|
|
|
|
function atlases:assign(element)
|
|
local avg, sum, canvasID = 0, 0, element.context:getCanvasIndex(true) or 1
|
|
|
|
for i, e in ipairs(element.renderBench) do
|
|
sum = sum + e
|
|
end
|
|
|
|
avg = sum/#element.renderBench
|
|
|
|
local areaBelow = self:getFreeArea(canvasID)
|
|
local area = element.view.h*element.view.w
|
|
|
|
local areaCoef = (2-(self:getRatio(canvasID)) )-(area/(areaBelow/(4+3*self:getRatio(canvasID))))
|
|
local speedCoef = avg/selfRenderTime
|
|
|
|
if not ((areaCoef+speedCoef)>coefficient) then
|
|
return
|
|
end
|
|
|
|
local elW = element.view.w
|
|
local elH = element.view.h
|
|
local canvas, quad, interQuad = self.atlases[canvasID]:assignElement(element)
|
|
if not canvas and self.atlases[canvasID].ideal_area < self.atlases[canvasID].taken_area*4 then
|
|
--print('refragmenting ;3')
|
|
self.atlases[canvasID]:refragment()
|
|
canvas, quad, interQuad = self.atlases[canvasID]:assignElement(element)
|
|
if not canvas then
|
|
--print('ran out of space')
|
|
end
|
|
else
|
|
--print('wont refragment', createdAtlas.ideal_area, createdAtlas.taken_area)
|
|
end
|
|
return canvas, quad, interQuad
|
|
end
|
|
|
|
function atlases:unassign(element)
|
|
local canvasID = element.context:getCanvasIndex(true) or 1
|
|
self.atlases[canvasID]:unassignElement(element)
|
|
end
|
|
|
|
function atlases:unassignAll()
|
|
self.atlases[1].users = {}
|
|
self.atlases[2].users = {}
|
|
|
|
self.atlases[1]:unMarkTiles(1, 1, self.atlases[1].tileW, self.atlases[1].tileH)
|
|
self.atlases[2]:unMarkTiles(1, 1, self.atlases[2].tileW, self.atlases[2].tileH)
|
|
|
|
self.atlases[1].taken_area = 0
|
|
self.atlases[2].taken_area = 0
|
|
end
|
|
|
|
function atlases:onresize(newW, newH)
|
|
for i, e in ipairs(self.atlases[1].users) do
|
|
e:reassignCanvas()
|
|
end
|
|
|
|
for i, e in ipairs(self.atlases[2].users) do
|
|
e:reassignCanvas()
|
|
end
|
|
|
|
self.atlases[1] = atlas.new(newW*1.10, newH*1.10)
|
|
self.atlases[2] = atlas.new(newW*1.10, newH*1.10)
|
|
end
|
|
|
|
function atlas.new(w, h)
|
|
local tiles = {}
|
|
|
|
local ymax = math.floor(h/BLOCK_SIZE)
|
|
local xmax = math.floor(w/BLOCK_SIZE)
|
|
for y = 1, ymax do
|
|
tiles[y] = {}
|
|
tiles[y].empty = xmax
|
|
for x = 1, xmax do
|
|
tiles[y][x] = {
|
|
taken = false
|
|
}
|
|
end
|
|
end
|
|
|
|
local self = {
|
|
w = w,
|
|
h = h,
|
|
tileW = xmax,
|
|
tileH = ymax,
|
|
ideal_area = w*h,
|
|
taken_area = 0,
|
|
canvas = love.graphics.newCanvas(w, h),
|
|
users = {},
|
|
tiles = tiles,
|
|
}
|
|
|
|
return setmetatable(self, atlas)
|
|
end
|
|
|
|
function atlas:assignElement(element)
|
|
local elH, elW = element.view.h, element.view.w
|
|
local tileSizeY, tileSizeX = math.ceil(elH/BLOCK_SIZE), math.ceil(elW/BLOCK_SIZE)
|
|
|
|
local t, y, x = self:find(tileSizeY, tileSizeX)
|
|
|
|
if t then
|
|
local quad, iquad
|
|
--Refragmenting path
|
|
if self.users[element] and self.users[element].quad and self.users[element].interQuad then
|
|
--update by reference owo
|
|
self.users[element].quad:setViewport((x-1)*BLOCK_SIZE, (y-1)*BLOCK_SIZE, elW, elH)
|
|
self.users[element].interQuad:setViewport(0, 0, elW, elH)
|
|
quad = self.users[element].quad
|
|
iquad = self.users[element].interQuad
|
|
else
|
|
quad = love.graphics.newQuad((x-1)*BLOCK_SIZE, (y-1)*BLOCK_SIZE, elW, elH, self.w, self.h)
|
|
iquad = love.graphics.newQuad(0, 0, elW, elH, sw, sh)
|
|
end
|
|
|
|
self.users[element] = {
|
|
element = element,
|
|
x = x,
|
|
y = y,
|
|
w = tileSizeX,
|
|
h = tileSizeY,
|
|
quad = quad,
|
|
}
|
|
|
|
self:markTiles(x, y, tileSizeX, tileSizeY)
|
|
self.taken_area = self.taken_area + ((tileSizeY*BLOCK_SIZE)*(tileSizeX*BLOCK_SIZE))
|
|
|
|
return self.canvas, self.users[element].quad, iquad
|
|
else
|
|
print('failed to allocate :X')
|
|
return false
|
|
end
|
|
end
|
|
|
|
local function sortFunc(el1, el2)
|
|
return el1.view.h > el2.view.h
|
|
end
|
|
|
|
function atlas:refragment()
|
|
self:unMarkTiles(1, 1, self.tileW-1, self.tileH-1)
|
|
self.taken_area = 0
|
|
|
|
local elementArray = {}
|
|
|
|
for i, e in pairs(self.users) do
|
|
i.settings.needsRendering = true
|
|
table.insert(elementArray, i)
|
|
end
|
|
|
|
--self.users = {}
|
|
love.graphics.setCanvas(self.canvas)
|
|
love.graphics.clear(0,0,0,0)
|
|
love.graphics.setCanvas()
|
|
--Should be sorted large to small
|
|
table.sort(elementArray, sortFunc)
|
|
|
|
for index, element in ipairs(elementArray) do
|
|
self:assignElement(element)
|
|
end
|
|
end
|
|
|
|
function atlas:markTiles(x, y, w, h)
|
|
for y = y, y+h do
|
|
self.tiles[y].empty = self.tiles[y].empty - w
|
|
|
|
for x = x, x+w do
|
|
self.tiles[y][x].taken = true
|
|
end
|
|
end
|
|
end
|
|
|
|
function atlas:unMarkTiles(x, y, w, h)
|
|
for y = y, y+h do
|
|
self.tiles[y].empty = self.tiles[y].empty + w
|
|
|
|
for x = x, x+w do
|
|
self.tiles[y][x].taken = false
|
|
end
|
|
end
|
|
end
|
|
|
|
--Work only with rounded values inside here
|
|
function atlas:find(sizeY, sizeX)
|
|
local maxX, maxY = #self.tiles[1], #self.tiles
|
|
|
|
for y = 1, #self.tiles-(sizeY+1) do
|
|
local skipUntilX=0
|
|
if self.tiles[y].empty > sizeY then
|
|
for x = 1, #self.tiles[1]-sizeX do
|
|
if not self.tiles[y][x].taken and x>skipUntilX then
|
|
local result, y, x = self:slice(y, x, sizeY, sizeX)
|
|
|
|
if result then
|
|
return true, y, x
|
|
else
|
|
skipUntilX = x
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
function atlas:slice(startY, startX, sizeY, sizeX)
|
|
for y = startY, startY+sizeY do
|
|
for x = startX, startX+sizeX do
|
|
if self.tiles[y][x].taken then
|
|
return false, y, x
|
|
end
|
|
end
|
|
end
|
|
return true, startY, startX
|
|
end
|
|
|
|
function atlas:unassignElement(element)
|
|
local user = self.users[element]
|
|
if user then
|
|
self:unMarkTiles(user.x, user.y, user.w, user.h)
|
|
self.taken_area = self.taken_area - ((user.w*BLOCK_SIZE)*(user.h*BLOCK_SIZE))
|
|
self.users[element] = nil
|
|
end
|
|
end
|
|
|
|
return atlases |