Zombie simulator: reworking roads and a new car PDF Print E-mail
Sunday, 07 February 2010 21:12

After playing around with the pathfinding for a while I decided I needed to make the roads wider.

Technically, the cars would "just fit" onto half the road tile, but it was a tight squeeze. It was at this point the real issue showed up: cornering. Unless the cars came to a dead stop at each corner, there simply wasn't enough room to have them arc around bend without crossing into the oncoming lane, so they'd have to continue to take corners Light Cycle style, just as they had in the last video.

Logically, simplest solution was just to expand the black road lines in the map bitmap to two pixels wide... that's when things became interesting.

My current algorithm for working out the road pieces that make up the map is actually very simple:

  • If a road pixel has two road pixels on opposite sides it's a straight
  • If a road pixel has two road pixels on adjacent sides then it's a corner
  • If a road pixel has a road pixel on three sides then it's a three way intersection
  • If a road pixel is surrounded by road pixels then it's a four way intersection

Following this bit of logic it gets more technical where polygons are created, adjoining road sections are merged into single polygon runs, and finally it's all textured.

Under the new system, the simplicity would be gone because it becomes lot harder to work out where each road piece begins and ends.

I fooled around with various ideas for dealing with this new map layout, all of which invariably came back to some sort of bitmap to vector algorithm followed by identifying which parts of the vector image are the various straights, intersections, etc. Yuck.

There had to be an easier way, and I finally found it.

I redrew the map using double thick road lines, then added a bit of code at the top of the road analyser that would examine each pixel on the map, starting at the top left, then clear out any road pixel that did not have another road pixel either directly to the right or the directly below it. This modified map was passed into the regular road generator which ran without change, apart from tweaking the constant that sets the width of road pieces to double the size.

Twelve lines of code, and Bob's your uncle.

To celebrate, I took some time off coding to add a new vehicle, a two door 4WD, loosely based on a Mitsubishi Pajero.

Here's a shot of the new car sitting on the new double wide road (that's still using the old road texture, now stretched, soon to be replaced).

 
Zombie simulator: pathfollowing PDF Print E-mail
Friday, 05 February 2010 19:54

Well that was a hell of a lot of work. Don't let anyone tell you that coding games is easy!

Since my last post I finished the full integration of opensteer and I was able to run some tests. I based the vehicle controller on the pedestrian demo that comes with opensteer since that came close to approximating what I was after. Unfortunately, the results weren't what I had hoped for and I had to do some pretty significant work to bend it into the right shape.

The biggest problem was that opensteer smooths out corners making turning more realistic, which isn't really suitable for my zombie infested world, since it's full of right angles. I found that I had to pump up the steering force settings really high to get the cars to keep within the road boundaries, but in doing so, it introduced a really annoying shudder like all the cars had shot suspension. The second issue this caused was that when a car approached a corner, about 30% of the time it would execute a neat circle and drive back the way it came... ouch.

Eventually I got fed up with tweaking settings to see what they did and simply stripped out most of the functionality built into the pedestrian class. I then implemented my own path generator which basically takes a vehicle's position and simply traces the road until it hits an intersection. The vehicle is then allowed to run this path, and when it reaches the end, a new path is generated and it all starts over. You can see the path that's been generated for each of the cars as the blue lines that appear and disappear overlaying the road.

The next step is to tidy this up - fix the glitches, keep the cars on their own side of the road, implement brakes so that cars slow when approaching a corner (allowing a smaller turning circle for the sharp corners) and stop completely when approaching an intersection. I can see that I'm going to have to work some magic to get the traffic lights happening...

All in all, it's taken a couple of days, but with opensteer implemented, it's now also available for powering the zombies and unfortunate citizens since vehicles and creatures share the same base class. Luckily I don't expect as much trouble with pedestrians as they only need to follow a rough approximation of the footpath and can turn on a dime. Having said that, I'm sure some sort of trouble will crop up... it always does.

Dim lights Embed Embed this video on your site

 
Zombie simulator: starting work on the AI PDF Print E-mail
Wednesday, 03 February 2010 18:54

Now that a couple of cars are in, it's time to work on the AI for the vehicles, people and zombies.

Having looked around the net a bit, there doesn't appear to be many steering libraries around. The most notable would seem to be OpenSteer, which has already been implemented as an Irrlicht class. True to form however, I need more control than a generic interface can provide, so I'm going to have to implement it manually myself.

