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

Merge pull request #22735 from AaronVanGeffen/mapgen

Rework map generation window
This commit is contained in:
Aaron van Geffen
2024-09-30 20:20:19 +02:00
committed by GitHub
15 changed files with 1312 additions and 1003 deletions

View File

@@ -1910,8 +1910,8 @@ STR_2670 :Scroll
STR_2680 :All research complete
STR_2684 :Large group of guests arrive
STR_2685 :Simplex Noise Parameters
STR_2686 :Low:
STR_2687 :High:
STR_2686 :Min. land height:
STR_2687 :Max. land height:
STR_2688 :Base Frequency:
STR_2689 :Octaves:
STR_2690 :Map Generation
@@ -3166,9 +3166,9 @@ STR_6041 :{BLACK}No mechanics are hired!
STR_6042 :Load height map
STR_6043 :Select height map
STR_6044 :Smooth height map
STR_6045 :Strength
STR_6045 :Strength:
STR_6046 :Normalise height map
STR_6047 :Smooth tiles
STR_6047 :Smooth tile edges
STR_6048 :Height map error
STR_6049 :Error reading PNG
STR_6050 :Error reading bitmap
@@ -3748,6 +3748,39 @@ STR_6673 :Transparent
STR_6674 :{MONTH}, Year {COMMA16}
STR_6675 :Peep Names
STR_6676 :At least one peep names object must be selected
STR_6677 :Add beaches around water bodies
STR_6678 :Heightmap source:
STR_6679 :Flatland
STR_6680 :Simplex noise
STR_6681 :Heightmap file
STR_6682 :Map Generator - Generator
STR_6683 :Map Generator - Terrain
STR_6684 :Map Generator - Water
STR_6685 :Map Generator - Forests
STR_6686 :Tree to land ratio:
STR_6687 :Min. tree altitude:
STR_6688 :Max. tree altitude:
STR_6689 :{UINT16}%
STR_6690 :Minimum land height
STR_6691 :Enter min. land height between {COMMA16} and {COMMA16}
STR_6692 :Maximum land height
STR_6693 :Enter max. land height between {COMMA16} and {COMMA16}
STR_6694 :Minimum tree altitude
STR_6695 :Enter min. tree altitude between {COMMA16} and {COMMA16}
STR_6696 :Maximum tree altitude
STR_6697 :Enter max. tree altitude between {COMMA16} and {COMMA16}
STR_6698 :Tree to land ratio
STR_6699 :Enter tree to land ratio between {COMMA16} and {COMMA16}
STR_6700 :Simplex Base Frequency
STR_6701 :Enter Base Frequency between {COMMA2DP32} and {COMMA2DP32}
STR_6702 :Simplex Octaves
STR_6703 :Enter Octaves between {COMMA16} and {COMMA16}
STR_6704 :{COMMA2DP32}
STR_6705 :Browse...
STR_6706 :{WINDOW_COLOUR_2}Current image file: {BLACK}{STRING}
STR_6707 :(none selected)
STR_6708 :Smooth Strength
STR_6709 :Enter Smooth Strength between {COMMA16} and {COMMA16}
#############
# Scenarios #

View File

