A few weeks ago I described elevation painting for mapgen4. The idea was to let the user paint areas for mountains, valleys, and oceans, and then use distance fields to interpolate between the areas. After some user testing, I decided to abandon this approach.

When I originally implemented it, I painted mountains and valleys on a minimap. On the main map, I used distance fields to transition between the mountain and valley areas.

Elevation constraints (red and green), and resulting elevation

I later switched from painting the minimap to to painting on the main map. User testing revealed a flaw. With the minimap, you can see the areas that are painted mountain or valley, so if you want to erase mountains, you can erase the red areas. When painting on the main map, you can't see the constraints. You only see the outputs. If you want to erase mountains, you paint valleys on those areas. But some of those mountains are already marked as valleys, so nothing happens!

I decided to instead directly paint the elevations. Instead of the distance field for interpolation, I'm trying a “feathered” brush. In the center of the painted area, I set the elevation to mountain or valley. Surrounding that central area, I only partially paint, by blending the elevation with the previous elevation.

I implemented this yesterday, and it feels much better than the previous approach. And because I'm no longer spending time calculating the distance fields, the new approach is much faster. I'm going to keep it.

The feathered painting approach however has its own issues. The main problem is what does "previous elevation" mean?

  1. It could mean before the current circle is painted. The partially painted area gets repeatedly painted until it's at 100%. The interpolated area disappears and I get discontinuities in the elevation.
  2. It could mean before the current stroke. If you paint two short strokes it will behave differently than if you draw one long stroke.
  3. It could mean before the current tool was selected. If you paint, then select another tool, then go back, it will behave differently than if you stayed with the original tool.

I've implemented style 1, and I'm going to implement style 2 or 3 next to see how it feels. In addition, it would be nice to paint repeatedly over an area to raise or lower the elevation, like an airbrush painting tool. Instead of having mountain and ocean and valley tools, maybe I should call them raise and lower and flatten.

I should have started earlier with the user interface. It's driving many decisions in how the rest of the system works.

Labels: , ,

4 comments:

Frank Gennari wrote at September 11, 2018 9:35 PM

I've done realtime terrain height editing before in my 3DWorld project: https://3dworldgen.blogspot.com/2017/03/realtime-terrain-editing.html
I went with style #1 myself, and I used the raise-lower-flatten "airbrush" flow. There are some nice properties of this flow. You can keep a stack of brush operations, which allows for saving/loading and undo/redo. Undo works by applying the negative of the previous brush operation, which undoes it. This only works with raise and lower. You need to be careful about clipping to max or min values (0% and 100%), because you can lose the ability to undo it. It's probably worth experimenting with various approaches.

Azgaar wrote at September 12, 2018 2:50 AM

I'm also using the 1st approach, it works acceptable even with a simple height colorization. And it looks people *don't* like when there too much brushes or any unobvious ones (like in my case), so the idea with 3 brushes only is pretty interesting. But in any case you have also add a brush radius input.

Frank Gennari wrote at September 12, 2018 11:12 PM

Yes, it helps to have parameters like radius and brush weight/intensity. I also added a brush delay value that can be used to switch between "painting/airbrush" and "stamping". These options allow users to paint large areas quickly while also working on fine details.

Amit wrote at September 20, 2018 11:52 AM

Thanks! Based on your feedback I tweaked style 1 some more and ended up with something I like.