rework/rendering #36
@ -32,22 +32,19 @@ function behavior:draw()
|
||||
local positioned = self.owner:has(Tree.behaviors.positioned)
|
||||
if not positioned then return end
|
||||
|
||||
Tree.level.render:enqueue(Tree.level.render.LAYERS.LIGHT, positioned.position.y, function()
|
||||
love.graphics.setBlendMode("add", "premultiplied")
|
||||
Tree.level.camera:attach()
|
||||
love.graphics.setCanvas(Tree.level.render.textures.lightLayer)
|
||||
local shader = Tree.assets.files.shaders.light
|
||||
shader:send("color", { self.color.x, self.color.y, self.color.z })
|
||||
shader:send("time", love.timer.getTime() + self.seed)
|
||||
shader:send("time", love.timer:getTime() + self.seed)
|
||||
love.graphics.setShader(shader)
|
||||
love.graphics.draw(Tree.assets.files.masks.circle128, positioned.position.x - self.intensity / 2,
|
||||
positioned.position.y - self.intensity / 2, 0, self.intensity / 128,
|
||||
self.intensity / 128)
|
||||
|
||||
love.graphics.setBlendMode("alpha")
|
||||
|
||||
love.graphics.setShader()
|
||||
love.graphics.setCanvas()
|
||||
Tree.level.camera:detach()
|
||||
love.graphics.setBlendMode("alpha")
|
||||
end)
|
||||
end
|
||||
|
||||
return behavior
|
||||
|
||||
@ -10,10 +10,7 @@ function behavior:draw()
|
||||
local sprite = self.owner:has(Tree.behaviors.sprite)
|
||||
local positioned = self.owner:has(Tree.behaviors.positioned)
|
||||
if not positioned then return end
|
||||
if not sprite then
|
||||
love.graphics.setCanvas()
|
||||
return
|
||||
end
|
||||
if not sprite then return end
|
||||
|
||||
local ppm = Tree.level.camera.pixelsPerMeter
|
||||
local position = positioned.position + Vec3 { 0.5, 0.5 }
|
||||
@ -25,18 +22,18 @@ function behavior:draw()
|
||||
table.insert(lights, Tree.level.characters[id])
|
||||
end
|
||||
|
||||
|
||||
Tree.level.camera:attach()
|
||||
love.graphics.setCanvas(Tree.level.render.textures.shadowLayer)
|
||||
-- 1. Эллипс тени
|
||||
Tree.level.render:enqueue(Tree.level.render.LAYERS.SHADOW, position.y, function()
|
||||
love.graphics.push()
|
||||
love.graphics.setColor(0, 0, 0, 1)
|
||||
love.graphics.translate(position.x, position.y)
|
||||
love.graphics.ellipse("fill", 0, 0, sprite.manifest.size / 2, sprite.manifest.size / 2 * math.cos(math.pi / 4))
|
||||
love.graphics.pop()
|
||||
end)
|
||||
|
||||
|
||||
|
||||
love.graphics.setCanvas(Tree.level.render.textures.spriteLightLayer)
|
||||
-- 2. Свет на спрайте
|
||||
if #lights > 0 then
|
||||
Tree.level.render:enqueue(Tree.level.render.LAYERS.SPRITE_LIGHT, position.y, function()
|
||||
love.graphics.setBlendMode("add")
|
||||
for _, light in ipairs(lights) do
|
||||
local lightPos = light:has(Tree.behaviors.positioned).position
|
||||
@ -56,10 +53,9 @@ function behavior:draw()
|
||||
position.y, nil, 1 / ppm * sprite.side, 1 / ppm, sprite.manifest.base.x, sprite.manifest.base.y)
|
||||
end
|
||||
love.graphics.setBlendMode("alpha")
|
||||
|
||||
Tree.level.camera:detach()
|
||||
love.graphics.setColor(1, 1, 1)
|
||||
love.graphics.setCanvas()
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
return behavior
|
||||
|
||||
@ -49,9 +49,7 @@ function sprite:draw()
|
||||
local ppm = Tree.level.camera.pixelsPerMeter
|
||||
local position = pos.position + Vec3 { 0.5, 0.5 }
|
||||
|
||||
love.graphics.setCanvas(Tree.level.render.textures.spriteLayer)
|
||||
Tree.level.camera:attach()
|
||||
|
||||
Tree.level.render:enqueue(Tree.level.render.LAYERS.SPRITE, position.y, function()
|
||||
love.graphics.setColor(1, 1, 1)
|
||||
if Tree.level.selector.id == self.owner.id then
|
||||
local texW, texH = self.sheets[self.state]:getWidth(),
|
||||
@ -66,8 +64,7 @@ function sprite:draw()
|
||||
position.y, nil, 1 / ppm * self.side, 1 / ppm, self.manifest.base.x, self.manifest.base.y)
|
||||
|
||||
love.graphics.setShader()
|
||||
Tree.level.camera:detach()
|
||||
love.graphics.setCanvas()
|
||||
end)
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
@ -9,17 +9,32 @@ map.__index = map
|
||||
--- @param size? Vec3
|
||||
local function new(type, template, size)
|
||||
local tMap = require('lib.level.' .. type).new(template, size)
|
||||
return setmetatable({ __grid = tMap }, map)
|
||||
local grid = setmetatable({ __grid = tMap }, map)
|
||||
grid:refreshBatch()
|
||||
return grid
|
||||
end
|
||||
|
||||
function map:refreshBatch()
|
||||
-- Находим атлас первого попавшегося тайла (предполагаем, что он один для всех)
|
||||
local _, firstTile = next(self.__grid)
|
||||
if not firstTile then return end
|
||||
|
||||
local atlas = firstTile.atlasData.atlas
|
||||
local count = 0
|
||||
for _ in pairs(self.__grid) do count = count + 1 end
|
||||
|
||||
self.batch = love.graphics.newSpriteBatch(atlas, count)
|
||||
for _, tile in pairs(self.__grid) do
|
||||
-- 1/32 это масштаб, так как размер тайла в мире 1x1 метр, а в атласе 32x32 пикселя
|
||||
self.batch:add(tile.atlasData.quad, tile.position.x, tile.position.y, 0, 1 / 32, 1 / 32)
|
||||
end
|
||||
end
|
||||
|
||||
function map:draw()
|
||||
love.graphics.setCanvas(Tree.level.render.textures.floorLayer)
|
||||
Tree.level.camera:attach()
|
||||
utils.each(self.__grid, function(el)
|
||||
el:draw()
|
||||
if not self.batch then return end
|
||||
Tree.level.render:enqueue(Tree.level.render.LAYERS.FLOOR, 0, function()
|
||||
love.graphics.draw(self.batch)
|
||||
end)
|
||||
Tree.level.camera:detach()
|
||||
love.graphics.setCanvas()
|
||||
end
|
||||
|
||||
return { new = new }
|
||||
|
||||
@ -56,9 +56,9 @@ end
|
||||
function level:draw()
|
||||
self.render:clear()
|
||||
self.tileGrid:draw()
|
||||
while not self.characterGrid.yOrderQueue:is_empty() do -- по сути это сортировка кучей за n log n
|
||||
self.characterGrid.yOrderQueue:pop():draw()
|
||||
end
|
||||
utils.each(self.characters, function(char)
|
||||
char:draw()
|
||||
end)
|
||||
|
||||
self.render:draw()
|
||||
end
|
||||
|
||||
@ -1,12 +1,24 @@
|
||||
--- @class Render
|
||||
--- @field textures table<string, love.Canvas>
|
||||
--- @field queue table[]
|
||||
local render = {
|
||||
textures = {}
|
||||
textures = {},
|
||||
queue = {},
|
||||
LAYERS = {
|
||||
FLOOR = 1,
|
||||
SHADOW = 2,
|
||||
LIGHT = 3,
|
||||
SPRITE = 4,
|
||||
SPRITE_LIGHT = 5,
|
||||
OVERLAY = 6
|
||||
}
|
||||
}
|
||||
|
||||
function render:clear()
|
||||
local weather = Tree.level.weather
|
||||
local txs = self.textures
|
||||
self.queue = {}
|
||||
|
||||
love.graphics.setCanvas(txs.shadowLayer)
|
||||
love.graphics.clear()
|
||||
love.graphics.setCanvas(txs.spriteLayer)
|
||||
@ -21,6 +33,10 @@ function render:clear()
|
||||
love.graphics.clear()
|
||||
end
|
||||
|
||||
function render:enqueue(layer, z, func)
|
||||
table.insert(self.queue, { layer = layer, z = z, func = func })
|
||||
end
|
||||
|
||||
function render:free()
|
||||
for _, tx in pairs(self.textures) do
|
||||
tx:release()
|
||||
@ -57,19 +73,98 @@ function render:applyBlur(input, radius)
|
||||
return self.textures.tmp2
|
||||
end
|
||||
|
||||
---@param params {w: number?, h: number?}
|
||||
---@return table|Render
|
||||
local function new(params)
|
||||
local w = params.w or love.graphics.getWidth()
|
||||
local h = params.h or love.graphics.getHeight()
|
||||
local lowResScale = 0.5
|
||||
|
||||
return setmetatable({
|
||||
lowResScale = lowResScale,
|
||||
queue = {},
|
||||
textures = {
|
||||
shadowLayer = love.graphics.newCanvas(w * lowResScale, h * lowResScale),
|
||||
spriteLayer = love.graphics.newCanvas(w, h),
|
||||
spriteLightLayer = love.graphics.newCanvas(w * lowResScale, h * lowResScale),
|
||||
floorLayer = love.graphics.newCanvas(w, h),
|
||||
overlayLayer = love.graphics.newCanvas(w, h),
|
||||
lightLayer = love.graphics.newCanvas(w * lowResScale, h * lowResScale),
|
||||
tmp1 = love.graphics.newCanvas(w * lowResScale, h * lowResScale),
|
||||
tmp2 = love.graphics.newCanvas(w * lowResScale, h * lowResScale),
|
||||
}
|
||||
}, { __index = render })
|
||||
end
|
||||
|
||||
function render:draw()
|
||||
local weather = Tree.level.weather
|
||||
local txs = self.textures
|
||||
|
||||
-- 1. Сортировка очереди
|
||||
table.sort(self.queue, function(a, b)
|
||||
if a.layer ~= b.layer then
|
||||
return a.layer < b.layer
|
||||
end
|
||||
return a.z < b.z
|
||||
end)
|
||||
|
||||
-- 2. Рендеринг очереди в соответствующие Canvas
|
||||
local currentLayer = nil
|
||||
for _, entry in ipairs(self.queue) do
|
||||
if entry.layer ~= currentLayer then
|
||||
if currentLayer then
|
||||
Tree.level.camera:detach()
|
||||
local wasLowRes = currentLayer == self.LAYERS.SHADOW or currentLayer == self.LAYERS.SPRITE_LIGHT or
|
||||
currentLayer == self.LAYERS.LIGHT
|
||||
if wasLowRes then
|
||||
love.graphics.pop()
|
||||
end
|
||||
end
|
||||
currentLayer = entry.layer
|
||||
local isLowRes = currentLayer == self.LAYERS.SHADOW or currentLayer == self.LAYERS.SPRITE_LIGHT or
|
||||
currentLayer == self.LAYERS.LIGHT
|
||||
|
||||
if currentLayer == self.LAYERS.FLOOR then
|
||||
love.graphics.setCanvas(txs.floorLayer)
|
||||
elseif currentLayer == self.LAYERS.SHADOW then
|
||||
love.graphics.setCanvas(txs.shadowLayer)
|
||||
elseif currentLayer == self.LAYERS.LIGHT then
|
||||
love.graphics.setCanvas(txs.lightLayer)
|
||||
love.graphics.draw(self:applyBlur(txs.shadowLayer, 4 * Tree.level.camera.scale))
|
||||
elseif currentLayer == self.LAYERS.SPRITE then
|
||||
love.graphics.setCanvas(txs.spriteLayer)
|
||||
elseif currentLayer == self.LAYERS.SPRITE_LIGHT then
|
||||
love.graphics.setCanvas(txs.spriteLightLayer)
|
||||
elseif currentLayer == self.LAYERS.OVERLAY then
|
||||
love.graphics.setCanvas(txs.overlayLayer)
|
||||
end
|
||||
|
||||
if isLowRes then
|
||||
love.graphics.push()
|
||||
love.graphics.scale(self.lowResScale)
|
||||
end
|
||||
Tree.level.camera:attach()
|
||||
end
|
||||
entry.func()
|
||||
end
|
||||
if currentLayer then
|
||||
Tree.level.camera:detach()
|
||||
local wasLowRes = currentLayer == self.LAYERS.SHADOW or currentLayer == self.LAYERS.SPRITE_LIGHT or
|
||||
currentLayer == self.LAYERS.LIGHT
|
||||
if wasLowRes then
|
||||
love.graphics.pop()
|
||||
end
|
||||
end
|
||||
love.graphics.setCanvas()
|
||||
|
||||
-- self.lightLayer:newImageData():encode("png", "lightLayer.png")
|
||||
-- os.exit(0)
|
||||
-- 3. Пост-процессинг и композиция
|
||||
love.graphics.setCanvas(txs.lightLayer)
|
||||
-- Радиус блюра тоже масштабируем, так как текстура меньше
|
||||
love.graphics.draw(self:applyBlur(txs.shadowLayer, 4 * Tree.level.camera.scale * self.lowResScale))
|
||||
love.graphics.setCanvas()
|
||||
|
||||
local lightShader = Tree.assets.files.shaders.light_postprocess
|
||||
lightShader:send("scene", txs.floorLayer)
|
||||
lightShader:send("light", self:applyBlur(txs.lightLayer, 2))
|
||||
lightShader:send("light", self:applyBlur(txs.lightLayer, 2 * self.lowResScale))
|
||||
lightShader:send("ambient", { weather.ambientLight.x, weather.ambientLight.y, weather.ambientLight.z })
|
||||
love.graphics.setShader(lightShader)
|
||||
love.graphics.draw(txs.floorLayer)
|
||||
@ -84,23 +179,4 @@ function render:draw()
|
||||
love.graphics.setShader()
|
||||
end
|
||||
|
||||
---@param params {w: number?, h: number?}
|
||||
---@return table|Render
|
||||
local function new(params)
|
||||
local w = params.w or love.graphics.getWidth()
|
||||
local h = params.h or love.graphics.getHeight()
|
||||
return setmetatable({
|
||||
textures = {
|
||||
shadowLayer = love.graphics.newCanvas(w, h),
|
||||
spriteLayer = love.graphics.newCanvas(w, h),
|
||||
spriteLightLayer = love.graphics.newCanvas(w, h),
|
||||
floorLayer = love.graphics.newCanvas(w, h),
|
||||
overlayLayer = love.graphics.newCanvas(w, h),
|
||||
lightLayer = love.graphics.newCanvas(w, h),
|
||||
tmp1 = love.graphics.newCanvas(w, h),
|
||||
tmp2 = love.graphics.newCanvas(w, h),
|
||||
}
|
||||
}, { __index = render })
|
||||
end
|
||||
|
||||
return { new = new }
|
||||
|
||||
@ -54,8 +54,7 @@ function spell:draw()
|
||||
local path = self.path --[[@as Deque?]]
|
||||
if not path then return end
|
||||
--- Это отрисовка пути персонажа к мышке
|
||||
Tree.level.camera:attach()
|
||||
love.graphics.setCanvas(Tree.level.render.textures.overlayLayer)
|
||||
Tree.level.render:enqueue(Tree.level.render.LAYERS.OVERLAY, 0, function()
|
||||
local i = 0
|
||||
path:pop_front()
|
||||
for p in path:values() do
|
||||
@ -64,12 +63,10 @@ function spell:draw()
|
||||
local quad = i > self.distance and icons:pickQuad('dev_path_closed') or icons:pickQuad('dev_path')
|
||||
love.graphics.draw(icons.atlas, quad, p.x, p.y, 0, s, s)
|
||||
end
|
||||
love.graphics.setCanvas()
|
||||
Tree.level.camera:detach()
|
||||
love.graphics.setColor(1, 1, 1)
|
||||
end)
|
||||
else
|
||||
Tree.level.camera:attach()
|
||||
love.graphics.setCanvas(Tree.level.render.textures.overlayLayer)
|
||||
Tree.level.render:enqueue(Tree.level.render.LAYERS.OVERLAY, 0, function()
|
||||
love.graphics.setColor(1, 1, 1, 0.5)
|
||||
for _, p in pairs(self.targets) do
|
||||
local s = self.tSize / Tree.level.camera.pixelsPerMeter
|
||||
@ -77,10 +74,8 @@ function spell:draw()
|
||||
love.graphics.draw(icons.atlas, quad, p.x + 0.5 - self.tSize / 2, p.y + 0.5 - self.tSize / 2, 0, s, s)
|
||||
end
|
||||
love.graphics.setShader()
|
||||
|
||||
love.graphics.setCanvas()
|
||||
Tree.level.camera:detach()
|
||||
love.graphics.setColor(1, 1, 1)
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user