Beispiel #1
0
/** 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;
	}
}
Beispiel #2
0
/**
 * 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;
			}
		}
	}
}
Beispiel #3
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();
	}
Beispiel #4
0
/**
 * 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);
}
Beispiel #5
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);
		}

		Mesh->recalculateBoundingBox();
	}
Beispiel #6
0
/**
 * 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;
		}
	}
}
Beispiel #7
0
/**
 * 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);
			}
		}
	}
}
Beispiel #8
0
/**
 * 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);
}
Beispiel #9
0
/** 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;
}
Beispiel #10
0
/**
 * 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);
}
Beispiel #11
0
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);
		}
	}
}
Beispiel #12
0
/**
 * 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);
		}
	}
}