diff --git a/data/language/en-GB.txt b/data/language/en-GB.txt index 9243e6b0be..87a03428f0 100644 --- a/data/language/en-GB.txt +++ b/data/language/en-GB.txt @@ -3205,7 +3205,6 @@ STR_6047 :Smooth tiles STR_6048 :Height map error STR_6049 :Error reading PNG STR_6050 :Error reading bitmap -STR_6051 :The width and height need to match STR_6052 :The heightmap is too big, and will be cut off STR_6053 :The heightmap cannot be normalised STR_6054 :Only 24-bit bitmaps are supported diff --git a/distribution/changelog.txt b/distribution/changelog.txt index 59d68c3c87..3a2e00294f 100644 --- a/distribution/changelog.txt +++ b/distribution/changelog.txt @@ -3,8 +3,9 @@ - Feature: [#13634] Add ability to sell merchandise in random colours. - Feature: [#16662] Show a warning message when g2.dat is mismatched. - Feature: [#17107] Ride operating settings can be set via text input. -- Improvement: [#15358] Park and scenario names can now contain up to 128 characters. -- Improvement: [#17575] You can now search for Authors in Object Selection. +- Improved: [#15358] Park and scenario names can now contain up to 128 characters. +- Improved: [#16840] Add support for rectangular heightmaps. +- Improved: [#17575] You can now search for Authors in Object Selection. - Change: [#17319] Giant screenshots are now cropped to the horizontal view-clipping selection. - Change: [#17499] Update error text when using vehicle incompatible with TD6 and add error when using incompatible track elements. - Fix: [#7466] Coaster track not drawn at tunnel exit. diff --git a/src/openrct2/localisation/StringIds.h b/src/openrct2/localisation/StringIds.h index 638f3cc432..f3c270a279 100644 --- a/src/openrct2/localisation/StringIds.h +++ b/src/openrct2/localisation/StringIds.h @@ -3342,7 +3342,7 @@ enum : uint16_t STR_HEIGHT_MAP_ERROR = 6048, STR_ERROR_READING_PNG = 6049, STR_ERROR_READING_BITMAP = 6050, - STR_ERROR_WIDTH_AND_HEIGHT_DO_NOT_MATCH = 6051, + STR_ERROR_HEIHGT_MAP_TOO_BIG = 6052, STR_ERROR_CANNOT_NORMALIZE = 6053, STR_ERROR_24_BIT_BITMAP = 6054, diff --git a/src/openrct2/world/MapGen.cpp b/src/openrct2/world/MapGen.cpp index 8f2700d083..9587cc57b2 100644 --- a/src/openrct2/world/MapGen.cpp +++ b/src/openrct2/world/MapGen.cpp @@ -668,6 +668,15 @@ static void mapgen_simplex(mapgen_settings* settings) #pragma region Heightmap +/** + * Return the tile coordinate that matches the given pixel of a heightmap + */ +static TileCoordsXY MapgenHeightmapCoordToTileCoordsXY(uint32_t x, uint32_t y) +{ + // The height map does not include the empty tiles around the map, so we add 1. + return TileCoordsXY(static_cast(y + 1), static_cast(x + 1)); +} + bool mapgen_load_heightmap(const utf8* path) { auto format = Imaging::GetImageFormatFromPath(path); @@ -680,23 +689,17 @@ bool mapgen_load_heightmap(const utf8* path) try { auto image = Imaging::ReadFromFile(path, format); - if (image.Width != image.Height) - { - context_show_error(STR_HEIGHT_MAP_ERROR, STR_ERROR_WIDTH_AND_HEIGHT_DO_NOT_MATCH, {}); - return false; - } - - auto size = image.Width; - if (image.Width > MAXIMUM_MAP_SIZE_PRACTICAL) + auto width = std::min(image.Width, MAXIMUM_MAP_SIZE_PRACTICAL); + auto height = std::min(image.Height, MAXIMUM_MAP_SIZE_PRACTICAL); + if (width != image.Width || height != image.Height) { context_show_error(STR_HEIGHT_MAP_ERROR, STR_ERROR_HEIHGT_MAP_TOO_BIG, {}); - size = std::min(image.Height, MAXIMUM_MAP_SIZE_PRACTICAL); } // Allocate memory for the height map values, one byte pixel - _heightMapData.mono_bitmap.resize(size * size); - _heightMapData.width = size; - _heightMapData.height = size; + _heightMapData.mono_bitmap.resize(width * height); + _heightMapData.width = width; + _heightMapData.height = height; // Copy average RGB value to mono bitmap constexpr auto numChannels = 4; @@ -790,15 +793,16 @@ static void mapgen_smooth_heightmap(std::vector& src, int32_t strength) void mapgen_generate_from_heightmap(mapgen_settings* settings) { - openrct2_assert(_heightMapData.width == _heightMapData.height, "Invalid height map size"); openrct2_assert(!_heightMapData.mono_bitmap.empty(), "No height map loaded"); openrct2_assert(settings->simplex_high != settings->simplex_low, "Low and high setting cannot be the same"); // Make a copy of the original height map that we can edit auto dest = _heightMapData.mono_bitmap; - auto maxSize = static_cast(_heightMapData.width + 2); // + 2 for the black tiles around the map - map_init({ maxSize, maxSize }); + // Get technical map size, +2 for the black tiles around the map + auto maxWidth = static_cast(_heightMapData.width + 2); + auto maxHeight = static_cast(_heightMapData.height + 2); + map_init({ maxHeight, maxWidth }); if (settings->smooth_height_map) { @@ -841,8 +845,8 @@ void mapgen_generate_from_heightmap(mapgen_settings* settings) for (uint32_t x = 0; x < _heightMapData.width; x++) { // The x and y axis are flipped in the world, so this uses y for x and x for y. - auto* const surfaceElement = map_get_surface_element_at( - TileCoordsXY{ static_cast(y + 1), static_cast(x + 1) }.ToCoordsXY()); + auto tileCoords = MapgenHeightmapCoordToTileCoordsXY(x, y); + auto* const surfaceElement = map_get_surface_element_at(tileCoords.ToCoordsXY()); if (surfaceElement == nullptr) continue; @@ -871,11 +875,12 @@ void mapgen_generate_from_heightmap(mapgen_settings* settings) while (true) { uint32_t numTilesChanged = 0; - for (uint32_t y = 1; y <= _heightMapData.height; y++) + for (uint32_t y = 0; y < _heightMapData.height; y++) { - for (uint32_t x = 1; x <= _heightMapData.width; x++) + for (uint32_t x = 0; x < _heightMapData.width; x++) { - numTilesChanged += tile_smooth(x, y); + auto tileCoords = MapgenHeightmapCoordToTileCoordsXY(x, y); + numTilesChanged += tile_smooth(tileCoords); } } diff --git a/src/openrct2/world/MapHelpers.cpp b/src/openrct2/world/MapHelpers.cpp index b8857f6302..3a6e301831 100644 --- a/src/openrct2/world/MapHelpers.cpp +++ b/src/openrct2/world/MapHelpers.cpp @@ -198,9 +198,9 @@ int32_t map_smooth(int32_t l, int32_t t, int32_t r, int32_t b) * This does not change the base height, unless all corners have been raised. * @returns 0 if no edits were made, 1 otherwise */ -int32_t tile_smooth(int32_t x, int32_t y) +int32_t tile_smooth(const TileCoordsXY& tileCoords) { - auto* const surfaceElement = map_get_surface_element_at(TileCoordsXY{ x, y }.ToCoordsXY()); + auto* const surfaceElement = map_get_surface_element_at(tileCoords.ToCoordsXY()); if (surfaceElement == nullptr) return 0; @@ -241,7 +241,8 @@ int32_t tile_smooth(int32_t x, int32_t y) continue; // Get neighbour height. If the element is not valid (outside of map) assume the same height - auto* neighbourSurfaceElement = map_get_surface_element_at(TileCoordsXY{ x + x_offset, y + y_offset }.ToCoordsXY()); + auto* neighbourSurfaceElement = map_get_surface_element_at( + (tileCoords + TileCoordsXY{ x_offset, y_offset }).ToCoordsXY()); neighbourHeightOffset.baseheight[index] = neighbourSurfaceElement != nullptr ? neighbourSurfaceElement->base_height : surfaceElement->base_height; diff --git a/src/openrct2/world/MapHelpers.h b/src/openrct2/world/MapHelpers.h index a2097c8a0a..cdb62a8102 100644 --- a/src/openrct2/world/MapHelpers.h +++ b/src/openrct2/world/MapHelpers.h @@ -10,6 +10,7 @@ #pragma once #include "../common.h" +#include "Location.hpp" enum { @@ -20,4 +21,4 @@ enum }; int32_t map_smooth(int32_t l, int32_t t, int32_t r, int32_t b); -int32_t tile_smooth(int32_t x, int32_t y); +int32_t tile_smooth(const TileCoordsXY& tileCoords);