I have two options for dealing with cars. One is to use OpenSteer, while the other is much simpler, and that's just to have the cars follow the road polygons around. As well as being simpler, I suspect that there's going to be a lot of extra work in dealing with making the cars avoid running into each other and maneuvering around road blocks and accidents. On the plus side, it'll probably use less CPU than OpenSteer.

With this in mind, I've opted to initially implement OpenSteer as a base class from which the mobile sprites and vehicles are both derived. Creatures will definitely need some sort of steering algorithm, so I'll find a use for it. If it proves too slow for use with the cars as well, then I can simply switch to something custom for the cars. I can't really tell one way or the other without actually implementing it because there is very little documentation for OpenSteer.

The basic idea for the path-finding will be to have people stay path find on the sidewalk, with the cars path finding on the roads, so neither will be on the look out for the other, which will hopefully cut down on performance issues. Pedestrians will cross at traffic lights and occasionally make a run for it without looking both ways... Zombies of course never look both ways. I'm also planning to make some drivers randomly aggressive and/or inattentive to give the city a bit more realism.

At this stage, I have completed integration of OpenSteer with the engine. The game compiles and runs without error, but the steering isn't working, so it's time to get debugging.

In the meantime, here's a bit of relevant amusement that I found while Googling today: AIGameDev's top 18 worst AI glitches.

 
Zombie simulator: new and updated cars PDF Print E-mail
Monday, 01 February 2010 19:09

Having finished my shader, I went back and updated the first car I did and it's looking really good, so good it's making the second car I did look bad. Guess I'll have to update the second one as well.

In the meantime, I added a four door police car, typical of what I'd see around town.

The nice thing about using the pixel shader is that it not only makes the cars look better, they use far fewer polygons. The hatchback originally used 154 polygons, now it's using just 104, while the police car is only using 108.

I made a couple of minor mods to the shader today, one to better handle the mixing of the cars colour with the texture colour, and secondly, I added a third texture channel for a second normal map, which will be the "damage map" used to make the cars look beaten up after an accident. The new map is added on top of the normal bump map and can be dialed in so the car can show minor scratches all the way up to looking like the bodywork is made of crumpled tinfoil.

 
Zombie simulator: pixel shaders PDF Print E-mail
Sunday, 31 January 2010 19:31

I started work on a second car, but It was looking very ordinary.

The first car worked well because it is so distinctive with the stripes, it's hard to mistake it for what it is, but the new car, which is loosely based on a Toyota/Lexus Soarer, looked nothing even remotely like the photos. The biggest issue was that with texture maps alone I could not get small details to look 3D-ish, like the side stiping. This was also a problem on the hatchback, but didn't show up as much.

I decided to look into using some of Irrlicht's fancier material types, ones that use normal mapping, so I could put some bump mapping on the cars for the little details like door handles.

The first step in this direction involved reworking the vehicle classes to create meshes with tangents, which are necessary for these more advanced materials.

Irrlicht's normal mapped materials

Next it was time to play with the new materials, and what a disappointment that was.

Sure, the bump mapping looked great - I used Irrlichts Earth bump map laid onto the cars which made them look heavily damaged, but unfortunately, there was one little problem.

The materials didn't support ambient light sources and they also didn't support directional light sources, which is the type of light source I have to use as the map is so huge.

I don't know why it's so limited, though I read something about it being as simple as possible to run on everyone's hardware - not much use when the shader will look out of place in anything except a night time scene in a small room!

So, that was then end of that, however, having seen how well the cars looked on their lit sides at least, I really wanted to get bump/normal mapping to work.

Investigating pixel shaders

I've not previously been involved with pixels shaders, so it was a steep learning curve.

I scoured the Irrlicht forums looking for information and I was inundated with new acronyms such as HLSL, GLSL, CG and CGfx.

There's also a number of different classes available for Irrlicht that make use of pixel shaders, including XEffects and IrrCG.

After reading for a while, I found out that GLSL was nVidia's pixel shader language for OpenGL, and as usual, Microsoft has their own called HLSL. Then nVidia released CG, which is a platform agnostic pixel shader language that's structurally very similar to HLSL. On top of that they later released CGfx which is a "package" containing different versions of a pixel shader for use in different situations, depending on the platform and hardware.

CG pixel shaders should theoretically be able to run on ATI and nVidia cards, and the structure was similar to HLSL, which meant that I should be able to find plenty of examples, so in the end, that was the one I selected.

