Oh my, what’s that I hear? Is that the sound of a New Blog Post?
No, of course not! Blog posts don’t make any noise at all (at least not from my end of things), but you probably aren’t mistaken if you think you’re currently looking at one. Yes, it has been five months since Part 9, and yes, I miss you too.
Part 10, hmm. Doesn’t that mean I should have something especially special in store to share? 10 is a multiple of 10, after all, the very basis of the decimal system. Also considering that I’ve built this post up for so long (5 months of suspense… it works like that, right?), I sure as anything better have made some interesting content to share with the Web of the World. Luckily for me, and for you if you are reading this voluntarily, I have expanded this project in as many degrees of freedom as I can comprehend since we last met.
Let us begin with the more minor improvements. I looked at my game (5 months ago) and thought, “Huh, what can I do to make this project more intuitive?” I know that words like “intuitive” and “streamlined” are pretentious words flung around by game developers which often indicate that some features have been cut from development, but this is not the case here. I’m instead adding features to make the gameplay experience a tad more bearable. Among these features include smoother movement, independent of frame-rate I might add; the ability to fly and easily create/destroy blocks while editing, nice touches for when one wishes to edit; and a form of auto-complete for the command console that is not infuriating in any way so far as I can tell. Through testing, I’ve tweaked these and added other small features here and there, which all contribute to generating a more unified experience overall. There are more of these to come, actually, but the more I make, the more I realize that I could be making more! And more! … And more! Yikes, there’s a lot to add to the project at this point and in so many different areas, but it’s smarter to remain focused on the important aspects of the project rather than to be distracted by the smaller ones.
Now, on to the good stuff. You’re probably wondering by now why I haven’t talked much about anything that you read about in this post’s title yet (save for the blocks, but those have been in it since the beginning. Have you been paying attention?). I can’t tell you exactly why I’ve been doing that, but I can tell you that it is time to move on. Okay, lighting! What kind of lighting? I’m glad you asked. The kind of lighting that I want is the kind of light that you get when you put some light-emitting source somewhere, and light emits from it. Sound good? I think so too. The real challenge, conceptually, is the idea of having light blocked by walls and other various sorts of objects when it tries to go through them. To accomplish this, in-game, we need a way for “light” to emanate from a source and to check (as it moves from one voxel to the next) if it’s going to collide with a solid block. To the best of my knowledge, this is considered to be, aptly named, “voxel lighting.” For testing purposes, let’s make the block of id = 2 our light source. For now the light does not travel outward; it should just light up the block where our light source is:
That’s a start! It shows us that, at the very least, something is happening, and the game still runs – two positive things. Now let’s have the light actually emit from the block. The way this works is that I have a function that takes in a block location, light intensity, and total light radius. It checks all 6 adjacent blocks to it’s source to see if they are solid or not. If the block it checks is not solid, it will call itself yet again, making this into what is known as a recursive function! This time, it uses the new block’s location and a new, less intense, intensity. This continues to happen until all of the paths that the light travels eventually dwindle down to an intensity of less-than-or-equal-to 0, making them imperceptible to the human eye (or any sort of eye, for that matter). This light information is then used by the rendering procedure to color each face of each block to its respective brightness. Let’s see how it looks now:
Neat! Not only does this work with static lights like these blocks, but it also works with dynamic ones, like if you shoot a focused beam of energy down a dark hallway to light it up as you find your way to your bedroom for the night.
Now, here’s where things get messy. See that frame-rate at the bottom-right of the screen? That’s 62, and I’m not limiting it to that. That’s my computer trying to run all of this as fast as it can, and though 62 frames-per-second is what many games shoot for (as most monitors I imagine run at 60 Hz), this will soon become unacceptable as more features are added. As the fps drops, the game appears more “jumpy” or “jittery,” and I’d prefer that I have more wiggle room with the frame-rate than this. That’s when we turn to what is known as optimization. Optimization is what happens when you find the places in your program that are eating up the most processing power, and you make them eat up as little power as possible. Usually, with games, the most time is spent rendering all of the pretty graphics. This is where I focus my attention. With some additions such as discerning between static and dynamic lights, re-calculating dynamic lights only when necessary, not rendering unseen cube faces, and saving the world mesh in memory, we get a significantly higher frame-rate and end up with something like this:
Now that we have all of our ducks in a row, it’s time to go back to rounding out the gameplay experience a little. As I stare at the lighting for a while, I become increasingly distracted by the blockiness of it all. I mean, yeah, the world is entirely composed of blocks, but it’s still too… blocky. It would be nice to have a way to smooth it out – to make it more appealing to look at. Actually, the picture you see above sparked an idea in my mind. Do you see how the black smoothly grades into the purple? It reminded me that my graphics environment (OpenGL, remember?) will automatically interpolate the colors between the points on every polygon, allowing me to have smooth transitions between colors on each face of each cube. Smooth lighting? I think affirmative.
Here’s a highly scientific diagram that I drew to help illustrate how smooth lighting works:
The black square outline in the center represents the face of the cube being tested for lighting, and its four corners are numbered. In order to accomplish smooth lighting on this cube’s face, each corner needs to take the average light from each adjacent block. For example, corner 3 will hold the value of the average light levels of cubes 8, 5, 6, and 7. Once each of the four corners has the correct light value set, the program moves on to the next cube face and then the next, until it is finished. When rendering time finally comes around, OpenGL interpolates the color values of each of the corners and makes a nice-looking cube face. Here is a scene with the usual lighting in place:
This project is certainly shaping up to look like something! Having lighting and optimization relatively in place at the moment really takes a huge burden off of my chest. I am excited to get to the juicy new stuff, but unfortunately I don’t have any of it to show as of right now. It will come soon enough, though! Be prepared, next time we’ll take a look at the effects of Reggae on the human psyche.