From 0f882da30093233cb88c9977aafc5cefb64ba3af Mon Sep 17 00:00:00 2001 From: Tim Anema Date: Tue, 21 Oct 2014 22:48:19 -0400 Subject: [PATCH] finally coming up with a proper strategy to handle translation and zooming of canvases --- examples/short.lua | 24 +++++----- lib/body.lua | 17 +++++-- lib/light.lua | 89 +++++++++++++++++----------------- lib/light_world.lua | 114 +++++++++++++++++--------------------------- lib/postshader.lua | 3 +- lib/stencils.lua | 29 ++++------- lib/util.lua | 40 ++++++++++++++++ main.lua | 2 +- 8 files changed, 164 insertions(+), 154 deletions(-) create mode 100644 lib/util.lua diff --git a/examples/short.lua b/examples/short.lua index b9577e1..7b4fc27 100644 --- a/examples/short.lua +++ b/examples/short.lua @@ -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) diff --git a/lib/body.lua b/lib/body.lua index dda4e26..60f57ba 100644 --- a/lib/body.lua +++ b/lib/body.lua @@ -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 diff --git a/lib/light.lua b/lib/light.lua index 4279ed4..59fa3ab 100644 --- a/lib/light.lua +++ b/lib/light.lua @@ -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 diff --git a/lib/light_world.lua b/lib/light_world.lua index dd700f7..0bacea1 100644 --- a/lib/light_world.lua +++ b/lib/light_world.lua @@ -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) diff --git a/lib/postshader.lua b/lib/postshader.lua index 2352429..94091ec 100644 --- a/lib/postshader.lua +++ b/lib/postshader.lua @@ -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) diff --git a/lib/stencils.lua b/lib/stencils.lua index 073fb15..c5561cb 100644 --- a/lib/stencils.lua +++ b/lib/stencils.lua @@ -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 diff --git a/lib/util.lua b/lib/util.lua new file mode 100644 index 0000000..f8a62b6 --- /dev/null +++ b/lib/util.lua @@ -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 diff --git a/main.lua b/main.lua index 651d50d..50e1420 100644 --- a/main.lua +++ b/main.lua @@ -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()