Wednesday, October 19, 2011

64 – 3D Week 4 in review

Ohhhh yeahhhh! One month of working on the 3D engine. Nice progress if I do say so myself: 

No more barrellands! This time you get to see the table and stool created by BrewStew. To limit performance loss only the current level is populated with items, but in the future I’ll add an option to render all levels with full detail. There is no reason not to allow people with beefy computers to use their full potential. Also useful for anyone for taking high detail screenshots. 

Also stick around until the second part of the video, where you get to see a long lost feature that has made its glorious return: selection with the mouse. The problem is that selection with the mouse is far too slow. You won’t see that in the video, but it is. Optimizing this will be a low priority task. First I must make sure that the geometry generation time for a volume unit is as fast as it gets. Then I must make sure that a minimal number of volume units are updated. 

The high priority task is to figure out which algorithm to use for world construction, geometry building and buffer management. I have at least 6 strong contenders, each with their own advantages and disadvantages, plus probably a bunch of buffer interaction and fragmentation properties that I am yet not aware of. I also need to stick a huge number of different entities in the same scene. Up to this moment I mostly stuck a huge number of the same entity in the scene (trees, barrels). BTW, the trees are not visible in the video because they are not mesh based, they are procedural. Because I transitioned to the new hardware buffer based rendering I need to update the procedural generation for trees and I did not get to it yet. 

The algorithm that I am using right now is different from the one that created low poly 300.000 barrels. This one focuses on fewer higher quality objects. It is also lossy. Each section has a potential for holding items based on maximal buffer sizes, and when you go over this potential, items start to get skipped. Using the density from the video skipping should not occur, but maybe one or two items were dropped from the busier section. The dropping is class based, meaning that if you have a ton of tables and only a few barrels, the excess tables will not cause the barrels to disappear. Only excess items from within a category are lost: if you can have at most 300 tables, 400 barrels or 500 stools alone in a section (example number), you can have all of them visible at once in the same section. But if you have 302 tables, 405 barrels and 600 stools, 2 tables, 5 barrels and 100 stools will be dropped. 

A natural evolution of this algorithm will be to determine how many high poly entities to convert to low poly in order to not drop a single item. So instead of trying to render 405 high poly barrels and ending up only rendering 400 high poly barrels and dropping 5, the engine should render 395 high poly barrels and 9 low poly barrels. Again, example numbers. This can be easily done and I’ll implement it soon, but first I must add an analyze and caching step to world creation, so you know the totals for each section. 

One problem that is especially visible with highly zoomed out top down view is that it is sometimes very hard to tell what is going on. I need to figure out a method to make thing pop or something so you can tell what you are looking at. This is not a problem for first person camera, where the current semi realistic proportions and first person viewpoint make it a lot easier to tell what is going on. One trick I’ll try is to scale up small objects based on the vertical distance from the surface plane of the current level and the camera position. When you are zoomed in close, proportions are similar to the current ones. As you zoom out, small objects become larger and larger. I can also experiment with adding a few black borders to the textures to fake a cell shading style border, but I am not prepared for real cell shading right now.

How does Starcraft 2 do it?

Sunday, October 16, 2011

63 – Barrellands

Using the technique I created and used for adding 15000 high poly barrels or 90000 low poly barrels to a scene, I started experimenting to see if I could make this my main method of world creation. I also had an eureka moment and with one single line of code changed I got an extra 50 FPS on the 90000 barrel scene.

So first step was to improve stability of the system and see how many low poly barrels I can fit in one scene:

That's 307800 barrels! I'm sure that I can fit more than 15000 high poly barrels now, but not by a lot. 307800 is the rough equivalent of 15000 when we change detail level. Don't get me wrong, I can actually fit a lot more high poly barrels, but I end up exhausting the resources of the GPU and this causes it to render the shape of the meshes, but with a fully white texture. The places where the GPU drops textures is actually quite indicative of the make up of the scene: I can practically see my algorithm mirrored in that pattern.

