Tuesday, December 8, 2015

Dynamic 2D light and shadows

This video shows my dynamic 2D light system based on Box2D (nothing to do with Box2DLights library) and Clipper:




This is how it all works:
- light is a textured quad/polygon
- Box2D is used to get all the sprites inside the light polygon by either using light as a sensor, or using b2World::QueryAABB()
- for convex shapes visible edges are calculated using a dotproduct of light and edge normals
- for concave shapes(which can only be chains in Box2D) all edges are considered visible
- shadow polygons are created by extending visible edges
- using polygon clipping library called Clipper a "difference" operation is performed between the light polygon and shadows to get the final light polygon
- UV mapping is performed on the resulting vertices
- the resulting polygon is rendered as a GL_TRIANGLE_FAN

No raycasting is performed, which you usually find in tutorials.

The "depth" mask uses the green channel to mark which pixels should be illuminated, and for the intensity (ex. transparent smoke should be illuminated less than a solid surface). Blue channel is used for depth, and works basically the same. It's not perfect, but I can get some nice effects with it.

Soft shadows are generated by rendering the lights on a FBO smaller than the screen size and rendering it with a blur shader.


Wednesday, November 25, 2015

Depth mask

I realized that, since I am rendering a mask to use it for light rendering I could as well render it in different color values and use it as a depth mask too. Layers that are further away are rendered in darker tones of the color for the mask, and when I use multitexturing to mix the mask and light FBO I multiply the final light values with the values of the mask and get light with different intensity:


Saturday, November 21, 2015

Light and magic

Using FBOs, multitexturing, different blending modes and few simple shaders I managed to make very simple lighting system. I think the end results look pretty good:

No light

Example 1 - blue tint

Example 2 - orange tint

What were the requirements:
a) sprites in a layer should be illuminated with light of different colors
- front layers should block the light from the back 
b) light should "bleed" over the edges of front layers (eg. solid walls)
- not affect back layers that are in distance (eg. sky)
c) different layers should be able to be illuminated seperatelly
d) layers should also be able to get darker

a) To illuminate sprites in a certain layer using some color I render them on a black FBO using a shader that colors the pixels of the sprite's texture in a set color. Then I can render that FBO over the final scene using (GL_DST_COLOR,GL_ONE) blend mode and color the sprites. But, not so fast.

After the sprites that will be illuminated are rendered on the light FBO in color,  I also render the front layers that should block that light in black to cover up those pixels. In the examples above the outside light should be blocked by the ground and the building on the left, the light from the building should be blocked by the building walls and ground.

b) Notice how the edges of the darker front sprites are also colored. To get the "bleeding" effect I blur the light FBO, so the light expands a bit:


To know which layers the blurred light should affect and which not, I need a FBO which serves as a mask. On that FBO I render all the sprites on which the final blurred light can fall, and using multitexturing and a shader I only render the pixels from the light FBO where the pixel is "on" on the mask FBO. Since the "sky" is not rendered on the mask, it wont be affected by the light, but, like seen above, it affects the front layers, which were rendered on the mask.

Mask FBO - green means light should be rendered there

 The sky didn't turn blue, but the wall on the bottom-left did

c) As you can see on the first images the outside area and the area inside the building on the left are illuminated differently. I can use the technique above on as many layers I want, then take the light FBO of the certain layer and render it all on the final light FBO using (GL_SRC_ALPHA,GL_ONE) blend mode to get alpha blending, then I can finally render that light FBO over the final scene using (GL_DST_COLOR,GL_ONE) blend mode.

Final light FBO is a mix of 2 FBOs

d) To darken certain layers(ground and building walls on the images above) I can take the above light FBO and render it over the scene using a mask where I previously rendered all the layers/sprites that should be darken up. Then I render the "dark" pixels where the pixel of the mask FBO is "on". Since the light FBO was already blurred and stuff, I get a nice fade to black at the edges, by using the RGB color values of the light FBO pixels to determine alpha value for the final dark pixel. Here the (GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA) blend mode is used.
 

Tuesday, October 13, 2015

Linux ?

Using Eclipse I managed to compile and run the game on Ubuntu/Linux inside VirtualBox. I have little experience using Eclipse, virtual machines and not to mention Linux, but it works more or less.
 

 
There are some problems:

- I can't load shaders and make FBOs, but that is probably because in VirtualBox I can only get OpenGL 2.1.
- FMOD doesn't work in the game and even the examples provided with the API don't work. It plays a sound for only one frame and turns it off. It could be a problem with the library, the way I installed the library, or the audio drivers in VirtualBox.
- Game crashes randomly.

Tuesday, October 6, 2015

Random #5

Few days ago I realized the engine has, more or less, all the features I wanted to make when I first started working on it. Many things are still duct taped together, but the final polish should come while working on a bigger project. So, what I am doing now is remaking Quake 2D in the new engine.

That was the plan all along, but it took a while to make all the necessary entities and features that an actual game could be even considered. From spawners to dynamic lights, the usability of all entities will be tested in real life/game scenario and revised as needed.

There's not much new stuff I need to code from ground up. Enemy AI is basically all that is left, because it is game specific. AI in the original was very simple, and wont be a problem to remake, but eventually I would like to make it more advanced, like the whole remake.

Tuesday, September 22, 2015

A* pathfinding and navigation mesh #3

Here is a little demonstration of A* pathfinding and navigation mesh. The first part of the video shows how I'm generating the navigation mesh and nodes for pathfinding using my editor, and the second part shows A* algorithm in action:


I have already described how most of it works in my previous posts:
http://antonior-software.blogspot.hr/2015/04/a-pathfinding-and-navigation-mesh.html
http://antonior-software.blogspot.hr/2015/04/basic-path-smoothing.html
http://antonior-software.blogspot.hr/2015/09/a-pathfinding-and-navigation-mesh-2.html

Clipper library is used to get the shape of the mesh by making holes where the obstacles are. Clipper supports vertex offsetting which I use so the nodes are placed a bit away from the obstacles, so the entities don't collide with them when they travel between nodes. Nodes are placed only in the concave vertices of the outer shell(orange) and convex vertices of the holes(yellow):

 
Poly2Tri is used to triangulate the mesh. Nodes are connected if they form the edge of a triangle, forming a tree structure.

Triangles are also used in real-time to connect the start and end points to the nodes in the tree;
- First I check inside what triangles the start and end points are (I'm using Box2D for this; triangles are bodies with a polygon fixture and I'm using a custom query callback just like when using a mouse joint to pick bodies in the testbed),
- then I create temporary start and end nodes and connect them to the nodes in the triangle's vertices,
- then I calculate the path using A* algorithm (white line in the video),
- then I optimize the path using visibility between the nodes (red line in the video(part with the tanks)).

For that "path smoothing" thing from the second link I also use Box2D, this time for ray casting. Instead of ray casting in the actual Box2D world where the game entities are, I use the outer shell of the navmesh and the holes to make bodies with chain fixtures(black lines), which serve as obstacles:

 
Vertices of those obstacles need to be offset using Clipper. If you look in the image above, the vertices of the obstacles for ray casting (black lines) are between the actual wall and the navmesh, so the entities don't cut corners while following the new path(see picture below), and that adjacent nodes can actually see each other:



 This is all probably very confusing... at least I tried :/

Sunday, September 13, 2015

A* pathfinding and navigation mesh #2

I was generating the graph for pathfinding by triangulating the navigation mesh. Every vertex of the mesh was one node, and the edges of the resulting triangles would be used to connect the nodes.

If you remove the nodes on the convex vertices of the outer shell of the mesh, and nodes on the concave vertices from the holes of the mesh, you can get an optimized graph, since no shortest path will ever go trough those nodes.

Before: 882 nodes, 3522 connections

After: 439 nodes, 1226 connections

Monday, August 24, 2015

Box2D buoyancy

This is a demonstration of iforce2d's buoyancy physics which I added to my engine. I use Clipper library to find intersection areas between the fixtures, and I made a class which keeps track of all the fixtures inside the water. I added this in July 2014, but never showed it.

You can find the tutorial here.

There are some problems when you throw objects into water with great speed. They can start spinning rapidly or even fly out of the water. This happens when lift is turned on. The problem is probably in force parameters which I haven't tweaked properly.