Pages

Friday, November 6, 2015

For about a month now, I've been working on a new game project, in my fairly limited free time, and
I just hit the minimum playable milestone. So it's time to post it up here!

I'd absolutely love to hear feedback, but keep in mind that this is the earliest of alphas. I've put just enough polish on this to not be completely embarrassing, but it's still very simple and feature poor, it doesn't have any sound yet, and most of the artwork is not final.

Game Summary

In Vector Control, you're fighting a pandemic, spreading across the globe threatening to destroy humanity. As an agent of the CDC, you move frantically from city to city chasing the plague, trying to get ahead of it's deadly multiplication.

The controls are entirely mouse driven. Click on a city to move there, and click on the city you're on to eliminate one unit of disease. Diseases will occasionally reproduce, generating another unit of disease on the city they're on, and they'll occasionally move to neighboring cities. However, the last unit of disease will never leave a city, so once infected, a city will never be cured without your intervention.

You win the game by eliminating all the units of disease on the map. You lose by the disease getting sufficiently out of control that there are 200 units on the map. You can look to the syringe on the bottom of the screen to see how close you are to victory or defeat.

Downloads

Note: The Mac version is not yet tested. I'm trusting that Unity built it correctly. If you try it out, please let me know whether it worked!

What's Next

The game as it stands is rather shallow, but I think it could be the seed of something great. So here are the things I hope to add to it, over time, to deepen it into a really compelling experience.

Special Abilities

I haven't yet decided how these will be made available or limited, but I have a few ideas. I'll likely experiment with a bunch of options before settling on one. Some specific abilities I have in mind are:

Quarantine - Drop in the current city. For some period of time, no diseases can enter or exit this city. Good for isolating infected parts of the map from clean parts.

Clinic - Drop in the current city. Lasts much longer than a quarantine, but still eventually runs out. Will periodically cure one unit of disease in the city. Useful to place on the clean end of very long connections, so you don't have to worry as much about diseases occasionally spreading along those lines.

Chartered Jet - Move very quickly directly to any city on the map. The value of this should be obvious.

I'm sure I'll think of a few more, given time, but these are a good starting point.

More Varied Maps

Variants of the world map with more or fewer cities, to provide faster or slower games.

Regional maps, which zoom in on, say, North America, Europe, China or Africa, and include a lot more of the local cities in those regions.

Maybe even some fictional maps!

Alternate maps should be easy to make, so I expect to eventually have a wide selection.

More Disease Types

I'd like to have diseases that have different attributes, to change up the game a bit. Possibly even include multiple different disease types in the same game at once.

Audio

The game will need sound effects and music. I'll likely need to get some help on this, as audio is really not my area.

Better Artwork

All of the art in the game right now I made myself. I'm not completely incapable in this area (unlike audio), but it's still not my area of expertise. I'd like to get a professional artist on this eventually.

Gamepad Support

I want to have solid support for both traditional gamepads and the Steam controller. This should be a good couch game.

If the game is sufficiently successful, I might even investigate console versions. But I have no idea what would be involved in getting it on PS4, XBox One or Wii U.

Mobile Support

This game should work very well on phones and tablets, so Android and iOS versions will hopefully come eventually.

Local Co-op

It would be great to be able to team up with your friends to fight more challenging scenarios together. This would build on the gamepad support.

Online Co-op

This is a harder thing, but if there's sufficient demand I'd give it a try.

Alternate Loss Conditions

The current loss conditions were just the simplest, most obvious option. I'm also considering the possibility of giving each city a population, which is slowly eaten away by the diseases in the city. You'd lose when the world population dropped too low to sustain itself. This is still an idea I'd have to experiment with. I'm not convinced it would actually be more fun than the current system.

Wednesday, January 14, 2015

So over the past two weeks, since I first published the article on simplectic noise, a number of people have ported it to other languages, and I've continued to work on and tweak it. Many people have pointed out flaws in the algorithm, and I've discovered a number of my own. All together, I'm left to conclude that simplectic noise doesn't actually have much value.

Surflet Size

The major problem is the surflet size. A surflet is a single gradient (hyper)sphere in a gradient noise

2D simplectic noise, without the corrected surflet
size, zoomed in with increased contrast to
reveal the subtle discontinuities

function. When implementing simplectic noise, I made a mistake with the gradient equation that determines the falloff of the surflet. I made it:

