atlas finished

This commit is contained in:
Elmārs Āboliņš 2020-08-27 02:43:58 +03:00
parent 8e7423fc86
commit 1a13369dae
4 changed files with 179 additions and 36 deletions

View File

@ -1,12 +1,51 @@
local atlas = {} local atlas = {}
local atlases = {} local createdAtlas
atlas.__index = atlas atlas.__index = atlas
local BLOCK_SIZE = 10 local BLOCK_SIZE = 5
function atlas.load()
if not createdAtlas then
atlas.init()
end
end
function atlas.getRatio()
return createdAtlas.taken_area/createdAtlas.ideal_area
end
function atlas.getFreeArea()
return createdAtlas.ideal_area - createdAtlas.taken_area
end
function atlas.init() function atlas.init()
local w, h = love.graphics.getDimensions() local w, h = love.graphics.getDimensions()
createdAtlas = atlas.new(w, h/2)
atlas.createdAtlas = createdAtlas
end
function atlas.assign(element)
local elW = element.view.w
local elH = element.view.h
local canvas, quad = createdAtlas:assignElement(element)
if not canvas and createdAtlas.ideal_area > createdAtlas.taken_area*1.25 then
print('refragmenting ;3')
createdAtlas:refragment()
local canvas, quad = createdAtlas:assignElement(element)
else
print('wont refragment')
end
return canvas, quad
end
function atlas.unassign(element)
createdAtlas:unassignElement(element)
end
function atlas.unassignAll()
createdAtlas.users = {}
createdAtlas:unMarkTiles(1, 1, createdAtlas.tileW, createdAtlas.tileH)
createdAtlas.taken_area = 0
end end
function atlas.onscreenchange(newW, newH) function atlas.onscreenchange(newW, newH)
@ -17,10 +56,11 @@ function atlas.new(w, h)
local tiles = {} local tiles = {}
local ymax = math.floor(h/BLOCK_SIZE) local ymax = math.floor(h/BLOCK_SIZE)
local xmax = math.floor(w/BLOCK_SIZE)
for y = 1, ymax do for y = 1, ymax do
tiles[y] = {} tiles[y] = {}
tiles[y].empty = ymax tiles[y].empty = xmax
for x = 1, math.floor(w/BLOCK_SIZE) do for x = 1, xmax do
tiles[y][x] = { tiles[y][x] = {
taken = false taken = false
} }
@ -30,7 +70,10 @@ function atlas.new(w, h)
local self = { local self = {
w = w, w = w,
h = h, h = h,
tileW = xmax,
tileH = ymax,
ideal_area = w*h, ideal_area = w*h,
taken_area = 0,
canvas = love.graphics.newCanvas(w, h), canvas = love.graphics.newCanvas(w, h),
users = {}, users = {},
tiles = tiles, tiles = tiles,
@ -39,17 +82,98 @@ function atlas.new(w, h)
return setmetatable(self, atlas) return setmetatable(self, atlas)
end end
function atlas:assignElement(elW, elH, element) function atlas:assignElement(element)
local tileSizeY, tileSizeW = math.ceil(elH/BLOCK_SIZE), math.ceil(elW/BLOCK_SIZE) 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
--Refragmenting path
if self.users[element] then
--update by reference owo
self.users[element].quad:setViewport((x-1)*BLOCK_SIZE, (y-1)*BLOCK_SIZE, elW, elH)
quad = self.users[element].quad
else
quad = love.graphics.newQuad((x-1)*BLOCK_SIZE, (y-1)*BLOCK_SIZE, elW, elH, self.w, self.h)
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
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 end
--Work only with rounded values inside here --Work only with rounded values inside here
function atlas:find(sizeY, sizeX) function atlas:find(sizeY, sizeX)
for y = 1, #self.tiles do local maxX, maxY = #self.tiles[1], #self.tiles
for y = 1, #self.tiles-sizeY+1 do
local skipUntilX=0 local skipUntilX=0
if self.tiles[y].empty > sizeY then if self.tiles[y].empty > sizeY then
for x = 1, #self.tiles[1] do for x = 1, #self.tiles[1]-sizeX do
if not self.tiles[y][x].taken and x>skipUntilX then if not self.tiles[y][x].taken and x>skipUntilX then
local result, y, x = self:slice(y, x, sizeY, sizeX) local result, y, x = self:slice(y, x, sizeY, sizeX)
@ -62,21 +186,25 @@ function atlas:find(sizeY, sizeX)
end end
end end
end end
return false
end end
function atlas:slice(startY, startX, sizeY, sizeX) function atlas:slice(startY, startX, sizeY, sizeX)
for y = startY, startY+sizeY do for y = startY, startY+sizeY do
for x = startX, sizeX do for x = startX, startX+sizeX do
if self.tiles[y][x].taken then if self.tiles[y][x].taken then
return false, y, x return false, y, x
end end
end end
end end
return true return true, startY, startX
end end
function atlas:unassignElement(element) function atlas:unassignElement(element)
local user = self.users[element]
self:unMarkTiles(user.x, user.y, user.w, user.h)
self.users[element] = nil
end end
return atlas return atlas

View File

@ -4,7 +4,7 @@ local path = string.sub(..., 1, string.len(...) - string.len(".core.element"))
local helium = require(path .. ".dummy") local helium = require(path .. ".dummy")
local context = require(path.. ".core.stack") local context = require(path.. ".core.stack")
---@class element ---@class Element
local element = {} local element = {}
element.__index = element element.__index = element
@ -103,8 +103,8 @@ end
--Random coefficients, if these reach 1.5 then canvas is made --Random coefficients, if these reach 1.5 then canvas is made
local childrenNum = 5 local childrenNum = 5
local selfRenderTime = false local selfRenderTime = false
local screenSize = 1/50 local screenSize = 1/4
local coefficient = 1000000 local coefficient = 1.50
function element.setBench(time) function element.setBench(time)
selfRenderTime = time selfRenderTime = time
@ -112,10 +112,10 @@ end
function element:calculateCanvasCoeficient(selfTime) function element:calculateCanvasCoeficient(selfTime)
local sW, sH = love.graphics.getDimensions() local sW, sH = love.graphics.getDimensions()
local areaBelow = (sW * sH) * screenSize local areaBelow = helium.atlas.getFreeArea()
local area = self.view.h * self.view.w local area = self.view.h * self.view.w
local areaCoef = 1 - (area/areaBelow) local areaCoef = (1-helium.atlas.getRatio()) - (area/areaBelow)
local childCoef = self.context:getChildrenCount()/childrenNum local childCoef = self.context:getChildrenCount()/childrenNum
local sizeCoef = selfTime/selfRenderTime local sizeCoef = selfTime/selfRenderTime
@ -124,11 +124,10 @@ end
local newCanvas, newQuad = love.graphics.newCanvas, love.graphics.newQuad local newCanvas, newQuad = love.graphics.newCanvas, love.graphics.newQuad
function element:createCanvas() function element:createCanvas()
self.settings.canvasW = self.view.w*1.25 self.settings.canvasW = self.view.w
self.settings.canvasH = self.view.h*1.25 self.settings.canvasH = self.view.h
self.canvas = newCanvas(self.view.w*1.25, self.view.h*1.25) self.canvas, self.quad = helium.atlas.assign(self)
self.quad = newQuad(0, 0, self.view.w, self.view.h, self.view.w*1.25, self.view.h*1.25)
end end
function element:setParam(p) function element:setParam(p)
@ -145,7 +144,7 @@ end
function element:updateInputCtx() function element:updateInputCtx()
self.context.inputContext:update() self.context.inputContext:update()
if self.settings.canvasW then --[[if self.settings.canvasW then
--If canvas too small make a bigger one --If canvas too small make a bigger one
if self.settings.canvasW < self.view.w or self.settings.canvasH < self.view.h then if self.settings.canvasW < self.view.w or self.settings.canvasH < self.view.h then
self.settings.canvasW = self.view.w*1.25 self.settings.canvasW = self.view.w*1.25
@ -161,7 +160,7 @@ function element:updateInputCtx()
end end
self.quad = love.graphics.newQuad(0, 0, self.view.w, self.view.h, self.settings.canvasW, self.settings.canvasH) self.quad = love.graphics.newQuad(0, 0, self.view.w, self.view.h, self.settings.canvasW, self.settings.canvasH)
end end]]
end end
local dummy = function() end local dummy = function() end
@ -202,19 +201,12 @@ function element:internalRender()
calcT = love.timer.getTime() calcT = love.timer.getTime()
end end
local status, err = pcall(self.renderer) self.renderer()
if self.settings.testRenderPasses > 0 and selfRenderTime then if self.settings.testRenderPasses > 0 and selfRenderTime then
self.settings.testRenderPasses = self.settings.testRenderPasses-1 self.settings.testRenderPasses = self.settings.testRenderPasses-1
local selfTime = love.timer.getTime()-calcT local selfTime = love.timer.getTime()-calcT
table.insert(self.renderBench, self.context:endSelfRender(selfTime)) table.insert(self.renderBench, self.context:endSelfRender(selfTime))
end end
if not status then
if helium.conf.HARD_ERROR then
error(status)
end
self:errorRender(status)
end
end end
local draw = love.graphics.draw local draw = love.graphics.draw
@ -242,9 +234,13 @@ function element:externalRender()
if self.settings.needsRendering then if self.settings.needsRendering then
if self.settings.hasCanvas then if self.settings.hasCanvas then
setCanvas(self.canvas) setCanvas(self.canvas)
love.graphics.clear(0,0,0,0) --need scissors
--love.graphics.clear(0,0,0,0)
love.graphics.push('all') love.graphics.push('all')
love.graphics.origin() love.graphics.origin()
local ox, oy = self.quad:getViewport()
love.graphics.translate(ox, oy)
self:renderWrapper() self:renderWrapper()
self.settings.needsRendering = false self.settings.needsRendering = false

View File

@ -37,6 +37,13 @@ end
local activeWindow local activeWindow
local windowStack = {} local windowStack = {}
function input.unload()
windowStack = {}
activeWindow = nil
windowMachine = {}
input.subscriptions = {}
end
local dummyfunc = function() end local dummyfunc = function() end
---@class subscription ---@class subscription
local subscription = {} local subscription = {}

View File

@ -10,6 +10,7 @@ helium.utils = require(path..".utils")
helium.element = require(path..".core.element") helium.element = require(path..".core.element")
helium.input = require(path..".core.input") helium.input = require(path..".core.input")
helium.loader = require(path..".loader") helium.loader = require(path..".loader")
helium.atlas = require(path..".core.atlas")
helium.elementBuffer = {} helium.elementBuffer = {}
helium.__index = helium helium.__index = helium
@ -26,7 +27,18 @@ end})
local first = true local first = true
local skip = true local skip = true
function helium.render()
function helium.load()
helium.atlas.load()
end
function helium.unload()
helium.atlas.unassignAll()
helium.elementBuffer = {}
end
function helium.draw()
if first and not skip then if first and not skip then
--love.graphics.setScissor(500, 500, 1, 1) --love.graphics.setScissor(500, 500, 1, 1)
@ -86,7 +98,7 @@ end
]] ]]
if helium.conf.AUTO_RUN then if helium.conf.AUTO_RUN then
function love.run() function love.run()
if love.load then love.load(love.arg.parseGameArguments(arg), arg) end if love.load then love.load() end--love.arg.parseGameArguments(arg), arg) end
-- We don't want the first frame's dt to include time taken by love.load. -- We don't want the first frame's dt to include time taken by love.load.
if love.timer then love.timer.step() end if love.timer then love.timer.step() end
@ -128,7 +140,7 @@ if helium.conf.AUTO_RUN then
if love.draw then love.draw() end if love.draw then love.draw() end
st = love.timer.getTime() st = love.timer.getTime()
helium.render() helium.draw()
heliumTime=heliumTime+love.timer.getTime()-st heliumTime=heliumTime+love.timer.getTime()-st
love.graphics.present() love.graphics.present()