So considering this a successful test, I started using this method for terrain. I reimplemented the terrain generator using the new systems and it was quite a hard task. Again, the GPU did not behave in a rational way and my successes were due more to trial and error than applying my knowledge of the GPU and it behaving accordingly. But I managed to create a world renderer capable of rendering and empty landscape with full elevation with over 1000 FPS. Without borders.

Adding borders drops the FPS by 200-300. I tried adding more materials to a scene node to avoid this drop, but in the end it did not work so now I have multiple scene nodes, one for each border type.

Bringing all the techniques together, I created this video where we have adjustable elevation slicing, digging of walls and a nice topping of barrels on top of the elevation:

As a stress test I render all of the barrels ignoring slicing. In the final engine I'll stick to rendering items only on active levels. The performance is still good enough for FRAPS to almost be able to keep up 60 FPS. There are some drops bellow this value, but if you look carefully at the video, you will see that these drops coincide with level change, which is a very CPU intensive operation.

So with digging implemented (change of landscape) and adding meshes (populating with items) the 3D engine is theoretically finished. I need to add meshes to everything and polish, polish, polish! I hope that in the next post we'll see dwarves actually doing the digging.

But not everything is rosy. I still can't turn on the GUI under DirectX because it will lock my FPS to 40. I started investigating this and I can say that neither the calculations the GUI does (which do drop the framerate by a negligible 10 at most) nor image blitting are the cause. The primary suspect is rectangle drawing, but I can't say for sure yet. And LOD switching needs to be reimplemented for the new system.

Friday, October 14, 2011

62 – WTF

Look at your tree! Now back at mine! Clearly, your tree looks better. In my defense, I did not model that ugly tree; I generated it procedurally from a sphere and a cylinder. 

Fortunately, Stewart Bridge, aka BrewStew offered to model a few objects for me. You may have seen the barrel in the comments section and now I also have a lovely table at my disposal. Thank you, BrewStew! He can’t fully exercise his talent because I have some strict polygon/face count requirements, but even with low poly models the assets looks great! I would advise you check out his future work when or if he makes it available. 

So what do you do with such assets? You place them in the world. But I do have the problem that I can render at most 300.000 faces. Over that performance becomes way too choppy for me to be comfortable with it. This is not Crysis, so I can’t really release something for futuristic PCs. Also, a friend pointed me towards some statistics, how even low end GPUs can render millions of triangles per second and since performance seems fairly constant on most platforms, one would be forgiven to think that I am not rendering at the GPU performance, but at the speed at which I can transfer data from the CPU to the GPU, something bus related. 

Haha, it’s hardware buffer time! Tanananananananananananananana… Or so I thought. This seemingly simple and short task turned out to be quite long and full of WTF moments and complete randomness. The first sign that things will be random happened actually while I was implementing borders. Everything worked fine until I loaded the last texture. The entire scene disappeared. Remove the last texture: it was back. I significantly reduced GPU RAM consumption by removing all unwanted textures, including the one from the 2D engine that still got loaded for some reason. Same thing. Did not matter what texture I loaded or where. One more => no scene. So I batch converted all PNGs to BMP. The scene was back and I no longer experienced any issues. I reverted to using the original PNGs and no issue. I readded the 2D engine textures: no issues. Very weird. But this was only the tip of the iceberg. 

Using the new barrels and hardware buffers, I managed to stick quite a few in a scene: 

That’s 15000 barrels with high res textures rendered with filtering at a smooth FPS of 60 or more (FRAPS caps it to 30). Normally one would not have so many objects in the scene. Gameplay dictates the maximum number of objects and you can do tricks, like not rendering any small items on levels that are not the current one. But let’s say you want to render so many objects. Let’s say you want more! 

Using the low poly version of the barrel provided by BrewStew, I managed to fill each cell: 90000 barrels! This was the last time everything went smooth. I saw the framerate, I was happy, so I was curious how fast it would go with only a few low poly barrels. The answer was: 40 FPS. Even with a single low res barrel 40 FPS. Replacing it with the high poly barrel: over 800. WTF? WTF? WTF? Do buffers have a minimum size requirement? How about a single buffer with hundreds of low poly barrels? 40 FPS. A few thousand? 40 FPS. Even more? Back to normal performance. 