As I mentioned previously, IrrCG provides that ability to load CG shaders, so that solved all my problems!

All I had to do then was install nVidia's CG toolkit, install IrrCG, figure out how it worked, integrate into my Zombie Simulator and finally, find a nice pixel shader to pretty up my cars.

Many hours later....

Ubuntu has nVidia's toolkit in the repo's it turns out. (nvidia-cg-toolkit) It is an older version, but it's more than enough for what I wanted

IrrCG turned out to be a bit of a pain for several different reasons:

  • Firstly, it wouldn't compile on my system because it was complaining about glXGetProcAddress not existing. I eventually tracked it down to the glx.h file, which I added to the IrrCG.cpp file thusly:
    #include "COpenGLDriver.h"
    #else
    #include <GL/glx.h>
    #include "glext.h"
    #endif
  • Next, I had all sorts of problems caused by my previous attempts to resolve the lack of ambient lighting in Irrlichts materials. I had upgraded to the latest SVN, and IrrCG wouldn't compile. After a while I found an entry in the forums which explained what to do.
  • Finally, the last problem I had was that there were almost no CG pixel shaders available! There was some talk on the forums of a Chernobyl shader pack for IrrCG, but it was never finished and only contains three shaders that aren't particularly useful.

Doh!

Where to get a shader?

The answer to that question is that I had to make it myself.

nVidia have a lot of freely downloadable HLSL and CGfx shaders avaialble, but unfortunately they're all in formats I can't use and the tools that can convert them either don't produce 100% complete code, or don't run on Linux. I can't run them in VMWare, and access to a proper Windows box with a modern video card, I had no way to use them.

That left me only the option of writing the shader myself.

My basic requirement was something that would provide phong shading which is superior to Irrlicht's Gourard, support for bump or normal mapping and directional and ambient light sources.

I set about dismantling a bunch of nVidia's example hlsl shaders, picked out the bits I wanted, combined that with various shaders and tutorials I'd found around the internet, all of which is far too long and boring to go into...

Anyway, here is the end result:

I've gone a little bit further than what I wanted - additionally, I've made it so that a base colour, passed to the shader, is used wherever there is a transparent pixel in the texture map. This resolved a problem I had initially with the cars in that I was having to build the colour of the car into the texture. Doing it this way means that I can have any car in any colour and use fewer textures, because the bump mapping will take care of the detail, shading the striping and handles to match colour passed in.

The latest car is looking pretty good and it uses quite a lot fewer polygons than the first... which means I'm going to have to go back and tweak the first one once this one is complete and I'm sure the system is working.

 
Zombie simulator: performance woes (and solutions) PDF Print E-mail
Thursday, 28 January 2010 18:12

More than one car

The video I showed recently with the spinning car demonstrated the car model in the "World of Zombie" simply by loading the model using SceneManager::getMesh().

I still had a lot of work to do to get the car into the engine properly, in a form that was usable. So, yesterday, I wrote the VehicleDef class for loading the model, dismantling and identifying the parts and storing them in a buffer for later recall. Following this I created the Vehicle class which included a VehicleDef, reconstituting it as an IMeshSceneNode. Then I upgraded it all to include an XML file so that more than one car model could be defined at the same time.

Once I had that out the way, I took the car model for a spin by creating 50 random red and blue hatchbacks, and dotted them around the top corner of the map!

Ouch. The frame rate dropped to below 40 frames per second.

Time to do some performance tweaking.

The biggest problem is...

I ran the engine for a few minutes with performance profiling enabled, then fired up GProf. The surprising result of this was that the worst offender, consuming 44% of the frame time, was Irrlicht itself, specifically a method of SColor called toOpenGLColor().

It turns out that Irrlicht stores colours in DirectX format, a 32 bit value, with eight bits for each of the colour components, arranged with the Alpha transparency first, followed by the RGB components, otherwise known as A8R8G8B8. OpenGL on the other hand stores the colour in the format R8G8B8A8.

This isn't a big deal until it comes to rendering. One of the objects passed to OpenGL is a colour buffer, which contains the colour of every vertex in OpenGL format. Because Irrlicht stores the colour in DirectX format, the colour needs to be converted. A typical scene in my Zombie World has about 25,000 vertices, all of which need the colour components rearranged for every frame. That's the ouch.

The solution is...

I'll have to fix it myself.