@@ -6,6 +6,7 @@
- Feature: [#22694] Park graphs have tooltips and can be resized like finance graphs.
- Feature: [#22758] The table of real peep names can now be changed using Peep Names objects.
- Improved: [#22470] Android: automatically detect RCT2 installs in /sdcard/rct2.
- Improved: [#22735] The map generator has a redesigned interface that is much more user friendly.
- Improved: [#22777] Add long flat-to-steep track pieces to the Wooden and Classic Wooden Roller Coasters.
- Improved: [#22815] Add long flat-to-steep track pieces to the Mine Train Coaster.
- Change: [#22494] Themes window now displays colours and checkboxes vertically.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -307,6 +307,11 @@
"x": 2,
"y": 2
},
{
"path": "icons/map_gen_terrain_tab.png",
"x": 1,
"y": 10
},
{
"path": "icons/map_gen_button.png"
},

View File

@@ -19,6 +19,7 @@ namespace OpenRCT2
STR_ADJUST_LARGER_LAND_TIP = 2379,
STR_ADJUST_SMALLER_LAND_TIP = 2378,
STR_BROKEN = 3124,
STR_BROWSE = 6705,
STR_CANCEL = 972,
STR_CANT_INCREASE_FURTHER = 3264,
STR_CANT_REDUCE_FURTHER = 3265,
@@ -36,6 +37,7 @@ namespace OpenRCT2
STR_ENTER_PARK_NAME = 1719,
STR_ENTER_SELECTION_SIZE = 5129,
STR_ERROR_INVALID_CHARACTERS = 5243,
STR_FORMAT_COMMA2DP32 = 6704,
STR_FORMAT_INTEGER = 5182,
STR_GRAPH_LABEL = 2222,
STR_LIGHTPINK_STRINGID = 5299,
@@ -896,25 +898,55 @@ namespace OpenRCT2
STR_SHOW_RIDES_STALLS_ON_MAP_TIP = 3144,
// Window: MapGen
STR_BASE_HEIGHT = 5183,
STR_BASE_HEIGHT_LABEL = 2691,
STR_ENTER_BASE_HEIGHT = 5184,
STR_BASE_HEIGHT = 5183, // unused
STR_BASE_HEIGHT_LABEL = 2691, // unused
STR_BEACHES_WATER_BODIES = 6677,
STR_ENTER_BASE_FREQUENCY = 6701,
STR_ENTER_BASE_HEIGHT = 5184, // unused
STR_ENTER_MAX_LAND = 6693,
STR_ENTER_MAX_TREE_ALTITUDE = 6697,
STR_ENTER_MIN_LAND = 6691,
STR_ENTER_MIN_TREE_ALTITUDE = 6695,
STR_ENTER_OCTAVES = 6703,
STR_ENTER_SMOOTH_STRENGTH = 6708,
STR_ENTER_TREE_TO_LAND_RATIO = 6699,
STR_ENTER_WATER_LEVEL = 5186,
STR_HEIGHTMAP_FILE = 6681,
STR_HEIGHTMAP_FLATLAND = 6679,
STR_HEIGHTMAP_SIMPLEX_NOISE = 6680,
STR_HEIGHTMAP_SOURCE = 6678,
STR_MAPGEN_ACTION_GENERATE = 2694,
STR_MAPGEN_CAPTION_FORESTS = 6685,
STR_MAPGEN_CAPTION_GENERATOR = 6682,
STR_MAPGEN_CAPTION_TERRAIN = 6683,
STR_MAPGEN_CAPTION_WATER = 6684,
STR_MAPGEN_CURRENT_HEIGHTMAP_FILE = 6706,
STR_MAPGEN_MAX_LAND_HEIGHT = 2687,
STR_MAPGEN_MIN_LAND_HEIGHT = 2686,
STR_MAPGEN_NONE_SELECTED = 6707,
STR_MAPGEN_NORMALIZE = 6046,
STR_MAPGEN_OPTION_PLACE_TREES = 2696,
STR_MAPGEN_OPTION_RANDOM_TERRAIN = 2695,
STR_MAPGEN_SELECT_HEIGHTMAP = 6043,
STR_MAPGEN_SIMPLEX_NOISE = 2685,
STR_MAPGEN_SIMPLEX_NOISE_BASE_FREQUENCY = 2688,
STR_MAPGEN_SIMPLEX_NOISE_HIGH = 2687,
STR_MAPGEN_SIMPLEX_NOISE_LOW_ = 2686,
STR_MAPGEN_SIMPLEX_NOISE_OCTAVES = 2689,
STR_MAPGEN_SMOOTH_HEIGHTMAP = 6044,
STR_MAPGEN_SMOOTH_STRENGTH = 6045,
STR_MAPGEN_SMOOTH_TILE = 6047,
STR_MAPGEN_WINDOW_TITLE = 2690,
STR_MAPGEN_TREE_MAX_ALTITUDE = 6688,
STR_MAPGEN_TREE_MIN_ALTITUDE = 6687,
STR_MAPGEN_TREE_TO_LAND_RATIO = 6686,
STR_MAPGEN_TREE_TO_LAND_RATIO_PCT = 6689,
STR_MAX_LAND_HEIGHT = 6692,
STR_MAX_TREE_ALTITUDE = 6696,
STR_MIN_LAND_HEIGHT = 6690,
STR_MIN_TREE_ALTITUDE = 6694,
STR_SIMPLEX_BASE_FREQUENCY = 6700,
STR_SIMPLEX_OCTAVES = 6702,
STR_SMOOTH_STRENGTH = 6709,
STR_TERRAIN_LABEL = 2693,
STR_TREE_TO_LAND_RATIO = 6694,
STR_WATER_LEVEL = 5185,
STR_WATER_LEVEL_LABEL = 2692,
@@ -2132,6 +2164,7 @@ namespace OpenRCT2
STR_HEIGHT_MARKS_ON_RIDE_TRACKS = 1153,
STR_HIGHLIGHT_PATH_ISSUES_MENU = 6219,
STR_LOAD_LANDSCAPE = 884,
STR_MAPGEN_MENU_ITEM = 2690,
STR_MULTIPLAYER_RECONNECT = 6309,
STR_NEW_GAME = 5715,
STR_OVERLAY_CLEARANCE_CHECKS_DISABLED = 6374,

View File

@@ -165,7 +165,7 @@ namespace OpenRCT2::Ui
const int16_t xPos = origin.x + size.width - 11;
const int16_t yPos = origin.y + 1;
const uint16_t width = 11;
const uint16_t height = 10;
const uint16_t height = size.height - 2;
return MakeWidget({ xPos, yPos }, { width, height }, WindowWidgetType::Button, colour, STR_DROPDOWN_GLYPH, tooltip);
}

View File

@@ -199,6 +199,7 @@ namespace OpenRCT2::Ui::Windows
else
{
ContextOpenWindow(WindowClass::Map);
ContextOpenWindow(WindowClass::Mapgen);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1173,7 +1173,7 @@ namespace OpenRCT2::Ui::Windows
gDropdownItems[i++].Format = STR_EXTRA_VIEWPORT;
if ((gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) && GetGameState().EditorStep == EditorStep::LandscapeEditor)
{
gDropdownItems[i++].Format = STR_MAPGEN_WINDOW_TITLE;
gDropdownItems[i++].Format = STR_MAPGEN_MENU_ITEM;
}
#ifdef ENABLE_SCRIPTING

View File

@@ -1420,7 +1420,7 @@ enum : StringId
STR_ERROR_READING_PNG = 6049,
STR_ERROR_READING_BITMAP = 6050,
STR_ERROR_HEIHGT_MAP_TOO_BIG = 6052,
STR_ERROR_HEIGHT_MAP_TOO_BIG = 6052,
STR_ERROR_CANNOT_NORMALIZE = 6053,
STR_ERROR_24_BIT_BITMAP = 6054,

View File

@@ -969,6 +969,7 @@ enum : ImageIndex
SPR_G2_ARROW_UP,
SPR_G2_ARROW_DOWN,
SPR_G2_RELOAD,
SPR_G2_MAP_GEN_TERRAIN_TAB,
SPR_G2_MAP_GEN_BTN,
SPR_G2_PEEP_SPAWN,

View File

@@ -39,6 +39,11 @@ int32_t MetresToFeet(int32_t metres)
return (metres * 840) / 256;
}
int32_t FeetToMetres(int32_t feet)
{
return feet * 256 / 840;
}
int32_t MphToKmph(int32_t mph)
{
// 1 mph = 1.60934 kmph
@@ -52,6 +57,16 @@ int32_t MphToDmps(int32_t mph)
return (mph * 73243) >> 14;
}
int32_t BaseZToMetres(int16_t baseZ)
{
return (baseZ / 2 - 7) * 1.5;
}
uint8_t MetresToBaseZ(int16_t metres)
{
return ((metres / 1.5) + 7) * 2;
}
/* Case insensitive logical compare */
// Example:
// - Guest 10

View File

@@ -25,8 +25,11 @@
int32_t SquaredMetresToSquaredFeet(int32_t squaredMetres);
int32_t MetresToFeet(int32_t metres);
int32_t FeetToMetres(int32_t feet);
int32_t MphToKmph(int32_t mph);
int32_t MphToDmps(int32_t mph);
int32_t BaseZToMetres(int16_t baseZ);
uint8_t MetresToBaseZ(int16_t metres);
inline int32_t UtilBitScanForward(uint32_t source)
{

View File

@@ -93,7 +93,40 @@ static constexpr std::string_view BaseTerrain[] = {
"rct2.terrain_surface.dirt", "rct2.terrain_surface.ice",
};
static void MapGenPlaceTrees();
static void MapGenGenerateBlank(MapGenSettings* settings);
static void MapGenGenerateSimplex(MapGenSettings* settings);
static void MapGenGenerateFromHeightmapImage(MapGenSettings* settings);
static void MapGenPlaceTrees(MapGenSettings* settings);
static void MapGenAddBeaches(MapGenSettings* settings);
void MapGenGenerate(MapGenSettings* settings)
{
// First, generate the height map
switch (settings->algorithm)
{
case MapGenAlgorithm::blank:
MapGenGenerateBlank(settings);
break;
case MapGenAlgorithm::simplexNoise:
MapGenGenerateSimplex(settings);
break;
case MapGenAlgorithm::heightmapImage:
MapGenGenerateFromHeightmapImage(settings);
break;
}
// Add beaches?
if (settings->beaches)
MapGenAddBeaches(settings);
// Place trees?
if (settings->trees)
MapGenPlaceTrees(settings);
}
static void MapGenSetWaterLevel(int32_t waterLevel);
static void MapGenSmoothHeight(int32_t iterations);
static void MapGenSetHeight(MapGenSettings* settings);
@@ -118,41 +151,14 @@ static void SetHeight(int32_t x, int32_t y, int32_t height)
_height[x + y * _heightSize.x] = height;
}
void MapGenGenerateBlank(MapGenSettings* settings)
static ObjectEntryIndex MapGenSurfaceTextureId(MapGenSettings* settings)
{
int32_t x, y;
MapClearAllElements();
MapInit(settings->mapSize);
for (y = 1; y < settings->mapSize.y - 1; y++)
{
for (x = 1; x < settings->mapSize.x - 1; x++)
{
auto surfaceElement = MapGetSurfaceElementAt(TileCoordsXY{ x, y });
if (surfaceElement != nullptr)
{
surfaceElement->SetSurfaceObjectIndex(settings->floor);
surfaceElement->SetEdgeObjectIndex(settings->wall);
surfaceElement->BaseHeight = settings->height;
surfaceElement->ClearanceHeight = settings->height;
}
}
}
MapGenSetWaterLevel(settings->water_level);
}
void MapGenGenerate(MapGenSettings* settings)
{
const auto& mapSize = settings->mapSize;
auto waterLevel = settings->water_level;
const auto selectedFloor = TerrainSurfaceObject::GetById(settings->floor);
std::string_view floorTexture = selectedFloor != nullptr ? selectedFloor->GetIdentifier() : "";
const auto selectedEdge = TerrainEdgeObject::GetById(settings->wall);
std::string_view edgeTexture = selectedFloor != nullptr ? selectedEdge->GetIdentifier() : "";
auto& objectManager = OpenRCT2::GetContext()->GetObjectManager();
if (floorTexture.empty())
const auto selectedFloor = TerrainSurfaceObject::GetById(settings->landTexture);
std::string_view surfaceTexture = selectedFloor != nullptr ? selectedFloor->GetIdentifier() : "";
if (surfaceTexture.empty())
{
std::vector<std::string_view> availableTerrains;
std::copy_if(std::begin(BaseTerrain), std::end(BaseTerrain), std::back_inserter(availableTerrains), [&](auto terrain) {
@@ -161,17 +167,31 @@ void MapGenGenerate(MapGenSettings* settings)
if (availableTerrains.empty())
// Fall back to the first available surface texture that is available in the park
floorTexture = TerrainSurfaceObject::GetById(0)->GetIdentifier();
surfaceTexture = TerrainSurfaceObject::GetById(0)->GetIdentifier();
else
floorTexture = availableTerrains[UtilRand() % availableTerrains.size()];
surfaceTexture = availableTerrains[UtilRand() % availableTerrains.size()];
}
auto surfaceTextureId = objectManager.GetLoadedObjectEntryIndex(ObjectEntryDescriptor(surfaceTexture));
return surfaceTextureId;
}
static ObjectEntryIndex MapGenEdgeTextureId(MapGenSettings* settings, ObjectEntryIndex surfaceTextureId)
{
auto& objectManager = OpenRCT2::GetContext()->GetObjectManager();
const auto selectedEdge = TerrainEdgeObject::GetById(settings->edgeTexture);
std::string_view edgeTexture = selectedEdge != nullptr ? selectedEdge->GetIdentifier() : "";
if (edgeTexture.empty())
{
auto surfaceObject = objectManager.GetLoadedObject(ObjectType::TerrainSurface, surfaceTextureId);
auto surfaceTexture = surfaceObject->GetIdentifier();
// Base edge type on surface type
if (floorTexture == "rct2.terrain_surface.dirt")
if (surfaceTexture == "rct2.terrain_surface.dirt")
edgeTexture = "rct2.terrain_edge.wood_red";
else if (floorTexture == "rct2.terrain_surface.ice")
else if (surfaceTexture == "rct2.terrain_surface.ice")
edgeTexture = "rct2.terrain_edge.ice";
else
edgeTexture = "rct2.terrain_edge.rock";
@@ -181,29 +201,46 @@ void MapGenGenerate(MapGenSettings* settings)
edgeTexture = TerrainEdgeObject::GetById(0)->GetIdentifier();
}
auto floorTextureId = objectManager.GetLoadedObjectEntryIndex(ObjectEntryDescriptor(floorTexture));
auto edgeTextureId = objectManager.GetLoadedObjectEntryIndex(ObjectEntryDescriptor(edgeTexture));
return edgeTextureId;
}
static void MapGenResetSurfaces(MapGenSettings* settings)
{
MapClearAllElements();
MapInit(settings->mapSize);
// Initialise the base map
MapInit(mapSize);
for (auto y = 1; y < mapSize.y - 1; y++)
const auto surfaceTextureId = MapGenSurfaceTextureId(settings);
const auto edgeTextureId = MapGenEdgeTextureId(settings, surfaceTextureId);
for (auto y = 1; y < settings->mapSize.y - 1; y++)
{
for (auto x = 1; x < mapSize.x - 1; x++)
for (auto x = 1; x < settings->mapSize.x - 1; x++)
{
auto surfaceElement = MapGetSurfaceElementAt(TileCoordsXY{ x, y });
if (surfaceElement != nullptr)
{
surfaceElement->SetSurfaceObjectIndex(floorTextureId);
surfaceElement->SetSurfaceObjectIndex(surfaceTextureId);
surfaceElement->SetEdgeObjectIndex(edgeTextureId);
surfaceElement->BaseHeight = settings->height;
surfaceElement->ClearanceHeight = settings->height;
surfaceElement->BaseHeight = settings->heightmapLow;
surfaceElement->ClearanceHeight = settings->heightmapLow;
}
}
}
}
static void MapGenGenerateBlank(MapGenSettings* settings)
{
MapGenResetSurfaces(settings);
MapGenSetWaterLevel(settings->waterLevel);
}
static void MapGenGenerateSimplex(MapGenSettings* settings)
{
MapGenResetSurfaces(settings);
// Create the temporary height map and initialise
auto& mapSize = settings->mapSize;
_heightSize = { mapSize.x * 2, mapSize.y * 2 };
_height = new uint8_t[_heightSize.y * _heightSize.x];
std::fill_n(_height, _heightSize.y * _heightSize.x, 0x00);
@@ -215,43 +252,47 @@ void MapGenGenerate(MapGenSettings* settings)
MapGenSetHeight(settings);
delete[] _height;
// Set the tile slopes so that there are no cliffs
while (MapSmooth(1, 1, mapSize.x - 1, mapSize.y - 1))
if (settings->smoothTileEdges)
{
// Set the tile slopes so that there are no cliffs
while (MapSmooth(1, 1, mapSize.x - 1, mapSize.y - 1))
{
}
}
// Add the water
MapGenSetWaterLevel(waterLevel);
MapGenSetWaterLevel(settings->waterLevel);
}
// Add sandy beaches
std::string_view beachTexture = floorTexture;
if (settings->floor == -1 && floorTexture == "rct2.terrain_surface.grass" && (UtilRand() & 1))
{
std::vector<std::string_view> availableBeachTextures;
if (objectManager.GetLoadedObject(ObjectEntryDescriptor("rct2.terrain_surface.sand")) != nullptr)
availableBeachTextures.push_back("rct2.terrain_surface.sand");
if (objectManager.GetLoadedObject(ObjectEntryDescriptor("rct2.terrain_surface.sand_brown")) != nullptr)
availableBeachTextures.push_back("rct2.terrain_surface.sand_brown");
static void MapGenAddBeaches(MapGenSettings* settings)
{
auto& objectManager = OpenRCT2::GetContext()->GetObjectManager();
if (!availableBeachTextures.empty())
beachTexture = availableBeachTextures[UtilRand() % availableBeachTextures.size()];
}
// Figure out what beach texture to use
std::vector<std::string_view> availableBeachTextures;
if (objectManager.GetLoadedObject(ObjectEntryDescriptor("rct2.terrain_surface.sand")) != nullptr)
availableBeachTextures.push_back("rct2.terrain_surface.sand");
if (objectManager.GetLoadedObject(ObjectEntryDescriptor("rct2.terrain_surface.sand_brown")) != nullptr)
availableBeachTextures.push_back("rct2.terrain_surface.sand_brown");
if (availableBeachTextures.empty())
return;
std::string_view beachTexture = availableBeachTextures[UtilRand() % availableBeachTextures.size()];
auto beachTextureId = objectManager.GetLoadedObjectEntryIndex(ObjectEntryDescriptor(beachTexture));
// Add sandy beaches
const auto& mapSize = settings->mapSize;
for (auto y = 1; y < mapSize.y - 1; y++)
{
for (auto x = 1; x < mapSize.x - 1; x++)
{
auto surfaceElement = MapGetSurfaceElementAt(TileCoordsXY{ x, y });
if (surfaceElement != nullptr && surfaceElement->BaseHeight < waterLevel + 6)
if (surfaceElement != nullptr && surfaceElement->BaseHeight < settings->waterLevel + 6)
surfaceElement->SetSurfaceObjectIndex(beachTextureId);
}
}
// Place the trees
if (settings->trees != 0)
MapGenPlaceTrees();
}
static void MapGenPlaceTree(ObjectEntryIndex type, const CoordsXY& loc)
@@ -306,7 +347,7 @@ template<typename T> static bool TryFindTreeInList(std::string_view id, const T&
/**
* Randomly places a selection of preset trees on the map. Picks the right tree for the terrain it is placing it on.
*/
static void MapGenPlaceTrees()
static void MapGenPlaceTrees(MapGenSettings* settings)
{
std::vector<int32_t> grassTreeIds;
std::vector<int32_t> desertTreeIds;
@@ -335,16 +376,14 @@ static void MapGenPlaceTrees()
}
// Place trees
CoordsXY pos;
float treeToLandRatio = (10 + (UtilRand() % 30)) / 100.0f;
float treeToLandRatio = static_cast<float>(settings->treeToLandRatio) / 100.0f;
auto& gameState = GetGameState();
for (int32_t y = 1; y < gameState.MapSize.y - 1; y++)
{
for (int32_t x = 1; x < gameState.MapSize.x - 1; x++)
{
pos.x = x * kCoordsXYStep;
pos.y = y * kCoordsXYStep;
auto pos = CoordsXY{ x, y } * kCoordsXYStep;
auto* surfaceElement = MapGetSurfaceElementAt(pos);
if (surfaceElement == nullptr)
continue;
@@ -353,6 +392,10 @@ static void MapGenPlaceTrees()
if (surfaceElement->GetWaterHeight() > 0)
continue;
if (settings->minTreeAltitude > surfaceElement->BaseHeight
|| settings->maxTreeAltitude < surfaceElement->BaseHeight)
continue;
// On sand surfaces, give the tile a score based on nearby water, to be used to determine whether to spawn
// vegetation
float oasisScore = 0.0f;
@@ -490,7 +533,7 @@ static void MapGenSetHeight(MapGenSettings* settings)
surfaceElement->BaseHeight = std::max(2, baseHeight * 2);
// If base height is below water level, lower it to create more natural shorelines
if (surfaceElement->BaseHeight >= 4 && surfaceElement->BaseHeight <= settings->water_level)
if (surfaceElement->BaseHeight >= 4 && surfaceElement->BaseHeight <= settings->waterLevel)
surfaceElement->BaseHeight -= 2;
surfaceElement->ClearanceHeight = surfaceElement->BaseHeight;
@@ -648,18 +691,16 @@ static float Grad(int32_t hash, float x, float y)
static void MapGenSimplex(MapGenSettings* settings)
{
int32_t x, y;
float freq = settings->simplex_base_freq * (1.0f / _heightSize.x);
float freq = settings->simplex_base_freq / 100.0f * (1.0f / _heightSize.x);
int32_t octaves = settings->simplex_octaves;
int32_t low = settings->simplex_low;
int32_t high = settings->simplex_high;
int32_t low = settings->heightmapLow / 2;
int32_t high = settings->heightmapHigh / 2 - low;
NoiseRand();
for (y = 0; y < _heightSize.y; y++)
for (int32_t y = 0; y < _heightSize.y; y++)
{
for (x = 0; x < _heightSize.x; x++)
for (int32_t x = 0; x < _heightSize.x; x++)
{
float noiseValue = std::clamp(FractalNoise(x, y, freq, octaves, 2.0f, 0.65f), -1.0f, 1.0f);
float normalisedNoiseValue = (noiseValue + 1.0f) / 2.0f;
@@ -682,7 +723,7 @@ static TileCoordsXY MapgenHeightmapCoordToTileCoordsXY(uint32_t x, uint32_t y)
return TileCoordsXY(static_cast<int32_t>(y + 1), static_cast<int32_t>(x + 1));
}
bool MapGenLoadHeightmap(const utf8* path)
bool MapGenLoadHeightmapImage(const utf8* path)
{
auto format = Imaging::GetImageFormatFromPath(path);
if (format == IMAGE_FORMAT::PNG)
@@ -698,7 +739,7 @@ bool MapGenLoadHeightmap(const utf8* path)
auto height = std::min<uint32_t>(image.Height, kMaximumMapSizePractical);
if (width != image.Width || height != image.Height)
{
ContextShowError(STR_HEIGHT_MAP_ERROR, STR_ERROR_HEIHGT_MAP_TOO_BIG, {});
ContextShowError(STR_HEIGHT_MAP_ERROR, STR_ERROR_HEIGHT_MAP_TOO_BIG, {});
}
// Allocate memory for the height map values, one byte pixel
@@ -743,7 +784,7 @@ bool MapGenLoadHeightmap(const utf8* path)
/**
* Frees the memory used to store the selected height map
*/
void MapGenUnloadHeightmap()
void MapGenUnloadHeightmapImage()
{
_heightMapData.mono_bitmap.clear();
_heightMapData.width = 0;
@@ -796,10 +837,10 @@ static void MapGenSmoothHeightmap(std::vector<uint8_t>& src, int32_t strength)
}
}
void MapGenGenerateFromHeightmap(MapGenSettings* settings)
static void MapGenGenerateFromHeightmapImage(MapGenSettings* settings)
{
Guard::Assert(!_heightMapData.mono_bitmap.empty(), "No height map loaded");
Guard::Assert(settings->simplex_high != settings->simplex_low, "Low and high setting cannot be the same");
Guard::Assert(settings->heightmapHigh != settings->heightmapLow, "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;
@@ -840,10 +881,13 @@ void MapGenGenerateFromHeightmap(MapGenSettings* settings)
}
Guard::Assert(maxValue > minValue, "Input range is invalid");
Guard::Assert(settings->simplex_high > settings->simplex_low, "Output range is invalid");
Guard::Assert(settings->heightmapHigh > settings->heightmapLow, "Output range is invalid");
const auto surfaceTextureId = MapGenSurfaceTextureId(settings);
const auto edgeTextureId = MapGenEdgeTextureId(settings, surfaceTextureId);
const uint8_t rangeIn = maxValue - minValue;
const uint8_t rangeOut = settings->simplex_high - settings->simplex_low;
const uint8_t rangeOut = (settings->heightmapHigh - settings->heightmapLow) * 2;
for (uint32_t y = 0; y < _heightMapData.height; y++)
{
@@ -857,7 +901,8 @@ void MapGenGenerateFromHeightmap(MapGenSettings* settings)
// Read value from bitmap, and convert its range
uint8_t value = dest[x + y * _heightMapData.width];
value = static_cast<uint8_t>(static_cast<float>(value - minValue) / rangeIn * rangeOut) + settings->simplex_low;
value = static_cast<uint8_t>(static_cast<float>(value - minValue) / rangeIn * rangeOut)
+ (settings->heightmapLow * 2);
surfaceElement->BaseHeight = value;
// Floor to even number
@@ -865,16 +910,20 @@ void MapGenGenerateFromHeightmap(MapGenSettings* settings)
surfaceElement->BaseHeight *= 2;
surfaceElement->ClearanceHeight = surfaceElement->BaseHeight;
// Set textures
surfaceElement->SetSurfaceObjectIndex(surfaceTextureId);
surfaceElement->SetEdgeObjectIndex(edgeTextureId);
// Set water level
if (surfaceElement->BaseHeight < settings->water_level)
if (surfaceElement->BaseHeight < settings->waterLevel)
{
surfaceElement->SetWaterHeight(settings->water_level * kCoordsZStep);
surfaceElement->SetWaterHeight(settings->waterLevel * kCoordsZStep);
}
}
}
// Smooth map
if (settings->smooth)
// Smooth tile edges
if (settings->smoothTileEdges)
{
// Keep smoothing the entire map until no tiles are changed anymore
while (true)

View File

@@ -12,33 +12,42 @@
#include "../core/StringTypes.h"
#include "Location.hpp"
enum class MapGenAlgorithm : uint8_t
{
blank,
simplexNoise,
heightmapImage,
};
struct MapGenSettings
{
// Base
TileCoordsXY mapSize;
int32_t height;
int32_t water_level;
int32_t floor;
int32_t wall;
MapGenAlgorithm algorithm = MapGenAlgorithm::blank;
TileCoordsXY mapSize{ 150, 150 };
int32_t waterLevel = 6;
int32_t landTexture = 0;
int32_t edgeTexture = 0;
int32_t heightmapLow = 14;
int32_t heightmapHigh = 60;
bool smoothTileEdges = true;
// Features (e.g. tree, rivers, lakes etc.)
int32_t trees;
bool trees = true;
int32_t treeToLandRatio = 25;
int32_t minTreeAltitude = 10;
int32_t maxTreeAltitude = 50;
bool beaches = true;
// Simplex Noise Parameters
int32_t simplex_low;
int32_t simplex_high;
float simplex_base_freq;
int32_t simplex_octaves;
int32_t simplex_base_freq = 175;
int32_t simplex_octaves = 6;
// Height map settings
bool smooth;
bool smooth_height_map;
uint32_t smooth_strength;
bool normalize_height;
bool smooth_height_map = true;
uint32_t smooth_strength = 1;
bool normalize_height = true;
};
void MapGenGenerateBlank(MapGenSettings* settings);
void MapGenGenerate(MapGenSettings* settings);
bool MapGenLoadHeightmap(const utf8* path);
void MapGenUnloadHeightmap();
void MapGenGenerateFromHeightmap(MapGenSettings* settings);
bool MapGenLoadHeightmapImage(const utf8* path);
void MapGenUnloadHeightmapImage();