Löve "Light vs. Shadow" Engine v2

Showcase your libraries, tools and other projects that help your fellow love users.
szensk
Party member
Posts: 155
Joined: Sat Jan 19, 2013 3:57 am

Re: Löve "Light vs. Shadow" Engine v2

Post by szensk »

PriorBlue wrote:I watched the last days over your code and i was surprised how much work do you put in the changes, i think the new direction of your system is very good, you have even added new postshader and improved the others like the scanline shader, awesome! I don't know, if you really need my help in the future, your changes look really great. :)

Oh and by the way, you can add your name in all files of the project or you can remove/change the MIT licence, if you want. The project is now officially yours ;).
I löve when there is a happy open-source fork :)
User avatar
Jeeper
Party member
Posts: 611
Joined: Tue Mar 12, 2013 7:11 pm
Contact:

Re: Löve "Light vs. Shadow" Engine v2

Post by Jeeper »

Just a PSA to anyone who intends to use this library and is using STI, the v2 of Shadow vs light changed the way it uses canvases and does not like STI for that reason. After struggling for a long time with trying to implement this updated version to my current project, I spoke to "drunken_thor" (Who manages the library) and he confirmed that it is not compatible with STI.

The old Shadow vs Light does not have this issue however, so If you intend to use STI then I would recommend using it. A few major improvements made in this version are missing though, but I have managed to fix most of them (Such as scaling and translation, removal of shadow and light bodies etc). So if you intend to do like me and use the older version, feel free to ask for help in case you run into any issues.
User avatar
nfey
Citizen
Posts: 63
Joined: Tue Feb 18, 2014 9:50 am
Location: Romania

Re: Löve "Light vs. Shadow" Engine v2

Post by nfey »

@drunken_thor : think you can post some info on the math used to calculate the shadows, per body? Specifically I am talking about body:calculatePolyShadow(). I'm looking at it and scratching my head and it's kinda hard to understand what it's doing with no comments and especially without being able to visualize what's happening.
I'm asking because I'm trying to reverse-engineer that part of the engine so that I can use it for other stuff, apart from lighting.

And of course, thanks a lot for your work on this.
User avatar
drunken_thor
Citizen
Posts: 75
Joined: Wed Oct 01, 2014 12:14 am
Location: Toronto, Canada
Contact:

Re: Löve "Light vs. Shadow" Engine v2

Post by drunken_thor »

nfey wrote:@drunken_thor : think you can post some info on the math used to calculate the shadows, per body? Specifically I am talking about body:calculatePolyShadow(). I'm looking at it and scratching my head and it's kinda hard to understand what it's doing with no comments and especially without being able to visualize what's happening.
I'm asking because I'm trying to reverse-engineer that part of the engine so that I can use it for other stuff, apart from lighting.

And of course, thanks a lot for your work on this.
Yeah sure just give me a a little while because I was just working on that specific part to make the shadows actually end somewhere and have more of a 3D quality to them.

I can get you started on it though.

Code: Select all

function body:calculatePolyShadow(light)
  if self.castsNoShadow then
    return nil
  end

  local curPolygon = self.data
  local edgeFacingTo = {}
-- for each edge check if the light strikes it
  for k = 1, #curPolygon, 2 do
    local indexOfNextVertex = (k + 2) % #curPolygon
-- get the unit vector of the edge
    local normal = {-curPolygon[indexOfNextVertex+1] + curPolygon[k + 1], curPolygon[indexOfNextVertex] - curPolygon[k]}
    normal = vector.normalize(normal)
-- get the unit vector of the light pointing at the current point we are examing
-- [hint] we are looking for the outter most edges of the polygon that the light hits
    local lightToPoint = {curPolygon[k] - light.x, curPolygon[k + 1] - light.y}
    lightToPoint = vector.normalize(lightToPoint)

--get the dot product of the two vectors to see if it faces the light
    local dotProduct = vector.dot(normal, lightToPoint)
-- if the dot product is greater than 0 it faces the light
    if dotProduct > 0 then 
      table.insert(edgeFacingTo, true)
-- if the dot product is 0 it is perpendicular
-- if it is less than 0 it faces away from the light
    else 
      table.insert(edgeFacingTo, false) 
    end
  end

--now we have a table that tells us which edges are hit by the light

  local curShadowGeometry = {}
-- for each point in the polygon again but referencing the edges
  for k = 1, #edgeFacingTo do
    local nextIndex = (k + 1) % #edgeFacingTo
    if nextIndex == 0 then nextIndex = #edgeFacingTo end
