diff --git a/gfx/ape.png b/gfx/ape.png new file mode 100644 index 0000000..cbd81be Binary files /dev/null and b/gfx/ape.png differ diff --git a/gfx/ape_glow.png b/gfx/ape_glow.png new file mode 100644 index 0000000..cb42328 Binary files /dev/null and b/gfx/ape_glow.png differ diff --git a/gfx/ape_normal.png b/gfx/ape_normal.png new file mode 100644 index 0000000..bb93f25 Binary files /dev/null and b/gfx/ape_normal.png differ diff --git a/gfx/chest_normal.png b/gfx/chest_normal.png index cb0decf..11dabee 100644 Binary files a/gfx/chest_normal.png and b/gfx/chest_normal.png differ diff --git a/gfx/circle_normal.png b/gfx/circle_normal.png index 77575b4..dc2f571 100644 Binary files a/gfx/circle_normal.png and b/gfx/circle_normal.png differ diff --git a/gfx/cone_large_normal.png b/gfx/cone_large_normal.png index a15dd6e..5fbba2c 100644 Binary files a/gfx/cone_large_normal.png and b/gfx/cone_large_normal.png differ diff --git a/gfx/cone_normal.png b/gfx/cone_normal.png index 34c5275..a8f4821 100644 Binary files a/gfx/cone_normal.png and b/gfx/cone_normal.png differ diff --git a/gfx/led_normal.png b/gfx/led_normal.png index 6890b1f..e24fa3f 100644 Binary files a/gfx/led_normal.png and b/gfx/led_normal.png differ diff --git a/gfx/machine2_normal.png b/gfx/machine2_normal.png index 879997f..658746d 100644 Binary files a/gfx/machine2_normal.png and b/gfx/machine2_normal.png differ diff --git a/gfx/machine_normal.png b/gfx/machine_normal.png index ff0cc5a..b1ab895 100644 Binary files a/gfx/machine_normal.png and b/gfx/machine_normal.png differ diff --git a/gfx/normal_palette.png b/gfx/normal_palette.png index 5eec06f..97580b2 100644 Binary files a/gfx/normal_palette.png and b/gfx/normal_palette.png differ diff --git a/gfx/sphere/cooper.png b/gfx/sphere/cooper.png new file mode 100644 index 0000000..3e36869 Binary files /dev/null and b/gfx/sphere/cooper.png differ diff --git a/gfx/sphere/glass.png b/gfx/sphere/glass.png new file mode 100644 index 0000000..9231624 Binary files /dev/null and b/gfx/sphere/glass.png differ diff --git a/gfx/sphere/glow.png b/gfx/sphere/glow.png new file mode 100644 index 0000000..ef90839 Binary files /dev/null and b/gfx/sphere/glow.png differ diff --git a/gfx/sphere/lava.png b/gfx/sphere/lava.png new file mode 100644 index 0000000..71c9912 Binary files /dev/null and b/gfx/sphere/lava.png differ diff --git a/gfx/sphere/neon.png b/gfx/sphere/neon.png new file mode 100644 index 0000000..45eaabb Binary files /dev/null and b/gfx/sphere/neon.png differ diff --git a/gfx/sphere/reflect.png b/gfx/sphere/reflect.png new file mode 100644 index 0000000..9cf2f1b Binary files /dev/null and b/gfx/sphere/reflect.png differ diff --git a/gfx/sphere/toon.png b/gfx/sphere/toon.png new file mode 100644 index 0000000..13cfc45 Binary files /dev/null and b/gfx/sphere/toon.png differ diff --git a/light.lua b/light.lua index eed23b6..d18dc7d 100644 --- a/light.lua +++ b/light.lua @@ -39,6 +39,7 @@ function love.light.newWorld() o.refractionMap2 = love.graphics.newCanvas() o.reflectionMap = love.graphics.newCanvas() o.reflectionMap2 = love.graphics.newCanvas() + o.normalInvert = false o.glowBlur = 1.0 o.isGlowBlur = false o.glowTimer = 0.0 @@ -49,6 +50,8 @@ function love.light.newWorld() o.shader = love.graphics.newShader("shader/poly_shadow.glsl") o.glowShader = love.graphics.newShader("shader/glow.glsl") o.normalShader = love.graphics.newShader("shader/normal.glsl") + o.normalInvertShader = love.graphics.newShader("shader/normal_invert.glsl") + o.materialShader = love.graphics.newShader("shader/material.glsl") o.refractionShader = love.graphics.newShader("shader/refraction.glsl") o.refractionShader:send("screen", {love.window.getWidth(), love.window.getHeight()}) o.reflectionShader = love.graphics.newShader("shader/reflection.glsl") @@ -219,14 +222,25 @@ function love.light.newWorld() for i = 1, #o.lights do if o.lights[i].visible then - o.normalShader:send('screenResolution', {love.graphics.getWidth(), love.graphics.getHeight()}) - o.normalShader:send('lightColor', {o.lights[i].red / 255.0, o.lights[i].green / 255.0, o.lights[i].blue / 255.0}) - o.normalShader:send('lightPosition',{o.lights[i].x, love.graphics.getHeight() - o.lights[i].y, o.lights[i].z / 255.0}) - o.normalShader:send('lightRange',{o.lights[i].range}) - o.normalShader:send("lightSmooth", o.lights[i].smooth) - o.normalShader:send("lightAngle", math.pi - o.lights[i].angle / 2.0) - o.normalShader:send("lightDirection", o.lights[i].direction) - love.graphics.setShader(o.normalShader) + if normalInvert then + o.normalInvertShader:send('screenResolution', {love.graphics.getWidth(), love.graphics.getHeight()}) + o.normalInvertShader:send('lightColor', {o.lights[i].red / 255.0, o.lights[i].green / 255.0, o.lights[i].blue / 255.0}) + o.normalInvertShader:send('lightPosition',{o.lights[i].x, love.graphics.getHeight() - o.lights[i].y, o.lights[i].z / 255.0}) + o.normalInvertShader:send('lightRange',{o.lights[i].range}) + o.normalInvertShader:send("lightSmooth", o.lights[i].smooth) + o.normalInvertShader:send("lightAngle", math.pi - o.lights[i].angle / 2.0) + o.normalInvertShader:send("lightDirection", o.lights[i].direction) + love.graphics.setShader(o.normalInvertShader) + else + o.normalShader:send('screenResolution', {love.graphics.getWidth(), love.graphics.getHeight()}) + o.normalShader:send('lightColor', {o.lights[i].red / 255.0, o.lights[i].green / 255.0, o.lights[i].blue / 255.0}) + o.normalShader:send('lightPosition',{o.lights[i].x, love.graphics.getHeight() - o.lights[i].y, o.lights[i].z / 255.0}) + o.normalShader:send('lightRange',{o.lights[i].range}) + o.normalShader:send("lightSmooth", o.lights[i].smooth) + o.normalShader:send("lightAngle", math.pi - o.lights[i].angle / 2.0) + o.normalShader:send("lightDirection", o.lights[i].direction) + love.graphics.setShader(o.normalShader) + end love.graphics.draw(o.normalMap, LOVE_LIGHT_TRANSLATE_X, LOVE_LIGHT_TRANSLATE_Y) end end @@ -408,6 +422,18 @@ function love.light.newWorld() love.graphics.draw(o.pixelShadow, LOVE_LIGHT_TRANSLATE_X, LOVE_LIGHT_TRANSLATE_Y) love.graphics.setBlendMode("alpha") end + -- draw material + o.drawMaterial = function() + love.graphics.setShader(o.materialShader) + for i = 1, #o.body do + if o.body[i].material and o.body[i].normal then + love.graphics.setColor(255, 255, 255) + o.materialShader:send("material", o.body[i].material) + love.graphics.draw(o.body[i].normal, o.body[i].x - o.body[i].nx + LOVE_LIGHT_TRANSLATE_X, o.body[i].y - o.body[i].ny + LOVE_LIGHT_TRANSLATE_Y) + end + end + love.graphics.setShader() + end -- draw glow o.drawGlow = function() love.graphics.setColor(255, 255, 255) @@ -507,6 +533,10 @@ function love.light.newWorld() o.setAmbientBlue = function(blue) o.ambient[3] = blue end + -- set normal invert + o.setNormalInvert = function(invert) + o.normalInvert = invert + end -- set blur o.setBlur = function(blur) o.blur = blur @@ -558,6 +588,14 @@ function love.light.newWorld() o.newRefractionHeightMap = function(heightMap, x, y, strength) return love.light.newRefractionHeightMap(o, heightMap, x, y, strength) end + -- new reflection + o.newReflection = function(normal, x, y) + return love.light.newReflection(o, normal, x, y) + end + -- new reflection from height map + o.newReflectionHeightMap = function(heightMap, x, y, strength) + return love.light.newReflectionHeightMap(o, heightMap, x, y, strength) + end -- new body o.newBody = function(type, ...) return love.light.newBody(o, type, ...) @@ -738,6 +776,7 @@ function love.light.newBody(p, type, ...) o.id = #p.body o.type = type o.normal = nil + o.material = nil o.glow = nil if o.type == "circle" then o.x = args[1] or 0 @@ -834,12 +873,41 @@ function love.light.newBody(p, type, ...) o.width = args[4] or 64 o.height = args[5] or 64 end - o.ox = o.width / 2.0 - o.oy = o.height / 2.0 + o.ox = o.width * 0.5 + o.oy = o.height * 0.5 o.reflection = false o.reflective = false o.refraction = true o.refractive = false + elseif o.type == "reflection" then + o.normal = args[1] + o.x = args[2] or 0 + o.y = args[3] or 0 + if o.normal then + o.normalWidth = o.normal:getWidth() + o.normalHeight = o.normal:getHeight() + o.width = args[4] or o.normalWidth + o.height = args[5] or o.normalHeight + o.nx = o.normalWidth * 0.5 + o.ny = o.normalHeight * 0.5 + o.normal:setWrap("repeat", "repeat") + o.normalVert = { + {0.0, 0.0, 0.0, 0.0}, + {o.width, 0.0, 1.0, 0.0}, + {o.width, o.height, 1.0, 1.0}, + {0.0, o.height, 0.0, 1.0} + } + o.normalMesh = love.graphics.newMesh(o.normalVert, o.normal, "fan") + else + o.width = args[4] or 64 + o.height = args[5] or 64 + end + o.ox = o.width * 0.5 + o.oy = o.height * 0.5 + o.reflection = true + o.reflective = false + o.refraction = false + o.refractive = false end o.shine = true o.red = 0 @@ -1062,13 +1130,13 @@ function love.light.newBody(p, type, ...) if mode == "top" then color = {127, 127, 255} elseif mode == "front" then - color = {127, 255, 127} - elseif mode == "back" then color = {127, 0, 127} + elseif mode == "back" then + color = {127, 255, 127} elseif mode == "left" then - color = {31, 255, 223} + color = {31, 0, 223} elseif mode == "right" then - color = {223, 223, 127} + color = {223, 0, 127} end for i = 0, o.imgHeight - 1 do @@ -1109,10 +1177,10 @@ function love.light.newBody(p, type, ...) end if verticalGradient == "gradient" then - ny = 127 + k * dy * 0.5 + ny = 127 - k * dy * 0.5 nz = 255 - k * dy * 0.5 elseif verticalGradient == "inverse" then - ny = 127 - k * dy * 0.5 + ny = 127 + k * dy * 0.5 nz = 127 - k * dy * 0.25 else ny = 255 @@ -1138,6 +1206,12 @@ function love.light.newBody(p, type, ...) o.nx = o.normalWidth * 0.5 o.ny = o.normalHeight * 0.5 end + -- set material + o.setMaterial = function(material) + if material then + o.material = material + end + end -- set normal o.setGlowMap = function(glow) o.glow = glow @@ -1242,6 +1316,17 @@ function love.light.newRefractionHeightMap(p, heightMap, x, y, strength) return love.light.newRefraction(p, normal, x, y) end +-- reflection object +function love.light.newReflection(p, normal, x, y, width, height) + return p.newBody("reflection", normal, x, y, width, height) +end + +-- reflection object (height map) +function love.light.newReflectionHeightMap(p, heightMap, x, y, strength) + local normal = HeightMapToNormalMap(heightMap, strength) + return love.light.newReflection(p, normal, x, y) +end + -- vector functions function normalize(v) local len = math.sqrt(math.pow(v[1], 2) + math.pow(v[2], 2)) @@ -1423,7 +1508,7 @@ function HeightMapToNormalMap(heightMap, strength) end red = (255 + ((matrix[1][2] - matrix[2][2]) + (matrix[2][2] - matrix[3][2])) * strength) / 2.0 - green = (255 - ((matrix[2][2] - matrix[1][1]) + (matrix[2][3] - matrix[2][2])) * strength) / 2.0 + green = (255 + ((matrix[2][2] - matrix[1][1]) + (matrix[2][3] - matrix[2][2])) * strength) / 2.0 blue = 192 imgData2:setPixel(k, i, red, green, blue) diff --git a/main.lua b/main.lua index b651436..a76ca56 100644 --- a/main.lua +++ b/main.lua @@ -72,6 +72,17 @@ function love.load() led_glow = love.graphics.newImage("gfx/led_glow.png") led_glow2 = love.graphics.newImage("gfx/led_glow2.png") led_glow3 = love.graphics.newImage("gfx/led_glow3.png") + ape = love.graphics.newImage("gfx/ape.png") + ape_normal = love.graphics.newImage("gfx/ape_normal.png") + ape_glow = love.graphics.newImage("gfx/ape_glow.png") + + -- materials + material = {} + + local files = love.filesystem.getDirectoryItems("gfx/sphere") + for i, file in ipairs(files) do + material[i] = love.graphics.newImage("gfx/sphere/" .. file) + end -- light world lightRange = 400 @@ -80,9 +91,10 @@ function love.load() lightWorld.setAmbientColor(15, 15, 31) lightWorld.setRefractionStrength(16.0) lightWorld.setReflectionVisibility(0.75) - mouseLight = lightWorld.newLight(0, 0, 255, 127, 63, lightRange) + mouseLight = lightWorld.newLight(0, 0, 255, 191, 127, lightRange) mouseLight.setGlowStrength(0.3) mouseLight.setSmooth(lightSmooth) + mouseLight.z = 63 lightDirection = 0.0 colorAberration = 0.0 @@ -270,7 +282,7 @@ function love.draw() love.graphics.setBlendMode("alpha") for i = 1, phyCnt do - if phyLight[i].getType() == "image" then + if phyLight[i].getType() == "image" and not phyLight[i].material then if not normalOn then math.randomseed(i) love.graphics.setColor(math.random(127, 255), math.random(127, 255), math.random(127, 255)) @@ -282,6 +294,8 @@ function love.draw() end end + lightWorld.drawMaterial() + -- draw pixel shadow if lightOn and not normalOn then lightWorld.drawPixelShadow() @@ -479,7 +493,7 @@ function love.keypressed(k, u) initScene() elseif k == "f12" then lightWorld.clearLights() - mouseLight = lightWorld.newLight(0, 0, 255, 127, 63, lightRange) + mouseLight = lightWorld.newLight(0, 0, 255, 191, 127, lightRange) mouseLight.setGlowStrength(0.3) mouseLight.setSmooth(lightSmooth) elseif k == "1" then @@ -493,24 +507,42 @@ function love.keypressed(k, u) 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, 16) - phyLight[phyCnt].setNormalMap(cone_normal) - phyLight[phyCnt].setShadowType("circle", 12, 0, -8) - phyBody[phyCnt] = love.physics.newBody(physicWorld, mx, my, "dynamic") - phyShape[phyCnt] = love.physics.newRectangleShape(0, 0, 24, 32) - phyFixture[phyCnt] = love.physics.newFixture(phyBody[phyCnt], phyShape[phyCnt]) - phyFixture[phyCnt]:setRestitution(0.5) + local r = lightWorld.getBodyCount() % 2 + if r == 0 then + -- add image + phyCnt = phyCnt + 1 + phyLight[phyCnt] = lightWorld.newImage(cone, mx, my, 24, 12, 12, 16) + phyLight[phyCnt].setNormalMap(cone_normal) + phyLight[phyCnt].setShadowType("circle", 12, 0, -8) + phyBody[phyCnt] = love.physics.newBody(physicWorld, mx, my, "dynamic") + phyShape[phyCnt] = love.physics.newRectangleShape(0, 0, 24, 32) + phyFixture[phyCnt] = love.physics.newFixture(phyBody[phyCnt], phyShape[phyCnt]) + phyFixture[phyCnt]:setRestitution(0.5) + elseif r == 1 then + -- add image + phyCnt = phyCnt + 1 + phyLight[phyCnt] = lightWorld.newImage(chest, mx, my, 32, 24, 16, 0) + phyLight[phyCnt].setNormalMap(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 elseif k == "3" then -- add image + local r = lightWorld.getBodyCount() % #material phyCnt = phyCnt + 1 - phyLight[phyCnt] = lightWorld.newImage(chest, mx, my, 32, 24, 16, 0) - phyLight[phyCnt].setNormalMap(chest_normal) + phyLight[phyCnt] = lightWorld.newImage(ape, mx, my, 160, 128, 80, 64) + phyLight[phyCnt].setNormalMap(ape_normal) + if r == 3 then + phyLight[phyCnt].setGlowMap(ape_glow) + end + phyLight[phyCnt].setMaterial(material[r + 1]) 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) + phyLight[phyCnt].setShadowType("image", 0, -16, 0.0) elseif k == "4" then -- add glow image local r = lightWorld.getBodyCount() % 5 diff --git a/shader/material.glsl b/shader/material.glsl new file mode 100644 index 0000000..ea8f8c1 --- /dev/null +++ b/shader/material.glsl @@ -0,0 +1,10 @@ +extern Image material; + +vec4 effect(vec4 color, Image texture, vec2 texture_coords, vec2 pixel_coords) { + vec4 normal = Texel(texture, texture_coords); + if(normal.a == 1.0) { + return Texel(material, vec2(normal.x, normal.y)); + } else { + return vec4(0.0); + } +} \ No newline at end of file diff --git a/shader/normal.glsl b/shader/normal.glsl index f73185c..987b6e0 100644 --- a/shader/normal.glsl +++ b/shader/normal.glsl @@ -25,7 +25,7 @@ vec4 effect(vec4 color, Image texture, vec2 texture_coords, vec2 pixel_coords) { } } - vec3 normal = vec3(pixelColor.r, 1 - pixelColor.g, pixelColor.b); + vec3 normal = pixelColor.rgb; float dist = distance(lightPosition, vec3(pixel_coords, normal.b)); if(dist < lightRange) { diff --git a/shader/normal_invert.glsl b/shader/normal_invert.glsl new file mode 100644 index 0000000..f73185c --- /dev/null +++ b/shader/normal_invert.glsl @@ -0,0 +1,50 @@ +#define PI 3.1415926535897932384626433832795 + +extern vec2 screenResolution; +extern vec3 lightPosition; +extern vec3 lightColor; +extern float lightRange; +extern float lightSmooth; +extern float lightDirection; +extern float lightAngle; + +vec4 effect(vec4 color, Image texture, vec2 texture_coords, vec2 pixel_coords) { + vec4 pixelColor = Texel(texture, texture_coords); + + if(pixelColor.a > 0.0) { + if(lightAngle > 0.0) { + float angle2 = atan(lightPosition.x - pixel_coords.x, pixel_coords.y - lightPosition.y) + PI; + if(lightDirection - lightAngle > 0 && lightDirection + lightAngle < PI * 2) { + if(angle2 < mod(lightDirection + lightAngle, PI * 2) && angle2 > mod(lightDirection - lightAngle, PI * 2)) { + return vec4(0.0, 0.0, 0.0, 1.0); + } + } else { + if(angle2 < mod(lightDirection + lightAngle, PI * 2) || angle2 > mod(lightDirection - lightAngle, PI * 2)) { + return vec4(0.0, 0.0, 0.0, 1.0); + } + } + } + + vec3 normal = vec3(pixelColor.r, 1 - pixelColor.g, pixelColor.b); + float dist = distance(lightPosition, vec3(pixel_coords, normal.b)); + + if(dist < lightRange) { + vec3 dir = vec3((lightPosition.xy - pixel_coords.xy) / screenResolution.xy, lightPosition.z); + + dir.x *= screenResolution.x / screenResolution.y; + + vec3 N = normalize(normal * 2.0 - 1.0); + vec3 L = normalize(dir); + + vec3 diff = lightColor * max(dot(N, L), 0.0); + + float att = clamp((1.0 - dist / lightRange) / lightSmooth, 0.0, 1.0); + + return vec4(diff * att, 1.0); + } else { + return vec4(0.0, 0.0, 0.0, 1.0); + } + } else { + return vec4(0.0); + } +} \ No newline at end of file