Maybe it is still related poly count. I export the barrel with some “normal normals”, not “high quality normals” from blender, which for same strange reason causes Irrlicht to report that the barrel has more faces. Problem solved. It works with a lot of a few, but with a lot the performance is lower because now I have a more faces. 

Meanwhile, OpenGL has not a single problem and works admirably with all scene setups. This is the first time in my life when OpenGL not only works (which is rare), but works a lot better than DirectX. But I think it is an Irrlicht issue. Irrlicht somehow does something with DirectX messing it up. 

So to recapitulate, now I can render 15000 high res barrels or 90000 low res barrels, but the low res only render at about 80% speed because I am forced to render more faces in order to prevent DirectX rendering at 40 FPS. Camera movement is not that smooth because Irrlicht containers are really not that well suited for making common operation “faster than they can be”. You know, using a container in a special way to amortize the cost of adding to it based on the nature and frequency of these operations. Anyway, I look over the Irrlicht source code, see what it does with the containers and manage to optimize it in such a way that camera movement is smooth again. 

The problem is that I create all objects in the beginning. I would be nice to be able to repopulate a single map section on demand so I can do dynamic LOD switching. I implement this with such a trivial change that there is no way you can break anything. I get under 10 FPS. WWWWWTTTTTTFFFFF. I only populate one section. Over 800 FPS. Two sections? 40. Three sections? 20! WTTTTFFFFFFF? 

The only difference is the number of scene objects Irrlicht sees. Adding more than one does something with the hardware buffers. Meanwhile, OpenGL is chilling in the corner, barely breaking a sweat and rendering anything I give it at high performance. 

So I rethink the entire hardware buffer system, adding pooling to it. Irrlicht is very poor at giving you any control, so I can’t really even free a hardware buffer, so they are not freed, just overwritten and reused. I start reading Ogre3D tutorials. Ogre3D seems to be a lot more about control. Using the pooling system and still Irrlicht I manage to create a dynamic LOD switching system: 

There are fewer objects, but the focus is not on the number of objects, but on the LOD switching. You can’t tell the difference, but distant barrels are rendered with low poly model and textures. In case you were wondering why the pattern: this is how many high res barrels I can fit inside a single buffer and for this sample I wanted to have one buffer per section. 

So to recapitulate, a lot of high res objects, a lot more low res objects and LOD switching works, but only under OpenGL. Turning off hardware buffers for OpenGL we get 38 FPS, which is very close to the 40. So I guess that DirectX ignores my request for the buffers to be directly accessible to the GPU or whatever. 


Did I do something with the GUI or does it use some primitive that causes DirectX under Irrlicht to die? I don’t know. I’ll have to investigate. So framerate is smooth now with both renderers. Using the low poly barrels, I render 90000 of them 100 FPS (30 in FRAPS). If you look at the video you will see that it is reporting over 50 million triangles. The labels are wrong and computations are not quite accurate. The map has dimensions of 300 cells, we have 96 indices per barrel. That is 2880000 triangles. But the display is not about exactly how many triangles are on screen, but how buffers are allocated and how much potential storage they have. Because the magical number of buffer redundancy that I introduced is 18, we get 2880000 * 18 = 51840000 that is almost as big as the number triangles reported. The rest of the difference is because of buffer rounding, because I am not going to allocate a buffer of 5. Buffer sizes and memory boundary requirements make them larger than needed: 

Clearly there is ton of optimizing to do. The 18 redundancy buffer pooler needs to become more dynamic. 

GUI is turned off because of the issue, but things finally work. 50 million faces are a tad bit more than 300000. The question is what to do with this power. 

Obviously, non dedicated GPUs that do not support hardware buffering won’t be able to take advantage of this. So I’m thinking creating three quality settings. On medium, I’ll have at most 300000 faces. On low at most 150000 faces, even if I have to billboard everything. And a high quality renderer, with 1 to 3 million polygons in the busiest scene possible. 

I need to fix somehow the GUI and I am sure this is only the first set of surprises that 3D has in stored for me. We are no longer in the realm of rationality. 

