mirror of
https://github.com/OpenRCT2/OpenRCT2
synced 2026-01-15 11:03:00 +01:00
Loading height map into memory
This commit is contained in:
committed by
Michał Janiszewski
parent
f93b7f761b
commit
03bfc68250
@@ -227,6 +227,7 @@ static rct_widget *PageWidgets[WINDOW_MAPGEN_PAGE_COUNT] = {
|
||||
|
||||
#pragma region Events
|
||||
|
||||
static void window_mapgen_shared_close(rct_window *w);
|
||||
static void window_mapgen_shared_mouseup(rct_window *w, sint32 widgetIndex);
|
||||
|
||||
static void window_mapgen_base_mouseup(rct_window *w, sint32 widgetIndex);
|
||||
@@ -256,7 +257,7 @@ static void window_mapgen_heightmap_invalidate(rct_window *w);
|
||||
static void window_mapgen_heightmap_paint(rct_window *w, rct_drawpixelinfo *dpi);
|
||||
|
||||
static rct_window_event_list BaseEvents = {
|
||||
NULL,
|
||||
window_mapgen_shared_close,
|
||||
window_mapgen_base_mouseup,
|
||||
NULL,
|
||||
window_mapgen_base_mousedown,
|
||||
@@ -287,7 +288,7 @@ static rct_window_event_list BaseEvents = {
|
||||
};
|
||||
|
||||
static rct_window_event_list RandomEvents = {
|
||||
NULL,
|
||||
window_mapgen_shared_close,
|
||||
window_mapgen_random_mouseup,
|
||||
NULL,
|
||||
window_mapgen_random_mousedown,
|
||||
@@ -318,7 +319,7 @@ static rct_window_event_list RandomEvents = {
|
||||
};
|
||||
|
||||
static rct_window_event_list SimplexEvents = {
|
||||
NULL,
|
||||
window_mapgen_shared_close,
|
||||
window_mapgen_simplex_mouseup,
|
||||
NULL,
|
||||
window_mapgen_simplex_mousedown,
|
||||
@@ -349,7 +350,7 @@ static rct_window_event_list SimplexEvents = {
|
||||
};
|
||||
|
||||
static rct_window_event_list HeightmapEvents = {
|
||||
NULL,
|
||||
window_mapgen_shared_close,
|
||||
window_mapgen_heightmap_mouseup,
|
||||
NULL,
|
||||
window_mapgen_heightmap_mousedown,
|
||||
@@ -598,6 +599,11 @@ rct_window *window_mapgen_open()
|
||||
return w;
|
||||
}
|
||||
|
||||
static void window_mapgen_shared_close(rct_window *w)
|
||||
{
|
||||
mapgen_unload_heightmap();
|
||||
}
|
||||
|
||||
static void window_mapgen_shared_mouseup(rct_window *w, sint32 widgetIndex)
|
||||
{
|
||||
switch (widgetIndex)
|
||||
@@ -1194,10 +1200,13 @@ static void window_mapgen_heightmap_generate_map(rct_window *w)
|
||||
|
||||
static void window_mapgen_heightmap_loadsave_callback(sint32 result, const utf8 *path)
|
||||
{
|
||||
if (result == MODAL_RESULT_OK)
|
||||
{
|
||||
strcpy(heightmap_path, path);
|
||||
if (result == MODAL_RESULT_OK) {
|
||||
if (!mapgen_load_heightmap(path)) {
|
||||
// TODO: Display error popup
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO #refactor: Window reference shouldn't be needed
|
||||
rct_window *const w = window_find_by_class(WC_MAPGEN);
|
||||
window_mapgen_heightmap_generate_map(w);
|
||||
}
|
||||
|
||||
@@ -26,9 +26,20 @@
|
||||
#include "mapgen.h"
|
||||
#include "scenery.h"
|
||||
|
||||
#pragma region Random objects
|
||||
#pragma region Height map struct
|
||||
|
||||
char heightmap_path[260] = "";
|
||||
static struct {
|
||||
uint32 width, height;
|
||||
uint8 *mono_bitmap;
|
||||
} _heightMapData = {
|
||||
.width = 0,
|
||||
.height = 0,
|
||||
.mono_bitmap = NULL
|
||||
};
|
||||
|
||||
#pragma endregion Height map struct
|
||||
|
||||
#pragma region Random objects
|
||||
|
||||
const char *GrassTrees[] = {
|
||||
// Dark
|
||||
@@ -771,32 +782,97 @@ static void mapgen_simplex(mapgen_settings *settings)
|
||||
|
||||
#pragma region Heightmap
|
||||
|
||||
/**
|
||||
* Applies box blur to the surface N times
|
||||
*/
|
||||
static void mapgen_smooth_heightmap(uint8 *pixels, const sint32 width, const sint32 height, const sint32 numChannels, const size_t pitch, sint32 strength)
|
||||
bool mapgen_load_heightmap(const utf8 *path)
|
||||
{
|
||||
// Create buffer to store one channel
|
||||
uint8 *dest = (uint8*)malloc(width * height);
|
||||
const char* extension = path_get_extension(path);
|
||||
uint8 *pixels;
|
||||
size_t pitch;
|
||||
uint32 numChannels;
|
||||
|
||||
// Overwrite the red channel with the average of the RGB channels
|
||||
for (sint32 y = 0; y < height; y++)
|
||||
if (strcicmp(extension, ".png") == 0) {
|
||||
if (!image_io_png_read(&pixels, &_heightMapData.width, &_heightMapData.height, path)) {
|
||||
printf("Error reading PNG\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
numChannels = 4;
|
||||
pitch = _heightMapData.width * numChannels;
|
||||
}
|
||||
else if (strcicmp(extension, ".bmp") == 0) {
|
||||
SDL_Surface *bitmap = SDL_LoadBMP(path);
|
||||
if (bitmap == NULL) {
|
||||
printf("Failed to load bitmap: %s\n", SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
|
||||
_heightMapData.width = bitmap->w;
|
||||
_heightMapData.height = bitmap->h;
|
||||
numChannels = bitmap->format->BytesPerPixel;
|
||||
pitch = bitmap->pitch;
|
||||
|
||||
// Copy pixels over, then discard the surface
|
||||
SDL_LockSurface(bitmap);
|
||||
pixels = malloc(_heightMapData.height * bitmap->pitch);
|
||||
memcpy(pixels, bitmap->pixels, _heightMapData.height * bitmap->pitch);
|
||||
SDL_UnlockSurface(bitmap);
|
||||
SDL_FreeSurface(bitmap);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (sint32 x = 0; x < width; x++)
|
||||
openrct2_assert(false, "A file with an invalid file extension was selected.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_heightMapData.width != _heightMapData.height) {
|
||||
log_warning("Width and height need to be the same");
|
||||
free(pixels);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Allocate memory for the height map values, one byte pixel
|
||||
_heightMapData.mono_bitmap = (uint8*)malloc(_heightMapData.width * _heightMapData.height);
|
||||
|
||||
// Copy average RGB value to mono bitmap
|
||||
for (uint32 x = 0; x < _heightMapData.width; x++)
|
||||
{
|
||||
for (uint32 y = 0; y < _heightMapData.height; y++)
|
||||
{
|
||||
const uint8 red = pixels[x * numChannels + y * pitch];
|
||||
const uint8 green = pixels[x * numChannels + y * pitch + 1];
|
||||
const uint8 blue = pixels[x * numChannels + y * pitch + 2];
|
||||
pixels[x * numChannels + y * pitch] = (red + green + blue) / 3;
|
||||
_heightMapData.mono_bitmap[x + y * _heightMapData.width] = (red + green + blue) / 3;
|
||||
}
|
||||
}
|
||||
|
||||
free(pixels);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Frees the memory used to store the selected height map
|
||||
*/
|
||||
void mapgen_unload_heightmap()
|
||||
{
|
||||
free(_heightMapData.mono_bitmap);
|
||||
_heightMapData.mono_bitmap = NULL;
|
||||
_heightMapData.width = 0;
|
||||
_heightMapData.height = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies box blur to the surface N times
|
||||
*/
|
||||
static void mapgen_smooth_heightmap(uint8 *src, sint32 strength)
|
||||
{
|
||||
// Create buffer to store one channel
|
||||
uint8 *dest = (uint8*)malloc(_heightMapData.width * _heightMapData.height);
|
||||
|
||||
for (sint32 i = 0; i < strength; i++)
|
||||
{
|
||||
// Calculate box blur value to all pixels of the surface
|
||||
for (sint32 y = 0; y < height; y++)
|
||||
for (uint32 y = 0; y < _heightMapData.height; y++)
|
||||
{
|
||||
for (sint32 x = 0; x < width; x++)
|
||||
for (uint32 x = 0; x < _heightMapData.width; x++)
|
||||
{
|
||||
uint32 heightSum = 0;
|
||||
|
||||
@@ -807,23 +883,23 @@ static void mapgen_smooth_heightmap(uint8 *pixels, const sint32 width, const sin
|
||||
{
|
||||
// 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 += pixels[readX * numChannels + readY * pitch];
|
||||
const sint32 readX = clamp((sint32)x + offsetX, 0, (sint32)_heightMapData.width - 1);
|
||||
const sint32 readY = clamp((sint32)y + offsetY, 0, (sint32)_heightMapData.height - 1);
|
||||
heightSum += src[readX + readY * _heightMapData.width];
|
||||
}
|
||||
}
|
||||
|
||||
// Take average
|
||||
dest[x + y * width] = heightSum / 9;
|
||||
dest[x + y * _heightMapData.width] = heightSum / 9;
|
||||
}
|
||||
}
|
||||
|
||||
// Now apply the blur to the source pixels
|
||||
for (sint32 y = 0; y < height; y++)
|
||||
for (uint32 y = 0; y < _heightMapData.height; y++)
|
||||
{
|
||||
for (sint32 x = 0; x < width; x++)
|
||||
for (uint32 x = 0; x < _heightMapData.width; x++)
|
||||
{
|
||||
pixels[x * numChannels + y * pitch] = dest[x + y * width];
|
||||
src[x + y * _heightMapData.width] = dest[x + y * _heightMapData.width];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -833,72 +909,34 @@ static void mapgen_smooth_heightmap(uint8 *pixels, const sint32 width, const sin
|
||||
|
||||
void mapgen_generate_from_heightmap(mapgen_settings *settings)
|
||||
{
|
||||
openrct2_assert(_heightMapData.width == _heightMapData.height, "Invallid height map size");
|
||||
openrct2_assert(_heightMapData.mono_bitmap != NULL, "No heightmap loaded");
|
||||
openrct2_assert(settings->simplex_high != settings->simplex_low, "Low and high setting cannot be the same");
|
||||
|
||||
// TODO: Move all loading to a callback
|
||||
const char* extension = path_get_extension(heightmap_path);
|
||||
sint32 width;
|
||||
sint32 height;
|
||||
uint8 numChannels;
|
||||
uint8 *pixels;
|
||||
size_t pitch;
|
||||
// Make a copy of the original heightmap that we can edit
|
||||
uint8 *dest = (uint8*)malloc(_heightMapData.width * _heightMapData.height);
|
||||
memcpy(dest, _heightMapData.mono_bitmap, _heightMapData.width * _heightMapData.width);
|
||||
|
||||
if (strcicmp(extension, ".png") == 0) {
|
||||
uint32 w, h;
|
||||
if (!image_io_png_read(&pixels, &w, &h, heightmap_path)) {
|
||||
printf("Error reading PNG\n");
|
||||
return;
|
||||
}
|
||||
|
||||
width = w;
|
||||
height = h;
|
||||
numChannels = 4;
|
||||
pitch = width * numChannels;
|
||||
} else if (strcicmp(extension, ".bmp") == 0) {
|
||||
SDL_Surface *bitmap = SDL_LoadBMP(heightmap_path);
|
||||
if (bitmap == NULL)
|
||||
{
|
||||
printf("Failed to load bitmap: %s\n", SDL_GetError());
|
||||
return;
|
||||
}
|
||||
|
||||
width = bitmap->w;
|
||||
height = bitmap->h;
|
||||
numChannels = bitmap->format->BytesPerPixel;
|
||||
pitch = bitmap->pitch;
|
||||
|
||||
// Copy pixels over to our own array
|
||||
SDL_LockSurface(bitmap);
|
||||
pixels = malloc(height * bitmap->pitch);
|
||||
memcpy(pixels, bitmap->pixels, width * height * numChannels);
|
||||
SDL_UnlockSurface(bitmap);
|
||||
SDL_FreeSurface(bitmap);
|
||||
} else {
|
||||
openrct2_assert(false, "A file with an invalid file extension was selected.");
|
||||
return;
|
||||
}
|
||||
|
||||
map_init(width + 2); // + 2 for the black tiles around the map
|
||||
|
||||
uint8 maxValue = 255;
|
||||
uint8 minValue = 0;
|
||||
map_init(_heightMapData.width + 2); // + 2 for the black tiles around the map
|
||||
|
||||
if (settings->smooth_height_map)
|
||||
{
|
||||
// Smooth height map
|
||||
mapgen_smooth_heightmap(pixels, width, height, numChannels, pitch, settings->smooth_strength);
|
||||
mapgen_smooth_heightmap(dest, settings->smooth_strength);
|
||||
}
|
||||
|
||||
uint8 maxValue = 255;
|
||||
uint8 minValue = 0;
|
||||
|
||||
if (settings->normalize_height)
|
||||
{
|
||||
// Get highest and lowest pixel value
|
||||
maxValue = 0;
|
||||
minValue = 0xff;
|
||||
for (sint32 y = 0; y < height; y++)
|
||||
for (uint32 y = 0; y < _heightMapData.height; y++)
|
||||
{
|
||||
for (sint32 x = 0; x < width; x++)
|
||||
for (uint32 x = 0; x < _heightMapData.width; x++)
|
||||
{
|
||||
uint8 value = pixels[x * numChannels + y * pitch];
|
||||
uint8 value = dest[x + y * _heightMapData.width];
|
||||
maxValue = max(maxValue, value);
|
||||
minValue = min(minValue, value);
|
||||
}
|
||||
@@ -918,15 +956,15 @@ void mapgen_generate_from_heightmap(mapgen_settings *settings)
|
||||
const uint8 rangeIn = maxValue - minValue;
|
||||
const uint8 rangeOut = settings->simplex_high - settings->simplex_low;
|
||||
|
||||
for (sint32 y = 0; y < height; y++)
|
||||
for (uint32 y = 0; y < _heightMapData.height; y++)
|
||||
{
|
||||
for (sint32 x = 0; x < width; x++)
|
||||
for (uint32 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.
|
||||
rct_map_element *const surfaceElement = map_get_surface_element_at(y + 1, x + 1);
|
||||
|
||||
// Read value from bitmap, and convert its range
|
||||
uint8 value = pixels[x * numChannels + y * pitch];
|
||||
uint8 value = dest[x + y * _heightMapData.width];
|
||||
value = (uint8)((float)(value - minValue) / rangeIn * rangeOut) + settings->simplex_low;
|
||||
surfaceElement->base_height = value;
|
||||
|
||||
@@ -949,10 +987,10 @@ void mapgen_generate_from_heightmap(mapgen_settings *settings)
|
||||
// Keep smoothing the entire map until no tiles are changed anymore
|
||||
while (true)
|
||||
{
|
||||
sint32 numTilesChanged = 0;
|
||||
for (sint32 y = 1; y <= height; y++)
|
||||
uint32 numTilesChanged = 0;
|
||||
for (uint32 y = 1; y <= _heightMapData.height; y++)
|
||||
{
|
||||
for (sint32 x = 1; x <= width; x++)
|
||||
for (uint32 x = 1; x <= _heightMapData.width; x++)
|
||||
{
|
||||
numTilesChanged += tile_smooth(x, y);
|
||||
}
|
||||
@@ -963,7 +1001,8 @@ void mapgen_generate_from_heightmap(mapgen_settings *settings)
|
||||
}
|
||||
}
|
||||
|
||||
free(pixels);
|
||||
// Clean up
|
||||
free(dest);
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
#ifndef _MAPGEN_H_
|
||||
#define _MAPGEN_H_
|
||||
|
||||
#include "../common.h"
|
||||
|
||||
typedef struct mapgen_settings {
|
||||
// Base
|
||||
sint32 mapSize;
|
||||
@@ -41,11 +43,11 @@ typedef struct mapgen_settings {
|
||||
bool normalize_height;
|
||||
} mapgen_settings;
|
||||
|
||||
extern char heightmap_path[260];
|
||||
|
||||
void mapgen_generate_blank(mapgen_settings *settings);
|
||||
void mapgen_generate(mapgen_settings *settings);
|
||||
void mapgen_generate_custom_simplex(mapgen_settings *settings);
|
||||
bool mapgen_load_heightmap(const utf8 *path);
|
||||
void mapgen_unload_heightmap();
|
||||
void mapgen_generate_from_heightmap(mapgen_settings *settings);
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user