From 735d5651423a66446990c604775005982a6c57b4 Mon Sep 17 00:00:00 2001 From: Tim Anema Date: Sat, 27 Sep 2014 15:58:15 -0400 Subject: [PATCH] split up calculate shadows a bit so it is a bit more managable --- lib/body.lua | 33 +++++++++ lib/light.lua | 185 ++++++++++++++++++++++++++++---------------------- 2 files changed, 135 insertions(+), 83 deletions(-) diff --git a/lib/body.lua b/lib/body.lua index 01e6cb5..9e70e08 100644 --- a/lib/body.lua +++ b/lib/body.lua @@ -168,6 +168,7 @@ function body:refresh() self.data[8] = self.y - self.oy + self.height end end + -- set position function body:setPosition(x, y) if x ~= self.x or y ~= self.y then @@ -177,6 +178,7 @@ function body:setPosition(x, y) self.world.changed = true end end + -- set x position function body:setX(x) if x ~= self.x then @@ -185,6 +187,7 @@ function body:setX(x) self.world.changed = true end end + -- set y position function body:setY(y) if y ~= self.y then @@ -193,30 +196,37 @@ function body:setY(y) self.world.changed = true end end + -- get x position function body:getX() return self.x end + -- get y position function body:getY(y) return self.y end + -- get width function body:getWidth() return self.width end + -- get height function body:getHeight() return self.height end + -- get image width function body:getImageWidth() return self.imgWidth end + -- get image height function body:getImageHeight() return self.imgHeight end + -- set dimension function body:setDimension(width, height) self.width = width @@ -224,6 +234,7 @@ function body:setDimension(width, height) self:refresh() self.world.changed = true end + -- set offset function body:setOffset(ox, oy) if ox ~= self.ox or oy ~= self.oy then @@ -235,6 +246,7 @@ function body:setOffset(ox, oy) self.world.changed = true end end + -- set offset function body:setImageOffset(ix, iy) if ix ~= self.ix or iy ~= self.iy then @@ -244,6 +256,7 @@ function body:setImageOffset(ix, iy) self.world.changed = true end end + -- set offset function body:setNormalOffset(nx, ny) if nx ~= self.nx or ny ~= self.ny then @@ -253,6 +266,7 @@ function body:setNormalOffset(nx, ny) self.world.changed = true end end + -- set glow color function body:setGlowColor(red, green, blue) self.glowRed = red @@ -260,15 +274,18 @@ function body:setGlowColor(red, green, blue) self.glowBlue = blue self.world.changed = true end + -- set glow alpha function body:setGlowStrength(strength) self.glowStrength = strength self.world.changed = true end + -- get radius function body:getRadius() return self.radius end + -- set radius function body:setRadius(radius) if radius ~= self.radius then @@ -276,25 +293,30 @@ function body:setRadius(radius) self.world.changed = true end end + -- set polygon data function body:setPoints(...) self.data = {...} self.world.changed = true end + -- get polygon data function body:getPoints() return unpack(self.data) end + -- set shadow on/off function body:setShadow(b) self.castsNoShadow = not b self.world.changed = true end + -- set shine on/off function body:setShine(b) self.shine = b self.world.changed = true end + -- set glass color function body:setColor(red, green, blue) self.red = red @@ -302,27 +324,33 @@ function body:setColor(red, green, blue) self.blue = blue self.world.changed = true end + -- set glass alpha function body:setAlpha(alpha) self.alpha = alpha self.world.changed = true end + -- set reflection on/off function body:setReflection(reflection) self.reflection = reflection end + -- set refraction on/off function body:setRefraction(refraction) self.refraction = refraction end + -- set reflective on other objects on/off function body:setReflective(reflective) self.reflective = reflective end + -- set refractive on other objects on/off function body:setRefractive(refractive) self.refractive = refractive end + -- set image function body:setImage(img) if img then @@ -333,6 +361,7 @@ function body:setImage(img) self.iy = self.imgHeight * 0.5 end end + -- set normal function body:setNormalMap(normal, width, height, nx, ny) if normal then @@ -355,10 +384,12 @@ function body:setNormalMap(normal, width, height, nx, ny) self.normalMesh = nil end end + -- set height map function body:setHeightMap(heightMap, strength) self:setNormalMap(height_map_conv.toNormalMap(heightMap, strength)) end + -- generate flat normal map function body:generateNormalMapFlat(mode) local imgData = self.img:getData() @@ -388,6 +419,7 @@ function body:generateNormalMapFlat(mode) self:setNormalMap(love.graphics.newImage(imgNormalData)) end + -- generate faded normal map function body:generateNormalMapGradient(horizontalGradient, verticalGradient) local imgData = self.img:getData() @@ -428,6 +460,7 @@ function body:generateNormalMapGradient(horizontalGradient, verticalGradient) self:setNormalMap(love.graphics.newImage(imgNormalData)) end + -- generate normal map function body:generateNormalMap(strength) self:setNormalMap(height_map_conv.toNormalMap(self.img, strength)) diff --git a/lib/light.lua b/lib/light.lua index 60c8495..2f6bda5 100644 --- a/lib/light.lua +++ b/lib/light.lua @@ -119,7 +119,6 @@ function light:updateShadow() and self.y + self.range > self.world.translate_y and self.y - self.range < love.graphics.getHeight() + self.world.translate_y then local lightposrange = {self.x, love.graphics.getHeight() - self.y, self.range} - local light = self self.world.direction = self.world.direction + 0.002 self.world.shader:send("lightPosition", {self.x - self.world.translate_x, love.graphics.getHeight() - (self.y - self.world.translate_y), self.z}) self.world.shader:send("lightRange", self.range) @@ -133,7 +132,7 @@ function light:updateShadow() love.graphics.clear() -- calculate shadows - local shadow_geometry = self.calculateShadows(light, self.world.body) + local shadow_geometry = self:calculateShadows() -- draw shadow love.graphics.setInvertedStencil(stencils.shadow(shadow_geometry, self.world.body)) @@ -187,98 +186,118 @@ function light:drawShine() end end -function light.calculateShadows(light, body) +local shadowLength = 100000 +function light:calculateShadows() local shadowGeometry = {} - local shadowLength = 100000 + local body = self.world.body for i = 1, #body do + local current if body[i].shadowType == "rectangle" or body[i].shadowType == "polygon" then - curPolygon = body[i].data - if not body[i].castsNoShadow then - local edgeFacingTo = {} - for k = 1, #curPolygon, 2 do - local indexOfNextVertex = (k + 2) % #curPolygon - local normal = {-curPolygon[indexOfNextVertex+1] + curPolygon[k + 1], curPolygon[indexOfNextVertex] - curPolygon[k]} - local lightToPoint = {curPolygon[k] - light.x, curPolygon[k + 1] - light.y} - - normal = vector.normalize(normal) - lightToPoint = vector.normalize(lightToPoint) - - local dotProduct = vector.dot(normal, lightToPoint) - if dotProduct > 0 then table.insert(edgeFacingTo, true) - else table.insert(edgeFacingTo, false) end - end - - local curShadowGeometry = {} - for k = 1, #edgeFacingTo do - local nextIndex = (k + 1) % #edgeFacingTo - if nextIndex == 0 then nextIndex = #edgeFacingTo end - if edgeFacingTo[k] and not edgeFacingTo[nextIndex] then - curShadowGeometry[1] = curPolygon[nextIndex*2-1] - curShadowGeometry[2] = curPolygon[nextIndex*2] - - local lightVecFrontBack = vector.normalize({curPolygon[nextIndex*2-1] - light.x, curPolygon[nextIndex*2] - light.y}) - curShadowGeometry[3] = curShadowGeometry[1] + lightVecFrontBack[1] * shadowLength - curShadowGeometry[4] = curShadowGeometry[2] + lightVecFrontBack[2] * shadowLength - - elseif not edgeFacingTo[k] and edgeFacingTo[nextIndex] then - curShadowGeometry[7] = curPolygon[nextIndex*2-1] - curShadowGeometry[8] = curPolygon[nextIndex*2] - - local lightVecBackFront = vector.normalize({curPolygon[nextIndex*2-1] - light.x, curPolygon[nextIndex*2] - light.y}) - curShadowGeometry[5] = curShadowGeometry[7] + lightVecBackFront[1] * shadowLength - curShadowGeometry[6] = curShadowGeometry[8] + lightVecBackFront[2] * shadowLength - end - end - if curShadowGeometry[1] - and curShadowGeometry[2] - and curShadowGeometry[3] - and curShadowGeometry[4] - and curShadowGeometry[5] - and curShadowGeometry[6] - and curShadowGeometry[7] - and curShadowGeometry[8] - then - curShadowGeometry.alpha = body[i].alpha - curShadowGeometry.red = body[i].red - curShadowGeometry.green = body[i].green - curShadowGeometry.blue = body[i].blue - shadowGeometry[#shadowGeometry + 1] = curShadowGeometry - end - end + current = self:calculatePolyShadow(body[i]) elseif body[i].shadowType == "circle" then - if not body[i].castsNoShadow then - local length = math.sqrt(math.pow(light.x - (body[i].x - body[i].ox), 2) + math.pow(light.y - (body[i].y - body[i].oy), 2)) - if length >= body[i].radius and length <= light.range then - local curShadowGeometry = {} - local angle = math.atan2(light.x - (body[i].x - body[i].ox), (body[i].y - body[i].oy) - light.y) + math.pi / 2 - local x2 = ((body[i].x - body[i].ox) + math.sin(angle) * body[i].radius) - local y2 = ((body[i].y - body[i].oy) - math.cos(angle) * body[i].radius) - local x3 = ((body[i].x - body[i].ox) - math.sin(angle) * body[i].radius) - local y3 = ((body[i].y - body[i].oy) + math.cos(angle) * body[i].radius) - - curShadowGeometry[1] = x2 - curShadowGeometry[2] = y2 - curShadowGeometry[3] = x3 - curShadowGeometry[4] = y3 - - curShadowGeometry[5] = x3 - (light.x - x3) * shadowLength - curShadowGeometry[6] = y3 - (light.y - y3) * shadowLength - curShadowGeometry[7] = x2 - (light.x - x2) * shadowLength - curShadowGeometry[8] = y2 - (light.y - y2) * shadowLength - curShadowGeometry.alpha = body[i].alpha - curShadowGeometry.red = body[i].red - curShadowGeometry.green = body[i].green - curShadowGeometry.blue = body[i].blue - shadowGeometry[#shadowGeometry + 1] = curShadowGeometry - end - end + current = self:calculateCircleShadow(body[i]) end + if current ~= nil then + shadowGeometry[#shadowGeometry + 1] = current + end end return shadowGeometry end +function light:calculatePolyShadow(poly) + if poly.castsNoShadow then + return nil + end + + local curPolygon = poly.data + local edgeFacingTo = {} + for k = 1, #curPolygon, 2 do + local indexOfNextVertex = (k + 2) % #curPolygon + local normal = {-curPolygon[indexOfNextVertex+1] + curPolygon[k + 1], curPolygon[indexOfNextVertex] - curPolygon[k]} + local selfToPoint = {curPolygon[k] - self.x, curPolygon[k + 1] - self.y} + + normal = vector.normalize(normal) + selfToPoint = vector.normalize(selfToPoint) + + local dotProduct = vector.dot(normal, selfToPoint) + if dotProduct > 0 then table.insert(edgeFacingTo, true) + else table.insert(edgeFacingTo, false) end + end + + local curShadowGeometry = {} + for k = 1, #edgeFacingTo do + local nextIndex = (k + 1) % #edgeFacingTo + if nextIndex == 0 then nextIndex = #edgeFacingTo end + if edgeFacingTo[k] and not edgeFacingTo[nextIndex] then + curShadowGeometry[1] = curPolygon[nextIndex*2-1] + curShadowGeometry[2] = curPolygon[nextIndex*2] + + local selfVecFrontBack = vector.normalize({curPolygon[nextIndex*2-1] - self.x, curPolygon[nextIndex*2] - self.y}) + curShadowGeometry[3] = curShadowGeometry[1] + selfVecFrontBack[1] * shadowLength + curShadowGeometry[4] = curShadowGeometry[2] + selfVecFrontBack[2] * shadowLength + + elseif not edgeFacingTo[k] and edgeFacingTo[nextIndex] then + curShadowGeometry[7] = curPolygon[nextIndex*2-1] + curShadowGeometry[8] = curPolygon[nextIndex*2] + + local selfVecBackFront = vector.normalize({curPolygon[nextIndex*2-1] - self.x, curPolygon[nextIndex*2] - self.y}) + curShadowGeometry[5] = curShadowGeometry[7] + selfVecBackFront[1] * shadowLength + curShadowGeometry[6] = curShadowGeometry[8] + selfVecBackFront[2] * shadowLength + end + end + if curShadowGeometry[1] + and curShadowGeometry[2] + and curShadowGeometry[3] + and curShadowGeometry[4] + and curShadowGeometry[5] + and curShadowGeometry[6] + and curShadowGeometry[7] + and curShadowGeometry[8] + then + curShadowGeometry.alpha = poly.alpha + curShadowGeometry.red = poly.red + curShadowGeometry.green = poly.green + curShadowGeometry.blue = poly.blue + return curShadowGeometry + else + return nil + end +end + +function light:calculateCircleShadow(circle) + if circle.castsNoShadow then + return nil + end + local length = math.sqrt(math.pow(self.x - (circle.x - circle.ox), 2) + math.pow(self.y - (circle.y - circle.oy), 2)) + if length >= circle.radius and length <= self.range then + local curShadowGeometry = {} + local angle = math.atan2(self.x - (circle.x - circle.ox), (circle.y - circle.oy) - self.y) + math.pi / 2 + local x2 = ((circle.x - circle.ox) + math.sin(angle) * circle.radius) + local y2 = ((circle.y - circle.oy) - math.cos(angle) * circle.radius) + local x3 = ((circle.x - circle.ox) - math.sin(angle) * circle.radius) + local y3 = ((circle.y - circle.oy) + math.cos(angle) * circle.radius) + + curShadowGeometry[1] = x2 + curShadowGeometry[2] = y2 + curShadowGeometry[3] = x3 + curShadowGeometry[4] = y3 + + curShadowGeometry[5] = x3 - (self.x - x3) * shadowLength + curShadowGeometry[6] = y3 - (self.y - y3) * shadowLength + curShadowGeometry[7] = x2 - (self.x - x2) * shadowLength + curShadowGeometry[8] = y2 - (self.y - y2) * shadowLength + curShadowGeometry.alpha = circle.alpha + curShadowGeometry.red = circle.red + curShadowGeometry.green = circle.green + curShadowGeometry.blue = circle.blue + return curShadowGeometry + else + return nil + end +end + function light:drawPixelShadow() if self.visible then if self.normalInvert then