My contract at OpenAI is up and it was a great experience - I highly recommend working for them if you get the chance.
I have decided to take a short break and get the source out for VQ. As usual I apologize for it having been quiet, but I had been fairly busy at work.
I may have discussed it elsewhere, but I am aiming on getting a few different releases out for VQ. There were roughly 5 major iterations of the engine (2 of the ray-casting ones were fairly similar). Each release will probably occur separately. The first release I am targeting actually an older version of the engine, as seen below, just because I think it is interesting content-wise (each new iteration often lost some of the content in favor of new incompatible methods for the tech).
Hi all - bet you are wondering what is going on at this point. The short answer is I did not know exactly how to proceed until now, finally having an idea of my employment situation.
I have talked to some people (in the forums, email, and elsewhere) about the current status but otherwise apologize for the silence. Basically for the past month I have been busy hunting for jobs and doing associated prep work or tasks. I also tried to get VQ funded via other means (licensing) but that did not work out. I am moving on to a fulltime job, so I am deciding to cancel the Patreon (and I will refund everyone's money here and elsewhere, of course). I don't feel right taking money when I probably won't have much time or energy to devote to the project.
The good news is that I am making all of Voxel Quest fully open source (MIT or similar, every iteration of the engine will be available), and I am doing a little bit of work to make things ready for this (I am even attempting to buy rights to the sprite sheet that Voxel Quest uses, which is a 3rd party asset). Stay tuned for more on this. I can't express how thankful I am that you chose to support me, and I hope that the years of work I put into VQ and all its source will be a small token of my gratitude. I will probably cancel my Patreon before the next pledge cycle so you don't need to necessarily manage your pledge, it should be automatically canceled soon. The Patreon peaked at $450/month, which I am really surprised at, although I expressed concern with starting it in the first place and turns out those concerns were valid. I will also soon organize a method for refunds (both for Patreon and everything else). Any questions or concerns, as usual, do not hesitate to contact me. Again, my goal is to keep everyone happy so let me know what I can do in that respect.
I am not fully abandoning VQ - I will attempt to provide as much support as I reasonably can to help people understand the source, and I will also probably chip in on the github repository every so often. But overall I will not be making great strides in the short term. I am hoping that someone can eventually make a commercial product with it, and that might kick up interest a bit.
For those curious, I am soon going to start contracting at OpenAI, with potential to move full time.
*Again, I stress that refunds are not required, but if anyone wants one I am offering without judgement.
Additionally, I have read through all of the comments below and I continue to be blown away by people's enthusiasm. I am not replying individually because my website's comment handler is a pain to use for many entries.
First, I just wanted to say "thanks" to everyone for the outpouring of enthusiasm and support. I tried to reply to many people but apologies if I missed you - there were literally hundreds of messages across Twitter, Facebook, email, Kickstarter, this website, etc. I was legitimately, deeply moved by many of the replies.
By popular request, I am not shoving your money back in your hands, it will be optional. I will put together a survey for this (likely when I get a means to start paying people back, be that employment or whatever). I also still need to figure out the logistics of what order people should be paid back in.
Also by popular request, I am putting up a Patreon page, which is located here. I put very little effort into it, but as noted I think my effort is better spent actually working on the game. Previously I did have a patronage page on this website, and even though I never once advertised or mentioned it, people found it on their own and I had over $100/month coming in from this, which really helped. It is also accessible via the "Patrons" link at the top of this site. Old patrons (who donated via Paypal) - please cancel your patronage (or I will do it for you when I get the chance). If you still wish to be a patron (and I really won't judge you either way), you can use the Patreon page. The old list of patrons will remain in place, for the historical record. :)
If you are not already aware of the state of things, I may have to get a job soon so I am hesitant to recommend providing further funds as my pace will be slow. If I get enough money (I mean, via whatever job I take), I might hire someone who costs less than I do to work on it (or at least to help me with certain tasks) - I am also supposed to have someone interning for me this summer but I don't know what that will yield (they volunteered to though). Anyhow, I am still evaluating all options at this point and trying to figure out the best plan of attack. There have been some good suggestions so far, feel free to add any more.
No point in dancing around the issue, I am out of money and trying to figure out what the next step is. Whatever happens, I am continuing to work on VQ, as that does not cost me anything but time. But I might need to allocate much of that time to a paying job, unless I can figure something else out.
The one thing that I have decided, for certain, is that I am returning everyone's money: Kickstarter backers, investors, patrons, and preorders (and anything else). Everyone will keep their rewards regardless. I made this decision before I even launched my Kickstarter campaign, although I've only talked about it with a few people. I am taking this step regardless of success or failure.
If I have to work another job, I will probably begin this return process as soon as possible (and it will take time to accumulate everything). Otherwise I will begin returning money when profitable. I am still planning to release something in the short term, as well as the source code.
I can understand if anybody is disappointed or angry, but I assure you no one has a heavier heart than I do. I invested over $100k of my own money, debt, and equity into this, in addition to about $500k of work (accounting for overtime and opportunity cost). I spent about a decade working in this area without any sort of return (other than it being "fun"), and over the past 3 years I put in well over 10,000 hours. I dragged my family through all of this as well. Nonetheless, if there is anything more I can do, please let me know.
This is not the end, but it is still a depressing position to be in. Still, I can't help but be grateful to have been given an opportunity to work full time on something like this.
As I have mentioned in the past, writing a game engine is a feasible (if unwise) thing to do, if you pick your battles carefully. I simply had too many battles, and too many directions I tried to tackle at once. My todo list never once shrank faster than it grew. Only at the tail end of this did I decide (for better or worse) to burn my entire set of goals and focus on one "simple" goal (although I must stress that nothing is simple).
If I sound like I have a bleak outlook, I don't. I still believe in this and I have something that I am proud of. This is the first time in my life I have worked on something of this magnitude, spanning 50k to 100k lines of code, and not wanted to scrap the entire thing. I've tackled a lot of new ground so I suppose the road is destined to be bumpy, but I continue to learn from my mistakes.
If anyone has any ideas of what I should do next, I am all ears. At the very least, I feel obligated to be transparent about my current situation. There is no outcome that I fear, I mostly just want to do what is best for everyone, even if that simply means getting a job and paying everyone back. I will probably investigate past offers I have gotten for funding, but I don't know if that will necessarily bear fruit.
One last thing I would like to make clear is that nobody owes me sympathy, and I am not even asking for it. My situation is a result of my own choices, and the only thing that I can make better is the future. All things considered, I think this journey has been great so far, and I am curious to see what the future holds.
On a happier note, let's discuss what I've been working on recently.
I've talked about (on Twitch) why I ended up doing yet another technical revision. If it seems crazy that I am doing another revision on the rendering, it might be, but also consider that teams with more members, more experience, and more money are going through the same thing that I am (see this video, if you have not already).
My former method, with the ray marching, was becoming a dead end (on my GPU, a GTX 980, it was dipping to 30 FPS in some cases, at 480x270 no less). It was not scaling, and it was getting to be too large (I had to break it down into two shaders because I exceeded the instruction limit). I had the choice to kill it or try and resurrect its performance somehow. The first thing I tried (and I tried many things) was to get performance acceptable. I couldn't (at least not without creating other serious issues), so I started a new method, which is pretty close to the second iteration of the engine (when it first transitioned from isometric to perspective mode). However, I still use the SDF / ray marching stuff where it makes sense, with animated things and quickly previewing objects, neither of which has a huge performance impact on its own. Overall not a lot of ground lost, especially since I am still using a good deal of code from the SDF stuff (not to mention that the rendering is a relatively small aspect of the code base).
I have transitioned voxel generation to the CPU, using a flood-fill approach that ignores nonvisible voxels and "air" voxels. It is much more flexible and less restrictive than using the GPU for generation. There is no instruction limit, there is not as big a penalty to branching, and there are many more sparse operations that can be done (for one, flood-filling is a sparse algorithm much better suited to the CPU). I also moved to a unified object and destruction model, where all objects and modifications are handled by one algorithm and modifications are voxel-independent, making it easy to do an "infinite" fast undo/redo queue. There is also caching of generated objects, so you only need to generate things once and they get dumped to a scratch disk (ideally these results will be compressed in the future).
I have a new rendering trick that I worked on independently, but I also heard that the team at Media Molecule is doing something similar on Dreams (actually it is pretty funny because our problems and solutions have crossed paths many times, even though we are both working more or less "in the dark"). For lack of a better term, I call it "deferred rasterization."
Polygons are fast, but they consume a lot of vertex memory (one cubic voxel might consume up to 8 vertices and 36 indices depending on the layout). That is up to 272 bytes of data for a single voxel. The naive alternative is to use points (like GL_POINTS) - a single point for a voxel would only consume 16 bytes if a 4-component vector were used, or 32 bytes in my case (two 4-component vectors).
But the problem with naive points is that they have massive amounts of overdraw (assuming they are large enough to fill gaps between them). Especially when looking at objects whose sides run parallel to the view vector. Also, naive points do not look very good - they are essentially tiny billboards.
My (reinvented) solution is to draw points as small as possible (1 pixel large), and grow them in screenspace, using ray casting to determine the cubic boundaries of each point. This is done in a horizontal pass and a vertical pass, as you would do with most kernel type shaders like a blur shader, to reduce the number of texture lookups. In my tests, it is approximately as fast as polygon rasterization of voxels (or faster), but uses a fraction of the memory.
The results speak for themselves. I can now run at 1080p at 120 FPS or greater (again, note that my last method only performed "ok" at 1/4 1080p, 470x270 - at 1080p it dropped below 20 FPS). And I still have plenty of room left for optimization. It also looks cleaner, visually (the SDF stuff often looked too noisy and had many artifacts, not to mention that jutting edges (such as grass) were too costly). I should be able to do VR if I can ever spare the time to implement it. Running at low resolution, I can get 600 to 1000 FPS, so this should scale decently (I know milliseconds are more meaningful, but more people understand FPS as a number).
There are many additional tasks and a lot of cleanup that I am doing as I prepare for the first release. I have had to rework a lot of the physics code because it was simply too unpredictable and not really well suited for voxel collision (Bullet is a great library, but be very careful what you choose to use it for). I couldn't bring myself to write a custom voxel collision handler for Bullet, so prior I was "faking it" and the voxels were not producing "real" collisions or contact pairs - they were simply causing objects to hover above them as if they were exerting a magnetic force. This led to many issues and collision and friction produced a user control feedback that felt wonky. There is something be said for simple, predictable, slightly unrealistic physics though.
I will put together a proper update video as well soon, but for now here are some shots and videos:
I'm going to use this space to reply to the comments, as (unfortunately) my web host is not that great and the comments section is rather annoying to type multiple entries in (and I would be typing the same response over and over). First of all, thank you all for the outpouring of compassion, I was not expecting a response like this. If you don't want your money back, I won't force it on you but the option remains there, and I won't hold it against you.
I don't have any firm plans for the future yet. A lot of people are asking me to put a Patreon up so they can give me more money. I'm kind of hesitant to do that, I feel a bit bad taking money while possibly having to spend much of my time working another job. I will think about it, but I don't have a yes or no reply yet.
Anyhow, just wanted to say thank you everyone, and I swear I'm not getting teary-eyed. ;)
Happy New Year! Ok, maybe a bit late for that, but I still want to do a review of the last year, what went right, what went wrong, and so forth. I will also try to preemptively address some of the common questions I get - please read the bottom of this post before you ask!
So, since the last update, there was so much stuff that I could not even cover it all in the video, and I won't cover most of it here. But here is a high-level list:
What Went Wrong:
What Went Right:
I don't have the greatest feedback mechanisms but it seems people are generally happy and impressed with my work so far. Somehow things are all coming together in the code, in ways I would not expect. I had never written a fluid simulator, yet it turned out well. I had never written a character animation system, but I was pleased with the results. I had never written a networking client or server (beyond trivial examples), yet somehow I did it (even though it is a simple TCP deterministic architecture, it was not trivial to write). I guess if nothing else, I have gained faith that I can tackle pretty much any task, given enough time.
Other than that, not really sure what to put here. :) There are plenty of ups and downs, but I remain confident in the outcome of the future.
The character animation system presented several new challenges. First of all, how do you efficiently ray cast with hundreds of dynamic limbs moving around on the screen? Well, limbs share a common trait: they are attached, and hence in the same proximity. So you only need to compute an intersection for one AABB (or sphere if you prefer) and that will let you know if you are anywhere near to hitting a limb in that body. Then you need not march into every limb of the body, but do an analytical prepass to determine roughly which shapes get hit, and collect some of them and march into those with a more complex algorithm. In this case, everything is just a line-line distance (one line being the view ray, the other line being the bone segment in question).
I had initial fears about passing in a bunch of bone data to the GPU, that the bottleneck would choke performance. But really its not so bad (really no different in practice than passing bone data to a vertex mesh), and you can animate a pretty large crowd - its not going to do Lord of the Rings scale battles, but that is ok (quality vs quantity).
Some of the biggest challenges came from trying to wrangle a physics system. There is a dichotomy between how the physics world wants to behave and how the user wants it to responsively react. This was compounded by trying to do complex animation systems with dynamically-oriented limbs over a custom collider for the voxel data of the terrain.
I'm going to add a few other technical notes soon as well, when I get a moment.
Been working on many things - increased view distance and improved rendering, much better AI / pathfinding, and more - I will post a "real" update on these things soon but for now here is a video and some images:
Since its inception, the rendering technology behind Voxel Quest has undergone 3 major revisions. The first was isometric chunk rendering (every voxel computed at a pixel scale), and you can find an explanation of how that worked here. The next revision added perspective, and meshed those voxels with a point cloud. The current and final revision only renders visible surface points, and it procedurally generates these in real time. Arguably the isometric rendering looked the best of all the methods, but I also spent over a year refining it. The current revision is only 3-4 months in so give it time to mature. :)
The fact that VQ has undergone three tech revisions over two years probably seems a bit ridiculous, and maybe it is. Something like this would normally kill a game. That said, the point here is not just to make a game (plenty of people are doing that already), but to make a unique engine, and that could not happen in a vacuum. All I know is that I am finally happy with where the engine is at in terms of performance and flexibility, and I couldn't have gotten here without knowing everything I've learned since the start.
So the most common question I get, of course, is how does this stuff work? It is actually simpler than it might seem.
At a high level it is based on ray tracing, specifically a method known as Signed Distance Field ray marching (SDF ray marching). But that is only part of what is going on.
Vanilla ray marching simply steps along a ray at fixed intervals until it hits something. This is costly with a small step size, and erroneous with a large step size. SDF ray marching computes the distance from the nearest objects, and moves along the ray no more than the smallest distance from any of these objects.
(Image credit: http://www.polygonpi.com/)
This is very fast when the ray actually hits the object, but considerably slower if the ray "just misses" the object as shown above. The interesting thing about SDF marching is that it works hand in hand with boolean geometry or solid modeling. Inigo Quilez has a great primer on distance functions and operations located here. You can also find many great live examples of SDF ray marching at shadertoy.com.
The problem with naive SDF is that every ray at every pixel is considering every object in the scene to be rendered. This becomes insanely expensive if you have many objects in the scene. For example, imagine I have 1920x1080 pixels to render, which amounts to about 2 million rays to cast. Each ray is calculating the distance from 1000 objects, 60 times per second. Lets imagine each ray travels 100 steps on average, and requires 100 instructions to make its calculations. You are looking at 1.2 quadrillion instructions per second, if I did my math right.
The trick I use is aggressively reducing the number of objects each ray considers, using very straightforward, naive cell hashing.
Here we have a ray (purple) intersecting 3 objects. The ray gradually marches through each cell, from position B3 to N13. If the cell is empty, it does nothing. If any objects are within the cell, it adds them to the list of objects which it should compute. It does not store duplicate object references. Using math, it determines exactly whether or not it hit a given object - but this is not a final result. If an object has a bumpy surface, or has holes in its surface, its possible a ray can hit the object but miss it when it renders the surface in detail. All of these objects are gathered up and then a traditional SDF pass is used to determine the details and exact hits. For some further clarity, cells can contain multiple references. Cell H8 contains references to both the green rectangle and red circle, but cell M11 only contains 1 reference (the orange pentagon). The way in which you implement the cell data is up to you, but I use OpenGL's samplerBuffer (which is like a texture but functions more like an array).
In order to step through cells efficiently, I recommend using a supercover line algorithm, such that each cell is only considered once as the ray travels through it (image credit: http://lifc.univ-fcomte.fr/home/~ededu/projects/bresenham/).
You will want to toy with cell sizes to hit the sweet spot: big enough so that one cell usually fits an entire object, but small enough such that there are not too many objects within a given cell.
Because VQ uses only one type of uber geometry (rounded boxes with super-ellipsoid-like corners), it can compute exact hits for every single type of primitive only using one set of instructions (from cones to cylinders to spheres to rounded boxes to you name it), eliminating the need for branching in that area. So it narrows it down to geometry that the ray hits exactly.
But it goes further than that. How many objects is a ray likely to miss? As it turns out, you can usually get away with only considering the first 4 to 8 objects and you will rarely get errors. In the illustration above I have just shown 3 objects, but imagine if that box is filled with hundreds of objects. Now, for each ray, I only need to consider the first couple of objects that get hit instead of hundreds of objects for every ray. As I have shown in the past, you can render millions of objects using this method and there is not really any slow down - it tends to run at a pretty constant speed. You can optionally improve performance further through use of the flyweight pattern or templates. If I am using an object that will be repeated many times (for example, a hallway) - I need only store the specifications for this object once, and then instance of it is just a reference with a bit of data like position and orientation.
Not every object is traditional geometry though. The terrain is composed of smooth chunks of earth, which is produced by sampling a volume texture with bilinear filtering. As it turns out, vanilla SDF won't work that great trying to compute distances based on textures. The trick I have found is to allow the SDF algorithm to overstep into a solid area, and then step backwards along the ray (correcting itself), back and forth a few times until it gets close to the surface. There are probably better ways of analytically computing distance from a bilinear filtered point, but this works for now.
This is not a full update, just a recent peek at some improvements and performance. In my next full update I will release a video. If you are curious to know more about the planned development timeline, see the bottom of this post.
A constant question I get is something along the lines of "what is performance like?" The truthful answer was - I did not know! It looked like it was getting at least 60 FPS. Accurately measuring FPS can be tricky in OpenGL without profiling software, and I have not yet gotten to the point where I am using that (I don't want to get sucked into tweaking the heck out of things to profile this early on). If you think it is as simple as doing a timer between frames, that is not true. See here for further details on why getting the FPS can be tricky.
In order to perform these frame tests, I did it the old fashioned way - render a large amount of frames without any sort of timer constraints, and measure the amount of time it takes (in this case, I am measuring how long it takes to render 10,000 frames).
Some VERY important notes :D
The new rendering methods now support any amount of overlapping geometry (previously, each piece of geometry was constrained to one instance within its own cell. There is still more work to do before I do a full writeup, as the process is still changing slightly and there are more steps to finish. I am also doing a huge amount of work just to manage dynamically placing objects in the scene, saving your creations, and a lot of stuff along those lines, which I will also talk about in the next update.
I'm not going to type a ton about the development status of the game - I've already done that in the forums if you are curious (check recent threads). But let me just provide a brief summary of recent issues:
Edit: I am soon going to a writeup on the rendering. I wrote this post in haste so have not gotten a chance to write much yet but in as few words as possible the new rendering is much faster because it only calculates visible surfaces instead of volumes. It is based on cell marching and distance field marching. More info to come soon!
Since the last update:
I have been fairly busy with "real life" issues, which seems to be the norm these days, but I have managed to get quite a bit done regardless.
Below is one of the earlier tests I put together - rendering terrain based on VQ's generated map.
Added in bilinear filtering to smooth out the water (sorry for all the twitter embeds, I am lazy):
And one more demo of the smooth water: