rework/rendering #36
85
assets/shaders/sprite_light.glsl
Normal file
85
assets/shaders/sprite_light.glsl
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
#define MAX_LIGHTS 8
|
||||||
|
|
||||||
|
struct Light {
|
||||||
|
vec2 position;
|
||||||
|
vec3 color;
|
||||||
|
float radius;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern Light lights[MAX_LIGHTS];
|
||||||
|
extern int num_lights;
|
||||||
|
extern vec2 sprite_pos; // Мировая позиция спрайта (в метрах)
|
||||||
|
extern vec3 ambient; // Эмбиентное освещение
|
||||||
|
extern vec3 sky; // Цвет неба
|
||||||
|
|
||||||
|
// Параметры выделения
|
||||||
|
extern bool is_selected;
|
||||||
|
extern vec2 tex_size;
|
||||||
|
extern float time;
|
||||||
|
|
||||||
|
// Функция для имитации easing.easeInSine
|
||||||
|
float easeInSine(float x) {
|
||||||
|
return 1.0 - cos((x * 3.14159) / 2.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 effect(vec4 vcolor, Image tex, vec2 texture_coords, vec2 screen_coords)
|
||||||
|
{
|
||||||
|
vec4 texColor = Texel(tex, texture_coords);
|
||||||
|
|
||||||
|
// ЛОГИКА ОБВОДКИ (Outline)
|
||||||
|
// Мы выполняем ее до расчетов освещения. Если пиксель прозрачный и объект выбран,
|
||||||
|
// проверяем соседей, чтобы понять, не граница ли это.
|
||||||
|
// Обводка рисуется "самосветящейся", чтобы выделение было видно даже в полной темноте.
|
||||||
|
if (is_selected && texColor.a <= 0.0) {
|
||||||
|
float maxAlpha = 0.0;
|
||||||
|
|
||||||
|
// Проверяем соседние пиксели (квадратом 3x3)
|
||||||
|
for (float x = -1.0; x <= 1.0; x++) {
|
||||||
|
for (float y = -1.0; y <= 1.0; y++) {
|
||||||
|
if (x == 0.0 && y == 0.0) continue;
|
||||||
|
vec2 offset = vec2(x, y) / tex_size;
|
||||||
|
maxAlpha = max(maxAlpha, Texel(tex, texture_coords + offset).a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (maxAlpha > 0.0) {
|
||||||
|
// Эффект пульсации и "бегущей волны" из оригинального шейдера outline.glsl
|
||||||
|
float modY = (0.75 + sin(time) * 0.25) * (0.5 + cos(texture_coords.y * 10.0 + time * 2.0) * 0.5);
|
||||||
|
return vec4(vec3(modY, 0.2 * sin(time) + 0.5, 0.5), 1.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (texColor.a == 0.0) {
|
||||||
|
return vec4(0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Расчет базового освещения мира: персонаж освещается смесью неба (Sky) и отраженного света (Ambient).
|
||||||
|
vec3 baseLight = ambient + (vec3(1.0) - ambient) * sky;
|
||||||
|
|
||||||
|
// Десатурация базового света на 30%.
|
||||||
|
float luma = dot(baseLight, vec3(0.2126, 0.7152, 0.0722));
|
||||||
|
vec3 characterBaseLight = mix(baseLight, vec3(luma), 0.3);
|
||||||
|
|
||||||
|
vec3 pointLight = vec3(0.0);
|
||||||
|
|
||||||
|
for (int i = 0; i < num_lights; i++) {
|
||||||
|
vec2 lightPos = lights[i].position;
|
||||||
|
float radius = lights[i].radius;
|
||||||
|
vec2 lightVec = lightPos - sprite_pos;
|
||||||
|
float dist = length(lightVec);
|
||||||
|
|
||||||
|
// Плавное затухание света по радиусу.
|
||||||
|
float radiusFalloff = smoothstep(radius, radius * 0.2, dist);
|
||||||
|
|
||||||
|
// Реализация псевдо-проекции (3D-эффект):
|
||||||
|
float frontWeight = smoothstep(-2.0, 0.5, lightVec.y);
|
||||||
|
|
||||||
|
float attenuation = radiusFalloff * frontWeight;
|
||||||
|
pointLight += lights[i].color * attenuation;
|
||||||
|
}
|
||||||
|
|
||||||
|
pointLight = clamp(pointLight, 0.0, 1.0);
|
||||||
|
|
||||||
|
// Финальный расчет цвета:
|
||||||
|
return vec4(texColor.rgb * (characterBaseLight + pointLight), texColor.a);
|
||||||
|
}
|
||||||
@ -32,22 +32,19 @@ function behavior:draw()
|
|||||||
local positioned = self.owner:has(Tree.behaviors.positioned)
|
local positioned = self.owner:has(Tree.behaviors.positioned)
|
||||||
if not positioned then return end
|
if not positioned then return end
|
||||||
|
|
||||||
|
Tree.level.render:enqueue(Tree.level.render.LAYERS.LIGHT, positioned.position.y, function()
|
||||||
love.graphics.setBlendMode("add", "premultiplied")
|
love.graphics.setBlendMode("add", "premultiplied")
|
||||||
Tree.level.camera:attach()
|
|
||||||
love.graphics.setCanvas(Tree.level.render.textures.lightLayer)
|
|
||||||
local shader = Tree.assets.files.shaders.light
|
local shader = Tree.assets.files.shaders.light
|
||||||
shader:send("color", { self.color.x, self.color.y, self.color.z })
|
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.setShader(shader)
|
||||||
love.graphics.draw(Tree.assets.files.masks.circle128, positioned.position.x - self.intensity / 2,
|
love.graphics.draw(Tree.assets.files.masks.circle128, positioned.position.x - self.intensity / 2,
|
||||||
positioned.position.y - self.intensity / 2, 0, self.intensity / 128,
|
positioned.position.y - self.intensity / 2, 0, self.intensity / 128,
|
||||||
self.intensity / 128)
|
self.intensity / 128)
|
||||||
|
|
||||||
love.graphics.setBlendMode("alpha")
|
|
||||||
|
|
||||||
love.graphics.setShader()
|
love.graphics.setShader()
|
||||||
love.graphics.setCanvas()
|
love.graphics.setBlendMode("alpha")
|
||||||
Tree.level.camera:detach()
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
return behavior
|
return behavior
|
||||||
|
|||||||
@ -10,10 +10,7 @@ function behavior:draw()
|
|||||||
local sprite = self.owner:has(Tree.behaviors.sprite)
|
local sprite = self.owner:has(Tree.behaviors.sprite)
|
||||||
local positioned = self.owner:has(Tree.behaviors.positioned)
|
local positioned = self.owner:has(Tree.behaviors.positioned)
|
||||||
if not positioned then return end
|
if not positioned then return end
|
||||||
if not sprite then
|
if not sprite then return end
|
||||||
love.graphics.setCanvas()
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local ppm = Tree.level.camera.pixelsPerMeter
|
local ppm = Tree.level.camera.pixelsPerMeter
|
||||||
local position = positioned.position + Vec3 { 0.5, 0.5 }
|
local position = positioned.position + Vec3 { 0.5, 0.5 }
|
||||||
@ -25,41 +22,14 @@ function behavior:draw()
|
|||||||
table.insert(lights, Tree.level.characters[id])
|
table.insert(lights, Tree.level.characters[id])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- 1. Эллипс тени
|
||||||
Tree.level.camera:attach()
|
Tree.level.render:enqueue(Tree.level.render.LAYERS.SHADOW, position.y, function()
|
||||||
love.graphics.setCanvas(Tree.level.render.textures.shadowLayer)
|
|
||||||
love.graphics.push()
|
love.graphics.push()
|
||||||
love.graphics.setColor(0, 0, 0, 1)
|
love.graphics.setColor(0, 0, 0, 1)
|
||||||
love.graphics.translate(position.x, position.y)
|
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.ellipse("fill", 0, 0, sprite.manifest.size / 2, sprite.manifest.size / 2 * math.cos(math.pi / 4))
|
||||||
love.graphics.pop()
|
love.graphics.pop()
|
||||||
|
end)
|
||||||
|
|
||||||
|
|
||||||
love.graphics.setCanvas(Tree.level.render.textures.spriteLightLayer)
|
|
||||||
love.graphics.setBlendMode("add")
|
|
||||||
for _, light in ipairs(lights) do
|
|
||||||
local lightPos = light:has(Tree.behaviors.positioned).position
|
|
||||||
local lightVec = lightPos - position
|
|
||||||
|
|
||||||
local lightColor = light:has(Tree.behaviors.light).color
|
|
||||||
if lightPos.y > position.y then
|
|
||||||
love.graphics.setColor(lightColor.x, lightColor.y, lightColor.z,
|
|
||||||
1 - 0.3 * lightVec:length())
|
|
||||||
elseif position.y - lightPos.y < 3 then
|
|
||||||
love.graphics.setColor(lightColor.x, lightColor.y, lightColor.z,
|
|
||||||
(1 - easing.easeInSine((position.y - lightPos.y))) - 0.3 * lightVec:length())
|
|
||||||
end
|
|
||||||
|
|
||||||
sprite.animationTable[sprite.state]:draw(sprite.sheets[sprite.state],
|
|
||||||
position.x,
|
|
||||||
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
|
||||||
|
|
||||||
return behavior
|
return behavior
|
||||||
|
|||||||
@ -49,25 +49,71 @@ function sprite:draw()
|
|||||||
local ppm = Tree.level.camera.pixelsPerMeter
|
local ppm = Tree.level.camera.pixelsPerMeter
|
||||||
local position = pos.position + Vec3 { 0.5, 0.5 }
|
local position = pos.position + Vec3 { 0.5, 0.5 }
|
||||||
|
|
||||||
love.graphics.setCanvas(Tree.level.render.textures.spriteLayer)
|
Tree.level.render:enqueue(Tree.level.render.LAYERS.SPRITE, position.y, function()
|
||||||
Tree.level.camera:attach()
|
|
||||||
|
|
||||||
love.graphics.setColor(1, 1, 1)
|
love.graphics.setColor(1, 1, 1)
|
||||||
if Tree.level.selector.id == self.owner.id then
|
|
||||||
local texW, texH = self.sheets[self.state]:getWidth(),
|
-- Собираем источники света для шейдера
|
||||||
self.sheets[self.state]:getHeight()
|
local queryRadius = 12 -- Увеличенный радиус для плавности
|
||||||
local shader = Tree.assets.files.shaders.outline
|
local lightIds = Tree.level.lightGrid:query(position, queryRadius)
|
||||||
shader:send("texSize", { texW, texH })
|
local lightsData = {}
|
||||||
shader:send("time", love.timer:getTime())
|
|
||||||
love.graphics.setShader(shader)
|
for _, id in ipairs(lightIds) do
|
||||||
|
local lightChar = Tree.level.characters[id]
|
||||||
|
local b = lightChar:has(Tree.behaviors.light) --[[@as LightBehavior]]
|
||||||
|
local lPos = lightChar:has(Tree.behaviors.positioned).position
|
||||||
|
local dist = (lPos - position):length()
|
||||||
|
|
||||||
|
-- Берем только те, что могут дотянуться до нас своим радиусом
|
||||||
|
if dist < b.intensity + 2 then
|
||||||
|
table.insert(lightsData, {
|
||||||
|
x = lPos.x,
|
||||||
|
y = lPos.y,
|
||||||
|
r = b.color.x,
|
||||||
|
g = b.color.y,
|
||||||
|
b = b.color.z,
|
||||||
|
radius = b.intensity,
|
||||||
|
dist = dist
|
||||||
|
})
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Сортируем по дистанции, чтобы выбрать 8 самых влиятельных
|
||||||
|
table.sort(lightsData, function(a, b) return a.dist < b.dist end)
|
||||||
|
|
||||||
|
local lightShader = Tree.assets.files.shaders.sprite_light
|
||||||
|
local weather = Tree.level.weather
|
||||||
|
local numLights = math.min(#lightsData, 8)
|
||||||
|
local isSelected = (Tree.level.selector.id == self.owner.id)
|
||||||
|
|
||||||
|
lightShader:send("sprite_pos", { position.x, position.y })
|
||||||
|
lightShader:send("sky", { weather.skyLight.x, weather.skyLight.y, weather.skyLight.z })
|
||||||
|
lightShader:send("ambient", { weather.ambientLight.x, weather.ambientLight.y, weather.ambientLight.z })
|
||||||
|
lightShader:send("num_lights", numLights)
|
||||||
|
|
||||||
|
lightShader:send("is_selected", isSelected)
|
||||||
|
if isSelected then
|
||||||
|
local sheet = self.sheets[self.state]
|
||||||
|
lightShader:send("tex_size", { sheet:getWidth(), sheet:getHeight() })
|
||||||
|
lightShader:send("time", love.timer:getTime())
|
||||||
|
end
|
||||||
|
|
||||||
|
for i = 1, numLights do
|
||||||
|
local l = lightsData[i]
|
||||||
|
local idx = i - 1
|
||||||
|
lightShader:send(string.format("lights[%d].position", idx), { l.x, l.y })
|
||||||
|
lightShader:send(string.format("lights[%d].color", idx), { l.r, l.g, l.b })
|
||||||
|
lightShader:send(string.format("lights[%d].radius", idx), l.radius)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
love.graphics.setShader(lightShader)
|
||||||
|
|
||||||
self.animationTable[self.state]:draw(self.sheets[self.state],
|
self.animationTable[self.state]:draw(self.sheets[self.state],
|
||||||
position.x,
|
position.x,
|
||||||
position.y, nil, 1 / ppm * self.side, 1 / ppm, self.manifest.base.x, self.manifest.base.y)
|
position.y, nil, 1 / ppm * self.side, 1 / ppm, self.manifest.base.x, self.manifest.base.y)
|
||||||
|
|
||||||
love.graphics.setShader()
|
love.graphics.setShader()
|
||||||
Tree.level.camera:detach()
|
end)
|
||||||
love.graphics.setCanvas()
|
|
||||||
end
|
end
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|||||||
@ -9,17 +9,32 @@ map.__index = map
|
|||||||
--- @param size? Vec3
|
--- @param size? Vec3
|
||||||
local function new(type, template, size)
|
local function new(type, template, size)
|
||||||
local tMap = require('lib.level.' .. type).new(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
|
end
|
||||||
|
|
||||||
function map:draw()
|
function map:draw()
|
||||||
love.graphics.setCanvas(Tree.level.render.textures.floorLayer)
|
if not self.batch then return end
|
||||||
Tree.level.camera:attach()
|
Tree.level.render:enqueue(Tree.level.render.LAYERS.FLOOR, 0, function()
|
||||||
utils.each(self.__grid, function(el)
|
love.graphics.draw(self.batch)
|
||||||
el:draw()
|
|
||||||
end)
|
end)
|
||||||
Tree.level.camera:detach()
|
|
||||||
love.graphics.setCanvas()
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return { new = new }
|
return { new = new }
|
||||||
|
|||||||
@ -56,9 +56,9 @@ end
|
|||||||
function level:draw()
|
function level:draw()
|
||||||
self.render:clear()
|
self.render:clear()
|
||||||
self.tileGrid:draw()
|
self.tileGrid:draw()
|
||||||
while not self.characterGrid.yOrderQueue:is_empty() do -- по сути это сортировка кучей за n log n
|
utils.each(self.characters, function(char)
|
||||||
self.characterGrid.yOrderQueue:pop():draw()
|
char:draw()
|
||||||
end
|
end)
|
||||||
|
|
||||||
self.render:draw()
|
self.render:draw()
|
||||||
end
|
end
|
||||||
|
|||||||
@ -1,18 +1,30 @@
|
|||||||
--- @class Render
|
--- @class Render
|
||||||
--- @field textures table<string, love.Canvas>
|
--- @field textures table<string, love.Canvas>
|
||||||
|
--- @field queue table[]
|
||||||
|
--- @field lowResScale number
|
||||||
|
--- @field LAYERS table<string, integer>
|
||||||
local render = {
|
local render = {
|
||||||
textures = {}
|
textures = {},
|
||||||
|
queue = {},
|
||||||
|
lowResScale = 1.0,
|
||||||
|
LAYERS = {
|
||||||
|
FLOOR = 1,
|
||||||
|
SHADOW = 2,
|
||||||
|
LIGHT = 3,
|
||||||
|
SPRITE = 4,
|
||||||
|
OVERLAY = 5
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function render:clear()
|
function render:clear()
|
||||||
local weather = Tree.level.weather
|
local weather = Tree.level.weather
|
||||||
local txs = self.textures
|
local txs = self.textures
|
||||||
|
self.queue = {}
|
||||||
|
|
||||||
love.graphics.setCanvas(txs.shadowLayer)
|
love.graphics.setCanvas(txs.shadowLayer)
|
||||||
love.graphics.clear()
|
love.graphics.clear()
|
||||||
love.graphics.setCanvas(txs.spriteLayer)
|
love.graphics.setCanvas(txs.spriteLayer)
|
||||||
love.graphics.clear()
|
love.graphics.clear()
|
||||||
love.graphics.setCanvas(txs.spriteLightLayer)
|
|
||||||
love.graphics.clear(weather.skyLight.x, weather.skyLight.y, weather.skyLight.z)
|
|
||||||
love.graphics.setCanvas(txs.floorLayer)
|
love.graphics.setCanvas(txs.floorLayer)
|
||||||
love.graphics.clear()
|
love.graphics.clear()
|
||||||
love.graphics.setCanvas(txs.lightLayer)
|
love.graphics.setCanvas(txs.lightLayer)
|
||||||
@ -21,6 +33,10 @@ function render:clear()
|
|||||||
love.graphics.clear()
|
love.graphics.clear()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function render:enqueue(layer, z, func)
|
||||||
|
table.insert(self.queue, { layer = layer, z = z, func = func })
|
||||||
|
end
|
||||||
|
|
||||||
function render:free()
|
function render:free()
|
||||||
for _, tx in pairs(self.textures) do
|
for _, tx in pairs(self.textures) do
|
||||||
tx:release()
|
tx:release()
|
||||||
@ -57,50 +73,102 @@ function render:applyBlur(input, radius)
|
|||||||
return self.textures.tmp2
|
return self.textures.tmp2
|
||||||
end
|
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),
|
||||||
|
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()
|
function render:draw()
|
||||||
local weather = Tree.level.weather
|
local weather = Tree.level.weather
|
||||||
local txs = self.textures
|
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.LIGHT
|
||||||
|
if wasLowRes then
|
||||||
|
love.graphics.pop()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
currentLayer = entry.layer
|
||||||
|
local isLowRes = currentLayer == self.LAYERS.SHADOW 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.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.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.LIGHT
|
||||||
|
if wasLowRes then
|
||||||
|
love.graphics.pop()
|
||||||
|
end
|
||||||
|
end
|
||||||
love.graphics.setCanvas()
|
love.graphics.setCanvas()
|
||||||
|
|
||||||
-- self.lightLayer:newImageData():encode("png", "lightLayer.png")
|
-- 3. Пост-процессинг и композиция
|
||||||
-- os.exit(0)
|
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
|
local lightShader = Tree.assets.files.shaders.light_postprocess
|
||||||
lightShader:send("scene", txs.floorLayer)
|
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 })
|
lightShader:send("ambient", { weather.ambientLight.x, weather.ambientLight.y, weather.ambientLight.z })
|
||||||
love.graphics.setShader(lightShader)
|
love.graphics.setShader(lightShader)
|
||||||
love.graphics.draw(txs.floorLayer)
|
love.graphics.draw(txs.floorLayer)
|
||||||
|
|
||||||
love.graphics.setShader()
|
love.graphics.setShader()
|
||||||
love.graphics.draw(txs.overlayLayer)
|
love.graphics.draw(txs.overlayLayer)
|
||||||
love.graphics.setShader(lightShader)
|
|
||||||
|
|
||||||
lightShader:send("scene", txs.spriteLayer)
|
-- Спрайты уже полностью освещены в SpriteBehavior (с учетом ambient и point lights)
|
||||||
lightShader:send("light", txs.spriteLightLayer)
|
-- Поэтому рисуем их "как есть"
|
||||||
love.graphics.draw(txs.spriteLayer)
|
love.graphics.draw(txs.spriteLayer)
|
||||||
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
|
end
|
||||||
|
|
||||||
return { new = new }
|
return { new = new }
|
||||||
|
|||||||
@ -54,8 +54,7 @@ function spell:draw()
|
|||||||
local path = self.path --[[@as Deque?]]
|
local path = self.path --[[@as Deque?]]
|
||||||
if not path then return end
|
if not path then return end
|
||||||
--- Это отрисовка пути персонажа к мышке
|
--- Это отрисовка пути персонажа к мышке
|
||||||
Tree.level.camera:attach()
|
Tree.level.render:enqueue(Tree.level.render.LAYERS.OVERLAY, 0, function()
|
||||||
love.graphics.setCanvas(Tree.level.render.textures.overlayLayer)
|
|
||||||
local i = 0
|
local i = 0
|
||||||
path:pop_front()
|
path:pop_front()
|
||||||
for p in path:values() do
|
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')
|
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)
|
love.graphics.draw(icons.atlas, quad, p.x, p.y, 0, s, s)
|
||||||
end
|
end
|
||||||
love.graphics.setCanvas()
|
|
||||||
Tree.level.camera:detach()
|
|
||||||
love.graphics.setColor(1, 1, 1)
|
love.graphics.setColor(1, 1, 1)
|
||||||
|
end)
|
||||||
else
|
else
|
||||||
Tree.level.camera:attach()
|
Tree.level.render:enqueue(Tree.level.render.LAYERS.OVERLAY, 0, function()
|
||||||
love.graphics.setCanvas(Tree.level.render.textures.overlayLayer)
|
|
||||||
love.graphics.setColor(1, 1, 1, 0.5)
|
love.graphics.setColor(1, 1, 1, 0.5)
|
||||||
for _, p in pairs(self.targets) do
|
for _, p in pairs(self.targets) do
|
||||||
local s = self.tSize / Tree.level.camera.pixelsPerMeter
|
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)
|
love.graphics.draw(icons.atlas, quad, p.x + 0.5 - self.tSize / 2, p.y + 0.5 - self.tSize / 2, 0, s, s)
|
||||||
end
|
end
|
||||||
love.graphics.setShader()
|
love.graphics.setShader()
|
||||||
|
|
||||||
love.graphics.setCanvas()
|
|
||||||
Tree.level.camera:detach()
|
|
||||||
love.graphics.setColor(1, 1, 1)
|
love.graphics.setColor(1, 1, 1)
|
||||||
|
end)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
10
main.lua
10
main.lua
@ -62,6 +62,14 @@ function love.load()
|
|||||||
Tree.level.turnOrder:add(id)
|
Tree.level.turnOrder:add(id)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- --- Это тестовый источник света, привязанный к мышке, и я очень прошу его не трогать
|
||||||
|
-- character.spawn("light")
|
||||||
|
-- :addBehavior {
|
||||||
|
-- Tree.behaviors.positioned.new(),
|
||||||
|
-- Tree.behaviors.cursor.new(),
|
||||||
|
-- Tree.behaviors.light.new { color = Vec3 { 0.5, 0.5, 0.5 }, intensity = 5 }
|
||||||
|
-- }
|
||||||
|
|
||||||
|
|
||||||
Tree.level.turnOrder:endRound()
|
Tree.level.turnOrder:endRound()
|
||||||
print("Now playing:", Tree.level.turnOrder.current)
|
print("Now playing:", Tree.level.turnOrder.current)
|
||||||
@ -130,5 +138,5 @@ function love.resize(w, h)
|
|||||||
local render = Tree.level.render
|
local render = Tree.level.render
|
||||||
if not render then return end
|
if not render then return end
|
||||||
render:free()
|
render:free()
|
||||||
Tree.level.render = (require "lib.level.render").new { w, h }
|
Tree.level.render = (require "lib.level.render").new { w = w, h = h }
|
||||||
end
|
end
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user