/** Start at given point, move in given direction, find and Smooth coast in that direction */ static void HeightMapSmoothCoastInDirection(int org_x, int org_y, int dir_x, int dir_y) { const int max_coast_dist_from_edge = 35; const int max_coast_Smooth_depth = 35; int x, y; int ed; // coast distance from edge int depth; height_t h_prev = 16; height_t h; assert(IsValidXY(org_x, org_y)); /* Search for the coast (first non-water tile) */ for (x = org_x, y = org_y, ed = 0; IsValidXY(x, y) && ed < max_coast_dist_from_edge; x += dir_x, y += dir_y, ed++) { /* Coast found? */ if (_height_map.height(x, y) > 15) break; /* Coast found in the neighborhood? */ if (IsValidXY(x + dir_y, y + dir_x) && _height_map.height(x + dir_y, y + dir_x) > 0) break; /* Coast found in the neighborhood on the other side */ if (IsValidXY(x - dir_y, y - dir_x) && _height_map.height(x - dir_y, y - dir_x) > 0) break; } /* Coast found or max_coast_dist_from_edge has been reached. * Soften the coast slope */ for (depth = 0; IsValidXY(x, y) && depth <= max_coast_Smooth_depth; depth++, x += dir_x, y += dir_y) { h = _height_map.height(x, y); h = min(h, h_prev + (4 + depth)); // coast softening formula _height_map.height(x, y) = h; h_prev = h; } }
/** * This routine sculpts in from the edge a random amount, again a Perlin * sequence, to avoid the rigid flat-edge slopes that were present before. The * Perlin noise map doesnt know where we are going to slice across, and so we * often cut straight through high terrain. the smoothing routine makes it * legal, gradually increasing up from the edge to the original terrain height. * By cutting parts of this away, it gives a far more irregular edge to the * map-edge. Sometimes it works beautifully with the existing sea & lakes, and * creates a very realistic coastline. Other times the variation is less, and * the map-edge shows its cliff-like roots. * * This routine may be extended to randomly sculpt the height of the terrain * near the edge. This will have the coast edge at low level (1-3), rising in * smoothed steps inland to about 15 tiles in. This should make it look as * though the map has been built for the map size, rather than a slice through * a larger map. * * Please note that all the small numbers; 53, 101, 167, etc. are small primes * to help give the perlin noise a bit more of a random feel. */ static void HeightMapCoastLines(uint8 water_borders) { int smallest_size = min(_settings_game.game_creation.map_x, _settings_game.game_creation.map_y); const int margin = 4; uint y, x; double max_x; double max_y; /* Lower to sea level */ for (y = 0; y <= _height_map.size_y; y++) { if (HasBit(water_borders, BORDER_NE)) { /* Top right */ max_x = abs((perlin_coast_noise_2D(_height_map.size_y - y, y, 0.9, 53) + 0.25) * 5 + (perlin_coast_noise_2D(y, y, 0.35, 179) + 1) * 12); max_x = max((smallest_size * smallest_size / 16) + max_x, (smallest_size * smallest_size / 16) + margin - max_x); if (smallest_size < 8 && max_x > 5) max_x /= 1.5; for (x = 0; x < max_x; x++) { _height_map.height(x, y) = 0; } } if (HasBit(water_borders, BORDER_SW)) { /* Bottom left */ max_x = abs((perlin_coast_noise_2D(_height_map.size_y - y, y, 0.85, 101) + 0.3) * 6 + (perlin_coast_noise_2D(y, y, 0.45, 67) + 0.75) * 8); max_x = max((smallest_size * smallest_size / 16) + max_x, (smallest_size * smallest_size / 16) + margin - max_x); if (smallest_size < 8 && max_x > 5) max_x /= 1.5; for (x = _height_map.size_x; x > (_height_map.size_x - 1 - max_x); x--) { _height_map.height(x, y) = 0; } } } /* Lower to sea level */ for (x = 0; x <= _height_map.size_x; x++) { if (HasBit(water_borders, BORDER_NW)) { /* Top left */ max_y = abs((perlin_coast_noise_2D(x, _height_map.size_y / 2, 0.9, 167) + 0.4) * 5 + (perlin_coast_noise_2D(x, _height_map.size_y / 3, 0.4, 211) + 0.7) * 9); max_y = max((smallest_size * smallest_size / 16) + max_y, (smallest_size * smallest_size / 16) + margin - max_y); if (smallest_size < 8 && max_y > 5) max_y /= 1.5; for (y = 0; y < max_y; y++) { _height_map.height(x, y) = 0; } } if (HasBit(water_borders, BORDER_SE)) { /* Bottom right */ max_y = abs((perlin_coast_noise_2D(x, _height_map.size_y / 3, 0.85, 71) + 0.25) * 6 + (perlin_coast_noise_2D(x, _height_map.size_y / 3, 0.35, 193) + 0.75) * 12); max_y = max((smallest_size * smallest_size / 16) + max_y, (smallest_size * smallest_size / 16) + margin - max_y); if (smallest_size < 8 && max_y > 5) max_y /= 1.5; for (y = _height_map.size_y; y > (_height_map.size_y - 1 - max_y); y--) { _height_map.height(x, y) = 0; } } } }
void init(const HeightMap &hm, f32 scale, colour_func cf, IVideoDriver *driver) { Scale = scale; const u32 mp = driver -> getMaximalPrimitiveCount(); Width = hm.width(); Height = hm.height(); const u32 sw = mp / (6 * Height); // the width of each piece u32 i=0; for(u32 y0 = 0; y0 < Height; y0 += sw) { u16 y1 = y0 + sw; if (y1 >= Height) y1 = Height - 1; // the last one might be narrower addstrip(hm, cf, y0, y1, i); ++i; } if (i<Mesh->getMeshBufferCount()) { // clear the rest for (u32 j=i; j<Mesh->getMeshBufferCount(); ++j) { Mesh->getMeshBuffer(j)->drop(); } Mesh->MeshBuffers.erase(i,Mesh->getMeshBufferCount()-i); } // set dirty flag to make sure that hardware copies of this // buffer are also updated, see IMesh::setHardwareMappingHint Mesh->setDirty(); Mesh->recalculateBoundingBox(); }
/** * 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); }
void init(const HeightMap &hm, f32 scale, colour_func cf, IVideoDriver *driver) { Scale = scale; const u32 mp = driver -> getMaximalPrimitiveCount(); Width = hm.width(); Height = hm.height(); const u32 sw = mp / (6 * Height); // the width of each piece u32 i=0; for(u32 y0 = 0; y0 < Height; y0 += sw) { u16 y1 = y0 + sw; if (y1 >= Height) y1 = Height - 1; // the last one might be narrower addstrip(hm, cf, y0, y1, i); ++i; } if (i<Mesh->getMeshBufferCount()) { // clear the rest for (u32 j=i; j<Mesh->getMeshBufferCount(); ++j) { Mesh->getMeshBuffer(j)->drop(); } Mesh->MeshBuffers.erase(i,Mesh->getMeshBufferCount()-i); } Mesh->recalculateBoundingBox(); }
/** * This routine provides the essential cleanup necessary before OTTD can * display the terrain. When generated, the terrain heights can jump more than * one level between tiles. This routine smooths out those differences so that * the most it can change is one level. When OTTD can support cliffs, this * routine may not be necessary. */ static void HeightMapSmoothSlopes(height_t dh_max) { for (int y = 0; y <= (int)_height_map.size_y; y++) { for (int x = 0; x <= (int)_height_map.size_x; x++) { height_t h_max = min(_height_map.height(x > 0 ? x - 1 : x, y), _height_map.height(x, y > 0 ? y - 1 : y)) + dh_max; if (_height_map.height(x, y) > h_max) _height_map.height(x, y) = h_max; } } for (int y = _height_map.size_y; y >= 0; y--) { for (int x = _height_map.size_x; x >= 0; x--) { height_t h_max = min(_height_map.height(x < _height_map.size_x ? x + 1 : x, y), _height_map.height(x, y < _height_map.size_y ? y + 1 : y)) + dh_max; if (_height_map.height(x, y) > h_max) _height_map.height(x, y) = h_max; } } }
/** * Base Perlin noise generator - fills height map with raw Perlin noise. * * This runs several iterations with increasing precision; the last iteration looks at areas * of 1 by 1 tiles, the second to last at 2 by 2 tiles and the initial 2**MAX_TGP_FREQUENCIES * by 2**MAX_TGP_FREQUENCIES tiles. */ static void HeightMapGenerate() { /* Trying to apply noise to uninitialized height map */ assert(_height_map.h != NULL); int start = max(MAX_TGP_FREQUENCIES - (int)min(MapLogX(), MapLogY()), 0); bool first = true; for (int frequency = start; frequency < MAX_TGP_FREQUENCIES; frequency++) { const amplitude_t amplitude = GetAmplitude(frequency); /* Ignore zero amplitudes; it means our map isn't height enough for this * amplitude, so ignore it and continue with the next set of amplitude. */ if (amplitude == 0) continue; const int step = 1 << (MAX_TGP_FREQUENCIES - frequency - 1); if (first) { /* This is first round, we need to establish base heights with step = size_min */ for (int y = 0; y <= _height_map.size_y; y += step) { for (int x = 0; x <= _height_map.size_x; x += step) { height_t height = (amplitude > 0) ? RandomHeight(amplitude) : 0; _height_map.height(x, y) = height; } } first = false; continue; } /* It is regular iteration round. * Interpolate height values at odd x, even y tiles */ for (int y = 0; y <= _height_map.size_y; y += 2 * step) { for (int x = 0; x <= _height_map.size_x - 2 * step; x += 2 * step) { height_t h00 = _height_map.height(x + 0 * step, y); height_t h02 = _height_map.height(x + 2 * step, y); height_t h01 = (h00 + h02) / 2; _height_map.height(x + 1 * step, y) = h01; } } /* Interpolate height values at odd y tiles */ for (int y = 0; y <= _height_map.size_y - 2 * step; y += 2 * step) { for (int x = 0; x <= _height_map.size_x; x += step) { height_t h00 = _height_map.height(x, y + 0 * step); height_t h20 = _height_map.height(x, y + 2 * step); height_t h10 = (h00 + h20) / 2; _height_map.height(x, y + 1 * step) = h10; } } /* Add noise for next higher frequency (smaller steps) */ for (int y = 0; y <= _height_map.size_y; y += step) { for (int x = 0; x <= _height_map.size_x; x += step) { _height_map.height(x, y) += RandomHeight(amplitude); } } } }
/** * One interpolation and noise round * * The heights on the map are generated in an iterative process. * We start off with a frequency of 1 (log_frequency == 0), and generate heights only for corners on the most coarsly mesh * (i.e. only for x/y coordinates which are multiples of the minimum edge length). * * After this initial step the frequency is doubled (log_frequency incremented) each iteration to generate corners on the next finer mesh. * The heights of the newly added corners are first set by interpolating the heights from the previous iteration. * Finally noise with the given amplitude is applied to all corners of the new mesh. * * Generation terminates, when the frequency has reached the map size. I.e. the mesh is as fine as the map, and every corner height * has been set. * * @param log_frequency frequency (logarithmic) to apply noise for * @param amplitude Amplitude for the noise * @return false if we are finished (reached the minimal step size / highest frequency) */ static bool ApplyNoise(uint log_frequency, amplitude_t amplitude) { uint size_min = min(_height_map.size_x, _height_map.size_y); uint step = size_min >> log_frequency; uint x, y; /* Trying to apply noise to uninitialized height map */ assert(_height_map.h != NULL); /* Are we finished? */ if (step == 0) return false; if (log_frequency == 0) { /* This is first round, we need to establish base heights with step = size_min */ for (y = 0; y <= _height_map.size_y; y += step) { for (x = 0; x <= _height_map.size_x; x += step) { height_t height = (amplitude > 0) ? RandomHeight(amplitude) : 0; _height_map.height(x, y) = height; } } return true; } /* It is regular iteration round. * Interpolate height values at odd x, even y tiles */ for (y = 0; y <= _height_map.size_y; y += 2 * step) { for (x = 0; x < _height_map.size_x; x += 2 * step) { height_t h00 = _height_map.height(x + 0 * step, y); height_t h02 = _height_map.height(x + 2 * step, y); height_t h01 = (h00 + h02) / 2; _height_map.height(x + 1 * step, y) = h01; } } /* Interpolate height values at odd y tiles */ for (y = 0; y < _height_map.size_y; y += 2 * step) { for (x = 0; x <= _height_map.size_x; x += step) { height_t h00 = _height_map.height(x, y + 0 * step); height_t h20 = _height_map.height(x, y + 2 * step); height_t h10 = (h00 + h20) / 2; _height_map.height(x, y + 1 * step) = h10; } } /* Add noise for next higher frequency (smaller steps) */ for (y = 0; y <= _height_map.size_y; y += step) { for (x = 0; x <= _height_map.size_x; x += step) { _height_map.height(x, y) += RandomHeight(amplitude); } } return (step > 1); }
/** Returns min, max and average height from height map */ static void HeightMapGetMinMaxAvg(height_t *min_ptr, height_t *max_ptr, height_t *avg_ptr) { height_t h_min, h_max, h_avg, *h; int64 h_accu = 0; h_min = h_max = _height_map.height(0, 0); /* Get h_min, h_max and accumulate heights into h_accu */ FOR_ALL_TILES_IN_HEIGHT(h) { if (*h < h_min) h_min = *h; if (*h > h_max) h_max = *h; h_accu += *h; } /* Get average height */ h_avg = (height_t)(h_accu / (_height_map.size_x * _height_map.size_y)); /* Return required results */ if (min_ptr != NULL) *min_ptr = h_min; if (max_ptr != NULL) *max_ptr = h_max; if (avg_ptr != NULL) *avg_ptr = h_avg; }
/** * 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 wont be high enough, and there will be very little tropic on the map. * Thus Tropic works best on Hilly or Mountainous. */ void GenerateTerrainPerlin() { uint x, y; 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 (y = 0; y < _height_map.size_y - 1; y++) MakeVoid(_height_map.size_x * y); for (x = 0; x < _height_map.size_x; x++) MakeVoid(x); } /* Transfer height map into OTTD map */ for (y = 0; y < _height_map.size_y; y++) { for (x = 0; x < _height_map.size_x; x++) { int height = H2I(_height_map.height(x, y)); if (height < 0) height = 0; if (height > 15) height = 15; TgenSetTileHeight(TileXY(x, y), height); } } IncreaseGeneratingWorldProgress(GWP_LANDSCAPE); FreeHeightMap(); GenerateWorldSetAbortCallback(NULL); }
static void HeightMapCurves(uint level) { height_t ht[lengthof(_curve_maps)]; /* Set up a grid to choose curve maps based on location */ uint sx = Clamp(1 << level, 2, 32); uint sy = Clamp(1 << level, 2, 32); byte *c = (byte *)alloca(sx * sy); for (uint i = 0; i < sx * sy; i++) { c[i] = Random() % lengthof(_curve_maps); } /* Apply curves */ for (uint 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 + 0.5f; 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 (uint 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 + 0.5f; 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); /* 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; 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); break; } } } /* 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); } } }
/** * 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); } } }