I’ll try to put together some realistic scenes with level geometry, trees, barrels and tables.

Tuesday, October 11, 2011

61 – Borderland

I managed to finish the new border system. Oh boy, did I ever. I created and absolute abomination. Probably the worst piece of code from the whole project. And the worst part was that after I thought I was done I realized I forget some special cases. I don't know what was wrong with me. Probably it was too late and I was coding on autopilot, with my fingers moving but my mind on some warm tee and hot cookies (literally speaking; get your mind out of the bloody gutter :) ).

So I was forced to rewrite it. The new solution is at least 20 times shorter! It handles all cases fine. The old solution did have one advantage though: it handled the small visual artifacts. The new one does not, but it will be able to do so quite easily. But I won't fix it right now. I don't want borders to turn into "ramps 2.0". The horror!

An "Anonymous" user had a really great comment and mock up which I ended up implementing. Thank you! And hooray for me for not knowing lighting basics.

In the next video you can see the two techniques working together to better illustrate the structure of the level:

It is a little bit rough around the edges, but I am really starting to love the look and feel of it! The little arrows have double function: they help to better differentiate borders and also point you to the direction the border will move when you dig out a wall. What do you think?

There is one problem though: the border system is a huge resource hog. Even if I limit borders only to the active level it might still be a little bit too slow on older computers, especially with a full fortress and freeform fly camera.

Monday, October 10, 2011

Screens of the day 18 - Contrast

Wow, a "screens" post. I has been a while. Well, I can't delight your senses every day with a new video. And you have no idea what joke came to me for the title of this post which I thankfully managed to avoid!

So while polishing stuff and preparing for the end of my 3rd week of work on the 3D engine I though of some simple techniques to increase contrast. You already saw in one of my previous screenshots and in the videos that you can toggle cell borders on and off and how these cell borders look. Every single cell has a border if you turn them on. But how would it look if borders would only appear at an elevation change? like this:

Because this is experimentation and a what if scenarios, I did not go to the trouble of adding the vertical borders, only the horizontal. Adding these borders is not easy. First of all, the code is not that short because these borders, as simple as they look, are quite intensive on the GPU side. Also, the advanced filtering I am using to greatly increase the visual quality causes some artifacts at texture borders, so I ended up using a dirty little trick to compensate for this: instead of drawing textures from texture coordinates 0 to 1, I draw them with a offset of 0.03. This is enough to confuse the GPU into not causing these artifacts, but I have no idea if this works on other video cards.

Let's see these borders from the isometric viewpoint:

I think it helps a little to have wall borders and to disable floor borders:

Since it is a little bit hard to tell what the end result will be because of the lack of vertical borders, I'll Photoshop them in. And by Photoshop I mean use Microsoft Paint:

Here is a bird's eye view:

Then I though that borders are not enough. What if we add some little arrows?

Later I'll combine these borders with horizontal ones and also recreate toggleable half-height walls.

But what do you think? Which border scheme works best? Do you have any better ideas on how to highlight cells and improve contrast? Drawing borders with a specific color that is not black is harder than drawing them with black, so I prefer to use black.

I await feedback! Ask a friend! Or your pet.

60 – 2Derp

Today we are going to talk about the advantages of a simple ASCII (or tileset) presentation versus the alternatives. I’ll leave out the simple stuff, like how it is easy to develop and update and how it leaves a lot to the imagination, making some people feel more involved with the whole thing. 

One of the advantages is that because you have a fixed tile width and you must force everything in that space is that you can create a very simple and modular world building block system. You do not get a freeform realistic system. I like to compare it to board games. Your game is a very advanced board game and you place your little figurines on it. This abstraction also makes you ignore a lot of inconsistencies easier: why is a wall so thick, how come a chair looks almost as big as a bed, why does this dragon fit in this narrow corridor? I’ve heard of Crouching Tiger, Hidden Dragon before, but what is this? Crouching Dragon, Hidden Dragon? Table top gaming systems have dealt with such inconsistencies for ages and found compromise solutions that don’t challenge you suspension of disbelief that much because you focus on what fun and involving thing you can do with you figurines rather than comparing the board with reality. 