(sqrt(0.5) - range^2)^4

When it should have been:

(0.5 - range^2)^4

Basically, I failed to account for the fact that the range was squared, and thus the value it's being subtracted from also needed to be squared. The surflets in 2D needed to have a radius of sqrt(0.5), so I need to subtract the range^2 from sqrt(0.5)^2, or 0.5. This resulting in the surflets having a radius of sqrt(sqrt(0.5)), or ~0.84089641525, which is larger than a simplex.

4D simplectic noise with corrected surflet sizes

This created discontinuities at the borders of the 2D simplexes. The gradient falloff function happens to go near zero at about 60% of the radius of the surflet, so these discontinuities were very subtle. In most cases, they were impossible to see with the naked eye, which is why I didn't notice them. However, at very low frequencies, or when used as a heightmap, the discontinuities can become very apparent.

So, easy enough to fix, right? Well, first of all, when you fix the math the surflets become just a little bit too small. It becomes easy to visually pick out individual surflets. Now, it turns out this problem exists in simplex noise as well, so it can't be that bad, right? Well, in simplectic noise, the problem is exacerbated in higher dimensions. It's not that bad in 2D, but it's horrible in 4D.

I've also gained a deeper understanding of OpenSimplex noise. The reason it's so complicated is because it's solving this exact problem. It samples a few extra surflets, from outside it's immediate shape, in order to allow the surflets to have a larger size. While this adds a lot to code complexity, and significantly hampers performance, it makes the quality of the noise actually higher than simplex, and much higher than simplectic.

Performance

Travis Watkins has continued to do amazing work optimizing the noise-rs library, in which I implemented simplectic noise. With every pass he's made, Perlin noise has benefited more than anything else. At this point, our Perlin implementation is incredibly fast. OpenSimplex has fallen behind, and simplectic even more so. Simplectic noise has also resisted all our attempts to significantly optimize it. Further, we've improved the quality of our benchmarks, and the more accurate results have not been kind to simplectic noise. Thus, I can no longer make any significant performance claims. The current numbers on my machine look like this:

Perlin Simplectic

2D 21 ns 36 ns

3D 48 ns 100 ns

4D 76 ns 211 ns

So in all dimensions, it's quite a bit slower than Perlin noise. And in 3D and 4D, it's also much lower quality. So there's really not much left to recommend it.

I'm sorry for publishing my results to this blog prematurely. Perhaps the concepts behind simplectic noise will inspire some other innovations, but for now I hope people haven't wasted too much time on it.

Reference Implementation

Since I no longer believe simplectic noise to be valuable, I'm pulling it out of the noise-rs library I'd originally written it in. To save the reference implementation for posterity, I've created a new github repo for it here.

Update:
One of the optimizations that Travis had made to Perlin noise had to be rolled back, which caused the 2D Perlin to get a bit slower, but otherwise had a minimal effect. I've updated the benchmarks above.

Friday, January 2, 2015

Notice: Since posting this, further work showed that the algorithm described here is not actually particularly useful. See the follow-up blog post for details. This post is preserved, with only slight modification, for posterity.

This post is about a new gradient noise function I've invented which should help to fill a hole that's existed in this space for some time now. I call it simplectic noise. But first, to put this in context, some history.

Perlin Noise

2D Perlin noise

Back in 1983, Ken Perlin published an algorithm known as Perlin noise. It was for creating smooth, continuous, random values over space, which has numerous applications in 3D graphics and elsewhere. Perlin noise, and it's variants, have become one of the primary tools for creating computer-generated random content (also called procedural content), particularly for natural terrain or textures.

Perlin noise works by dividing space up into squares, cubes, tesseracts, or whatever kind of hypercube is appropriate for the number of dimensions you're using. It figures out which cell you're in, and assigns a random gradient to each corner of that cell, then blends those gradients together. Because the given corner's gradient is chosen solely based on the coordinates of the corner, a neighboring cell sharing the same corner will choose the same gradient for it. This means that neighboring cells blend smoothly with each other.

Because of the way it works, Perlin noise and it's variants are collectively known as gradient noise.

Simplex Noise

2D simplex noise

Perlin noise is pretty useful, but it has some flaws. As you can see in the image above, it tends to have subtle directional artifacts. Because of this Ken Perlin in 2001 published an improved version of the algorithm, called simplex noise. The principle is similar, except instead of dividing space into hypercubes, it divides them into simplexes. A simplex is the simplest possible space-filling shape in a given dimension. This is a triangle in 2D, a tetrahedron in 3D, and increasingly hard-to-visualize shapes in higher dimensions. There are two key advantages to simplex noise over Perlin noise:

