- shadows demo

- scaling
 - math fix
 - refactoring
This commit is contained in:
Ivan Yuriev 2025-04-17 03:52:58 +03:00
parent 8fd4805cc7
commit 6a8bc1e07c
17 changed files with 140 additions and 57 deletions

View File

@ -9,5 +9,8 @@
"Lua.runtime.special": {
"love.filesystem.load": "loadfile"
},
"Lua.workspace.checkThirdParty": false
"Lua.workspace.checkThirdParty": false,
"Lua.diagnostics.disable": [
"undefined-field"
]
}

View File

@ -0,0 +1,25 @@
extern vec2 light_dir; // направление света, нормализованное
extern float flatten; // насколько тень сплющена (0.2 - норм)
extern float softness; // степень мягкости по краям
vec4 effect(vec4 color, Image tex, vec2 tex_coords, vec2 screen_coords)
{
// Получаем оригинальный цвет спрайта
vec4 tex_color = Texel(tex, tex_coords);
// Преобразуем цвет в "тень"
float alpha = tex_color.a;
vec3 shadow_color = vec3(0.0); // черная тень
// Модифицируем UV, чтобы проецировать вниз и вбок
vec2 offset = tex_coords;
offset.y -= tex_coords.y * flatten; // сплющиваем вниз
offset.x += tex_coords.y * flatten * light_dir.x; // сдвигаем в сторону
vec4 proj = Texel(tex, offset);
// Мягкость края тени (можно отключить, если не нужно)
float edge_fade = smoothstep(0.0, softness, proj.a);
return vec4(shadow_color, proj.a * edge_fade * 0.6); // альфа настраивается
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 26 KiB

View File

@ -2,7 +2,6 @@ local __Animation = {
spriteSheet = nil,
quads = nil,
fps = 12,
currentTime = 0
}
function Animation(image, width, height)
@ -21,16 +20,8 @@ function Animation(image, width, height)
return setmetatable(animation, { __index = __Animation })
end
function __Animation:getQuad()
local frametime = 1 / self.fps
local frame = math.floor(self.currentTime / frametime)
function __Animation:getQuad(t)
local duration = #self.quads / self.fps
local frame = math.floor(t * duration * #self.quads)
return self.quads[frame + 1]
end
function __Animation:update(dt)
self.currentTime = (self.currentTime + dt) % (#self.quads / self.fps)
end
function __Animation:reset()
self.currentTime = 0
end

View File

@ -43,7 +43,10 @@ function AssetBundle.loadFile(path)
img:setFilter("nearest", "nearest")
return
Some(img)
elseif (ext == "glsl") then
return Some(love.graphics.newShader(path));
end
return None
end

View File

@ -6,10 +6,11 @@ __Entity = {
sprite = __AnimatedSprite,
position = Vec3 {},
velocity = Vec3 {},
rotation = 0, -- clockwise radians
rotation = 0, -- clockwise radians
friction = 0.98,
speed = 3, -- m/s
rotation_speed = 1 --rad/sec
speed = 3, -- m/s
rotation_speed = 1, --rad/sec
state = "walk"
}
function Entity(id)
@ -23,8 +24,6 @@ end
function __Entity:update(dt)
self:processMovement()
if self.sprite then
local dir = self:namedDirection()
self.sprite.playing = "walk_" .. dir
self.sprite:update(dt)
end
end
@ -35,29 +34,12 @@ function __Entity:processMovement()
self.rotation = self.velocity:direction()
end
function __Entity:namedDirection()
local get_direction_index = function(rotation)
local pi = math.pi
rotation = rotation % (2 * pi)
local shifted = (rotation + pi / 8) % (2 * pi)
local index = math.floor(shifted / (pi / 4)) + 1
return index
end
local lookup = {
"e",
"ne",
"n",
"nw",
"w",
"sw",
"s",
"se",
}
return lookup[get_direction_index(self.rotation)]
end
function __Entity:lookAt(vec)
self.rotation = (vec - self.position):direction()
self.rotation = self.position:angle_to(vec)
end
function __Entity:spriteFromAngle(angle)
local dir = math.named_direction(angle - self.rotation)
local key = self.state .. "_" .. dir
return self.sprite:getTexture(key), self.sprite:getQuad(key)
end

22
lib/math2.lua Normal file
View File

@ -0,0 +1,22 @@
math.named_direction = function(rotation)
local get_direction_index = function(rotation)
local pi = math.pi
rotation = rotation % (2 * pi)
local shifted = (rotation + pi / 8) % (2 * pi)
local index = math.floor(shifted / (pi / 4)) + 1
return index
end
local lookup = {
"s",
"se",
"e",
"ne",
"n",
"nw",
"w",
"sw",
}
return lookup[get_direction_index(rotation)]
end

View File

@ -2,34 +2,34 @@ require 'lib.asset_bundle'
require 'lib.animation'
__AnimatedSprite = {}
__AnimatedSprite = {
t = 0,
}
function AnimatedSprite(id)
local table = {}
local bundle = AssetBundle.files.sprites[id]
for key, value in pairs(bundle) do
table[key] = Animation(value, 96, 96)
table[key] = Animation(value, value:getHeight(), value:getHeight())
end
return setmetatable(table, { __index = __AnimatedSprite })
end
function __AnimatedSprite:update(dt)
if self.playing then
self[self.playing]:update(dt)
end
self.t = (self.t + dt) % 1
end
function __AnimatedSprite:getQuad()
if self.playing then
return self[self.playing]:getQuad()
function __AnimatedSprite:getQuad(key)
if key then
return self[key]:getQuad(self.t)
end
return love.graphics.newQuad(0, 0, 235, 235, AssetBundle.files.sprites.fallback)
end
function __AnimatedSprite:getTexture()
if self.playing then
return self[self.playing].spriteSheet
function __AnimatedSprite:getTexture(key)
if key then
return self[key].spriteSheet
end
return AssetBundle.files.sprites.fallback
end

View File

@ -52,7 +52,7 @@ function __Vec3:normalize()
end
function __Vec3:direction()
return -math.atan2(self.y, self.x)
return math.atan2(self.y, self.x)
end
function __Vec3:dot(other)
@ -62,3 +62,7 @@ end
function __Vec3:__tostring()
return "Vec3{" .. self.x .. ", " .. self.y .. ", " .. self.z .. "}"
end
function __Vec3:angle_to(other)
return (other - self):direction()
end

View File

@ -1,12 +1,18 @@
require "lib.asset_bundle"
require "lib.player"
require "lib.math2"
PIXEL_PER_METER = 48
P = nil
function love.load()
AssetBundle:load()
love.window.setMode(1080, 720, { resizable = true, msaa = 4 })
P = Player('fox')
P.entity.position = Vec3 { 200, 200 }
print('ready')
end
function love.update(dt)
@ -14,11 +20,58 @@ function love.update(dt)
end
function love.draw()
love.graphics.draw(P.entity.sprite:getTexture(), P.entity.sprite:getQuad(), P.entity.position.x, P.entity.position.y,
P.entity.direction,
2, 2, 48, 48)
love.graphics.clear(1, 1, 1)
local spriteCanvas = love.graphics.newCanvas()
spriteCanvas:setFilter("linear", "linear")
love.graphics.setCanvas(spriteCanvas)
love.graphics.clear(1, 1, 1)
drawShadow(P.entity, Vec3 { 200, 200 }, 200)
drawShadow(P.entity, Vec3 { 300, 200 }, 200)
drawShadow(P.entity, Vec3 { 400, 200 }, 200)
love.graphics.setColor(1, 0, 0, 1)
love.graphics.circle("fill", 200, 200, 10)
love.graphics.circle("fill", 300, 200, 10)
love.graphics.circle("fill", 400, 200, 10)
love.graphics.setColor(1, 1, 1, 1)
local tex, quad = P.entity:spriteFromAngle(math.pi / 2)
love.graphics.draw(tex, quad, P.entity.position.x, P.entity.position.y,
0,
1, 1, tex:getHeight() / 2, tex:getHeight())
love.graphics.setCanvas()
local scale = math.min(love.graphics.getDimensions()) / (15 * PIXEL_PER_METER);
love.graphics.draw(spriteCanvas, 0, 0, 0, scale, scale)
love.graphics.setColor(1, 1, 1, 1)
end
function love.conf(t)
t.console = true
end
function drawShadow(entity, light, radius)
local shadow_vec = light - entity.position
local dist = shadow_vec:length()
if dist > radius then return end
love.graphics.setColor(0, 0, 0, math.min(0.5, 1 - (dist / radius)))
local tex, quad = P.entity:spriteFromAngle(shadow_vec:direction())
love.graphics.draw(tex, quad, P.entity.position.x, P.entity.position.y - 10,
shadow_vec:direction() - math.pi / 2,
1, math.sin(20), tex:getHeight() / 2, tex:getHeight())
-- love.graphics.push()
-- love.graphics.setColor(0, 0, 0, 0.5)
-- love.graphics.translate(entity.position.x, entity.position.y)
-- love.graphics.scale(1, 0.342)
-- love.graphics.circle("fill", 0, 0, 20)
-- love.graphics.pop()
love.graphics.setColor(1, 1, 1, 1) -- сброс цвета
end