But as graphical fidelity increases, this becomes harder. It is already harder with a tileset replacing ASCII art, it gets even harder with an isometric viewpoint where you need to do a lot of tricks to make everything more believable and with 3D it can all break apart. You need to be careful. Or you can go with a free form realistic system, where item placement is no longer constrained to the grid. I have decided that such a system would be too much work for what it is worth, so I’ll be sticking with the tabletop figurines metaphor. I started the 3D engine with more realistic proportions, but I’ll start scaling everything for practicality. This representation and time compression go well together as an explanation for what your game session is: you are basically reliving the memories of your expedition leader, memories which you are creating in real time as you play. This is probably how the leader could relive in his mind the events of the day, with things scaled around to emphasize importance, stopping to ponder on important events and skipping over unimportant ones. 

Another advantage that the ASCII system has is contrast. You are rendering characters with different colors. You can’t really get contrast any better than this. Once you get used to the display you will find it easy to read. On the other hand, with the 3D engine I am struggling to get good contrast and clarity. 

I analyzed all cameras: fly and dwarf camera are fine as they are, since you don’t need great overview of the micro setup of you world and out of isometric and topdown, especially when dealing with walls, topdown is a clear winner. What works for one camera does not work for another one. So I’ll focus my attention on each in turn. I have picked the topdown camera as first. I’ll try and give it the best overview possibilities I am capable of while rebuilding all the lost functionality in the 3D transition. 

First thing I did I implemented some non-aggressive culling specifically for this view point. Average framerate went up from 80-120 to 200-300. I also tested the result of the digging operation with the most straightforward implementation possible. Before the culling in debug mode it used to take 300ms and in release mode was a lot faster. After it around 60 ms in debug mode and as fast as it needs to be in release mode. But I’ll need to create a smart implementation for it. With this speed improvement I hope I can do something (something computationally intensive) to improve contrast on the scene.

Saturday, October 8, 2011

59 – It's good to be backhh

Before I talk about today's topic, let me get back to the topic of the last LL3DLGLD. Because of the left-handed Cartesian coordinate systems it seems I can't avoid building the world upside down, but I can avoid the need for meshes to be created upside down. So the creation of models can be done as usual I hope.

So if you have some models lying around mail me a few so I can do some quick world population tests. I will create and search for some meshes myself, but some help is always appreciated. I am not asking you to create meshes for my game. But if you have some meshes available or know links to some sites that offer such meshes send me a link. I do not know what format to use so I'll try to load anything that I can get my hands on and later decide which one is best for my needs.

So back to the subject: basic merger with DwarvesH is done and GUI and a few simple interactions are working again:

The merger was fairly uneventful. I ended up greatly enhancing all those thing a GUI system does behind the curtains  like focus management, event handling, order system, etc. I worked 6 hours only on this. I could write pages upon pages on this subject, but I would bore you so let me just say that the GUI system is slowly becoming a really great and to my liking widgeting system for games. The only problem is that the framerate took a noticeable hit. Could the GUI eat up so many resources or did I introduce a bottleneck somewhere else?

I also started experimenting with shadows and lighting. I still need to learn a lot, but I'll leave you with this very ugly first failed experiment:

Friday, October 7, 2011

LL3DLGLD – 11 – I see you, jakesillyminer

Let’s add the final piece to the puzzle: the dwarf first person camera. Irrlicht has already a built in first person camera, but I looked over the code and it would be hard to make it behave the way I want to. So we are going to write one. Actually, we are not going to write a specialized camera class like Irrlicht has for different roles; we are just going to create a normal camera and make it behave like a first person one only by handling keyboard and mouse movement. There are a few popular first person models and behaviors in games out there. I’ll pick one that I like the most. For starters, mouse look will be what I call normal: moving the mouse up (away from you) will raise your head and moving the mouse down (closer to you) will lower your head. Some people out there suffer from a very strange condition and consider this model to be “inverted”: they want to look down when you move your mouse up. This behavior is even more confusing when you play something with a game pad. What up for a mouse means can be debated if you are silly. But you can’t debate that for a thumbstick. In the future I might add the option for inverted controls to cater for all audiences. For 4.99$. 

