finally coming up with a proper strategy to handle translation and zooming of canvases

This commit is contained in:
Tim Anema 2014-10-21 22:48:19 -04:00
parent 22c45f7bac
commit 0f882da300
8 changed files with 164 additions and 154 deletions

View File

@ -1,11 +1,11 @@
-- Example: Short Example
local gamera = require "vendor/gamera"
local LightWorld = require "lib/light_world"
function love.load()
testShader = 0
x = 0
y = 0
scale = 1
camera = gamera.new(0,0,2000,2000)
-- load images
image = love.graphics.newImage("gfx/machine2.png")
image_normal = love.graphics.newImage("gfx/cone_normal.png")
@ -62,7 +62,6 @@ end
function love.update(dt)
love.window.setTitle("Light vs. Shadow Engine (FPS:" .. love.timer.getFPS() .. ")")
local x, y = camera:getPosition()
if love.keyboard.isDown("up") then
y = y - dt * 200
elseif love.keyboard.isDown("down") then
@ -81,20 +80,23 @@ function love.update(dt)
scale = scale + 0.01
end
camera:setPosition(x, y)
camera:setScale(scale)
lightMouse:setPosition(camera:toWorld(love.mouse.getX(), love.mouse.getY()))
lightMouse:setPosition(love.mouse.getX(), love.mouse.getY())
end
function love.draw()
camera:draw(function(l,t,w,h)
lightWorld:draw(l,t,w,h,scale)
end)
love.graphics.push()
love.graphics.translate(x, y)
love.graphics.scale(scale)
lightWorld:draw(x,y,scale)
love.graphics.pop()
end
function drawBackground(l,t,w,h)
love.graphics.setColor(255, 255, 255)
love.graphics.rectangle("fill", 0, 0, 2000, 2000)
love.graphics.push()
love.graphics.origin()
love.graphics.setColor(255, 255, 255)
love.graphics.rectangle("fill", 0, 0, w, h)
love.graphics.pop()
end
function drawForground(l,t,w,h)

View File

@ -363,7 +363,6 @@ function body:setShadowType(type, ...)
self:refresh()
elseif self.shadowType == "polygon" then
self.data = args or {0, 0, 0, 0, 0, 0}
print(self.data)
elseif self.shadowType == "image" then
if self.img then
self.width = self.imgWidth
@ -388,7 +387,19 @@ function body:setShadowType(type, ...)
end
end
function body:drawShadow(light, l,t,w,h)
function body:shadowStencil()
if self.shadowType == "circle" then
love.graphics.circle("fill", self.x - self.ox, self.y - self.oy, self.radius)
elseif self.shadowType == "rectangle" then
love.graphics.rectangle("fill", self.x - self.ox, self.y - self.oy, self.width, self.height)
elseif self.shadowType == "polygon" then
love.graphics.polygon("fill", unpack(self.data))
elseif self.shadowType == "image" then
--love.graphics.rectangle("fill", self.x - self.ox, self.y - self.oy, self.width, self.height)
end
end
function body:drawShadow(light, l,t,w,h,s)
if self.alpha < 1.0 then
love.graphics.setBlendMode("multiplicative")
love.graphics.setColor(self.red, self.green, self.blue)
@ -446,7 +457,7 @@ function body:drawShadow(light, l,t,w,h)
}
self.shadowMesh:setVertices(self.shadowVert)
love.graphics.draw(self.shadowMesh, self.x - self.ox + l, self.y - self.oy + t)
love.graphics.draw(self.shadowMesh, self.x - self.ox + l, self.y - self.oy + t, 0, s, s)
end
end

View File