-- if this edge is in the light but not the next 
-- create the first 2 points of the polygon we have found an edge of the shadow
    if edgeFacingTo[k] and not edgeFacingTo[nextIndex] then
      curShadowGeometry[1] = curPolygon[nextIndex*2-1]
      curShadowGeometry[2] = curPolygon[nextIndex*2]

--cast the edge beyond the shape in the direction of the vector from the light to the point
      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
-- if this edfe is not in the light but the next one is the we found the last 2 points of the shadow
    elseif not edgeFacingTo[k] and edgeFacingTo[nextIndex] then
      curShadowGeometry[7] = curPolygon[nextIndex*2-1]
      curShadowGeometry[8] = curPolygon[nextIndex*2]

--cast the edge beyond the shape in the direction of the vector from the light to the point
      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
--we only draw long rectangular shadows
--so they must have 4 points
  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 = self.alpha
    curShadowGeometry.red = self.red
    curShadowGeometry.green = self.green
    curShadowGeometry.blue = self.blue
    return curShadowGeometry
  else
    return nil
  end
end
Light_world.lua for all your lighting needs
Talkies for all dialog needs
Github
twitter
User avatar
drunken_thor
Citizen
Posts: 75
Joined: Wed Oct 01, 2014 12:14 am
Location: Toronto, Canada
Contact:

Re: Löve "Light vs. Shadow" Engine v2

Post by drunken_thor »

nfey wrote:@drunken_thor : think you can post some info on the math used to calculate the shadows, per body? Specifically I am talking about body:calculatePolyShadow(). I'm looking at it and scratching my head and it's kinda hard to understand what it's doing with no comments and especially without being able to visualize what's happening.
I'm asking because I'm trying to reverse-engineer that part of the engine so that I can use it for other stuff, apart from lighting.

And of course, thanks a lot for your work on this.
@nfey okay so I improved the shadow body calculations in the library and really found this paper helpful when doing so http://web.cs.wpi.edu/~matt/courses/cs5 ... hadow.html
Light_world.lua for all your lighting needs
Talkies for all dialog needs
Github
twitter
User avatar
nfey
Citizen
Posts: 63
Joined: Tue Feb 18, 2014 9:50 am
Location: Romania

Re: Löve "Light vs. Shadow" Engine v2

Post by nfey »

@drunken_thor - thanks a lot, this really helps with understanding what's going on.
Relazy
Citizen
Posts: 51
Joined: Thu Jul 10, 2014 11:10 pm

Re: Löve "Light vs. Shadow" Engine v2

Post by Relazy »

Is there a way to make this work with Hump camera? I can't set the translation right. I can attach a light to the mouse co-ordinates on the camera but it fails when I want to set a fixed location for a shadow body on world co-ordinates. I tried to :setPosition() within camera attach and detach functions but it didn't make any difference.
User avatar
drunken_thor
Citizen
Posts: 75
Joined: Wed Oct 01, 2014 12:14 am
Location: Toronto, Canada
Contact:

Re: Löve "Light vs. Shadow" Engine v2

Post by drunken_thor »

Relazy wrote:Is there a way to make this work with Hump camera? I can't set the translation right. I can attach a light to the mouse co-ordinates on the camera but it fails when I want to set a fixed location for a shadow body on world co-ordinates. I tried to :setPosition() within camera attach and detach functions but it didn't make any difference.
It should work with all translations done with love, are you passing in your translation and scale on the lightworld:draw() call? can I see your code?
Light_world.lua for all your lighting needs
Talkies for all dialog needs
Github
twitter
Relazy
Citizen
Posts: 51
Joined: Thu Jul 10, 2014 11:10 pm

Re: Löve "Light vs. Shadow" Engine v2

Post by Relazy »

drunken_thor wrote:
Relazy wrote:Is there a way to make this work with Hump camera? I can't set the translation right. I can attach a light to the mouse co-ordinates on the camera but it fails when I want to set a fixed location for a shadow body on world co-ordinates. I tried to :setPosition() within camera attach and detach functions but it didn't make any difference.
It should work with all translations done with love, are you passing in your translation and scale on the lightworld:draw() call? can I see your code?
I am not sure what translation to use because hump handles the camera with two sets of co-ordinates: one for camera:worldCoords() and one for camera:cameraCoords(), heres an example(use WSAD to move camera. up key + enter for selection of the translation):

Code: Select all

local LightWorld = require "lib"

	local lightworld = LightWorld({
			drawBackground = drawBackground,
			drawForeground = drawForeground,
			ambient = {50,50,50},
		})
		