Another convention that I will be adopting: directional keys or WASD are for forward, backwards and strafing. No turning. You turn with the mouse. 

Before I continue, let me explain again how the world is set up in 3D space. This passage will be doubly important to people who would like to send me some meshes. Let’s consider that you are standing somewhere in the middle of a perfectly horizontal surface. Your head points to the north. You raise your arms sideways, with your left arm pointing exactly to the west and your right to the east. Like a lot of humans before you, you consider yourself the center of the universe: the origin point (0, 0, 0) is exactly under your feet. The X axis goes along the line created by your arms, with left/west being negative and right/east being positive. To the north, the direction you are looking at in front of you we get the positive Y coordinates, and behind you we have the negative ones. The Z coordinate decreases as things get farther away from the level of the floor. You are very tall and have a height of exactly 2 meters, so the top of your head has a Z coordinate of -2 meters. If you were to go fishing, a 100 meters deep lake would have its bottom at 100 meters on the Z axis and you would look form -2 to 100 over a distance of 102. I hope I did not confuse you even more. 

On the other hand, 3D modelers should be fairly familiar with this model as long as they mind the Z axis. 

So the first step is to set camera coordinates. We determine the Z coordinate based on elevation and set the rest to zero for the camera position. We do the same for the target, but this time we increase the Y coordinate with some number. I put 100 and need to test if this has any effect based on FOV (field of view). Setting these coordinates should place the camera on the floor so you probably won’t even see it. We decrease both Z coordinates with the height of a dwarf and this should fix it. If it does not work we check the camera up vector, which should be (0, 0, -1). 

For the first phase of movement, we add or subtract the same constant form either the X or Y axis for both camera position and target. For forward we add to Y and for backwards we subtract. And we modify X for strafing. And that it. Now we have a four direction smooth strafe. 

Next we add camera look with mouse. To achieve this I’ll keep the same camera model and rely on camera rotation. Irrlicht can adjust your camera target based on camera position and camera rotation. To achieve rotation we rotate on the X and Z axis. By setting the starting X rotation to -90 degrees and the Y rotation to 0 we get the same north facing viewpoint. 

Then we set the mouse cursor to center of the screen and keep this center in a variable. We create another variable for the mouse position that we update every time we get a mouse move event. We use relative position with floating point numbers. Every time the current position is different from the center, we calculate the X and Y difference, update rotations and set the current to center. Getting the X and Y is as simple as: 

float y = (0.5f - cursorPos.Y) * 100
float x = (0.5f - cursorPos.X) * 100 

And that’s it again! Mouse movement triggers head rotation. For the final step we must make forward/backwards movement take into account these angles. I tried a lot of complicated matrix and vector math, but in the end I solved this by the simplest formula ever. Forward: 

  • AdjustCamera(-sin(rotz * M_PI / 180), cos(rotz * M_PI / 180), 0); 


  • AdjustCamera(sin(rotz * M_PI / 180), -cos(rotz * M_PI / 180), 0); 

Let’s see all of this put together in video form: 

Things are not perfect yet: 
  • Proportions are a little of. Dwarves are too small. Trees are too big. Walls are about right. 
  • I’m not sure about the FOV. Need to experiment with a lot of different values. 
  • Pressing two movement keys at the same time composes the effect and you move faster. Need to adjust for this. 
  • Need to adjust for framerate. With FRAPS reducing my framerate to around 30 I ended up walking slower than usual. You should have the same movement speed with all framerates. 

But still a nice result. Did you notice the dwarf? It comes with Irrlicht and I have no idea why the default animation is tee bagging. I swear I did not do this on purpose! Or did I? :P 

And I can’t shake the feeling that at least one of the coordinates in my space model is inverted! GAAHHHHH!!!! 

Wednesday, October 5, 2011

58 – 3D Week 2 in review

Wow! I have been working on the 3D engine for two weeks now. 

Let’s start with performance. It is OK. But there are still a lot of things to add to the engine, and when you add things performance will drop. There is no way around it. So I improved current performance: I get an extra 10 to 50 FPS on the same scene after this change. 

Mesh generation is divided into two kinds: meshes that have different LOD levels and ones that have only one. Stone tiles have only one, so in consequence meshes formed by stone blocks are now only generated when you change levels. The other type is updated as needed on camera movement. 

This caused some extra strain on the CPU since we generate larger areas less frequently. So now sections cache some parts of them that remain consistent between level changes and it is fairly smooth now but it will never work perfectly smooth with huge level gaps. This did not work in 2D so it won’t work in 3D where there is more work. But this is a small problem in extreme cases. 

Taking this opportunity I rewrote the basic terrain mesh generator to a shorter and considerably more elegant solution. I did lose the ability to have different grass levels, but this is only temporary. Multiple grass levels were cosmetic anyway. The important thing is that the new generator is great. 

I also tried to increase general visual quality and also create some contrast. Even though floors and walls made out of the same materials should look the same, I use different textures with the same color to give better contrast to the scene and wall borders have different color than floor borders: 

What do you think? Is it easier to tell what is going on? I wonder how I can better increase the contrast and clarity. Can it be done by texture work alone? 

So terrain generation is done for now. For next week I’ll merge the 3D engine package with the DwarvesH package to finalize the 3D transition. 

I’ll leave you with this sample of random starting locations:

Monday, October 3, 2011

57 – Catching up

I had two goals that I wanted to reach before today’s post: realize the basic DHCore integration and make the game less ugly!

DHCore integration was pretty straight forward and there is really not much to say: the 3D engine now uses the constants, data structures, API and terrain data provided by DHCore. The model that was designed for a 2D engine holds up surprisingly well in real 3D space and while the artificial distinction of floors and walls is… well… artificial yet again… it does makes things simpler to navigate. Memory consumption is under 100 MiBs but it is going to get higher.

The real hard part was the not ugly bit. I am not talking about direct things that are easy to fix: low res or inappropriate textures, low poly models, lack of shaders, etc. I am talking about things that transcend the notion of “this asset has been created with little artistic skill”. Basically, everything that can go wrong in a 3D engine going wrong: texture melding, texture bleeding, textures becoming muddy, texture stretching, Z buffer fighting and a whole bunch of other visual artifact. The best part about these artifacts is that they are not random: they form patterns that shift at the smallest camera movement so you are sure to pick them up. These get worse farther away from the camera and I’m sure you noticed a few, especially in the videos.

I need to fix these without sacrificing a lot of performance.

A lot of these artifacts are caused by my renderer. Rendering complex first person worlds is fairly easy. Rendering strategical views of maps that only have a surface to worry about is fairly easy. Rendering worlds where every single Z level is important and you can switch on the fly from strategic to first person view is a lot harder. For me at least. So each renderer is a compromise. It is near impossible to get one to do everything. So I’ll use more.

I created a new renderer for stone (and sand and soil) blocks. This one can be both faster and slower than the one used for grass, but it looks better:

Since it looks better I also must trade in something: this renderer gets progressively slower with every new texture it uses. The grass renderer handled 1 to 256 textures (that’s why my grass progression was as smooth) and only cared about poly count. The new one cares about poly count and number of textures. I am going to experiment and check out how far I can push it with a single texture. It is also optimized for layers where there is a predominant material. Here is the wireframe mesh:

The great thing is that it does not get muddy, stretch or cause artifacts at distance from the camera. Here is an entire level rendered:

Crystal clear! Bland, but clear! The performance is acceptable, but in the future I might add the possibility of dropping distant detail.

The grass renderer also got a small overhaul. For starters, I was using 256 levels of grass just because I could. Extremely smooth visual grass transition isn’t really the selling point of my game, so I reduced it to 16. I also wrote a new algorithm to approximate distant grass:

It still looks kind of forced, but it is a lot better, especially when compared to distant grass in my video “Experimental: Procedural Grass Progression”.

And finally, we have cell borders that can be toggled on or off:

The strength of the border is distance dependant and not quite perfect, but I do like the borders. They give a nostalgic 8bit feel to the world, especially on bright and colorful blocks.

Using these techniques, I put together this small little video: