Looking for your expertise / suggestions on approaching a voxel editor. (98% certain my current plan will be a disaster.)

I need to support a grid of up to 256x256x256 (16,000,000+) cubes.

I have done quite a bit of experimentation at this point and got instancing figured out (to some extent, at least) and have a few POCs, but, so far, they all rely on building cube by cube and maintaining a 3 dimensional array of cube states to update and trigger threejs updates, which has not and is not going to be very effective. -_-

So I have been frying my brain trying to imagine the computations, edge cases, etc I’ll need to ace in order to allow cubes to be resizable in 3 dimensions, yet colorable / texturized on a cube by cube basis.

  • layers
  • unions
  • intersections (cut out from one shape based on the geometry of another)

Ultimately, I have only a handful of months of experience with 3d in JS at all. So I am hoping some of you can give me a better idea how I should be looking at this, and suggestions on how I might efficiently approach it.

Please and thank you. :pray:

A good article (You may not need it but it discusses some useful/relevant topics):

Generally folks break the world into “chunks” of 32x32xWorld_Height voxel columns and update a column at a time. So each column is a single mesh.
The main thing is to try to eliminate faces that are interior to the mesh, and then if possible also do some “greedy meshing” as describe in that article… Or not…

Since you already have a [ x ][y][z] representation of the cubes… determining interior faces shouldn’t be too hard…
Then you can use either a regular geometry and write its .attributes.position, uv, and normals
To write your individual cube faces to the geometry.

It’s also advisable to put your cube textures in a single texture (a texture atlas) and select them for each face by writing the UVs for each tile in the image.

Or do something clever with instancing perhaps…

Just getting the basics of editing/adding/removing cubes is a lot of work… Getting individual cubes to be uniquely editable is gonna be a lot more work. :smiley:


Just want to follow up and say wow, I have had my ass kicked so much on this project. It has been refreshing, yet frustrating, to have something that I have no idea how to approach for the first time since the beginning of my career almost a decade ago.

I actually opted out of maintaining a 3D array of gridSize x gridSize x gridSize length and went for a much more simplistic approach with less overhead, that I refer to as “index projection” but its basically just “this hurts my brain less”.

Index projection: Knowing the gridSize, which in my application defines the width, depth, and height of the 3d voxel canvas, I can determine the position of a voxel based on its index.

const gridSize = 4
const indexes = [3, 27]
// [{x: 3, y: 0, z: 0}, {x: 3, y: 1, z: 2 }]

Likewise, I can convert an x/y/z position to an index with index = (z * gridSize) + (y * (gridSize * gridSize)) + x.

I have a gridSize x gridSize square grid of tiles on the bottom of my voxel grid and each one is responsive to pointerOver and click to add the initial voxels to the grid, from which point I intend to make voxels’ sides reactive to pointerOver and click, but haven’t got that far yet. (I have gotten far enouigh to try and fail, but not enough to show anything lmao.)

The hardest part has been the algorithm for clicking and dragging from one floorTile to another and having a “ghost” path of voxels appear that will be committed to and applied to the voxel grid on pointerUp. (See gist 0.)

I have had to do sketchy stuff that I don’t trust very well for transferring pointerOver from a floorTile to a voxel for highlighting, but it works for the time being.

Although refreshing, I believe I may be in over my head, and may be trying a less intensive 3D application for what is basically my first use of 3D in JS. This high-cognitive-load logic mixed with the mutable, non-reactive patterns I have to use that split from my years of thinking immutably / reactively is just a lot to handle all at once.

Pretty happy with what I accomplished here, though!

Gist 0: getGridPath
Gist 1: grid.store

1 Like

Take a look at the sparse grids - the amount of calculations you can save by using log2 masking and a sliding window is kinda mind blowing. It’ll let you cull out entire chunks of the world instantly, turning these 16mil cubes into 4000-10000, that you can then instance and render at normal FPS.