Camera = require "hump.camera"
function love.load()
	fthis = 0
	this2 = 0
	cx = 360 
	cy = 350
	px = 400
	py = 300
	camera = Camera(0,0)
	a = lightworld:newLight(love.mouse.getX(),love.mouse.getY(),155,155,20,200)
	b = lightworld:newRectangle(px,py,200,200)
end
function love.draw()
	if this2 == 1 then
		x,y = camera:worldCoords(10,10)
	elseif this2 == 2 then
		x,y = camera:cameraCoords(10,10)
	elseif this2 == 3 then
	  local tx,ty = love.graphics.getWidth()/(2*camera.scale), love.graphics.getHeight()/(2*camera.scale)
                --the previous method from old light v shadow
		x = tx-10
		y = ty-10
	else
		x = 0
		y = 0
	end
	love.graphics.push()
	love.graphics.translate(x,y)
	-- I am currently on default scale which is 1 so I assume there isn't a need to translate by love.graphics.scale().
	lightworld:draw(x,y,1)
	love.graphics.pop()
	
	love.graphics.setColor(126,85,98)
	love.graphics.rectangle("fill",0,100,300,20)
	if fthis == 1 then
		love.graphics.setColor(240,200,200)
	end
	love.graphics.rectangle("fill",0,120,300,20)
	love.graphics.setColor(126,85,98)
	if fthis == 2 then 
		love.graphics.setColor(240,200,200)
	end
	love.graphics.rectangle("fill",0,140,300,20)
	love.graphics.setColor(126,85,98)
	if fthis == 3 then
		love.graphics.setColor(240,200,200)
	end
	love.graphics.rectangle("fill",0,160,300,20)
	love.graphics.setColor(126,85,98)
	
	love.graphics.setColor(255,255,255)
	love.graphics.print("The current translation is: "..this2,0,100)
	love.graphics.print("1:Set translation to camera:worldCoords()",0,120)
	love.graphics.print("2:Set translation to camera:camCoords()",0,140)
	love.graphics.print("3:Set translation to light v shadow v1",0,160)
	love.graphics.print("camera x:"..camera.x,0,180)
	love.graphics.print("camera y:"..camera.y,0,190)
end
function love.keypressed(key)
	-- movement and selection of translation
	if key == "a" then
		px = px + 40
	elseif key == "d" then
		px = px - 40
	end
	if key == "w" then
		py = py + 40
	elseif key == "s" then
		py = py - 40
	end
	if fthis < 4 and fthis > 0 then
		if key == "up" then
			fthis = fthis + 1
		end
		if key == "return" then
			this2 = fthis
		end
	else
		fthis = 1
	end
end
function love.update(dt)
	a:setPosition(love.mouse.getX(),love.mouse.getY())
	love.window.setTitle(""..love.timer.getFPS())
	camera:lookAt(px,py)
end
function lightworld:drawBackground()
		love.graphics.setColor(152,150,97)
		love.graphics.rectangle("fill",0,0,800,600)
		love.graphics.setColor(255,255,255)
	camera:attach()
	camera:detach()
end
function lightworld:drawForeground()
	camera:attach()
		--cx2,cy2 = camera:worldCoords(cx,cy)
		--b:setPosition(cx2,cy2)
		b:setPosition(cx,cy)
		love.graphics.setColor(100,100,190)
		love.graphics.rectangle("fill",cx,cy,200,200)
		love.graphics.setColor(200,200,200)
		love.graphics.rectangle("fill",px,py,20,20) -- illustrated the point at which camera looks at
		love.graphics.setColor(255,255,255)
	camera:detach()
end
Attachments
this.love
(45.14 KiB) Downloaded 176 times
User avatar
drunken_thor
Citizen
Posts: 75
Joined: Wed Oct 01, 2014 12:14 am
Location: Toronto, Canada
Contact:

Re: Löve "Light vs. Shadow" Engine v2

Post by drunken_thor »

@relazy I will be releasing a new, simpler, faster version of this library within a week. I will add example of both hump and gamera. I will let you know when I release it.

small teasers of what is to come to hopefully help the wait:
- much faster drawing (took out 3 separate shaders and integrated them all into one draw)
- single callback draw call (like gamera's draw call) to simplify use
- integrated animation using normal maps :O
- [and now] example for most used cameras
Light_world.lua for all your lighting needs
Talkies for all dialog needs
Github
twitter
Post Reply

Who is online

Users browsing this forum: Ahrefs [Bot] and 48 guests