OpenTTD Source 20251019-master-g9f7f314f81
heightmap.cpp
Go to the documentation of this file.
1/*
2 * This file is part of OpenTTD.
3 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
6 */
7
10#include "stdafx.h"
11#include "heightmap.h"
12#include "landscape.h"
13#include "clear_map.h"
14#include "strings_func.h"
15#include "void_map.h"
16#include "error.h"
17#include "saveload/saveload.h"
18#include "bmp.h"
19#include "gfx_func.h"
20#include "fios.h"
21#include "fileio_func.h"
22
23#include "table/strings.h"
24
25#ifdef WITH_PNG
26#include <png.h>
27#endif /* WITH_PNG */
28
29#include "safeguards.h"
30
37
38/*
39 * Maximum size in pixels of the heightmap image.
40 */
41static const uint MAX_HEIGHTMAP_SIZE_PIXELS = 256 << 20; // ~256 million
42/*
43 * When loading a PNG or BMP the 24 bpp variant requires at least 4 bytes per pixel
44 * of memory to load the data. Make sure the "reasonable" limit is well within the
45 * maximum amount of memory allocatable on 32 bit platforms.
46 */
47static_assert(MAX_HEIGHTMAP_SIZE_PIXELS < UINT32_MAX / 8);
48
58static inline bool IsValidHeightmapDimension(size_t width, size_t height)
59{
60 return (uint64_t)width * height <= MAX_HEIGHTMAP_SIZE_PIXELS &&
61 width > 0 && width <= MAX_HEIGHTMAP_SIDE_LENGTH_IN_PIXELS &&
62 height > 0 && height <= MAX_HEIGHTMAP_SIDE_LENGTH_IN_PIXELS;
63}
64
69static inline uint8_t RGBToGreyscale(uint8_t red, uint8_t green, uint8_t blue)
70{
71 /* To avoid doubles and stuff, multiply it with a total of 65536 (16bits), then
72 * divide by it to normalize the value to a byte again. */
73 return ((red * 19595) + (green * 38470) + (blue * 7471)) / 65536;
74}
75
76
77#ifdef WITH_PNG
78
82static void ReadHeightmapPNGImageData(std::span<uint8_t> map, png_structp png_ptr, png_infop info_ptr)
83{
84 uint x, y;
85 uint8_t gray_palette[256];
86 png_bytep *row_pointers = nullptr;
87 bool has_palette = png_get_color_type(png_ptr, info_ptr) == PNG_COLOR_TYPE_PALETTE;
88 uint channels = png_get_channels(png_ptr, info_ptr);
89
90 /* Get palette and convert it to greyscale */
91 if (has_palette) {
92 int i;
93 int palette_size;
94 png_color *palette;
95 bool all_gray = true;
96
97 png_get_PLTE(png_ptr, info_ptr, &palette, &palette_size);
98 for (i = 0; i < palette_size && (palette_size != 16 || all_gray); i++) {
99 all_gray &= palette[i].red == palette[i].green && palette[i].red == palette[i].blue;
100 gray_palette[i] = RGBToGreyscale(palette[i].red, palette[i].green, palette[i].blue);
101 }
102
109 if (palette_size == 16 && !all_gray) {
110 for (i = 0; i < palette_size; i++) {
111 gray_palette[i] = 256 * i / palette_size;
112 }
113 }
114 }
115
116 row_pointers = png_get_rows(png_ptr, info_ptr);
117
118 /* Read the raw image data and convert in 8-bit greyscale */
119 for (x = 0; x < png_get_image_width(png_ptr, info_ptr); x++) {
120 for (y = 0; y < png_get_image_height(png_ptr, info_ptr); y++) {
121 uint8_t *pixel = &map[y * png_get_image_width(png_ptr, info_ptr) + x];
122 uint x_offset = x * channels;
123
124 if (has_palette) {
125 *pixel = gray_palette[row_pointers[y][x_offset]];
126 } else if (channels == 3) {
127 *pixel = RGBToGreyscale(row_pointers[y][x_offset + 0],
128 row_pointers[y][x_offset + 1], row_pointers[y][x_offset + 2]);
129 } else {
130 *pixel = row_pointers[y][x_offset];
131 }
132 }
133 }
134}
135
141static bool ReadHeightmapPNG(std::string_view filename, uint *x, uint *y, std::vector<uint8_t> *map)
142{
143 png_structp png_ptr = nullptr;
144 png_infop info_ptr = nullptr;
145
146 auto fp = FioFOpenFile(filename, "rb", HEIGHTMAP_DIR);
147 if (!fp.has_value()) {
148 ShowErrorMessage(GetEncodedString(STR_ERROR_PNGMAP), GetEncodedString(STR_ERROR_PNGMAP_FILE_NOT_FOUND), WL_ERROR);
149 return false;
150 }
151
152 png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
153 if (png_ptr == nullptr) {
154 ShowErrorMessage(GetEncodedString(STR_ERROR_PNGMAP), GetEncodedString(STR_ERROR_PNGMAP_MISC), WL_ERROR);
155 return false;
156 }
157
158 info_ptr = png_create_info_struct(png_ptr);
159 if (info_ptr == nullptr || setjmp(png_jmpbuf(png_ptr))) {
160 ShowErrorMessage(GetEncodedString(STR_ERROR_PNGMAP), GetEncodedString(STR_ERROR_PNGMAP_MISC), WL_ERROR);
161 png_destroy_read_struct(&png_ptr, &info_ptr, nullptr);
162 return false;
163 }
164
165 png_init_io(png_ptr, *fp);
166
167 /* Allocate memory and read image, without alpha or 16-bit samples
168 * (result is either 8-bit indexed/greyscale or 24-bit RGB) */
169 png_set_packing(png_ptr);
170 png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_PACKING | PNG_TRANSFORM_STRIP_ALPHA | PNG_TRANSFORM_STRIP_16, nullptr);
171
172 /* Maps of wrong colour-depth are not used.
173 * (this should have been taken care of by stripping alpha and 16-bit samples on load) */
174 if ((png_get_channels(png_ptr, info_ptr) != 1) && (png_get_channels(png_ptr, info_ptr) != 3) && (png_get_bit_depth(png_ptr, info_ptr) != 8)) {
175 ShowErrorMessage(GetEncodedString(STR_ERROR_PNGMAP), GetEncodedString(STR_ERROR_PNGMAP_IMAGE_TYPE), WL_ERROR);
176 png_destroy_read_struct(&png_ptr, &info_ptr, nullptr);
177 return false;
178 }
179
180 uint width = png_get_image_width(png_ptr, info_ptr);
181 uint height = png_get_image_height(png_ptr, info_ptr);
182
183 if (!IsValidHeightmapDimension(width, height)) {
184 ShowErrorMessage(GetEncodedString(STR_ERROR_PNGMAP), GetEncodedString(STR_ERROR_HEIGHTMAP_TOO_LARGE), WL_ERROR);
185 png_destroy_read_struct(&png_ptr, &info_ptr, nullptr);
186 return false;
187 }
188
189 if (map != nullptr) {
190 map->resize(static_cast<size_t>(width) * height);
191 ReadHeightmapPNGImageData(*map, png_ptr, info_ptr);
192 }
193
194 *x = width;
195 *y = height;
196
197 png_destroy_read_struct(&png_ptr, &info_ptr, nullptr);
198 return true;
199}
200
201#endif /* WITH_PNG */
202
203
207static void ReadHeightmapBMPImageData(std::span<uint8_t> map, const BmpInfo &info, const BmpData &data)
208{
209 uint8_t gray_palette[256];
210
211 if (!data.palette.empty()) {
212 bool all_gray = true;
213
214 if (info.palette_size != 2) {
215 for (uint i = 0; i < info.palette_size && (info.palette_size != 16 || all_gray); i++) {
216 all_gray &= data.palette[i].r == data.palette[i].g && data.palette[i].r == data.palette[i].b;
217 gray_palette[i] = RGBToGreyscale(data.palette[i].r, data.palette[i].g, data.palette[i].b);
218 }
219
226 if (info.palette_size == 16 && !all_gray) {
227 for (uint i = 0; i < info.palette_size; i++) {
228 gray_palette[i] = 256 * i / info.palette_size;
229 }
230 }
231 } else {
236 gray_palette[0] = 0;
237 gray_palette[1] = 16;
238 }
239 }
240
241 /* Read the raw image data and convert in 8-bit greyscale */
242 for (uint y = 0; y < info.height; y++) {
243 uint8_t *pixel = &map[y * static_cast<size_t>(info.width)];
244 const uint8_t *bitmap = &data.bitmap[y * static_cast<size_t>(info.width) * (info.bpp == 24 ? 3 : 1)];
245
246 for (uint x = 0; x < info.width; x++) {
247 if (info.bpp != 24) {
248 *pixel++ = gray_palette[*bitmap++];
249 } else {
250 *pixel++ = RGBToGreyscale(*bitmap, *(bitmap + 1), *(bitmap + 2));
251 bitmap += 3;
252 }
253 }
254 }
255}
256
262static bool ReadHeightmapBMP(std::string_view filename, uint *x, uint *y, std::vector<uint8_t> *map)
263{
264 auto f = FioFOpenFile(filename, "rb", HEIGHTMAP_DIR);
265 if (!f.has_value()) {
266 ShowErrorMessage(GetEncodedString(STR_ERROR_BMPMAP), GetEncodedString(STR_ERROR_PNGMAP_FILE_NOT_FOUND), WL_ERROR);
267 return false;
268 }
269
270 RandomAccessFile file(filename, HEIGHTMAP_DIR);
271 BmpInfo info{};
272 BmpData data{};
273
274 if (!BmpReadHeader(file, info, data)) {
275 ShowErrorMessage(GetEncodedString(STR_ERROR_BMPMAP), GetEncodedString(STR_ERROR_BMPMAP_IMAGE_TYPE), WL_ERROR);
276 return false;
277 }
278
279 if (!IsValidHeightmapDimension(info.width, info.height)) {
280 ShowErrorMessage(GetEncodedString(STR_ERROR_BMPMAP), GetEncodedString(STR_ERROR_HEIGHTMAP_TOO_LARGE), WL_ERROR);
281 return false;
282 }
283
284 if (map != nullptr) {
285 if (!BmpReadBitmap(file, info, data)) {
286 ShowErrorMessage(GetEncodedString(STR_ERROR_BMPMAP), GetEncodedString(STR_ERROR_BMPMAP_IMAGE_TYPE), WL_ERROR);
287 return false;
288 }
289
290 map->resize(static_cast<size_t>(info.width) * info.height);
291 ReadHeightmapBMPImageData(*map, info, data);
292 }
293
294 *x = info.width;
295 *y = info.height;
296
297 return true;
298}
299
307static void GreyscaleToMapHeights(uint img_width, uint img_height, std::span<const uint8_t> map)
308{
309 /* Defines the detail of the aspect ratio (to avoid doubles) */
310 const uint num_div = 16384;
311 /* Ensure multiplication with num_div does not cause overflows. */
312 static_assert(num_div <= std::numeric_limits<uint>::max() / MAX_HEIGHTMAP_SIDE_LENGTH_IN_PIXELS);
313
314 uint width, height;
315 uint row, col;
316 uint row_pad = 0, col_pad = 0;
317 uint img_scale;
318 uint img_row, img_col;
319 TileIndex tile;
320
321 /* Get map size and calculate scale and padding values */
323 default: NOT_REACHED();
325 width = Map::SizeX();
326 height = Map::SizeY();
327 break;
328 case HM_CLOCKWISE:
329 width = Map::SizeY();
330 height = Map::SizeX();
331 break;
332 }
333
334 if ((img_width * num_div) / img_height > ((width * num_div) / height)) {
335 /* Image is wider than map - center vertically */
336 img_scale = (width * num_div) / img_width;
337 row_pad = (1 + height - ((img_height * img_scale) / num_div)) / 2;
338 } else {
339 /* Image is taller than map - center horizontally */
340 img_scale = (height * num_div) / img_height;
341 col_pad = (1 + width - ((img_width * img_scale) / num_div)) / 2;
342 }
343
345 for (uint x = 0; x < Map::SizeX(); x++) MakeVoid(TileXY(x, 0));
346 for (uint y = 0; y < Map::SizeY(); y++) MakeVoid(TileXY(0, y));
347 }
348
349 /* Form the landscape */
350 for (row = 0; row < height; row++) {
351 for (col = 0; col < width; col++) {
353 default: NOT_REACHED();
354 case HM_COUNTER_CLOCKWISE: tile = TileXY(col, row); break;
355 case HM_CLOCKWISE: tile = TileXY(row, col); break;
356 }
357
358 /* Check if current tile is within the 1-pixel map edge or padding regions */
360 (row < row_pad) || (row >= (height - row_pad - (_settings_game.construction.freeform_edges ? 0 : 1))) ||
361 (col < col_pad) || (col >= (width - col_pad - (_settings_game.construction.freeform_edges ? 0 : 1)))) {
362 SetTileHeight(tile, 0);
363 } else {
364 /* Use nearest neighbour resizing to scale map data.
365 * We rotate the map 45 degrees (counter)clockwise */
366 img_row = (((row - row_pad) * num_div) / img_scale);
368 default: NOT_REACHED();
370 img_col = (((width - 1 - col - col_pad) * num_div) / img_scale);
371 break;
372 case HM_CLOCKWISE:
373 img_col = (((col - col_pad) * num_div) / img_scale);
374 break;
375 }
376
377 assert(img_row < img_height);
378 assert(img_col < img_width);
379
380 uint heightmap_height = map[img_row * img_width + img_col];
381
382 if (heightmap_height > 0) {
383 /* 0 is sea level.
384 * Other grey scales are scaled evenly to the available height levels > 0.
385 * (The coastline is independent from the number of height levels) */
386 heightmap_height = 1 + (heightmap_height - 1) * _settings_game.game_creation.heightmap_height / 255;
387 }
388
389 SetTileHeight(tile, heightmap_height);
390 }
391 /* Only clear the tiles within the map area. */
392 if (IsInnerTile(tile)) {
393 MakeClear(tile, CLEAR_GRASS, 3);
394 }
395 }
396 }
397}
398
404{
405 uint width, height;
406 int row, col;
407 uint8_t current_height;
408 uint8_t max_height = _settings_game.construction.map_height_limit;
409
410 /* Adjust height difference to maximum one horizontal/vertical change. */
411 width = Map::SizeX();
412 height = Map::SizeY();
413
414 /* Top and left edge */
415 for (row = 0; (uint)row < height; row++) {
416 for (col = 0; (uint)col < width; col++) {
417 current_height = MAX_TILE_HEIGHT;
418 if (col != 0) {
419 /* Find lowest tile; either the top or left one */
420 current_height = TileHeight(TileXY(col - 1, row)); // top edge
421 }
422 if (row != 0) {
423 if (TileHeight(TileXY(col, row - 1)) < current_height) {
424 current_height = TileHeight(TileXY(col, row - 1)); // left edge
425 }
426 }
427
428 /* Does the height differ more than one? */
429 TileIndex tile = TileXY(col, row);
430 if (TileHeight(tile) >= (uint)current_height + 2) {
431 /* Then change the height to be no more than one */
432 SetTileHeight(tile, current_height + 1);
433 /* Height was changed so now there's a chance, more likely at higher altitude, of the
434 * tile turning into rock. */
435 if (IsInnerTile(tile) && RandomRange(max_height) <= current_height) {
436 MakeClear(tile, CLEAR_ROCKS, 3);
437 }
438 }
439 }
440 }
441
442 /* Bottom and right edge */
443 for (row = height - 1; row >= 0; row--) {
444 for (col = width - 1; col >= 0; col--) {
445 current_height = MAX_TILE_HEIGHT;
446 if ((uint)col != width - 1) {
447 /* Find lowest tile; either the bottom and right one */
448 current_height = TileHeight(TileXY(col + 1, row)); // bottom edge
449 }
450
451 if ((uint)row != height - 1) {
452 if (TileHeight(TileXY(col, row + 1)) < current_height) {
453 current_height = TileHeight(TileXY(col, row + 1)); // right edge
454 }
455 }
456
457 /* Does the height differ more than one? */
458 TileIndex tile = TileXY(col, row);
459 if (TileHeight(tile) >= (uint)current_height + 2) {
460 /* Then change the height to be no more than one */
461 SetTileHeight(tile, current_height + 1);
462 /* Height was changed so now there's a chance, more likely at higher altitude, of the
463 * tile turning into rock. */
464 if (IsInnerTile(tile) && RandomRange(max_height) <= current_height) {
465 MakeClear(tile, CLEAR_ROCKS, 3);
466 }
467 }
468 }
469 }
470}
471
481static bool ReadHeightMap(DetailedFileType dft, std::string_view filename, uint *x, uint *y, std::vector<uint8_t> *map)
482{
483 switch (dft) {
484 default:
485 NOT_REACHED();
486
487#ifdef WITH_PNG
489 return ReadHeightmapPNG(filename, x, y, map);
490#endif /* WITH_PNG */
491
493 return ReadHeightmapBMP(filename, x, y, map);
494 }
495}
496
505bool GetHeightmapDimensions(DetailedFileType dft, std::string_view filename, uint *x, uint *y)
506{
507 return ReadHeightMap(dft, filename, x, y, nullptr);
508}
509
517bool LoadHeightmap(DetailedFileType dft, std::string_view filename)
518{
519 uint x, y;
520 std::vector<uint8_t> map;
521
522 if (!ReadHeightMap(dft, filename, &x, &y, &map)) {
523 return false;
524 }
525
526 GreyscaleToMapHeights(x, y, map);
527
528 FixSlopes();
529
530 /* If all map borders are water, we will draw infinite water. */
532
534
535 return true;
536}
537
542void FlatEmptyWorld(uint8_t tile_height)
543{
544 int edge_distance = _settings_game.construction.freeform_edges ? 0 : 2;
545 for (uint row = edge_distance; row < Map::SizeY() - edge_distance; row++) {
546 for (uint col = edge_distance; col < Map::SizeX() - edge_distance; col++) {
547 SetTileHeight(TileXY(col, row), tile_height);
548 }
549 }
550
551 FixSlopes();
553}
Read and write support for bmps.
A file from which bytes, words and double words are read in (potentially) a random order.
Map accessors for 'clear' tiles.
@ CLEAR_GRASS
0-3
Definition clear_map.h:20
@ CLEAR_ROCKS
3
Definition clear_map.h:22
void MakeClear(Tile t, ClearGround g, uint density)
Make a clear tile.
Definition clear_map.h:247
Functions related to errors.
@ WL_ERROR
Errors (eg. saving/loading failed)
Definition error.h:26
void ShowErrorMessage(EncodedString &&summary_msg, int x, int y, CommandCost &cc)
Display an error message in a window.
std::optional< FileHandle > FioFOpenFile(std::string_view filename, std::string_view mode, Subdirectory subdir, size_t *filesize)
Opens a OpenTTD file somewhere in a personal or global directory.
Definition fileio.cpp:242
Functions for Standard In/Out file operations.
DetailedFileType
Kinds of files in each AbstractFileType.
Definition fileio_type.h:28
@ DFT_HEIGHTMAP_BMP
BMP file.
Definition fileio_type.h:34
@ DFT_HEIGHTMAP_PNG
PNG file.
Definition fileio_type.h:35
@ HEIGHTMAP_DIR
Subdirectory of scenario for heightmaps.
Definition fileio_type.h:93
Declarations for savegames operations.
Functions related to the gfx engine.
void MarkWholeScreenDirty()
This function mark the whole screen as dirty.
Definition gfx.cpp:1547
static const uint MAX_HEIGHTMAP_SIDE_LENGTH_IN_PIXELS
Maximum number of pixels for one dimension of a heightmap image.
Definition heightmap.cpp:36
static bool ReadHeightmapBMP(std::string_view filename, uint *x, uint *y, std::vector< uint8_t > *map)
Reads the heightmap and/or size of the heightmap from a BMP file.
bool GetHeightmapDimensions(DetailedFileType dft, std::string_view filename, uint *x, uint *y)
Get the dimensions of a heightmap.
static bool ReadHeightmapPNG(std::string_view filename, uint *x, uint *y, std::vector< uint8_t > *map)
Reads the heightmap and/or size of the heightmap from a PNG file.
void FlatEmptyWorld(uint8_t tile_height)
Make an empty world where all tiles are of height 'tile_height'.
static void GreyscaleToMapHeights(uint img_width, uint img_height, std::span< const uint8_t > map)
Converts a given greyscale map to something that fits in OTTD map system and create a map of that dat...
static void ReadHeightmapPNGImageData(std::span< uint8_t > map, png_structp png_ptr, png_infop info_ptr)
The PNG Heightmap loader.
Definition heightmap.cpp:82
static bool ReadHeightMap(DetailedFileType dft, std::string_view filename, uint *x, uint *y, std::vector< uint8_t > *map)
Reads the heightmap with the correct file reader.
bool LoadHeightmap(DetailedFileType dft, std::string_view filename)
Load a heightmap from file and change the map in its current dimensions to a landscape representing t...
static uint8_t RGBToGreyscale(uint8_t red, uint8_t green, uint8_t blue)
Convert RGB colours to Greyscale using 29.9% Red, 58.7% Green, 11.4% Blue (average luminosity formula...
Definition heightmap.cpp:69
static bool IsValidHeightmapDimension(size_t width, size_t height)
Check whether the loaded dimension of the heightmap image are considered valid enough to attempt to l...
Definition heightmap.cpp:58
void FixSlopes()
This function takes care of the fact that land in OpenTTD can never differ more than 1 in height.
static void ReadHeightmapBMPImageData(std::span< uint8_t > map, const BmpInfo &info, const BmpData &data)
The BMP Heightmap loader.
Functions related to creating heightmaps from files.
@ HM_CLOCKWISE
Rotate the map clockwise 45 degrees.
Definition heightmap.h:21
@ HM_COUNTER_CLOCKWISE
Rotate the map counter clockwise 45 degrees.
Definition heightmap.h:20
bool IsMapSurroundedByWater()
Check if all tiles on the map edge should be considered water borders.
Functions related to OTTD's landscape.
uint DistanceFromEdge(TileIndex tile)
Param the minimum distance to an edge.
Definition map.cpp:202
static debug_inline TileIndex TileXY(uint x, uint y)
Returns the TileIndex of a coordinate.
Definition map_func.h:372
static const uint MAX_MAP_SIZE
Maximal map size = 4096.
Definition map_type.h:40
uint32_t RandomRange(uint32_t limit, const std::source_location location=std::source_location::current())
Pick a random number between 0 and limit - 1, inclusive.
A number of safeguards to prevent using unsafe methods.
Functions/types related to saving and loading games.
GameSettings _settings_game
Game settings of a running game or the scenario editor.
Definition settings.cpp:61
Definition of base types and functions in a cross-platform compatible way.
EncodedString GetEncodedString(StringID str)
Encode a string with no parameters into an encoded string.
Definition strings.cpp:90
Functions related to OTTD's strings.
Definition bmp.h:26
Definition bmp.h:16
uint32_t height
bitmap height
Definition bmp.h:19
uint32_t width
bitmap width
Definition bmp.h:18
uint32_t palette_size
number of colours in palette
Definition bmp.h:23
uint16_t bpp
bits per pixel
Definition bmp.h:21
bool freeform_edges
allow terraforming the tiles at the map edges
uint8_t map_height_limit
the maximum allowed heightlevel
uint8_t heightmap_rotation
rotation director for the heightmap
uint8_t heightmap_height
highest mountain for heightmap (towards what it scales)
ConstructionSettings construction
construction of things in-game
GameCreationSettings game_creation
settings used during the creation of a game (map)
static uint SizeY()
Get the size of the map along the Y.
Definition map_func.h:278
static debug_inline uint SizeX()
Get the size of the map along the X.
Definition map_func.h:269
bool IsInnerTile(Tile tile)
Check if a tile is within the map (not a border)
Definition tile_map.h:109
void SetTileHeight(Tile tile, uint height)
Sets the height of a tile.
Definition tile_map.h:57
static debug_inline uint TileHeight(Tile tile)
Returns the height of a tile.
Definition tile_map.h:29
static constexpr uint MAX_TILE_HEIGHT
Maximum allowed tile height.
Definition tile_type.h:24
Map accessors for void tiles.
void MakeVoid(Tile t)
Make a nice void tile ;)
Definition void_map.h:19