@ -1,6 +1,7 @@
local _PACKAGE = (...):match("^(.+)[%./][^%./]+") or ""
local class = require(_PACKAGE.."/class")
local stencils = require(_PACKAGE..'/stencils')
local util = require(_PACKAGE..'/util')
local light = class()
@ -127,20 +128,8 @@ function light:inRange(l,t,w,h)
return self.x + self.range > l and self.x - self.range < (l+w) and self.y + self.range > t and self.y - self.range < (t+h)
end
function light:updateShadow(l,t,w,h, bodies)
love.graphics.setShader(self.shader)
function light:drawShadow(l,t,w,h,s,bodies, canvas)
if self.visible and self:inRange(l,t,w,h) then
self.shader:send("lightPosition", {self.x - l, h - (self.y - t), self.z})
self.shader:send("lightRange", self.range)
self.shader:send("lightColor", {self.red / 255.0, self.green / 255.0, self.blue / 255.0})
self.shader:send("lightSmooth", self.smooth)
self.shader:send("lightGlow", {1.0 - self.glowSize, self.glowStrength})
self.shader:send("lightAngle", math.pi - self.angle / 2.0)
self.shader:send("lightDirection", self.direction)
self.shadow:clear()
love.graphics.setCanvas(self.shadow)
-- calculate shadows
local shadow_geometry = {}
for i = 1, #bodies do
@ -150,46 +139,54 @@ function light:updateShadow(l,t,w,h, bodies)
end
end
self.shadow:clear()
self.shader:send("lightPosition", {self.x - l, h - (self.y - t), self.z})
self.shader:send("lightRange", self.range)
self.shader:send("lightColor", {self.red / 255.0, self.green / 255.0, self.blue / 255.0})
self.shader:send("lightSmooth", self.smooth)
self.shader:send("lightGlow", {1.0 - self.glowSize, self.glowStrength})
self.shader:send("lightAngle", math.pi - self.angle / 2.0)
self.shader:send("lightDirection", self.direction)
-- draw shadow
love.graphics.setInvertedStencil(stencils.shadow(shadow_geometry, bodies))
love.graphics.setBlendMode("additive")
love.graphics.rectangle("fill", l,t,w,h)
util.drawto(self.shadow, l, t, s, function()
love.graphics.setShader(self.shader)
love.graphics.setInvertedStencil(stencils.shadow(shadow_geometry, bodies))
love.graphics.setBlendMode("additive")
love.graphics.rectangle("fill", l,t,w,h)
-- draw color shadows
love.graphics.setBlendMode("multiplicative")
love.graphics.setShader()
for k = 1,#shadow_geometry do
if shadow_geometry[k].alpha < 1.0 then
love.graphics.setColor(
shadow_geometry[k].red * (1.0 - shadow_geometry[k].alpha),
shadow_geometry[k].green * (1.0 - shadow_geometry[k].alpha),
shadow_geometry[k].blue * (1.0 - shadow_geometry[k].alpha)
)
love.graphics.polygon("fill", unpack(shadow_geometry[k]))
-- draw color shadows
love.graphics.setBlendMode("multiplicative")
love.graphics.setShader()
for k = 1,#shadow_geometry do
if shadow_geometry[k].alpha < 1.0 then
love.graphics.setColor(
shadow_geometry[k].red * (1.0 - shadow_geometry[k].alpha),
shadow_geometry[k].green * (1.0 - shadow_geometry[k].alpha),
shadow_geometry[k].blue * (1.0 - shadow_geometry[k].alpha)
)
love.graphics.polygon("fill", unpack(shadow_geometry[k]))
end
end
end
for k = 1, #bodies do
bodies[k]:drawShadow(self, l,t,w,h)
end
for k = 1, #bodies do
bodies[k]:drawShadow(self,l,t,w,h,s)
end
end)
-- draw shine
love.graphics.setShader(self.shader)
love.graphics.setCanvas(self.shine)
self.shine:clear(255, 255, 255)
love.graphics.setBlendMode("alpha")
love.graphics.setStencil(stencils.poly(bodies))
love.graphics.rectangle("fill", l,t,w,h)
love.graphics.setStencil()
end
love.graphics.setShader()
end
util.drawto(self.shine, l, t, s, function()
love.graphics.setShader(self.shader)
self.shine:clear(255, 255, 255)
love.graphics.setBlendMode("alpha")
love.graphics.setStencil(stencils.colorShadow(bodies))
love.graphics.rectangle("fill", 0,0,w,h)
end)
function light:drawShadow(l,t,w,h)
if self.visible then
love.graphics.setColor(255, 255, 255)
love.graphics.setBlendMode("additive")
love.graphics.draw(self.shadow, l, t)
love.graphics.setStencil()
love.graphics.setShader()
util.drawCanvasToCanvas(self.shadow, canvas, {blendmode = "additive"})
end
end

View File

@ -25,6 +25,7 @@ local _PACKAGE = (...):match("^(.+)[%./][^%./]+") or ""
local class = require(_PACKAGE..'/class')
local Light = require(_PACKAGE..'/light')
local Body = require(_PACKAGE..'/body')
local util = require(_PACKAGE..'/util')
local normal_map = require(_PACKAGE..'/normal_map')
local PostShader = require(_PACKAGE..'/postshader')
require(_PACKAGE..'/postshader')
@ -62,44 +63,19 @@ function light_world:init(options)
self:refreshScreenSize()
end
function light_world:drawBlur(blendmode, blur, canvas, canvas2, l, t, w, h)
function light_world:drawBlur(blendmode, blur, canvas, canvas2, l, t, w, h, s)
if blur <= 0 then
return
end
love.graphics.setColor(255, 255, 255)
canvas2:clear()
self.blurv:send("steps", blur)
self.blurh:send("steps", blur)
love.graphics.setBlendMode(blendmode)
canvas2:clear()
love.graphics.setCanvas(canvas2)
love.graphics.setShader(self.blurv)
love.graphics.draw(canvas, l, t)
love.graphics.setCanvas(canvas)
love.graphics.setShader(self.blurh)
love.graphics.draw(canvas2, l, t)
util.drawCanvasToCanvas(canvas, canvas2, {shader = self.blurv, blendmode = blendmode})
util.drawCanvasToCanvas(canvas2, canvas, {shader = self.blurh, blendmode = blendmode})
end
function light_world:updateShadows(l,t,w,h)
for i = 1, #self.lights do
self.lights[i]:updateShadow(l,t,w,h, self.body)
end
-- update shadow
love.graphics.setCanvas(self.shadow)
love.graphics.setColor(unpack(self.ambient))
love.graphics.setBlendMode("alpha")
love.graphics.rectangle("fill", l, t, w, h)
for i = 1, #self.lights do
self.lights[i]:drawShadow(l,t,w,h)
end
light_world:drawBlur("alpha", self.blur, self.shadow, self.shadow2, l, t, w, h)
love.graphics.setCanvas(self.render_buffer)
end
function light_world:updateShine(l,t,w,h)
function light_world:updateShine(l,t,w,h,s)
-- update shine
love.graphics.setCanvas(self.shine)
love.graphics.setColor(unpack(self.ambient))
@ -112,8 +88,7 @@ function light_world:updateShine(l,t,w,h)
self.lights[i]:drawShine(l,t,w,h)
end
--light_world:drawBlur("additive", self.blur, self.shine, self.shine2, l, t, w, h)
love.graphics.setCanvas(self.render_buffer)
--light_world:drawBlur("additive", self.blur, self.shine, self.shine2, l, t, w, h, s)
end
function light_world:updatePixelShadows(l,t,w,h)
@ -148,11 +123,9 @@ function light_world:updatePixelShadows(l,t,w,h)
love.graphics.setBlendMode("additive")
love.graphics.setColor({self.ambient[1], self.ambient[2], self.ambient[3]})
love.graphics.rectangle("fill", l,t,w,h)
love.graphics.setBlendMode("alpha")
love.graphics.setCanvas(self.render_buffer)
end
function light_world:updateGlow(l,t,w,h)
function light_world:updateGlow(l,t,w,h,s)
-- create glow map
self.glowMap:clear(0, 0, 0)
love.graphics.setCanvas(self.glowMap)
@ -173,8 +146,7 @@ function light_world:updateGlow(l,t,w,h)
self.body[i]:drawGlow(l,t,w,h)
end
light_world:drawBlur("alpha", self.glowBlur, self.glowMap, self.glowMap2, l, t, w, h)
love.graphics.setCanvas(self.render_buffer)
light_world:drawBlur("alpha", self.glowBlur, self.glowMap, self.glowMap2, l, t, w, h, s)
end
function light_world:updateRefraction(l,t,w,h)
@ -189,8 +161,6 @@ function light_world:updateRefraction(l,t,w,h)
love.graphics.setBlendMode("alpha")
love.graphics.setCanvas(self.refractionMap2)
love.graphics.draw(self.render_buffer, l, t)
love.graphics.setShader()
love.graphics.setCanvas(self.render_buffer)
end
function light_world:updateRelfection(l,t,w,h)
@ -205,8 +175,6 @@ function light_world:updateRelfection(l,t,w,h)
love.graphics.setBlendMode("alpha")
love.graphics.setCanvas(self.reflectionMap2)
love.graphics.draw(self.render_buffer, l, t)
love.graphics.setShader()
love.graphics.setCanvas(self.render_buffer)
end
function light_world:refreshScreenSize(w, h)
@ -239,32 +207,24 @@ function light_world:refreshScreenSize(w, h)
self.post_shader:refreshScreenSize(w, h)
end
function light_world:draw(l,t,w,h,s)
l,t,w,h,s = (l or 0), (t or 0), (w or love.graphics.getWidth()), (h or love.graphics.getHeight()), s or 1
function light_world:draw(l,t,s)
l,t,s = (l or 0), (t or 0), s or 1
local w, h = (love.graphics.getWidth() / s), (love.graphics.getHeight() / s)
if s ~= self.scale then
--self:refreshScreenSize(w, h)
self.scale = scale
end
util.drawto(self.render_buffer, l, t, s, function()
self.drawBackground( l,t,w,h,s)
self:drawShadow( l,t,w,h,s)
self.drawForground( l,t,w,h,s)
--self:drawMaterial( l,t,w,h,s)
--self:drawShine( l,t,w,h,s)
--self:drawPixelShadow(l,t,w,h,s)
--self:drawGlow( l,t,w,h,s)
--self:drawRefraction( l,t,w,h,s)
--self:drawReflection( l,t,w,h,s)
end)
local last_buffer = love.graphics.getCanvas()
love.graphics.setCanvas(self.render_buffer)
love.graphics.push()
love.graphics.scale(1/s)
local sl, st, sw, sh = (l*s), (t*s), (w*s), (h*s)
self.drawBackground( sl,st,sw,sh,s)
self:drawShadow( sl,st,sw,sh,s)
self.drawForground( sl,st,sw,sh,s)
self:drawMaterial( sl,st,sw,sh,s)
self:drawShine( sl,st,sw,sh,s)
self:drawPixelShadow( sl,st,sw,sh,s)
self:drawGlow( sl,st,sw,sh,s)
self:drawRefraction( sl,st,sw,sh,s)
self:drawReflection( sl,st,sw,sh,s)
love.graphics.pop()
self.post_shader:drawWith(self.render_buffer, l, t, s)
util.drawCanvasToCanvas(self.render_buffer)
--self.post_shader:drawWith(self.render_buffer, l, t, s)
end
-- draw shadow
@ -272,12 +232,19 @@ function light_world:drawShadow(l,t,w,h,s)
if not self.isShadows and not self.isLight then
return
end
self:updateShadows(l,t,w,h)
love.graphics.setColor(255, 255, 255)
love.graphics.setBlendMode("multiplicative")
love.graphics.setShader()
love.graphics.draw(self.shadow, l, t)
love.graphics.setBlendMode("alpha")
-- draw ambient
util.drawto(self.shadow, l, t, s, function()
love.graphics.setColor(unpack(self.ambient))
love.graphics.setBlendMode("alpha")
love.graphics.rectangle("fill", l, t, w, h)
for i = 1, #self.lights do
self.lights[i]:drawShadow(l,t,w,h,s,self.body, self.shadow)
end
end)
light_world:drawBlur("alpha", self.blur, self.shadow, self.shadow2, l, t, w, h, s)
util.drawCanvasToCanvas(self.shadow, self.render_buffer, {blendmode = "multiplicative"})
end
-- draw shine
@ -286,6 +253,7 @@ function light_world:drawShine(l,t,w,h,s)
return
end
self:updateShine(l,t,w,h)
love.graphics.setCanvas(self.render_buffer)
love.graphics.setColor(255, 255, 255)
love.graphics.setBlendMode("multiplicative")
love.graphics.setShader()
@ -299,6 +267,7 @@ function light_world:drawPixelShadow(l,t,w,h,s)
return
end
self:updatePixelShadows(l,t,w,h)
love.graphics.setCanvas(self.render_buffer)
love.graphics.setColor(255, 255, 255)
love.graphics.setBlendMode("multiplicative")
love.graphics.setShader()
@ -319,6 +288,7 @@ function light_world:drawGlow(l,t,w,h,s)
return
end
self:updateGlow(l,t,w,h)
love.graphics.setCanvas(self.render_buffer)
love.graphics.setColor(255, 255, 255)
love.graphics.setBlendMode("additive")
love.graphics.setShader()
@ -331,6 +301,7 @@ function light_world:drawRefraction(l,t,w,h,s)
return
end
self:updateRefraction(l,t,w,h)
love.graphics.setCanvas(self.render_buffer)
self.refractionShader:send("backBuffer", self.refractionMap2)
self.refractionShader:send("refractionStrength", self.refractionStrength)
love.graphics.setShader(self.refractionShader)
@ -344,6 +315,7 @@ function light_world:drawReflection(l,t,w,h,s)
return
end
self:updateRelfection(l,t,w,h)
love.graphics.setCanvas(self.render_buffer)
self.reflectionShader:send("backBuffer", self.reflectionMap2)
self.reflectionShader:send("reflectionStrength", self.reflectionStrength)
self.reflectionShader:send("reflectionVisibility", self.reflectionVisibility)