The simplexes aren't axis-aligned, so there aren't any obvious directional artifacts.

The number of points that must be considered only increases by 1 for each additional dimension, whereas Perlin noise doubles the number of points with each dimension, so simplex noise is much faster to calculate at higher dimensions.

This is an excellent noise function. By most measures, simplex noise is still the best gradient noise function available.

But there's a problem. Soon after Perlin published simplex noise, he also secured a patent for it. This makes it a non-starter for a lot of smaller, independent developers, who don't want to deal with the potential legal issues of using a patented algorithm.

OpenSimplex Noise

2D OpenSimplex noise

In September of 2014, only a little over 3 months before this blog post, a post was made on Tumblr describing an algorithm they called OpenSimplex noise. The primary intent is to achieve the advantages of simplex noise without hitting the patent restrictions.

This uses yet another kind of spatial tesselation, known as a simplectic honeycomb. In 2D, a simplectic honeycomb is identical to a the triangular tesselation used by 2D simplex noise, but that's okay because the simplex noise patent only covers 3 or more dimensions. In 3D, a simplectic honeycomb is actually a combination of tetrahedrons and octohedrons. This makes the process of figuring out which cell a given point is in actually very complicated, and in 4D it's a great deal more so. In addition, OpenSimplex noise expands the range of the gradients a bit, so they can extend a little bit into neighboring cells. This theoretically makes the noise a little bit smoother, but it also means that extra cells need to be checked.

Despite this, the blog post claims that OpenSimplex noise achieves better performance than Perlin noise, even if it's not as good as simplex noise. My own experiments disagreed, but this is possibly due to not sufficiently optimizing the OpenSimplex port I was working with. This optimization is a difficult prospect, given that the complexity of the algorithm.

Simplectic Noise

2D simplectic noise

And this leads us to my own noise function. I'm working in Rust, which is an experimental new language I've grown very fond of. The OpenSimplex reference implementation is in Java. So I needed to port it over. The 2D OpenSimplex function wasn't so bad. The 3D one, however, was 558 lines of complicated nested if statements. It was nigh impossible to really understand it, so I just did a raw port, pasting it into Rust and chasing down compile errors until it worked. And surprisingly, it did work. Then I looked at the 4D noise function, which is 1311 lines of even deeper and more complicated nested ifs. My resolve broke.

So I started exploring 3D tesselations myself, figuring I had to be able to find something better. And after digging around some, and playing with lattices of toothpicks and play-doh to better visualize what I was doing, I stumbled on the simplectic honeycomb again... Only I was looking at it a bit differently now. My key realization was this:

2D slice of 3D simplectic noise

An N-dimension simplectic honeycomb is composed of layers of (N-1)-dimensional simplectic honeycombs.

This means that in order to get the value for a given point in N-dimension space, I just need to figure out which two layers it's between, and call the (N-1)-dimensional simplectic noise function twice, once for the layer above this point, and once for the layer below it.

This makes the code incredibly clean and easy to understand. While I've only extended this up to 4D, I could very easily write 5D, 6D or 7D versions. Unlike simplex or OpenSimplex noise, the code size increases linearly with the number of dimensions, and I'd argue that the code complexity increases is something like log(n), because the higher-dimensional versions are just simple functions that call into the lower-dimensional versions.

2D slice of 4D simplectic noise

I initially assumed this would be slower than OpenSimplex noise, but once I benchmarked it, this turned out to not be the case. It's actually faster! My benchmark results are as follows:

Note: The benchmarks originally posted were very different, and inaccurate. This has been corrected, but the blog post from this point down has been heavily edited to reflect that change.

Perlin 2D ....... 30 ns

Perlin 3D ....... 98 ns

Perlin 4D ....... 212 ns

Simplectic 2D ... 39 ns

Simplectic 3D ... 94 ns

Simplectic 4D ... 207 ns

OpenSimplex 2D .. 48 ns

OpenSimplex 3D .. 122 ns

