1
0
mirror of https://github.com/OpenRCT2/OpenRCT2 synced 2026-01-15 11:03:00 +01:00

Add heightmap smoothing

Fixed underflow error that was visible for non-tilable maps.
Reordered widgets to reflect the same order of the algorithm.
Always regenerate the map when a setting is changed.
This commit is contained in:
Broxzier
2017-02-12 19:25:10 +01:00
committed by Michał Janiszewski
parent 26989ea8f6
commit 55c1787a13
3 changed files with 117 additions and 77 deletions

View File

@@ -87,8 +87,11 @@ enum {
WIDX_SIMPLEX_WALL_TEXTURE,
WIDX_HEIGHTMAP_SMOOTH_HEIGHTMAP = TAB_BEGIN,
WIDX_HEIGHTMAP_SMOOTH_TILES,
WIDX_HEIGHTMAP_STRENGTH,
WIDX_HEIGHTMAP_STRENGTH_UP,
WIDX_HEIGHTMAP_STRENGTH_DOWN,
WIDX_HEIGHTMAP_NORMALIZE,
WIDX_HEIGHTMAP_SMOOTH_TILES,
WIDX_HEIGHTMAP_LOW,
WIDX_HEIGHTMAP_LOW_UP,
WIDX_HEIGHTMAP_LOW_DOWN,
@@ -202,21 +205,25 @@ static rct_widget HeightmapWidgets[] = {
{ WWT_DROPDOWN_BUTTON, 1, 104, 198, 52, 63, STR_MAPGEN_ACTION_GENERATE, STR_NONE }, // WIDX_GENERATE
{ WWT_CHECKBOX, 1, 4, 103, 52, 63, STR_MAPGEN_SMOOTH_HEIGHTMAP,STR_NONE }, // WIDX_HEIGHTMAP_SMOOTH_HEIGHTMAP
{ WWT_CHECKBOX, 1, 4, 103, 70, 81, STR_MAPGEN_SMOOTH_TILE, STR_NONE }, // WIDX_HEIGHTMAP_SMOOTH_TILES
{ WWT_SPINNER, 1, 104, 198, 70, 81, STR_NONE, STR_NONE }, // WIDX_HEIGHTMAP_STRENGTH
{ WWT_DROPDOWN_BUTTON, 1, 187, 197, 71, 75, STR_NUMERIC_UP, STR_NONE }, // WIDX_HEIGHTMAP_STRENGTH_UP
{ WWT_DROPDOWN_BUTTON, 1, 187, 197, 76, 80, STR_NUMERIC_DOWN, STR_NONE }, // WIDX_HEIGHTMAP_STRENGTH_DOWN
{ WWT_CHECKBOX, 1, 4, 103, 88, 99, STR_MAPGEN_NORMALIZE, STR_NONE }, // WIDX_HEIGHTMAP_NORMALIZE
{ WWT_SPINNER, 1, 104, 198, 106, 117, STR_NONE, STR_NONE }, // WIDX_HEIGHTMAP_LOW
{ WWT_DROPDOWN_BUTTON, 1, 187, 197, 107, 111, STR_NUMERIC_UP, STR_NONE }, // WIDX_HEIGHTMAP_LOW_UP
{ WWT_DROPDOWN_BUTTON, 1, 187, 197, 112, 116, STR_NUMERIC_DOWN, STR_NONE }, // WIDX_HEIGHTMAP_LOW_DOWN
{ WWT_CHECKBOX, 1, 4, 103, 106, 117, STR_MAPGEN_SMOOTH_TILE, STR_NONE }, // WIDX_HEIGHTMAP_SMOOTH_TILES
{ WWT_SPINNER, 1, 104, 198, 124, 135, STR_NONE, STR_NONE }, // WIDX_HEIGHTMAP_HIGH
{ WWT_DROPDOWN_BUTTON, 1, 187, 197, 125, 129, STR_NUMERIC_UP, STR_NONE }, // WIDX_HEIGHTMAP_HIGH_UP
{ WWT_DROPDOWN_BUTTON, 1, 187, 197, 130, 134, STR_NUMERIC_DOWN, STR_NONE }, // WIDX_HEIGHTMAP_HIGH_DOWN
{ WWT_SPINNER, 1, 104, 198, 124, 135, STR_NONE, STR_NONE }, // WIDX_HEIGHTMAP_LOW
{ WWT_DROPDOWN_BUTTON, 1, 187, 197, 125, 129, STR_NUMERIC_UP, STR_NONE }, // WIDX_HEIGHTMAP_LOW_UP
{ WWT_DROPDOWN_BUTTON, 1, 187, 197, 130, 134, STR_NUMERIC_DOWN, STR_NONE }, // WIDX_HEIGHTMAP_LOW_DOWN
{ WWT_SPINNER, 1, 104, 198, 142, 153, STR_NONE, STR_NONE }, // WIDX_HEIGHTMAP_WATER_LEVEL
{ WWT_DROPDOWN_BUTTON, 1, 187, 197, 143, 147, STR_NUMERIC_UP, STR_NONE }, // WIDX_HEIGHTMAP_WATER_LEVEL_UP
{ WWT_DROPDOWN_BUTTON, 1, 187, 197, 148, 152, STR_NUMERIC_DOWN, STR_NONE }, // WIDX_HEIGHTMAP_WATER_LEVEL_DOWN
{ WWT_SPINNER, 1, 104, 198, 142, 153, STR_NONE, STR_NONE }, // WIDX_HEIGHTMAP_HIGH
{ WWT_DROPDOWN_BUTTON, 1, 187, 197, 143, 147, STR_NUMERIC_UP, STR_NONE }, // WIDX_HEIGHTMAP_HIGH_UP
{ WWT_DROPDOWN_BUTTON, 1, 187, 197, 148, 152, STR_NUMERIC_DOWN, STR_NONE }, // WIDX_HEIGHTMAP_HIGH_DOWN
{ WWT_SPINNER, 1, 104, 198, 160, 171, STR_NONE, STR_NONE }, // WIDX_HEIGHTMAP_WATER_LEVEL
{ WWT_DROPDOWN_BUTTON, 1, 187, 197, 161, 165, STR_NUMERIC_UP, STR_NONE }, // WIDX_HEIGHTMAP_WATER_LEVEL_UP
{ WWT_DROPDOWN_BUTTON, 1, 187, 197, 166, 170, STR_NUMERIC_DOWN, STR_NONE }, // WIDX_HEIGHTMAP_WATER_LEVEL_DOWN
{ WIDGETS_END },
};
@@ -465,14 +472,17 @@ static uint32 PageEnabledWidgets[] = {
(1ULL << WIDX_TAB_4) |
(1ULL << WIDX_GENERATE) |
(1ULL << WIDX_HEIGHTMAP_SMOOTH_HEIGHTMAP) |
(1ULL << WIDX_HEIGHTMAP_SMOOTH_TILES) |
(1ULL << WIDX_HEIGHTMAP_STRENGTH) |
(1ULL << WIDX_HEIGHTMAP_STRENGTH_UP) |
(1ULL << WIDX_HEIGHTMAP_STRENGTH_DOWN) |
(1ULL << WIDX_HEIGHTMAP_NORMALIZE) |
(1ULL << WIDX_HEIGHTMAP_LOW) |
(1ULL << WIDX_HEIGHTMAP_LOW_UP) |
(1ULL << WIDX_HEIGHTMAP_LOW_DOWN) |
(1ULL << WIDX_HEIGHTMAP_SMOOTH_TILES) |
(1ULL << WIDX_HEIGHTMAP_HIGH) |
(1ULL << WIDX_HEIGHTMAP_HIGH_UP) |
(1ULL << WIDX_HEIGHTMAP_HIGH_DOWN) |
(1ULL << WIDX_HEIGHTMAP_LOW) |
(1ULL << WIDX_HEIGHTMAP_LOW_UP) |
(1ULL << WIDX_HEIGHTMAP_LOW_DOWN) |
(1ULL << WIDX_HEIGHTMAP_WATER_LEVEL) |
(1ULL << WIDX_HEIGHTMAP_WATER_LEVEL_UP) |
(1ULL << WIDX_HEIGHTMAP_WATER_LEVEL_DOWN)
@@ -518,6 +528,7 @@ static const sint32 TabAnimationLoops[] = {
#define BASESIZE_MAX 60
#define WATERLEVEL_MIN 0
#define WATERLEVEL_MAX 54
#define MAX_SMOOTH_ITERATIONS 20
static void window_mapgen_set_page(rct_window *w, sint32 page);
static void window_mapgen_set_pressed_tab(rct_window *w);
@@ -549,8 +560,10 @@ static sint32 _simplex_high = 10;
static sint32 _simplex_base_freq = 60;
static sint32 _simplex_octaves = 4;
static bool _heightmapSmoothMap = false;
static sint32 _heightmapSmoothStrength = 1;
static bool _heightmapNormalize = true;
static sint32 _heightmapLow = 10;
static sint32 _heightmapLow = 6;
static sint32 _heightmapHigh = 40;
rct_window *window_mapgen_open()
@@ -1132,50 +1145,48 @@ static void window_mapgen_simplex_paint(rct_window *w, rct_drawpixelinfo *dpi)
static void window_mapgen_heightmap_mouseup(rct_window *w, sint32 widgetIndex)
{
mapgen_settings mapgenSettings;
switch (widgetIndex)
{
// The close and tabs return, so that the map doesn't get regenerated
case WIDX_CLOSE:
window_close(w);
break;
return;
case WIDX_TAB_1:
case WIDX_TAB_2:
case WIDX_TAB_3:
case WIDX_TAB_4:
window_mapgen_set_page(w, widgetIndex - WIDX_TAB_1);
break;
return;
// Page widgets
case WIDX_GENERATE:
mapgenSettings.water_level = _waterLevel;
mapgenSettings.smooth = widget_is_pressed(w, WIDX_HEIGHTMAP_SMOOTH_TILES);
mapgenSettings.smooth_height_map = widget_is_pressed(w, WIDX_HEIGHTMAP_SMOOTH_HEIGHTMAP);
mapgenSettings.normalize_height = _heightmapNormalize;
mapgenSettings.simplex_low = _heightmapLow;
mapgenSettings.simplex_high = _heightmapHigh;
mapgen_generate_from_heightmap(&mapgenSettings);
gfx_invalidate_screen();
// Handled at the end of this function
break;
case WIDX_HEIGHTMAP_SMOOTH_HEIGHTMAP:
widget_set_checkbox_value(w, WIDX_HEIGHTMAP_SMOOTH_HEIGHTMAP, !widget_is_pressed(w, WIDX_HEIGHTMAP_SMOOTH_HEIGHTMAP));
_heightmapSmoothMap = !_heightmapSmoothMap;
widget_set_checkbox_value(w, WIDX_HEIGHTMAP_SMOOTH_HEIGHTMAP, _heightmapSmoothMap);
widget_set_enabled(w, WIDX_HEIGHTMAP_STRENGTH, _heightmapSmoothMap);
widget_set_enabled(w, WIDX_HEIGHTMAP_STRENGTH_UP, _heightmapSmoothMap);
widget_set_enabled(w, WIDX_HEIGHTMAP_STRENGTH_DOWN, _heightmapSmoothMap);
widget_invalidate(w, WIDX_HEIGHTMAP_SMOOTH_HEIGHTMAP);
widget_invalidate(w, WIDX_HEIGHTMAP_STRENGTH);
break;
case WIDX_HEIGHTMAP_SMOOTH_TILES:
widget_set_checkbox_value(w, WIDX_HEIGHTMAP_SMOOTH_TILES, !widget_is_pressed(w, WIDX_HEIGHTMAP_SMOOTH_TILES));
widget_invalidate(w, WIDX_HEIGHTMAP_SMOOTH_TILES);
case WIDX_HEIGHTMAP_STRENGTH_UP:
_heightmapSmoothStrength = min(_heightmapSmoothStrength + 1, MAX_SMOOTH_ITERATIONS);
widget_invalidate(w, WIDX_HEIGHTMAP_STRENGTH);
break;
case WIDX_HEIGHTMAP_STRENGTH_DOWN:
_heightmapSmoothStrength = max(_heightmapSmoothStrength - 1, 0);
widget_invalidate(w, WIDX_HEIGHTMAP_STRENGTH);
break;
case WIDX_HEIGHTMAP_NORMALIZE:
_heightmapNormalize = !_heightmapNormalize;
widget_set_checkbox_value(w, WIDX_HEIGHTMAP_NORMALIZE, _heightmapNormalize);
widget_invalidate(w, WIDX_HEIGHTMAP_NORMALIZE);
break;
case WIDX_HEIGHTMAP_HIGH_UP:
_heightmapHigh = min(_heightmapHigh + 1, 142);
window_invalidate(w);
break;
case WIDX_HEIGHTMAP_HIGH_DOWN:
_heightmapHigh = max(_heightmapHigh - 1, 2 + 1);
_heightmapLow = min(_heightmapLow, _heightmapHigh - 1);
window_invalidate(w);
case WIDX_HEIGHTMAP_SMOOTH_TILES:
widget_set_checkbox_value(w, WIDX_HEIGHTMAP_SMOOTH_TILES, !widget_is_pressed(w, WIDX_HEIGHTMAP_SMOOTH_TILES));
widget_invalidate(w, WIDX_HEIGHTMAP_SMOOTH_TILES);
break;
case WIDX_HEIGHTMAP_LOW_UP:
_heightmapLow = min(_heightmapLow + 1, 142 - 1);
@@ -1186,6 +1197,15 @@ static void window_mapgen_heightmap_mouseup(rct_window *w, sint32 widgetIndex)
_heightmapLow = max(_heightmapLow - 1, 2);
window_invalidate(w);
break;
case WIDX_HEIGHTMAP_HIGH_UP:
_heightmapHigh = min(_heightmapHigh + 1, 142);
window_invalidate(w);
break;
case WIDX_HEIGHTMAP_HIGH_DOWN:
_heightmapHigh = max(_heightmapHigh - 1, 2 + 1);
_heightmapLow = min(_heightmapLow, _heightmapHigh - 1);
window_invalidate(w);
break;
case WIDX_HEIGHTMAP_WATER_LEVEL_UP:
_waterLevel = min(_waterLevel + 2, 54);
window_invalidate(w);
@@ -1195,6 +1215,19 @@ static void window_mapgen_heightmap_mouseup(rct_window *w, sint32 widgetIndex)
window_invalidate(w);
break;
}
// Always regenerate the map after one of the page widgets has been changed
mapgen_settings mapgenSettings;
mapgenSettings.water_level = _waterLevel;
mapgenSettings.smooth = widget_is_pressed(w, WIDX_HEIGHTMAP_SMOOTH_TILES);
mapgenSettings.smooth_height_map = _heightmapSmoothMap;
mapgenSettings.smooth_strength = _heightmapSmoothStrength;
mapgenSettings.normalize_height = _heightmapNormalize;
mapgenSettings.simplex_low = _heightmapLow;
mapgenSettings.simplex_high = _heightmapHigh;
mapgen_generate_from_heightmap(&mapgenSettings);
gfx_invalidate_screen();
}
static void window_mapgen_heightmap_invalidate(rct_window *w)
@@ -1218,6 +1251,10 @@ static void window_mapgen_heightmap_paint(rct_window *w, rct_drawpixelinfo *dpi)
window_draw_widgets(w, dpi);
window_mapgen_draw_tab_images(dpi, w);
// Smooth strength label and value
gfx_draw_string_left(dpi, STR_MAPGEN_SMOOTH_STRENGTH, NULL, w->colours[1], w->x + 5, w->y + w->widgets[WIDX_HEIGHTMAP_STRENGTH].top + 1);
gfx_draw_string_left(dpi, STR_COMMA16, &_heightmapSmoothStrength, w->colours[1], w->x + w->widgets[WIDX_HEIGHTMAP_STRENGTH].left + 1, w->y + w->widgets[WIDX_HEIGHTMAP_STRENGTH].top + 1);
// Low label and value
gfx_draw_string_left(dpi, STR_MAPGEN_SIMPLEX_NOISE_LOW_, NULL, COLOUR_BLACK, w->x + 5, w->y + w->widgets[WIDX_HEIGHTMAP_LOW].top + 1);
gfx_draw_string_left(dpi, STR_COMMA16, &_heightmapLow, w->colours[1], w->x + w->widgets[WIDX_HEIGHTMAP_LOW].left + 1, w->y + w->widgets[WIDX_HEIGHTMAP_LOW].top + 1);

View File

@@ -767,53 +767,55 @@ static void mapgen_simplex(mapgen_settings *settings)
#pragma region Heightmap
/**
* Applies box Gaussian blur to the surface
* Applies box blur to the surface N times
*/
void mapgen_smooth_heightmap(SDL_Surface *surface)
void mapgen_smooth_heightmap(SDL_Surface *surface, sint32 strength)
{
SDL_LockSurface(surface);
// Apply box Gaussian blur
const uint32 width = surface->w;
const uint32 height = surface->h;
const uint32 numChannels = surface->format->BytesPerPixel;
const sint32 width = surface->w;
const sint32 height = surface->h;
const sint32 numChannels = surface->format->BytesPerPixel;
const size_t pitch = surface->pitch;
// Create buffer to store one channel
uint8 *dest = (uint8*)malloc(width * height);
uint8 *src = surface->pixels;
// Calculate box Gaussian blur value to all pixels of the surface
for (uint32 y = 0; y < height; y++)
for (sint32 i = 0; i < strength; i++)
{
for (uint32 x = 0; x < width; x++)
// Calculate box blur value to all pixels of the surface
for (sint32 y = 0; y < height; y++)
{
uint32 sum = 0;
// Loop over neightbour pixels, all of them have the same weight
for (sint8 offsetX = -1; offsetX <= 1; offsetX++)
for (sint32 x = 0; x < width; x++)
{
for (sint8 offsetY = -1; offsetY <= 1; offsetY++)
uint32 heightSum = 0;
// Loop over neightbour pixels, all of them have the same weight
for (sint8 offsetX = -1; offsetX <= 1; offsetX++)
{
// Clamp x and y so they stay within the image
// This assumes the height map is not tiled, and increases the weight of the edges
const sint32 readX = clamp(x + offsetX, 0, width - 1);
const sint32 readY = clamp(y + offsetY, 0, height - 1);
sum += src[readX * numChannels + readY * pitch];
for (sint8 offsetY = -1; offsetY <= 1; offsetY++)
{
// Clamp x and y so they stay within the image
// This assumes the height map is not tiled, and increases the weight of the edges
const sint32 readX = clamp(x + offsetX, 0, width - 1);
const sint32 readY = clamp(y + offsetY, 0, height - 1);
heightSum += src[readX * numChannels + readY * pitch];
}
}
// Take average
dest[x + y * width] = heightSum / 9;
}
// Take average
dest[x + y * width] = sum / 9;
}
}
// Now apply the Gaussian blur to the real pixels
for (uint32 y = 0; y < height; y++)
{
for (uint32 x = 0; x < width; x++)
// Now apply the blur to the source pixels
for (sint32 y = 0; y < height; y++)
{
src[x * numChannels + y * pitch] = dest[x + y * width];
for (sint32 x = 0; x < width; x++)
{
src[x * numChannels + y * pitch] = dest[x + y * width];
}
}
}
@@ -832,8 +834,8 @@ void mapgen_generate_from_heightmap(mapgen_settings *settings)
return;
}
const uint32 width = bitmap->w;
const uint32 height = bitmap->h;
const sint32 width = bitmap->w;
const sint32 height = bitmap->h;
const uint8 numChannels = bitmap->format->BytesPerPixel;
map_init(width + 2); // + 2 for the black tiles around the map
@@ -844,7 +846,7 @@ void mapgen_generate_from_heightmap(mapgen_settings *settings)
if (settings->smooth_height_map)
{
// Smooth height map
mapgen_smooth_heightmap(bitmap);
mapgen_smooth_heightmap(bitmap, settings->smooth_strength);
}
SDL_LockSurface(bitmap);
@@ -854,9 +856,9 @@ void mapgen_generate_from_heightmap(mapgen_settings *settings)
// Get highest and lowest pixel value
maxValue = 0;
minValue = 0xff;
for (uint32 y = 0; y < height; y++)
for (sint32 y = 0; y < height; y++)
{
for (uint32 x = 0; x < width; x++)
for (sint32 x = 0; x < width; x++)
{
uint8 value = src[x * numChannels + y * bitmap->pitch];
maxValue = max(maxValue, value);
@@ -878,9 +880,9 @@ void mapgen_generate_from_heightmap(mapgen_settings *settings)
const uint8 rangeIn = maxValue - minValue;
const uint8 rangeOut = settings->simplex_high - settings->simplex_low;
for (uint32 y = 0; y < height; y++)
for (sint32 y = 0; y < height; y++)
{
for (uint32 x = 0; x < width; x++)
for (sint32 x = 0; x < width; x++)
{
// The x and y axis are flipped in the world, so this uses y for x and x for y.
rct_map_element *const surfaceElement = map_get_surface_element_at(y + 1, x + 1);
@@ -910,9 +912,9 @@ void mapgen_generate_from_heightmap(mapgen_settings *settings)
while (true)
{
sint32 numTilesChanged = 0;
for (uint32 y = 1; y <= height; y++)
for (sint32 y = 1; y <= height; y++)
{
for (uint32 x = 1; x <= width; x++)
for (sint32 x = 1; x <= width; x++)
{
numTilesChanged += tile_smooth(x, y);
}

View File

@@ -37,6 +37,7 @@ typedef struct mapgen_settings {
// Height map settings
bool smooth;
bool smooth_height_map;
uint32 smooth_strength;
bool normalize_height;
} mapgen_settings;