diff --git a/chest.png b/chest.png new file mode 100644 index 0000000..f34bcc8 Binary files /dev/null and b/chest.png differ diff --git a/chest_normal.png b/chest_normal.png new file mode 100644 index 0000000..0bdf2b1 Binary files /dev/null and b/chest_normal.png differ diff --git a/circle.png b/circle.png new file mode 100644 index 0000000..e7f8b99 Binary files /dev/null and b/circle.png differ diff --git a/circle_normal.png b/circle_normal.png new file mode 100644 index 0000000..77575b4 Binary files /dev/null and b/circle_normal.png differ diff --git a/cone.png b/cone.png new file mode 100644 index 0000000..01b7fd5 Binary files /dev/null and b/cone.png differ diff --git a/cone_normal.png b/cone_normal.png new file mode 100644 index 0000000..879997f Binary files /dev/null and b/cone_normal.png differ diff --git a/light.lua b/light.lua index 525594f..a5080e6 100644 --- a/light.lua +++ b/light.lua @@ -1,6 +1,7 @@ LOVE_LIGHT_CURRENT = nil LOVE_LIGHT_CIRCLE = nil LOVE_LIGHT_POLY = nil +LOVE_LIGHT_IMAGE = nil LOVE_LIGHT_LAST_BUFFER = nil LOVE_LIGHT_BLURV = love.graphics.newShader("shader/blurv.glsl") @@ -13,11 +14,15 @@ function love.light.newWorld() local o = {} o.lights = {} o.ambient = {0, 0, 0} - o.poly = {} o.circle = {} + o.poly = {} + o.img = {} o.shadow = love.graphics.newCanvas() o.shine = love.graphics.newCanvas() - o.shader = love.graphics.newShader("shader/light.glsl") + o.pixelShadow = love.graphics.newCanvas() + o.pixelShadow2 = love.graphics.newCanvas() + o.shader = love.graphics.newShader("shader/poly_shadow.glsl") + o.shader2 = love.graphics.newShader("shader/pixel_self_shadow.glsl") o.changed = true o.blur = true -- update @@ -34,8 +39,9 @@ function love.light.newWorld() end local lightsOnScreen = 0 - LOVE_LIGHT_POLY = o.poly LOVE_LIGHT_CIRCLE = o.circle + LOVE_LIGHT_POLY = o.poly + LOVE_LIGHT_IMAGE = o.img for i = 1, #o.lights do if o.lights[i].changed or o.changed then local curLightX = o.lights[i].x @@ -88,6 +94,7 @@ function love.light.newWorld() end end + -- update shadow love.graphics.setShader() if not o.changed then love.graphics.setCanvas(o.shadow) @@ -102,8 +109,9 @@ function love.light.newWorld() end end + -- update shine love.graphics.setCanvas(o.shine) - love.graphics.setColor(0,0,0) + love.graphics.setColor(unpack(o.ambient)) love.graphics.setBlendMode("alpha") love.graphics.rectangle("fill", 0, 0, love.graphics.getWidth(), love.graphics.getHeight()) love.graphics.setColor(255,255,255) @@ -112,6 +120,50 @@ function love.light.newWorld() love.graphics.draw(o.lights[i].shine) end + love.graphics.setColor(255, 255, 255) + love.graphics.setBlendMode("alpha") + o.pixelShadow:clear() + math.randomseed(2) + love.graphics.setShader() + love.graphics.setCanvas(o.pixelShadow) + + for i = 1, #o.img do + if o.img[i].normal then + love.graphics.draw(o.img[i].normal, o.img[i].x - o.img[i].ox2, o.img[i].y - o.img[i].oy2) + end + end + + -- update pixel shadow + o.pixelShadow2:clear() + love.graphics.setCanvas(o.pixelShadow2) + love.graphics.setBlendMode("additive") + love.graphics.setShader(o.shader2) + + local curLightAmbient = { + o.ambient[1] / 255.0, + o.ambient[2] / 255.0, + o.ambient[3] / 255.0 + } + for i = 1, #o.lights do + local curLightColor = { + o.lights[i].red / 255.0, + o.lights[i].green / 255.0, + o.lights[i].blue / 255.0 + } + o.shader2:send("lightPosition", {o.lights[i].x, love.graphics.getHeight() - o.lights[i].y, 16}) + o.shader2:send("lightRange", {o.lights[i].range}) + o.shader2:send("lightColor", curLightColor) + o.shader2:send("lightAmbient", curLightAmbient) + o.shader2:send("lightSmooth", {o.lights[i].smooth}) + love.graphics.draw(o.pixelShadow) + end + + love.graphics.setShader() + o.pixelShadow:clear(255, 255, 255) + love.graphics.setCanvas(o.pixelShadow) + love.graphics.setBlendMode("alpha") + love.graphics.draw(o.pixelShadow2) + love.graphics.setShader() love.graphics.setBlendMode("alpha") love.graphics.setStencil() @@ -142,7 +194,6 @@ function love.light.newWorld() love.graphics.setBlendMode("alpha") end end - -- draw shine o.drawShine = function() love.graphics.setColor(255, 255, 255) @@ -151,6 +202,29 @@ function love.light.newWorld() love.graphics.draw(o.shine) love.graphics.setBlendMode("alpha") end + -- draw pixel shadow + o.drawPixelShadow = function() + love.graphics.setColor(255, 255, 255) + if o.blur and false then + LOVE_LIGHT_LAST_BUFFER = love.graphics.getCanvas() + love.graphics.setBlendMode("alpha") + love.graphics.setCanvas(o.pixelShadow) + love.graphics.setShader(LOVE_LIGHT_BLURV) + love.graphics.draw(o.pixelShadow) + love.graphics.setShader(LOVE_LIGHT_BLURH) + love.graphics.draw(o.pixelShadow) + love.graphics.setCanvas(LOVE_LIGHT_LAST_BUFFER) + love.graphics.setBlendMode("multiplicative") + love.graphics.setShader() + love.graphics.draw(o.pixelShadow) + love.graphics.setBlendMode("alpha") + else + love.graphics.setBlendMode("multiplicative") + love.graphics.setShader() + love.graphics.draw(o.pixelShadow) + love.graphics.setBlendMode("alpha") + end + end -- new light o.newLight = function(x, y, red, green, blue, range) o.lights[#o.lights + 1] = love.light.newLight(o, x, y, red, green, blue, range) @@ -165,6 +239,7 @@ function love.light.newWorld() o.clearObjects = function() o.poly = {} o.circle = {} + o.img = {} end -- set ambient color o.setAmbientColor = function(red, green, blue) @@ -199,6 +274,10 @@ function love.light.newWorld() o.newPolygon = function(...) return love.light.newPolygon(o, ...) end + -- new image + o.newImage = function(img, x, y, width, height, ox, oy) + return love.light.newImage(o, img, x, y, width, height, ox, oy) + end -- set polygon data o.setPoints = function(n, ...) o.poly[n].data = {...} @@ -238,6 +317,7 @@ function love.light.newLight(p, x, y, red, green, blue, range) o.shine = love.graphics.newCanvas() o.x = x o.y = y + o.z = 1 o.red = red o.green = green o.blue = blue @@ -351,6 +431,14 @@ function love.light.newRectangle(p, x, y, w, h) o.shine = b p.changed = true end + -- get x + o.getX = function() + return o.x + end + -- get y + o.getY = function() + return o.y + end -- get type o.getType = function() return "rectangle" @@ -384,6 +472,16 @@ function love.light.newCircle(p, x, y, radius) p.changed = true end end + -- set shadow on/off + o.setShadow = function(b) + o.castsNoShadow = not b + p.changed = true + end + -- set shine on/off + o.setShine = function(b) + o.shine = b + p.changed = true + end -- get x o.getX = function() return o.x @@ -421,6 +519,16 @@ function love.light.newPolygon(p, ...) o.data = {...} p.changed = true end + -- set shadow on/off + o.setShadow = function(b) + o.castsNoShadow = not b + p.changed = true + end + -- set shine on/off + o.setShine = function(b) + o.shine = b + p.changed = true + end -- get polygon data o.getPoints = function() return unpack(o.data) @@ -433,6 +541,87 @@ function love.light.newPolygon(p, ...) return o end +-- image object +function love.light.newImage(p, img, x, y, width, height, ox, oy) + local o = {} + p.poly[#p.poly + 1] = o + p.img[#p.img + 1] = o + o.id = #p.img + o.img = img + o.normal = nil + o.x = x + o.y = y + o.width = width or img:getWidth() + o.height = height or img:getHeight() + o.ox = o.width / 2.0 + o.oy = o.height / 2.0 + o.ox2 = ox or o.width / 2.0 + o.oy2 = oy or o.height / 2.0 + o.shine = true + p.changed = true + o.data = { + o.x - o.ox, + o.y - o.oy, + o.x - o.ox + o.width, + o.y - o.oy, + o.x - o.ox + o.width, + o.y - o.oy + o.height, + o.x - o.ox, + o.y - o.oy + o.height + } + -- refresh + o.refresh = function() + o.data[1] = o.x - o.ox + o.data[2] = o.y - o.oy + o.data[3] = o.x - o.ox + o.width + o.data[4] = o.y - o.oy + o.data[5] = o.x - o.ox + o.width + o.data[6] = o.y - o.oy + o.height + o.data[7] = o.x - o.ox + o.data[8] = o.y - o.oy + o.height + end + -- set position + o.setPosition = function(x, y) + if x ~= o.x or y ~= o.y then + o.x = x + o.y = y + o.refresh() + p.changed = true + end + end + -- set dimension + o.setDimension = function(width, height) + o.width = width + o.height = height + o.refresh() + p.changed = true + end + -- set shadow on/off + o.setShadow = function(b) + o.castsNoShadow = not b + p.changed = true + end + -- set shine on/off + o.setShine = function(b) + o.shine = b + p.changed = true + end + -- set image + o.setImage = function(img) + o.img = img + end + -- set normal + o.setNormal = function(normal) + o.normal = normal + end + -- get type + o.getType = function() + return "image" + end + + return o +end + -- vector functions function normalize(v) local len = math.sqrt(math.pow(v[1], 2) + math.pow(v[2], 2)) @@ -452,7 +641,7 @@ function length(v) return math.sqrt(lengthSqr(v)) end -function calculateShadows(lightsource, geometry, circle) +function calculateShadows(lightsource, geometry, circle, image) local shadowGeometry = {} local shadowLength = 10000 @@ -532,26 +721,34 @@ end shadowStencil = function() local shadowGeometry = calculateShadows(LOVE_LIGHT_CURRENT, LOVE_LIGHT_POLY, LOVE_LIGHT_CIRCLE) - for i=1,#shadowGeometry do + for i = 1,#shadowGeometry do love.graphics.polygon("fill", unpack(shadowGeometry[i])) end - for i=1, #LOVE_LIGHT_POLY do + for i = 1, #LOVE_LIGHT_POLY do love.graphics.polygon("fill", unpack(LOVE_LIGHT_POLY[i].data)) end - for i=1, #LOVE_LIGHT_CIRCLE do + for i = 1, #LOVE_LIGHT_CIRCLE do love.graphics.circle("fill", LOVE_LIGHT_CIRCLE[i].getX(), LOVE_LIGHT_CIRCLE[i].getY(), LOVE_LIGHT_CIRCLE[i].getRadius()) end + for i = 1, #LOVE_LIGHT_IMAGE do + --love.graphics.rectangle("fill", LOVE_LIGHT_IMAGE[i].x, LOVE_LIGHT_IMAGE[i].y, LOVE_LIGHT_IMAGE[i].width, LOVE_LIGHT_IMAGE[i].height) + end end polyStencil = function() - for i=1, #LOVE_LIGHT_POLY do - if LOVE_LIGHT_POLY[i].shine then - love.graphics.polygon("fill", unpack(LOVE_LIGHT_POLY[i].data)) - end - end - for i=1, #LOVE_LIGHT_CIRCLE do + for i = 1, #LOVE_LIGHT_CIRCLE do if LOVE_LIGHT_CIRCLE[i].shine then love.graphics.circle("fill", LOVE_LIGHT_CIRCLE[i].getX(), LOVE_LIGHT_CIRCLE[i].getY(), LOVE_LIGHT_CIRCLE[i].getRadius()) end end + for i = 1, #LOVE_LIGHT_POLY do + if LOVE_LIGHT_POLY[i].shine then + love.graphics.polygon("fill", unpack(LOVE_LIGHT_POLY[i].data)) + end + end + for i = 1, #LOVE_LIGHT_IMAGE do + if LOVE_LIGHT_IMAGE[i].shine then + --love.graphics.rectangle("fill", LOVE_LIGHT_IMAGE[i].x, LOVE_LIGHT_IMAGE[i].y, LOVE_LIGHT_IMAGE[i].width, LOVE_LIGHT_IMAGE[i].height) + end + end end \ No newline at end of file diff --git a/main.lua b/main.lua index c35104e..5f4e3a3 100644 --- a/main.lua +++ b/main.lua @@ -38,15 +38,23 @@ end function love.load() love.graphics.setBackgroundColor(0, 0, 0) + love.graphics.setDefaultFilter("nearest", "nearest") quadScreen = love.graphics.newQuad(0, 0, love.window.getWidth(), love.window.getHeight(), 32, 32) imgFloor = love.graphics.newImage("floor.png") imgFloor:setWrap("repeat", "repeat") + circle = love.graphics.newImage "circle.png" + circle_normal = love.graphics.newImage "circle_normal.png" + cone = love.graphics.newImage "cone.png" + cone_normal = love.graphics.newImage "cone_normal.png" + chest = love.graphics.newImage "chest.png" + chest_normal = love.graphics.newImage "chest_normal.png" + -- light world lightRange = 400 lightSmooth = 1.0 lightWorld = love.light.newWorld() - lightWorld.setAmbientColor(15, 15, 15) + lightWorld.setAmbientColor(15, 15, 31) mouseLight = lightWorld.newLight(0, 0, 255, 127, 63, lightRange) mouseLight.setGlowStrength(0.3) @@ -54,7 +62,7 @@ function love.load() initScene() helpOn = true - physicOn = true + physicOn = false lightOn = true gravityOn = 1 shadowBlurOn = true @@ -65,12 +73,16 @@ end function love.update(dt) love.window.setTitle("FPS:" .. love.timer.getFPS()) mouseLight.setPosition(love.mouse.getX(), love.mouse.getY()) + mx = love.mouse.getX() + my = love.mouse.getY() for i = 1, phyCnt do if phyBody[i]:isAwake() then - if phyShape[i]:getType() == "polygon" then + if phyLight[i].getType() == "polygon" then phyLight[i].setPoints(phyBody[i]:getWorldPoints(phyShape[i]:getPoints())) - else + elseif phyLight[i].getType() == "circle" then + phyLight[i].setPosition(phyBody[i]:getX(), phyBody[i]:getY()) + elseif phyLight[i].getType() == "image" then phyLight[i].setPosition(phyBody[i]:getX(), phyBody[i]:getY()) end end @@ -128,7 +140,7 @@ function love.draw() love.graphics.setColor(math.sin(i) * 255, math.cos(i) * 255, math.tan(i) * 255) if phyLight[i].getType() == "polygon" then love.graphics.polygon("fill", phyLight[i].getPoints()) - else + elseif phyLight[i].getType() == "circle" then love.graphics.circle("fill", phyLight[i].getX(), phyLight[i].getY(), phyLight[i].getRadius()) end end @@ -138,25 +150,77 @@ function love.draw() lightWorld.drawShine() end + for i = 1, phyCnt do + love.graphics.setColor(191 + math.sin(i) * 63, 191 + math.cos(i) * 63, 191 + math.tan(i) * 63) + if phyLight[i].getType() == "image" then + love.graphics.draw(phyLight[i].img, phyLight[i].x - phyLight[i].ox2, phyLight[i].y - phyLight[i].oy2) + end + end + + -- draw pixel shadow + lightWorld.drawPixelShadow() + -- draw help if helpOn then love.graphics.setColor(0, 0, 0, 191) - love.graphics.rectangle("fill", 8, 8, 210, 16 * 15) - love.graphics.setColor(255, 255, 255) + love.graphics.rectangle("fill", 8, 8, 210, 16 * 16) + love.graphics.setColor(0, 127, 255) love.graphics.print("WASD: Move objects", 16, 16) - love.graphics.print("F1: Help on/off", 16, 32) - love.graphics.print("F2: Physic on/off", 16, 48) - love.graphics.print("F3: Light on/off", 16, 64) - love.graphics.print("F4: Clear objects", 16, 80) - love.graphics.print("F5: Clear lights", 16, 96) - love.graphics.print("F6: Gravity on/off", 16, 112) - love.graphics.print("F7: Shadowblur on/off", 16, 128) - love.graphics.print("F8: Bloom on/off", 16, 144) - love.graphics.print("F9: Texture on/off", 16, 160) + love.graphics.setColor(0, 255, 0) + love.graphics.print("F1: Help on", 16, 32) + if physicOn then + love.graphics.setColor(0, 255, 0) + love.graphics.print("F2: Physic on", 16, 48) + else + love.graphics.setColor(255, 0, 0) + love.graphics.print("F2: Physic off", 16, 48) + end + if lightOn then + love.graphics.setColor(0, 255, 0) + love.graphics.print("F3: Light on", 16, 64) + else + love.graphics.setColor(255, 0, 0) + love.graphics.print("F3: Light off", 16, 64) + end + if gravityOn == 1.0 then + love.graphics.setColor(0, 255, 0) + love.graphics.print("F4: Gravity on", 16, 80) + else + love.graphics.setColor(255, 0, 0) + love.graphics.print("F4: Gravity off", 16, 80) + end + if shadowBlurOn then + love.graphics.setColor(0, 255, 0) + love.graphics.print("F5: Shadowblur on", 16, 96) + else + love.graphics.setColor(255, 0, 0) + love.graphics.print("F5: Shadowblur off", 16, 96) + end + if bloomOn then + love.graphics.setColor(0, 255, 0) + love.graphics.print("F6: Bloom on", 16, 112) + else + love.graphics.setColor(255, 0, 0) + love.graphics.print("F6: Bloom off", 16, 112) + end + if textureOn then + love.graphics.setColor(0, 255, 0) + love.graphics.print("F7: Texture on", 16, 128) + else + love.graphics.setColor(255, 0, 0) + love.graphics.print("F7: Texture off", 16, 128) + end + love.graphics.setColor(255, 255, 255) + love.graphics.print("F11: Clear objects", 16, 144) + love.graphics.print("F12: Clear lights", 16, 160) love.graphics.print("M.left: Add cube", 16, 176) love.graphics.print("M.middle: Add light", 16, 192) love.graphics.print("M.right: Add circle", 16, 208) love.graphics.print("M.scroll: Change smooth", 16, 224) + love.graphics.print("1-3: Add image", 16, 240) + else + love.graphics.setColor(255, 255, 255, 63) + love.graphics.print("F1: Help", 8, 8) end -- draw shader @@ -215,22 +279,49 @@ function love.keypressed(k, u) elseif k == "f3" then lightOn = not lightOn elseif k == "f4" then + gravityOn = 1 - gravityOn + physicWorld:setGravity(0, gravityOn * 9.81 * 64) + elseif k == "f5" then + shadowBlurOn = not shadowBlurOn + lightWorld.setBlur(shadowBlurOn) + elseif k == "f6" then + bloomOn = not bloomOn + elseif k == "f7" then + textureOn = not textureOn + elseif k == "f11" then physicWorld:destroy() lightWorld.clearObjects() initScene() - elseif k == "f5" then + elseif k == "f12" then lightWorld.clearLights() - mouseLight = lightWorld.newLight(0, 0, 127, 63, 0, lightRange) + mouseLight = lightWorld.newLight(0, 0, 255, 127, 63, lightRange) mouseLight.setGlowStrength(0.3) - elseif k == "f6" then - gravityOn = 1 - gravityOn - physicWorld:setGravity(0, gravityOn * 9.81 * 64) - elseif k == "f7" then - shadowBlurOn = not shadowBlurOn - lightWorld.setBlur(shadowBlurOn) - elseif k == "f8" then - bloomOn = not bloomOn - elseif k == "f9" then - textureOn = not textureOn + elseif k == "1" then + -- add image + phyCnt = phyCnt + 1 + phyLight[phyCnt] = lightWorld.newImage(circle, mx, my) + phyLight[phyCnt].setNormal(circle_normal) + phyBody[phyCnt] = love.physics.newBody(physicWorld, mx, my, "dynamic") + phyShape[phyCnt] = love.physics.newRectangleShape(0, 0, 32, 32) + phyFixture[phyCnt] = love.physics.newFixture(phyBody[phyCnt], phyShape[phyCnt]) + phyFixture[phyCnt]:setRestitution(0.5) + elseif k == "2" then + -- add image + phyCnt = phyCnt + 1 + phyLight[phyCnt] = lightWorld.newImage(cone, mx, my, 24, 12, 12, 28) + phyLight[phyCnt].setNormal(cone_normal) + phyBody[phyCnt] = love.physics.newBody(physicWorld, mx, my, "dynamic") + phyShape[phyCnt] = love.physics.newRectangleShape(0, 0, 25, 32) + phyFixture[phyCnt] = love.physics.newFixture(phyBody[phyCnt], phyShape[phyCnt]) + phyFixture[phyCnt]:setRestitution(0.5) + elseif k == "3" then + -- add image + phyCnt = phyCnt + 1 + phyLight[phyCnt] = lightWorld.newImage(chest, mx, my, 32, 24, 16, 36) + phyLight[phyCnt].setNormal(chest_normal) + phyBody[phyCnt] = love.physics.newBody(physicWorld, mx, my, "dynamic") + phyShape[phyCnt] = love.physics.newRectangleShape(0, 0, 32, 24) + phyFixture[phyCnt] = love.physics.newFixture(phyBody[phyCnt], phyShape[phyCnt]) + phyFixture[phyCnt]:setRestitution(0.5) end end \ No newline at end of file diff --git a/normal_palett.png b/normal_palett.png new file mode 100644 index 0000000..92b6c0c Binary files /dev/null and b/normal_palett.png differ diff --git a/shader/pixel_self_shadow.glsl b/shader/pixel_self_shadow.glsl new file mode 100644 index 0000000..c430c1e --- /dev/null +++ b/shader/pixel_self_shadow.glsl @@ -0,0 +1,24 @@ +extern vec3 lightPosition; +extern float lightRange; +extern vec3 lightColor; +extern vec3 lightAmbient; +extern float lightSmooth; + +vec4 effect(vec4 color, Image texture, vec2 texture_coords, vec2 pixel_coords) { + vec3 lightDirection = vec3(pixel_coords.xy, 0) - lightPosition.xyz; + float distance = length(lightDirection); + + vec4 pixel = Texel(texture, texture_coords); + vec3 normal = vec3(pixel.x, 1 - pixel.y, pixel.z); + normal = mix(vec3(-1), vec3(1), normal); + + float att = 1 - distance / lightRange; + + if(distance < lightRange && pixel.a > 0.0) { + return vec4(vec3(clamp(1 - dot(normal, lightDirection), 0.0, 1.0)) * lightColor * pow(att, lightSmooth) + lightAmbient, 1.0); + } else if(pixel.a == 0.0) { + return vec4(0.0); + } else { + return vec4(0.0, 0.0, 0.0, 1.0); + } +} \ No newline at end of file diff --git a/shader/light.glsl b/shader/poly_shadow.glsl similarity index 76% rename from shader/light.glsl rename to shader/poly_shadow.glsl index 93fb397..51ed71d 100644 --- a/shader/light.glsl +++ b/shader/poly_shadow.glsl @@ -1,7 +1,7 @@ extern vec3 lightPositionRange; extern vec3 lightColor; -extern float smooth = 1.0; -extern vec2 glow = vec2(0.95, 0.2); +extern float smooth; +extern vec2 glow; vec4 effect(vec4 color, Image texture, vec2 texture_coords, vec2 pixel_coords){ vec4 pixel = Texel(texture, texture_coords); @@ -11,7 +11,7 @@ vec4 effect(vec4 color, Image texture, vec2 texture_coords, vec2 pixel_coords){ if (distance <= lightPositionRange.z) { if (glow.x < 1.0 && glow.y > 0.0) { - pixel.rgb = lightColor * pow(att, smooth) + pow(smoothstep(glow.x, 1.0, att), smooth) * glow.y; + pixel.rgb = clamp(lightColor * pow(att, smooth) + pow(smoothstep(glow.x, 1.0, att), smooth) * glow.y, 0.0, 1.0); } else { pixel.rgb = lightColor * pow(att, smooth); }