There is no OpenSimplex 4D in this benchmark, because porting it to Rust was a chore I haven't had the will to undertake. There is no simplex noise in this table because it's patent encumbered, so if I implemented it, it would be just for the sake of this benchmark. I'm not going to use it otherwise. That said, 2D simplectic noise is identical to 2D simplex noise, which again, is acceptable because the simplex patent only applies to 3D and above. (The really observant among you may have noticed that my 2D simplex and 2D simplectic images are actually the same image.)

As you can see, in 2D Perlin is the fastest, but in 3D and 4D Perlin and simplectic are almost the same performance. OpenSimplex is the slowest in both 2D and 3D, though again this may be at least partially due to my inability to effectively optimize the 3D OpenSimplex noise, since it's so difficult to understand.

So we can see that simplectic noise provides higher quality output than Perlin noise, with about the same performance characteristics. OpenSimplex noise doesn't SEEM to live up to it's performance claims, but I'm not willing to make that claim too strongly based on the implementation I have.

Anyway, if you're anything like me, you're probably itching to see the implementation. You can find it on GitHub here.

The library is under the Apache license, but unlike Ken Perlin, I'm not going to take any steps to protect the algorithm itself. Port it to whatever languages you want. Innovate and hack on it. Make it better, if you can! All I ask is to let me know if you do anything really cool with it.

Update:
Some new developments are throwing my performance numbers into doubt. The LLVM optimizer may have been aggressively const folding my code. I'll post more here when I have better info.

Update 2:Travis "Amaranth" Watkins has done some work on fixing up my benchmarks, so with his help I've got better numbers now. I've updated the post above to reflect his information. It's not nearly as flattering to simplectic noise, which has now been shown to have generally comparable performance to Perlin. This is still valuable, however.

Update 3:
Discovered a tragic flaw in the algorithm, and further work showed the performance to be significantly worse than what's reflected in this post, even after the last update. I can no longer recommend anybody use simplectic noise. I've added the notice at the top of this post to that effect. See the follow-up blog post for details.

Sunday, May 20, 2012

I've spent a while shuffling languages and ideas, trying to zero in on the best way to build an engine that can support the game I want to make.

I was originally working in Unity, but eventually decided that while Unity is a very powerful engine for rapid game development, its real strength lies in its 3D editor, which I wasn't using. My game is based almost entirely on procedural generation, well outside of what Unity is designed to handle. So I spent far more time than I liked with trying to figure out how to work around limitations in the engine, and in some places simply had to compromise on what I could do. I eventually decided this wasn't acceptable.

While working with Unity, though, I had fallen in love with C#. So I determined I'd make the engine in C#, but targeted at Mono, with the intent of keeping it multi-platform. Toward that end, I grabbed a library called OpenTK, which provides bindings for OpenGL, OpenAL and OpenCL, a set of multi-platform open standards for graphics, audio and general-purpose GPU processing, respectively. I made some pretty substantial progress using those, but when trying to utilize the more advanced graphics functionality I encountered some flaws with the bindings that I couldn't work around.

And thus I've arrived back at my old standby: C++, with SDL and OpenGL.

Unfortunately, all of this environment switching has a high cost. I'm not quite starting from scratch each time, but it almost feels like I am. It's a major setback, and a huge amount of work has to be redone. However, I'm confident I won't be switching again. C++ is a bit more complicated to work in, but it applies no artificial limits. Anything I want to do, and can't, will be because the hardware can't handle it, not because the environment is constrained.

Now this isn't the only reshuffling I've done. The first part of the game I'm attempting to implement is the terrain engine. The reason for this is that it's the riskiest component, and it's very central, in that how I implement it will heavily influence how I implement everything else, and even influence a lot of details of the game design. I've been unsure exactly how best to go about it, or whether I could even find a way to meet all of my fairly steep requirements. Those requirements are:

It needs to be fully dynamic, so it can be reshaped on the fly.

