Thursday, December 30, 2010

Colored light

It would be nice to have colored light. For example, you could set the sunlight to something yellowish, and the sky light to a light blue.

Broville never looked sunnier.

Not only does this mean we can have green glowing radioactive ooze, and caves basking in the deep red glow of magma. The system already supports semi-transparent materials, so we can also have cathedrals with stained glass windows casting colorful light patterns onto the floor, trees with a greenish shadow,....

The downside... it costs yet more memory. It's using 74 KB per chunk at the moment, just for the light map. And there are hundreds of chunks in a scene. Once occlusion culling works properly, it will become difficult to predict just how taxing a given scene will be. I guess I'll have to dynamically change the view distance based on the current frame rate and the memory load.

Saturday, December 25, 2010

Christmas shopping for a new UI library

After fighting with CEGUI for the past week, I've come to the conclusion that this is not the right library for me. The user documentation is pretty much what I'd expect from an open-source project, but that's okay. As long as I have the source code, I'll find my way. No, it's the amount of work that needs to be done just to do something simple.

I was hoping I could show a couple of cool things this weekend, but alas. In the meantime, I'm going to check out libRocket, Right Brain GUI, and MyGUI.

Sunday, December 19, 2010


I've made the changes I talked about in the previous post.

It looks so much better than the old, per-voxel light model:

Next week: server-side Lua scripting, and maybe a first glance at the user interface.

Saturday, December 18, 2010

Different lighting model

Looking at the screenshots from the previous post, I decided I don't like how light and shadows curl around edges. I'm going to switch from calculating the light map per voxel to one that does it per face. This is going to make it six times bigger, but so be it.

