Sunday 14 November 2010

Linked lists, Managers and actors

Yesterday evening, after writing the erudite blog post about textures, lighting and what I'd been working on, I went and watched a show about the goodies with my dad. I took with me a pad, and sketched out a rough diagram of the relationships between actors, lights, physics bodies, and managers (for the lights and actors) in my game.

Today, I sat down and implemented it.

Instead of the mess I had yesterday, I now have a beautifully organised, manageable,  expandable system for controlling actors and lights. Writing the system also introduced me to the beauty of linked lists.

I used linked lists in each of the manager classes, for both actors, and for lights. The linked list was formed of either Actor or Light objects, with pointers to the next and previous objects in the list. This isn't an especially new idea, but I had a couple of main reasons for wanting to structure the managers like this:

1) Eaisily implementable, expandable storage.
I can hear you now screaming "adam, if you wanted expandable storage, why didnt you use the std::vector???", or, I would if anyone read this blog. Of course, I could have used the std::vector class, but I had one massive reason against it. Pointers. Each of the bodies in the physics engine stores a pointer to its Actor, each Actor stores a pointer to its Light and each Light stores a pointer to its actor. If I was to implement the managers storage as a vector then it would have meant a lot more work for me in the long run figuring out how to manage pointers to members of vectors, and I couldn't be bothered.  As is, the linked list system works very well, as well as being very easy to iterate through.

2) As a learning exercise in pointers.
Despite having programmed quite a lot in c++, I am a bit of a novice with pointers. Implementing the data as a linked list forced me to really get to grips with pointers, manipulating them, and initialising them with new.

Having finished the core of the Actor, Actor_manager, Light and Light_manager classes, I can now begin to expand on them, and to implement further functionality, like shadows.

I still have two main hurdles to jump before properly getting shadows working:
1) Sensor shapes for the lights.
Ideally I would like to have a Box2d shape associated with the light, so that I can test intersection with bodies in the world, and thus whether I need to draw shadows for them.
2) Contact callbacks.
Instead of testing intersections manually, Box2d allows the programmer to define a contact callback class, which will call functions when two fixtures (wrappers for shapes) intersect. Once I have this up and running, I can configure it so that shadows will be drawn for fixtures when they intersect with shapes.

So, onwards and upwards!

Saturday 13 November 2010

Coding, Coding, Coding...

Yay! Coding!

I've been getting back into coding this week, after a bit of a hiatus. I've thrown myself into a new project, a kind of spiritual successor to kreis, and so far its going well.

I've got some lighting sorted out, which so far is much prettier looking, and more realistic than what kreis had.
Basically, this time, instead of assuming everything is "lit" from the off, then drawing in shadows to darken bits, I'm simply drawing a black (unlit) texture over everything and "cutting out" bits of it which are lit.
The process is slightly more complicated than that however, and goes a bit like this:

1) Render the white light to a texture:
This is relatively simple, you simply get a gradiated circle, with 100% alpha at the centre, and 0% at the edges:
void draw_light(float x, float y, float rad, float intensity)
{
float nx,ny;
glBegin(GL_TRIANGLE_FAN);
glColor4f(1,1,1,intensity);
glVertex2f(x,y);
glColor4f(1,1,1,0);
for (float ang = (2*pi)+(pi/24);ang>=0;ang-=pi/24)
{
nx = (rad*sin(ang))+x;
ny = (rad*cos(ang))+y;
glVertex2f(nx,ny);
}
glEnd();
}

2) Composite the lights onto one render texture.
Again, this is pretty simple. You simply draw the light texture of each of the lights in turn onto one master texture:
void predraw_light(GLuint tex, int x_l, int y_t, int x_r, int y_b)
{
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, tex);
glColor4f(1,1,1,1);
glBegin( GL_QUADS );
glTexCoord2d(0.0,0.0); glVertex2d(x_l,y_t);
glTexCoord2d(1.0,0.0); glVertex2d(x_r,y_t);
glTexCoord2d(1.0,1.0); glVertex2d(x_r,y_b);
glTexCoord2d(0.0,1.0); glVertex2d(x_l,y_b);
glEnd();
glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);
}
Where "tex" is the lights texture, and "x_l, x_r, y_t, y_b" are the right, left, top and bottom positions of the texture.

3) Render the master texture onto the scene, inverting the alpha values.
This is when it starts to get trickier. We need to render all the lights to the scene, but at the moment, we just have a collection of white blobs on a transparent black texture. What we do is draw the texture, whilst REVERSING the alpha. This means that the alpha of every pixel becomes 1-its alpha. So, during reversing, all the transparent black becomes opaque (unlit) and our lights become transparent, and therefore lit.
void draw_light_tex(GLuint tex, int x_l, int y_t, int x_r, int y_b)
{
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, tex);
glColor4f(1,1,1,1);
glBlendFunc( GL_SRC_ALPHA, GL_SRC_ALPHA );
glBegin( GL_QUADS );
glTexCoord2d(0.0,0.0); glVertex2d(x_l,y_t);
glTexCoord2d(1.0,0.0); glVertex2d(x_r,y_t);
glTexCoord2d(1.0,1.0); glVertex2d(x_r,y_b);
glTexCoord2d(0.0,1.0); glVertex2d(x_l,y_b);
glEnd();
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);
}
Tex, x_l, x_r... all stand for the same things. Note the opengl blend functions:
glBlendFunc( GL_SRC_ALPHA, GL_SRC_ALPHA );
...
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
The first is the magic of this system. It reverses how the alpha is normally drawn (ie, the second function) and thus allows all our lights to work nicely.

Once you combine this with some nice physics (another thing I've been working on, getting Box2d integrated) you get lovely effects like this:

This is still VERY much wip, and I'm planning to tidy up the code a bit this weekend and package up some of the lighting stuff into its own class.

After that, I've got a couple of major targets which I want to meet:
1) Get shadows working.
So far, I've got some nice lights going, and darkness where there isn't light. However, what I dont have is objects casting shadows. I've done most of the legwork for this before (with kreis) so this time I just need to get it sitting nicely with the texture based lights.

2) Get some actor classes working.
I'll need an actor class eventually, so I may as well get one going now to tie together Box2d bodies, and the lights.

Apart from that, things are going pretty well! Box2d was a bit of an effort to get going, but only because of working out which projects to compile, and integrating it nicely.

Also, if anybody wants the full code, or the code for the texture generation and drawing, just email me at the address given on my website (harries {dot} adam {at} googlemail {dot} com), and I'll get back to you asap with it...