Irrlicht simply can't support OpenGL colours because Microsoft's proprietary way of doing things clashes with it.

What I have to do to fix this is simply remove support for DirectX from the SColor class in Irrlicht and switch to OpenGL's way of doing things.

Sure, I won't have access to Microsoft's latest proprietary furry tongue pixel shader in DirectX 11 or whatever else they've been cooking up, but I don't think that will really matter. I've got enough on my plate trying to keep the engine's performance up with 1990's graphics technology.

A partial fix

I did come up with one partial fix that's pretty specific to what I'm doing, but may help other users that are similarly afflicted.

I modified SColor.h, changing the toOpenGLColor() method starting at line 234 so that it checks if the colour requested is all- white, with all colour components equal to 255. If so, then the function simply copies the colour to the output buffer without executing the conversion. The reason I chose this value is that most of my models have all-white vertices, and all-white is the same in OpenGL or DirectX format.

void toOpenGLColor(u8* dest) const
{
if (color == 0xFFFFFFFF) *((u32*) dest) = 0xFFFFFFFF;
else {

*dest = (u8)getRed();

*++dest = (u8)getGreen();

*++dest = (u8)getBlue();

*++dest = (u8)getAlpha();
}
}

Re-profiling the code after this change showed that toOpenGLColor() was now consuming only 21% of the frame time, so it's a pretty big saving, but I will be able to save a lot more once I've dumped the Microsoft evil from the SColor class. Unfortunately, that will take time, so for now, I'll live with it. Who knows, it might turn out fast enough as-is?

Multiple IMeshSceneNodes with the same material cause big slowdowns

Checking through the code, I can see that Irrlicht orders the nodes prior to rendering by the texture they are using, presumably to send the texture to the video card once, rather than once for each node. This is a known problem and a good solution, unfortunately, it doesn't appear to work as efficiently as it could.

I've previously created a custom scene node for rendering the buildings since I didn't want to use the standard frustum culling, which doesn't work properly with the particular Orthogonal camera perspective I'm using. To test out rendering efficiency, I commented out the "driver->setMaterial(material);" line in my custom node's render function, added the line to the main engine loop (specifying the building's material) prior to the call to SceneManager::drawAll(), disabled all the other mesh nodes, recompiled and ran the code.

I was very surprised with the result. The engine went from about 350fps to over 450fps, with no change to the appearance of the buildings!

To check whether it was a problem with Irrlicht switching the texture in and out of video memory, I put the code back the way it was, then removed all the calls to set the textures for the building nodes. Executing the engine showed all the buildings in white without textures and the engine's speed had fallen back to 350fps.

The problem must be that simply changing the material for each mesh buffer belonging to each mesh node is consuming about 20% of the frame time.

Planning ahead was a big help

I had suspected that something like this might come up, so I planned ahead when I started this and have been putting all the related textures into single files. This means that all my building textures, walls, rooves, etc. are in one big image, all my car textures are in one image, and all my sprites have been divided up into a few images grouped by sprite size.

This has permitted me to make a couple of changes that have taken the engine from about 40fps back to over 300fps with no changes to the number of objects on screen.

Based on my testing, the fastest way to render is not just have few textures, but try not to change materials as well. Unfortunately, you generally won't have the ability to keep the materials constant as Irrlicht apparently will reload all the material properties, even if nothing has changed.

With this in mind, I've switched the buildings to using a custom render loop, and removed the call to set the material in the scene node itself. Instead, I make a single call to set the building material, then draw all the buildings that are visible. The change was actually pretty minor, just a case of writing a 20 line render function and commenting out a couple of lines. The result, as mentioned, was a saving of about 20% of a frame's render time.

The second big change was to switch from having a separate mesh buffer for each sprite, to creating a separate mesh buffer for each sprite texture. When a sprite is created, it's vertices are simply appended to the mesh buffer that has been assigned the texture it wants to use, with the correct texture coordinates of course. This increases the efficiency of Irrlicht because at render time, when SceneManager::drawAll() is called, it will draw all the sprites usingĀ  the current texture before moving on, drastically cutting down the number of material changes.

This been a far better result than the change I made to the buildings, because I can now have thousands of billboard sprites with little affect to the frame rate, though admittedly, it's a little hard to work with moving groups of vertices around, rather than a node. It's well worth the pain though.

 
<< Start < Prev 1 2 3 4 5 6 7 8 9 10 Next > End >>

Page 1 of 14

debug area