@drewdevault You're right, definitely don't do them one at a time.
That everything is cubes still makes life way easier. You generate a grid of vertices, then all your cubes index into the grid, and there's no gaps. Skipping faces based on the presence of an adjacent block is relatively straightforward. If you have more arbitrary geometry then you have to work waaay harder.
I didn't do transparent blocks (besides air) when I did this, so no need for a second render pass