There are some advantages that will make it worth it. I can add Lambert's cosine law, which will improve realism in general, but especially things such as sunlight coming through a window will be much prettier. And it's going to speed up the radiosity pass (because oblique faces can be ignored, something you can't do with per-voxel light).

Small drawback: can't write it back to a Minecraft world anymore, or at least not without a little extra work and losing some detail. Oh well.

Minecraft level reader

Most of the framework consists of modules that can be chained together. One example is the part that reads and writes the game world to some kind of persistent storage. The default module is something that sits on top of the face-meltingly fast sqlite. (Bonus: save games are just one single file, not thousands.) And of course there's the binvox reader. But yesterday I wrote a module that can read Minecraft files, just to have some cool pre-made worlds to test with.

This module reads a Minecraft chunk, slices and dices it in 8 cubes, roughly translates the block types (far from perfect), and passes it to the server. Everything higher than 64 blocks is air, everything below -64 is rock. If the requested chunk is outside the map, it falls through to the "null" module, which does nothing. This triggers the terrain generator, which right now is a simple, infinitely large flat plain.

Today we're going to have a look around the Broville map. It is legen... wait for it... dary:

One of the problems I've encountered here is that windows don't let much light through:

The room is rather dark for several reasons.  One, the walls are 1m thick, so the sides occlude a good part of the sky outside.  Two, there's no radiosity: the light doesn't bounce around the room like it usually does.  I was planning to add a radiosity pass after I've implemented point light sources, and it should be interesting to see how much of a difference this makes.

Even the church with its big windows looks gloomy... (The trees right in front of all the windows don't help either...)

If the radiosity solution doesn't work, I guess people will have to build huge sun-facing windows and place plenty of lanterns. :P

(Minecraft doesn't have this problem; the light is always spread out across 15 blocks in a diamond pattern.)

The next step would be to convert the light map back to the Minecraft files. It would look odd once MC's own light model takes over again, but perhaps it'd be a cool addition for the static parts of a multiplayer map.

Also, check out this blog entry. Very interesting techniques! I've been toying with the idea of using geometry shaders to build the meshes, and this is an excellent demonstration of how to do that.

Monday, December 13, 2010

Semi-transparency and shadows

I decided to add transparency to the shadow calculations anyway. It's not as slow as I had feared, and it's great for shading the bottom of lakes and oceans.  When casting a ray to test for occlusion, it now adds the opacity of all the intersecting blocks. Air has an opacity of zero, rocks and such have 1. Water is 0.2, leaves are 0.4. As soon as the sum of the opacity of the blocks along the ray reaches 1, the loop stops.  The sum of all the rays is then used to determine how much light the block receives.

For water, this results in a nice gradient:

And... *peers*... some sort of dark lines along the edges of the chunks. I'll have to look into that.

As for the trees, it does make the shadows a bit softer, but the difference isn't huge:

I've also managed to get rid of that z-ordering problem I had earlier. I no longer use my own custom scene node, I just generate plain SMesh'es. Less code, works better.  (So why use a custom scene node in the first place? Eh, I was following a tutorial. :P )

Another cool side-effect is that I can now make things emit light as well, simply by setting their opacity to a negative value:

Light sources will be done in a different way, this is just a little trick I thought of right now.

Sunday, December 12, 2010

First look at trees

Hacked in some trees to test with:

(Yeah yeah, trees are growing on the beach, I know.  This will be much better once biomes are implemented.)

The shadows look nice, but I wonder if it would look more natural with some sort of volume ray casting algorithm. This would not be hard to implement; instead of simply checking whether a ray intersects a block, you assign a transparency to every block type, and calculate how much light is left after it passes through every block that intersects the ray. But it would slow down the light map generation even more.

Also, I noticed something odd while looking around:

The far trees overlap the nearby tree. Hmm, I thought one of the reasons to use a 3-D engine is that you don't have to worry about z-ordering and all that low-level stuff?  I'm sure this is just me being a n00b with Irrlicht, and there's an easy and obvious fix for it...

Tuesday, December 7, 2010

Terrain generation and lighting

The terrain generator is quite simple at the moment. It starts with 2-D Perlin noise to generate a height map. In some places, it adds 3-D noise around the surface, up to a certain vertical distance above and below (right now set to 30 meters).  The amount of extra noise that gets mixed in tapers off at the edges. The result is a world with bits of rolling hills, and bits with weird rock formations. And the occasional floating boulder.

Now something didn't go quite right, the transition between the two terrain types isn't as smooth as I had hoped.  It's easy to fix, but on the other hand, now you get all these cool faults and fissures. Hmm, I think I'll keep this glitch, but make it rare.  Keeps the game world fun to explore.

The lighting is calculated per block. From the center of each block, a bunch of rays are shot out, to see if the sky is visible in that direction.  The total number of rays that don't intersect with another block determine how much light reaches that particular block.

For the ambient occlusion, the rays are distributed in a hemisphere, to model an overcast sky that evenly lights everything. But if you add a couple of extra rays, very close together like a narrow beam, one part of the sky starts acting like a very bright disc.  So in the same algorithm, we can get ambient light, sunlight, and soft shadows. Here's the Stanford dragon again, but this time you can see how his head casts a shadow on the neck:

Ergh, that would have looked much better without the distance fog...

The first results looked promising, but had a lot of artifacts in it. A block on a hillside that has three blocks around it (say, north, east, and down) would already be almost halfway occluded. This leads to a lot of dark spots in an otherwise sunny hillside. To prevent this, there's a blur filter applied to the light map as a second step.  (It doesn't yet work perfectly at the edges of a chunk, so you will still see some dark specks:)

You might be wondering if it doesn't take ages to calculate the lighting. The first version was indeed incredibly slow. But there are some ways to speed this up tremendously. For example, you only need to calculate the light voxels that are close to the surface of the terrain. Right now, with 56 sampling rays, it runs at about 300 chunks (=16x16x16) per second on my 3 Ghz AMD64, but there's still some really dumb code in there that should be optimized. Using the other 3 cores would be an easy speed boost, too.

The speed isn't much of an issue though. It has to be done only once. And the client also generates a preliminary light map using a much faster method. Only when the server is done number-crunching it replaces it with the awesomer version. Remember that new terrain is only generated as the player explores new areas. The new chunks are far away, so the switch is not very noticeable.

Next time, water and trees. Now that the world isn't 64 blocks high anymore, we can skip the little 6-block fruit trees and plant mighty oaks and sequoias, and more importantly, build a proper tree house. There's still a limit in the current code that prevents me from generating a Na'vi home tree, but I hope to have that part redesigned next week.