Direction for a performant grid based map editor

Hi All! I’m new to Three.js and 3D rendering in general (though professionally, I am a JavaScript developer). I’ve been digging through this forum over the last month or two and it’s been a great resource getting started.

A little background on what led me here: I was working on a personal project building a relatively simple grid based map builder (something that could be used as a tool to build maps that might be used in table top games). I started out just building an HTML/CSS/JS solution, however, ran into performance issues once a map started to get bigger than 60x60 tiles, simply due to the number of elements in the dom, and having to manipulate them all when panning the map for example. There are definitely ways to work around this performance issue just using HTML/CSS/JS, however, at this point I thought a Three.js solution might be easier, and ultimately, much more robust.

I’ve done a handful of basic tutorials and went through most of the suggested resources in the Three.js docs, so I feel like I have a general understanding of the basics. I started out by working toward a proof on concept in the most simple, but naive way. Each tile is a separate mesh created based on the settings chosen for that tile. I quickly realized that number of draw calls is a big factor for performance in WebGL even if the objects are simple.

At this point, I feel like I don’t quite have enough experience to know exactly what the right questions are to ask here. But, currently, I’m thinking of two potential approaches to build a proof of concept.

  1. Understand how to more performantly create many similar meshes to create a grid based map that can be updated dynamically in an editor type context.
  2. Take the approach of providing a subset of elements that can be placed. For example - a room with given dimensions, or a hallway, etc. Ending up with a much more reasonable number of draw calls for even a large map (but also creating a more limited creation experience).

The first option seems like the way to create a flexible tool, though also possibly much more complicated to implement.

I’ve seen some projects like https://github.com/vonWolfehaus/von-grid that create a grid based builder, but this also breaks down at medium to large grid sizes.

I’ve also looked at multiple different voxel based projects (http://maxogden.github.io/voxel-engine/, http://www.voxeljs.com/ for example). These all seem to create a structure around grouping and chunking a large number of items efficiently, though, most aren’t maintained or current, so it feels like updating and leveraging a tool like that might be a little bit out of depth of an initial Three.js project.

So this ultimately brings me to my question. I’m trying to understand what a reasonable approach in Three.js for this type of functionality might be with performance in mind. I realize this is a pretty big topic, so I’m not looking for someone to lay out an entire plan, but help by describing a general direction to investigate or additional resources/patterns to start with. Are those 2 directions above a reasonable way to think about the problem at hand? Are there any existing, open projects that might resemble this type behavior that would provide a good example? (I haven’t been able to find one, though I’m still very new to the WebGL/Three.js community.)

I appreciate any help on suggestions getting started here, thanks!

1 Like

Could you say a bit more about what a “tile” might contain? Do you expect tiles to be reused with just different locations and rotations? Or reused with different colors or textures? Or maybe tiles are not reused, but elements appearing within a tile can be reused?

Broadly I think your options come down to:

(a) Merging parts of the map that aren’t currently being edited, keeping source information so that you can split tiles out for editing when necessary. Reusing objects isn’t necessary in this case, but it also becomes more complicated to use textures. THREE.BufferGeometryUtils has a helper method for merging objects, but most of the management work would fall on your application.

(b) Reuse tiles, or elements within tiles, as much as possible. If reused elements share exactly the same material, this is pretty easy with InstancedMesh (examples). If you need different colors or textures on different instances, that’s still possible but may require writing some GLSL shaders rather than using built-in materials, and packing textures into an atlas.

I don’t really know of any out-of-the-box resources to start with, but wouldn’t be surprised if some existed.

1 Like

Thanks! This is all really helpful for understanding some of the high level ways of thinking about this type of problem within Three.js.

As far as what I’m thinking of a “tile” representing, if I’m working to replicate the type of thing I was trying to create with a webapp without using WebGL, a tile would be reused with different positions and rotations, but always have the same material. The idea would be as an editor, you have a set of possible tiles to use and you can compose a map assembling them however you imagine, setting every square in your space that needs a tile definition.

Based on your description above, it seems like this is probably the simplest case to handle and implement within Three.js, so I might just try to replicate the 2D-ish version that I was envisioning before I was considering Three.js as a starting point. This maybe isn’t the best long term direction as it could get quite tedious building something that isn’t quite small, though it seems a like a good place to start while also getting familiar with Three.js.

Another thing that you pointed out, which I wasn’t thinking too much about, is the possibility of presenting the map differently when it will be viewed but not edited. ie - when it’s not being edited, maybe more pieces can be merged to increase performance, and that’s where performance is the most important anyway.

Thanks a lot for the thoughtful response, it was really helpful in getting a better understanding on how to move forward!

1 Like