It needs to support overhangs and caves (a simple heightmap won't do).

It needs a viewing distance of at least a few km, and a geometry resolution of no larger than a meter, on commonly available PC hardware.

Originally, I was trying to do this with voxel cube terrain, because that definitely fulfills the first two requirements, and the third is just optimization. Unfortunately, I couldn't find a way to optimize it sufficiently. It's easy to blame the slowness and low viewing distance (144m) of Minecraft on the fact that it's in Java, or accuse Notch of being a bad programmer, but the reality is that drawing that many cubes is really hard on the GPU. Also, given that there is no clean way to apply level of detail, and your view gets wider as the viewing distance increases, when you double the viewing distance, you roughly quadruple the amount of geometry you have to draw.

So in order to achieve point 3, I went back to the drawing board. I spent a couple weeks brainstorming, and reading. I came across this amazing blog called ProcWorld, by a very talented programmer, Miguel Cepero, who is working on his own procedural world generator. Many of his techniques aren't useful to me, because his worlds are pre-generated over hundreds of hours on a render farm, whereas mine need to be made on the fly. However, it exposed me to an algorithm by the name of marching cubes, which I'm honestly a bit embarrassed I hadn't heard of before.

Marching cubes is a very fast, clean and elegant way of taking a mathematically defined surface and turning it into a set of polygons, at an arbitrary resolution. This surface has to be described in terms of a 3D density map, and a threshold value, such that points that are denser than the threshold are inside the surface, and points less dense than the threshold are outside the surface.

The first chapter of GPU Gems 3 describes not just how to implement marching cubes, but how to implement it very efficiently in the GPU, using a multi-pass shader. I realized it would be relatively easy to extend their implementation to allow for smooth, seamless level of detail with geomorphing.

Unfortunately, this is where OpenTK failed me, and I realized I needed to switch away from C#. I got the basic, naive GPU implemention of marching cubes working, but of course it was way too slow. So I began work on the multi-pass version described in GPU Gems 3, and discovered that OpenTK had a bug I couldn't work around (or figured out how to fix) with its support for transform feedback, the OpenGL feature that allows for the multi-pass technique.

So now I'm back in C++, building up all the basic infrastructure of a new engine. I decided that, since I'm starting a new engine anyway, I'd build it with a component-based architecture. Which has slowed me down a little, because it's unfamiliar territory, but I believe it will pay dividends down the road. Anyway, I now have that mostly in place, and fairly soon I should be able to dive back into the terrain engine itself.

No promises on when I'll have visible results to show. I still have a day job, after all.

Friday, April 27, 2012

It turns out I was able to extract my old posts from my browser cache, so I've restored them below, with the appropriate dates attached and everything. I even updated the download links to point at their new Google Drive locations.

Thursday, April 26, 2012

So spiritofiron.com was previously hosted on Linode. It wasn't terribly difficult to setup. I got WordPress working on it, and it chugged along just fine for about six months.

Then two days ago, I got a series of six messages from their customer support, as follows:

They were going to reboot my server because they had detected some issues with it.

The machine was experiencing a hardware failure, and they were going to transfer the drives to a known good machine.

This was taking longer than expected, and they had no ETA.

Their attempt to recover my data was unsuccessful, but they were going to setup a new, equivalent Linode on a new machine for me, and give me a three month credit on my account, as way of apology.

Instructions for how to restore my data if I had paid for the extra backup service (which I hadn't even realized existed as a separate, special service).

The new Linode was setup.

The entire thread happened over the course of a few hours early Wednesday morning. By the time I got up, it had already played out, and I read over the entire thread multiple times, in shocked disbelief.

Needless to say, I decided I was done doing business with Linode. They're friendly and helpful, their rates aren't unreasonable, and there's a lot of good things I can say about them. However, their competitors can offer me real assurances, which I have reason to trust, that this sort of event will never occur, short of an apocalyptic disaster.

I briefly considered AWS, as they seem to be the most reliable hosting service available. However, I realized that what I was hosting was essentially just a blog, with an attached gallery and a few downloads. Blogger can give me all that, and it's a hell of a lot less effort. That means I can spend more time working on the project that this site is meant to showcase, and less on building and maintaining the site itself.

Tuesday, August 23, 2011

I’ve just finished transferring this site to a new host, owned and operated solely by me (and Linode, that I’m renting the server from). Previously, I was using space generously donated by Ashelia over at hellmode.com, but unfortunately I couldn’t configure things to work quite how I wanted without sabotaging the other sites I was sharing the space with. So, time to strike out on my own!

In the process, I also put in the time to improve the appearance of the site, including actually putting a logo at the top. I’m quite fond of the little iron spirit. I think he needs a name, though I haven’t decided on one yet. Suggestions are welcome.

I also finally have e-mail attached to this domain. So I can be contacted at contact, admin, webmaster, mike, michael and mpowell, @spiritofiron.com, @spiritofiron.org and @spiritofiron.net. I like to be thorough. But for now, I’m mostly advertising mpowell@spiritofiron.com.