/** * The main new land generator using Perlin noise. Desert landscape is handled * different to all others to give a desert valley between two high mountains. * Clearly if a low height terrain (flat/very flat) is chosen, then the tropic * areas won't be high enough, and there will be very little tropic on the map. * Thus Tropic works best on Hilly or Mountainous. */ void GenerateTerrainPerlin() { if (!AllocHeightMap()) return; GenerateWorldSetAbortCallback(FreeHeightMap); HeightMapGenerate(); IncreaseGeneratingWorldProgress(GWP_LANDSCAPE); HeightMapNormalize(); IncreaseGeneratingWorldProgress(GWP_LANDSCAPE); /* First make sure the tiles at the north border are void tiles if needed. */ if (_settings_game.construction.freeform_edges) { for (uint x = 0; x < MapSizeX(); x++) MakeVoid(TileXY(x, 0)); for (uint y = 0; y < MapSizeY(); y++) MakeVoid(TileXY(0, y)); } int max_height = H2I(TGPGetMaxHeight()); /* Transfer height map into OTTD map */ for (int y = 0; y < _height_map.size_y; y++) { for (int x = 0; x < _height_map.size_x; x++) { TgenSetTileHeight(TileXY(x, y), Clamp(H2I(_height_map.height(x, y)), 0, max_height)); } } IncreaseGeneratingWorldProgress(GWP_LANDSCAPE); FreeHeightMap(); GenerateWorldSetAbortCallback(NULL); }
/** * Height map terraform post processing: * - water level adjusting * - coast Smoothing * - slope Smoothing * - height histogram redistribution by sine wave transform */ static void HeightMapNormalize() { int sea_level_setting = _settings_game.difficulty.quantity_sea_lakes; const amplitude_t water_percent = sea_level_setting != (int)CUSTOM_SEA_LEVEL_NUMBER_DIFFICULTY ? _water_percent[sea_level_setting] : _settings_game.game_creation.custom_sea_level * 1024 / 100; const height_t h_max_new = TGPGetMaxHeight(); const height_t roughness = 7 + 3 * _settings_game.game_creation.tgen_smoothness; HeightMapAdjustWaterLevel(water_percent, h_max_new); byte water_borders = _settings_game.construction.freeform_edges ? _settings_game.game_creation.water_borders : 0xF; if (water_borders == BORDERS_RANDOM) water_borders = GB(Random(), 0, 4); HeightMapCoastLines(water_borders); HeightMapSmoothSlopes(roughness); HeightMapSmoothCoasts(water_borders); HeightMapSmoothSlopes(roughness); HeightMapSineTransform(I2H(1), h_max_new); if (_settings_game.game_creation.variety > 0) { HeightMapCurves(_settings_game.game_creation.variety); } HeightMapSmoothSlopes(I2H(1)); }
/** * Get the amplitude associated with the currently selected * smoothness and maximum height level. * @param frequency The frequency to get the amplitudes for * @return The amplitudes to apply to the map. */ static amplitude_t GetAmplitude(int frequency) { /* Base noise amplitudes (multiplied by 1024) and indexed by "smoothness setting" and log2(frequency). */ static const amplitude_t amplitudes[][7] = { /* lowest frequency ...... highest (every corner) */ {16000, 5600, 1968, 688, 240, 16, 16}, ///< Very smooth {24000, 12800, 6400, 2700, 1024, 128, 16}, ///< Smooth {32000, 19200, 12800, 8000, 3200, 256, 64}, ///< Rough {48000, 24000, 19200, 16000, 8000, 512, 320}, ///< Very rough }; /* * Extrapolation factors for ranges before the table. * The extrapolation is needed to account for the higher map heights. They need larger * areas with a particular gradient so that we are able to create maps without too * many steep slopes up to the wanted height level. It's definitely not perfect since * it will bring larger rectangles with similar slopes which makes the rectangular * behaviour of TGP more noticeable. However, these height differentiations cannot * happen over much smaller areas; we basically double the "range" to give a similar * slope for every doubling of map height. */ static const double extrapolation_factors[] = { 3.3, 2.8, 2.3, 1.8 }; int smoothness = _settings_game.game_creation.tgen_smoothness; /* Get the table index, and return that value if possible. */ int index = frequency - MAX_TGP_FREQUENCIES + lengthof(amplitudes[smoothness]); amplitude_t amplitude = amplitudes[smoothness][max(0, index)]; if (index >= 0) return amplitude; /* We need to extrapolate the amplitude. */ double extrapolation_factor = extrapolation_factors[smoothness]; int height_range = I2H(16); do { amplitude = (amplitude_t)(extrapolation_factor * (double)amplitude); height_range <<= 1; index++; } while (index < 0); return Clamp((TGPGetMaxHeight() - height_range) / height_range, 0, 1) * amplitude; }
/** * Additional map variety is provided by applying different curve maps * to different parts of the map. A randomized low resolution grid contains * which curve map to use on each part of the make. This filtered non-linearly * to smooth out transitions between curves, so each tile could have between * 100% of one map applied or 25% of four maps. * * The curve maps define different land styles, i.e. lakes, low-lands, hills * and mountain ranges, although these are dependent on the landscape style * chosen as well. * * The level parameter dictates the resolution of the grid. A low resolution * grid will result in larger continuous areas of a land style, a higher * resolution grid splits the style into smaller areas. * @param level Rough indication of the size of the grid sections to style. Small level means large grid sections. */ static void HeightMapCurves(uint level) { height_t mh = TGPGetMaxHeight() - I2H(1); // height levels above sea level only /** Basically scale height X to height Y. Everything in between is interpolated. */ struct control_point_t { height_t x; ///< The height to scale from. height_t y; ///< The height to scale to. }; /* Scaled curve maps; value is in height_ts. */ #define F(fraction) ((height_t)(fraction * mh)) const control_point_t curve_map_1[] = { { F(0.0), F(0.0) }, { F(0.8), F(0.13) }, { F(1.0), F(0.4) } }; const control_point_t curve_map_2[] = { { F(0.0), F(0.0) }, { F(0.53), F(0.13) }, { F(0.8), F(0.27) }, { F(1.0), F(0.6) } }; const control_point_t curve_map_3[] = { { F(0.0), F(0.0) }, { F(0.53), F(0.27) }, { F(0.8), F(0.57) }, { F(1.0), F(0.8) } }; const control_point_t curve_map_4[] = { { F(0.0), F(0.0) }, { F(0.4), F(0.3) }, { F(0.7), F(0.8) }, { F(0.92), F(0.99) }, { F(1.0), F(0.99) } }; #undef F /** Helper structure to index the different curve maps. */ struct control_point_list_t { size_t length; ///< The length of the curve map. const control_point_t *list; ///< The actual curve map. }; const control_point_list_t curve_maps[] = { { lengthof(curve_map_1), curve_map_1 }, { lengthof(curve_map_2), curve_map_2 }, { lengthof(curve_map_3), curve_map_3 }, { lengthof(curve_map_4), curve_map_4 }, }; height_t ht[lengthof(curve_maps)]; MemSetT(ht, 0, lengthof(ht)); /* Set up a grid to choose curve maps based on location; attempt to get a somewhat square grid */ float factor = sqrt((float)_height_map.size_x / (float)_height_map.size_y); uint sx = Clamp((int)(((1 << level) * factor) + 0.5), 1, 128); uint sy = Clamp((int)(((1 << level) / factor) + 0.5), 1, 128); byte *c = AllocaM(byte, sx * sy); for (uint i = 0; i < sx * sy; i++) { c[i] = Random() % lengthof(curve_maps); } /* Apply curves */ for (int x = 0; x < _height_map.size_x; x++) { /* Get our X grid positions and bi-linear ratio */ float fx = (float)(sx * x) / _height_map.size_x + 1.0f; uint x1 = (uint)fx; uint x2 = x1; float xr = 2.0f * (fx - x1) - 1.0f; xr = sin(xr * M_PI_2); xr = sin(xr * M_PI_2); xr = 0.5f * (xr + 1.0f); float xri = 1.0f - xr; if (x1 > 0) { x1--; if (x2 >= sx) x2--; } for (int y = 0; y < _height_map.size_y; y++) { /* Get our Y grid position and bi-linear ratio */ float fy = (float)(sy * y) / _height_map.size_y + 1.0f; uint y1 = (uint)fy; uint y2 = y1; float yr = 2.0f * (fy - y1) - 1.0f; yr = sin(yr * M_PI_2); yr = sin(yr * M_PI_2); yr = 0.5f * (yr + 1.0f); float yri = 1.0f - yr; if (y1 > 0) { y1--; if (y2 >= sy) y2--; } uint corner_a = c[x1 + sx * y1]; uint corner_b = c[x1 + sx * y2]; uint corner_c = c[x2 + sx * y1]; uint corner_d = c[x2 + sx * y2]; /* Bitmask of which curve maps are chosen, so that we do not bother * calculating a curve which won't be used. */ uint corner_bits = 0; corner_bits |= 1 << corner_a; corner_bits |= 1 << corner_b; corner_bits |= 1 << corner_c; corner_bits |= 1 << corner_d; height_t *h = &_height_map.height(x, y); /* Do not touch sea level */ if (*h < I2H(1)) continue; /* Only scale above sea level */ *h -= I2H(1); /* Apply all curve maps that are used on this tile. */ for (uint t = 0; t < lengthof(curve_maps); t++) { if (!HasBit(corner_bits, t)) continue; bool found = false; const control_point_t *cm = curve_maps[t].list; for (uint i = 0; i < curve_maps[t].length - 1; i++) { const control_point_t &p1 = cm[i]; const control_point_t &p2 = cm[i + 1]; if (*h >= p1.x && *h < p2.x) { ht[t] = p1.y + (*h - p1.x) * (p2.y - p1.y) / (p2.x - p1.x); found = true; break; } } assert(found); } /* Apply interpolation of curve map results. */ *h = (height_t)((ht[corner_a] * yri + ht[corner_b] * yr) * xri + (ht[corner_c] * yri + ht[corner_d] * yr) * xr); /* Readd sea level */ *h += I2H(1); } } }
/** * Get the amplitude associated with the currently selected * smoothness and maximum height level. * @param frequency The frequency to get the amplitudes for * @return The amplitudes to apply to the map. */ static amplitude_t GetAmplitude(int frequency) { /* Base noise amplitudes (multiplied by 1024) and indexed by "smoothness setting" and log2(frequency). * Used for maps that have their smallest side smaller than 512. */ static const amplitude_t amplitudes_small[][10] = { /* lowest frequency ...... highest (every corner) */ {60000, 2273, 4142, 2253, 421, 213, 137, 177, 37, 16}, ///< Very smooth {50000, 2273, 4142, 2253, 421, 213, 137, 177, 37, 61}, ///< Smooth {40000, 2273, 4142, 2253, 421, 213, 137, 177, 37, 91}, ///< Rough {30000, 2273, 4142, 2253, 421, 213, 137, 177, 37, 161}, ///< Very rough }; /* Base noise amplitudes (multiplied by 1024) and indexed by "smoothness setting" and log2(frequency). * Used for maps that have their smallest side equal to 512. */ static const amplitude_t amplitudes_middle[][10] = { {55000, 2273, 5142, 253, 2421, 213, 137, 177, 37, 16}, ///< Very smooth {45000, 2273, 5142, 253, 2421, 213, 137, 177, 37, 61}, ///< Smooth {35000, 2273, 5142, 253, 2421, 213, 137, 177, 37, 91}, ///< Rough {25000, 2273, 5142, 253, 2421, 213, 137, 177, 37, 161}, ///< Very rough }; /* Base noise amplitudes (multiplied by 1024) and indexed by "smoothness setting" and log2(frequency). * Used for maps that have their smallest side bigger than 512. */ static const amplitude_t amplitudes_large[][10] = { /* lowest frequency ...... highest (every corner) */ {55000, 2273, 5142, 253, 421, 2213, 137, 177, 37, 16}, ///< Very smooth {45000, 2273, 5142, 253, 421, 2213, 137, 177, 37, 61}, ///< Smooth {35000, 2273, 5142, 253, 421, 2213, 137, 177, 37, 91}, ///< Rough {25000, 2273, 5142, 253, 421, 2213, 137, 177, 37, 161}, ///< Very rough }; /* Make sure arrays cover all smoothness settings. */ assert_compile(lengthof(amplitudes_small) == TGEN_SMOOTHNESS_END); assert_compile(lengthof(amplitudes_middle) == TGEN_SMOOTHNESS_END); assert_compile(lengthof(amplitudes_large) == TGEN_SMOOTHNESS_END); /* Extrapolation factors for ranges before the table. * The extrapolation is needed to account for the higher map heights. They need larger * areas with a particular gradient so that we are able to create maps without too * many steep slopes up to the wanted height level. It's definitely not perfect since * it will bring larger rectangles with similar slopes which makes the rectangular * behaviour of TGP more noticable. However, these height differentiations cannot * happen over much smaller areas; we basically double the "range" to give a similar * slope for every doubling of map height. */ static const double extrapolation_factors[] = { 3.3, 2.8, 2.3, 1.8 }; int smoothness = _settings_game.game_creation.tgen_smoothness; int smallest_size = min(_settings_game.game_creation.map_x, _settings_game.game_creation.map_y); int index; amplitude_t amplitude; if (smallest_size < 9) { // Smallest map side is less than 2^9 == 512. index = frequency - MAX_TGP_FREQUENCIES + lengthof(amplitudes_small[0]); amplitude = amplitudes_small[smoothness][max(0, index)]; } else if (smallest_size == 9) { index = frequency - MAX_TGP_FREQUENCIES + lengthof(amplitudes_middle[0]); amplitude = amplitudes_middle[smoothness][max(0, index)]; } else { index = frequency - MAX_TGP_FREQUENCIES + lengthof(amplitudes_large[0]); amplitude = amplitudes_large[smoothness][max(0, index)]; } if (index >= 0) return amplitude; /* We need to extrapolate the amplitude. */ double extrapolation_factor = extrapolation_factors[smoothness]; int height_range = I2H(16); do { amplitude = (amplitude_t)(extrapolation_factor * (double)amplitude); height_range <<= 1; index++; } while (index < 0); return Clamp((TGPGetMaxHeight() - height_range) / height_range, 0, 1) * amplitude; }