Vector lighting explained
The code behind the lighting and visibility is actually quite interesting, here's a run down on how it works:
Step 1: Determine the edges of line of sight around each light or vision object
Each light or vision casting object creates and updates the outer border of what it can see. This is done by iterating over the front facing collision edges within each object's radius , creating projection lines, and then finding the single border line that connects continuously around the edge of the sight. Coincidentally, this shape can be described by a Triangle Fan, with each outer edge being the outside of the triangle, and with the lines coming from the vision or light source radiating out from the center.
Step 2: Create a stencil based on vision casting objects
The view stencil
The fact that it is a triangle fan is nice, because that's a really easy object to render. So the next thing I do is render each vision casting triangle fan to a stencil buffer. Anything within this stencil will be rendered with full lighting.
Step 3: Render Ambient lights to a texture, shadowed by geometry on floors above
Ambient lighting, shadowed by the floor above
The Outdoor ambient lighting is brighter and bluer than the indoor ambient lighting. So I render the ambient lighting to a texture first, before rendering the actual lights. One thing I have not yet implemented but plan to soon is animated clouds that cast shadows on the outdoor ambient areas.
Step 4: Render light objects to the same texture
The lights
Next thing I do is render all the triangle fans from the light casting objects to a texture. This is cool because for each light object, I can actually render the triangle fan with a unique texture. I can do all kinds of cool effects with this, including attenuating the light, creating shaped lights, creating animated lights, etc.
Note that some of the lights have the "god ray" effect built into the texture, while some of the "god ray" effects seen in the level occur naturally, as lights stream out of windows.
Step 5: Blur the lights, and in the process, use the stencil buffer to stop the render to areas that can't be seen
The lights blurred and stenciled
At this point I do a gaussian blur on the light texture, which is generally done by blurring first in the horizontal direction, then in the vertical direction. I do this so that the lights (and shadows) have nice soft edges, and also because my light texture is actually only half the size of the actual game render target, which means it would have pixelly jaggies if I didn't blur it.
During the second step of the blur, I use the vision-stencil buffer to block off the areas that can't be seen. The reason I do this now, and not beforehand when I was originally rendering the lights to the texture, is so that the slight bleed of a light that happens to be just outside of our vision isn't lost just because I've stenciled out a light.
Step 6: Render spy cameras as green lights, using the inverse of the stencil
Spy camera lights added, using the reverse of the stencil
By using the inverse of the visibility stencil, we can make spy camera light areas that are seen by them, but NOT the player, in green. When those areas come into your actual view, they will be lit normally, but when your character can't actually see them, they will be lit in green.
Step 7: Render the "basement" and the blueprints to the screen
Blueprint rendered to the screen as the background
Whenever you change floors, I actually create a new texture which represents all the areas below the floor your character is on. This way I can accurately render whatever is below the character, if for instance, they are standing on a balcony. So during the frame rendering process I render this texture to the screen.
Then I render the blueprint tiles to the screen. Blueprint tiles that have been explored are rendered, while areas that haven't been explored are rendered black.
Step 8: Render the visible game world to the screen using a shader that modulates color based on the light texture generated in Step 5
Visible world rendered on top, using the lights to modulate color
Make sense? Basically I use the light texture I generated to modulate the color of the textures for the game world.
Step 9: Render emissive objects to the screen
Emissive objects (loot) rendered
Some objects want to be rendered without using the lighting of the game world. The yellow loot you pick up, for instance, is always rendered at full bright, so we want to render it separately.
Step 10: Do a post-process bloom effect.
When all is said and done...

