Stone Devlog: (Re)building Blocks

Been a while, let's chat. As may be evident by the lack of activity on the old blog, there hasn't been a whole lot of progress in the 8 months or so. But, as this August marks 2 years since I started this endeavor, I finally recollected myself and got cracking again.

I was originally going to go over all the trials and tribulations of the last several months, but I'd rather just skip over that. Check out what I've got right now:

Dual Contouring vertex placement methods. Left to right: blocky, average, QEF

This might look like more of the same from prior posts, but there are 2 important things to note here. First off, when we last left off, I was playing with other people's isosurface extraction implementations, hoping to find something that fixed the issues I had posted about previously. I never found it. Rather than continue to chase perfection, I went back to the basics. I started with the incomplete, blocky implementation of Dual Contouring featured in this post and focused on making it work for me.

To start things off, I refactored the original implementation to be driven by my own data structure representing the sampling function. Most of the implementations I tried out called a sample function in the class itself, requiring me to crudely tack my own functions in the code as I played around. I did unholy things in the Surface Nets code... The other problematic aspect of almost all the implementations I found was that they set up the sampling grid in increments of 1, meaning a higher resolution sampling was also a larger space. In a future post I'll go over the data structure for my sample spaces, but the short of it is, I can pass a sample space and resolution to the contouring algorithm and it takes care of the rest. This wasn't too challenging to integrate, but it helps that Dual Contouring is a comparatively simple algorithm and I was actually familiar with the code I was working with. It was one of the first algorithms I looked at, and my original blocky implementation was based on this tutorial, so I can even act like I wrote the code myself. The only reason I never finished it was because the part about Quadratic Error Functions stopped me dead in my tracks and sent me down a rabbit hole. 

That brings us to the other big win: I got the QEF vertex placement working. There's nothing to see on the spheres above, but it is evident on the center and right cubes. The center cube has those beveled edges I discussed in a previous post, whereas the QEF placement maintains the edge. Obviously, the blocky placement method produced sharp edges as well, but the cube is actually slightly larger than cube defined by the function, as vertices are simply plopped in the center of grid cells.

Blocky placement (left) vs QEF (right)

The extra effort to rein in the contouring algorithm was absolutely worth it, as it allowed me to start structuring how I will handle compound objects. Here's the same shapes with a sphere subtracted from them:

Left to right: blocky, average, QEF.

As seen on the right, the QEF is showing some signs of 'imperfection', where the edges bevel and produce the same sort of geometry as the averaging placement method. I'm well past the point of caring about absolute precision. What I have now is good enough to get a rough first pass on the knapping system, and that is far overdue. My next steps will be cleaning up the code and then starting a prototype.

That's about all for now. This post ended up being meatier than I anticipated, and that's without griping about 8 months of  wasted effort. I'm going to crawl back into my hole and figure out what code I still need to commit and what I need to jettison.

I'll leave you with a cool combination of the operations I implemented, making the shape hollow before subtracting the sphere:

Left to right: blocky, average, QEF