local tunnel={}
local texture,shader,mesh
local centerPixelX=1900
local centerPixelY=1060
tunnel.downscaleCap=2048
function tunnel.setCenter(x,y)
centerPixelX=x
centerPixelY=y
end
local function pow2_floor(n)
local p=1
while(p*2)<=n do p=p*2 end
return p
end
local function resampleNearest(src,sw,sh,dw,dh)
local dst=love.image.newImageData(dw,dh)
for y=0,dh-1 do
local sy=math.floor((y*sh)/dh)
if sy<0 then sy=0 end
if sy>sh-1 then sy=sh-1 end
for x=0,dw-1 do
local sx=math.floor((x*sw)/dw)
if sx<0 then sx=0 end
if sx>sw-1 then sx=sh-1 end
local r,g,b,a=src:getPixel(sx,sy)
dst:setPixel(x,y,r,g,b,a)
end end
return dst
end
function tunnel.load()
if shader then return end
local w,h=love.graphics.getDimensions()
local limits=love.graphics.getSystemLimits()
local maxTex=(limits and limits.texturesize) or 8192
local srcData=love.image.newImageData("tunnel.jpg")
local sw,sh=srcData:getDimensions()
local needDownscale=(sw>maxTex) or (sh>maxTex)
local finalData=srcData
if needDownscale then
local cap=math.min(maxTex,tunnel.downscaleCap or 2048)
local maxDim=math.max(sw,sh)
local scale=maxDim/cap
local dw=math.floor(sw/scale)
local dh=math.floor(sh/scale)
dw=pow2_floor(math.max(1,math.min(dw,cap)))
dh=pow2_floor(math.max(1,math.min(dh,cap)))
finalData=resampleNearest(srcData,sw,sh,dw,dh)
end
texture=love.graphics.newImage(finalData)
texture:setWrap("repeat","repeat")
mesh=love.graphics.newMesh({{0,0,0,0},{w,0,1,0},{w,h,1,1},{0,h,0,1}},"fan","static")
mesh:setTexture(texture)
local aspect=w/h
local maxRadius=math.sqrt((0.5*aspect)^2+0.5^2)
shader=love.graphics.newShader([[
extern number speed = 0.3;
extern number aspect;
extern number maxRadius;
extern number centerX;
extern number centerY;
extern number time;
const number PI = 3.14159265359;
vec4 effect(vec4 color, Image tex, vec2 uv, vec2 screen_coords) {
vec2 centered = uv - vec2(centerX, centerY);
centered.x *= aspect;
float angle= atan(centered.y, centered.x) / (2.0 * PI) + 0.5;
float radius = length(centered);
float offset = 1.0 - radius / maxRadius;
float scroll = fract(time * speed + offset);
vec2 tc = vec2(angle, scroll);
return Texel(tex, tc) * color;
} ]])
shader:send("aspect",aspect)
shader:send("maxRadius",maxRadius)
end
function tunnel.draw()
if not shader then tunnel.load() end
local w,h=love.graphics.getDimensions()
local baseX=centerPixelX or ((windowWidth or w)*0.5)
local baseY=centerPixelY or ((windowHeight or h)*0.5)
shader:send("centerX",baseX/w)
shader:send("centerY",baseY/h)
shader:send("time",love.timer.getTime())
love.graphics.setShader(shader)
love.graphics.draw(mesh,0,0)
love.graphics.setShader()
end
return tunnel