View File

@ -69,7 +69,7 @@ function post_shader:toggleEffect(shaderName, ...)
end
end
function post_shader:drawWith(canvas, l, t)
function post_shader:drawWith(canvas, l, t, scale)
for shader, args in pairs(self.effects) do
if shader == "bloom" then
self:drawBloom(canvas, args)
@ -274,7 +274,6 @@ function post_shader:drawTest(canvas, args)
effect[1]:send(def, defaults[def])
end
end
print(effect[3])
love.graphics.setShader(effect[1])
love.graphics.draw(canvas)

View File

@ -2,40 +2,29 @@ local stencils = {}
function stencils.shadow(geometry, bodies)
return function()
--cast shadows
for i = 1,#geometry do
if geometry[i].alpha == 1.0 then
love.graphics.polygon("fill", unpack(geometry[i]))
end
end
-- underneath shadows
for i = 1, #bodies do
if not bodies[i].castsNoShadow then
if bodies[i].shadowType == "circle" then
love.graphics.circle("fill", bodies[i].x - bodies[i].ox, bodies[i].y - bodies[i].oy, bodies[i].radius)
elseif bodies[i].shadowType == "rectangle" then
love.graphics.rectangle("fill", bodies[i].x - bodies[i].ox, bodies[i].y - bodies[i].oy, bodies[i].width, bodies[i].height)
elseif bodies[i].shadowType == "polygon" then
love.graphics.polygon("fill", unpack(bodies[i].data))
elseif bodies[i].shadowType == "image" then
--love.graphics.rectangle("fill", bodies[i].x - bodies[i].ox, bodies[i].y - bodies[i].oy, bodies[i].width, bodies[i].height)
end
bodies[i]:shadowStencil()
end
end
end
end
function stencils.poly(bodies)
function stencils.colorShadow(bodies)
return function()
for i = 1, #bodies do
if bodies[i].shine and (bodies[i].glowStrength == 0.0 or (bodies[i].type == "image" and not bodies[i].normal)) then
if bodies[i].shadowType == "circle" then
love.graphics.circle("fill", bodies[i].x - bodies[i].ox, bodies[i].y - bodies[i].oy, bodies[i].radius)
elseif bodies[i].shadowType == "rectangle" then
love.graphics.rectangle("fill", bodies[i].x - bodies[i].ox, bodies[i].y - bodies[i].oy, bodies[i].width, bodies[i].height)
elseif bodies[i].shadowType == "polygon" then
love.graphics.polygon("fill", unpack(bodies[i].data))
elseif bodies[i].shadowType == "image" then
--love.graphics.rectangle("fill", bodies[i].x - bodies[i].ox, bodies[i].y - bodies[i].oy, bodies[i].width, bodies[i].height)
end
if bodies[i].shine and
(bodies[i].glowStrength == 0.0 or
(bodies[i].type == "image" and not bodies[i].normal))
then
bodies[i]:shadowStencil()
end
end
end

40
lib/util.lua Normal file
View File

@ -0,0 +1,40 @@
local util = {}
function util.drawCanvasToCanvas(canvas, other_canvas, options)
options = options or {}
local last_buffer = love.graphics.getCanvas()
love.graphics.push()
love.graphics.origin()
love.graphics.setCanvas(other_canvas)
if options["blendmode"] then
love.graphics.setBlendMode(options["blendmode"])
end
if options["shader"] then
love.graphics.setShader(options["shader"])
end
love.graphics.setColor(255,255,255)
love.graphics.draw(canvas,0,0)
love.graphics.setCanvas(last_buffer)
if options["blendmode"] then
love.graphics.setBlendMode("alpha")
end
if options["shader"] then
love.graphics.setShader()
end
love.graphics.pop()
end
function util.drawto(canvas, x, y, scale, cb)
local last_buffer = love.graphics.getCanvas()
love.graphics.push()
love.graphics.origin()
love.graphics.setCanvas(canvas)
love.graphics.translate(x, y)
love.graphics.scale(scale)
cb()
love.graphics.setCanvas(last_buffer)
love.graphics.pop()
end
return util

View File

@ -58,7 +58,7 @@ function exf.update(dt)
end
function exf.draw()
lightWorld:draw(0, 0, love.window.getWidth(), love.window.getHeight(), 1)
lightWorld:draw()
end
function exf.drawBackground()