Changeset - r27058:f196a4b1ea62
[Not reviewed]
master
0 5 0
Rubidium - 20 months ago 2023-02-25 22:58:46
rubidium@openttd.org
Change: make GetPartialZ consistent, meaning Z of adjacent slopes continue

Previously, on a straight line of a one corner up slope with the adjacent
steep sloop the Z would increase one step every two sub pixels, except for one
case where one sub pixel is skipped. Similarly, a steep slope with two
adjacent one corner up slopes, would have a bump in the height line along the
diagonal whenever it enters/leaves the steep slope tile.
5 files changed with 120 insertions and 122 deletions:
0 comments (0 inline, 0 general)
src/ground_vehicle.hpp
Show inline comments
 
@@ -165,25 +165,25 @@ struct GroundVehicle : public Specialize
 
		if (HasBit(this->gv_flags, GVF_GOINGUP_BIT)) {
 
			switch (this->direction) {
 
				case DIR_NE:
 
					this->z_pos += (this->x_pos & 1); break;
 
					this->z_pos += (this->x_pos & 1) ^ 1; break;
 
				case DIR_SW:
 
					this->z_pos += (this->x_pos & 1) ^ 1; break;
 
					this->z_pos += (this->x_pos & 1); break;
 
				case DIR_NW:
 
					this->z_pos += (this->y_pos & 1); break;
 
					this->z_pos += (this->y_pos & 1) ^ 1; break;
 
				case DIR_SE:
 
					this->z_pos += (this->y_pos & 1) ^ 1; break;
 
					this->z_pos += (this->y_pos & 1); break;
 
				default: break;
 
			}
 
		} else if (HasBit(this->gv_flags, GVF_GOINGDOWN_BIT)) {
 
			switch (this->direction) {
 
				case DIR_NE:
 
					this->z_pos -= (this->x_pos & 1); break;
 
					this->z_pos -= (this->x_pos & 1) ^ 1; break;
 
				case DIR_SW:
 
					this->z_pos -= (this->x_pos & 1) ^ 1; break;
 
					this->z_pos -= (this->x_pos & 1); break;
 
				case DIR_NW:
 
					this->z_pos -= (this->y_pos & 1); break;
 
					this->z_pos -= (this->y_pos & 1) ^ 1; break;
 
				case DIR_SE:
 
					this->z_pos -= (this->y_pos & 1) ^ 1; break;
 
					this->z_pos -= (this->y_pos & 1); break;
 
				default: break;
 
			}
 
		}
 
@@ -212,8 +212,7 @@ struct GroundVehicle : public Specialize
 
			int8 d = DiagDirToAxis(dir) == AXIS_X ? x_pos : y_pos;
 
			/* We need only the least significant bit */
 
			d &= 1;
 
			/* Conditional "^ 1". Optimised to "(dir - 1) <= 1". */
 
			d ^= (int8)(dir == DIAGDIR_SW || dir == DIAGDIR_SE);
 
			d ^= (int8)(dir == DIAGDIR_NW || dir == DIAGDIR_NE);
 
			/* Subtraction instead of addition because we are testing for GVF_GOINGUP_BIT.
 
			 * GVF_GOINGUP_BIT is used because it's bit 0, so simple AND can be used,
 
			 * without any shift */
src/landscape.cpp
Show inline comments
 
@@ -210,137 +210,79 @@ uint ApplyFoundationToSlope(Foundation f
 

	
 

	
 
/**
 
 * Determines height at given coordinate of a slope
 
 * @param x x coordinate
 
 * @param y y coordinate
 
 * Determines height at given coordinate of a slope.
 
 *
 
 * At the northern corner (0, 0) the result is always a multiple of TILE_HEIGHT.
 
 * When the height is a fractional Z, then the height is rounded down. For example,
 
 * when at the height is 0 at x = 0 and the height is 8 at x = 16 (actually x = 0
 
 * of the next tile), then height is 0 at x = 1, 1 at x = 2, and 7 at x = 15.
 
 * @param x x coordinate (value from 0 to 15)
 
 * @param y y coordinate (value from 0 to 15)
 
 * @param corners slope to examine
 
 * @return height of given point of given slope
 
 */
 
uint GetPartialPixelZ(int x, int y, Slope corners)
 
{
 
	if (IsHalftileSlope(corners)) {
 
		/* A foundation is placed on half the tile at a specific corner. This means that,
 
		 * depending on the corner, that one half of the tile is at the maximum height. */
 
		switch (GetHalftileSlopeCorner(corners)) {
 
			case CORNER_W:
 
				if (x - y >= 0) return GetSlopeMaxPixelZ(corners);
 
				if (x > y) return GetSlopeMaxPixelZ(corners);
 
				break;
 

	
 
			case CORNER_S:
 
				if (x - (y ^ 0xF) >= 0) return GetSlopeMaxPixelZ(corners);
 
				if (x + y >= (int)TILE_SIZE) return GetSlopeMaxPixelZ(corners);
 
				break;
 

	
 
			case CORNER_E:
 
				if (y - x >= 0) return GetSlopeMaxPixelZ(corners);
 
				if (x <= y) return GetSlopeMaxPixelZ(corners);
 
				break;
 

	
 
			case CORNER_N:
 
				if ((y ^ 0xF) - x >= 0) return GetSlopeMaxPixelZ(corners);
 
				if (x + y < (int)TILE_SIZE) return GetSlopeMaxPixelZ(corners);
 
				break;
 

	
 
			default: NOT_REACHED();
 
		}
 
	}
 

	
 
	int z = 0;
 

	
 
	switch (RemoveHalftileSlope(corners)) {
 
		case SLOPE_W:
 
			if (x - y >= 0) {
 
				z = (x - y) >> 1;
 
			}
 
			break;
 

	
 
		case SLOPE_S:
 
			y ^= 0xF;
 
			if ((x - y) >= 0) {
 
				z = (x - y) >> 1;
 
			}
 
			break;
 

	
 
		case SLOPE_SW:
 
			z = (x >> 1) + 1;
 
			break;
 
		case SLOPE_FLAT: return 0;
 

	
 
		case SLOPE_E:
 
			if (y - x >= 0) {
 
				z = (y - x) >> 1;
 
			}
 
			break;
 

	
 
		case SLOPE_EW:
 
		case SLOPE_NS:
 
		case SLOPE_ELEVATED:
 
			z = 4;
 
			break;
 
		/* One corner is up.*/
 
		case SLOPE_N: return x + y <= (int)TILE_SIZE ? (TILE_SIZE - x - y)     >> 1 : 0;
 
		case SLOPE_E: return y >= x                  ? (1 + y - x)             >> 1 : 0;
 
		case SLOPE_S: return x + y >= (int)TILE_SIZE ? (1 + x + y - TILE_SIZE) >> 1 : 0;
 
		case SLOPE_W: return x >= y                  ? (x - y)                 >> 1 : 0;
 

	
 
		case SLOPE_SE:
 
			z = (y >> 1) + 1;
 
			break;
 

	
 
		case SLOPE_WSE:
 
			z = 8;
 
			y ^= 0xF;
 
			if (x - y < 0) {
 
				z += (x - y) >> 1;
 
			}
 
			break;
 

	
 
		case SLOPE_N:
 
			y ^= 0xF;
 
			if (y - x >= 0) {
 
				z = (y - x) >> 1;
 
			}
 
			break;
 
		/* Two corners next to eachother are up. */
 
		case SLOPE_NE: return (TILE_SIZE - x) >> 1;
 
		case SLOPE_SE: return (y + 1) >> 1;
 
		case SLOPE_SW: return (x + 1) >> 1;
 
		case SLOPE_NW: return (TILE_SIZE - y) >> 1;
 

	
 
		case SLOPE_NW:
 
			z = (y ^ 0xF) >> 1;
 
			break;
 

	
 
		case SLOPE_NWS:
 
			z = 8;
 
			if (x - y < 0) {
 
				z += (x - y) >> 1;
 
			}
 
			break;
 
		/* Three corners are up on the same level. */
 
		case SLOPE_ENW: return x + y >= (int)TILE_SIZE ? TILE_HEIGHT - ((1 + x + y - TILE_SIZE) >> 1) : TILE_HEIGHT;
 
		case SLOPE_SEN: return y < x                   ? TILE_HEIGHT - ((x - y)                 >> 1) : TILE_HEIGHT;
 
		case SLOPE_WSE: return x + y <= (int)TILE_SIZE ? TILE_HEIGHT - ((TILE_SIZE - x - y)     >> 1) : TILE_HEIGHT;
 
		case SLOPE_NWS: return x < y                   ? TILE_HEIGHT - ((1 + y - x)             >> 1) : TILE_HEIGHT;
 

	
 
		case SLOPE_NE:
 
			z = (x ^ 0xF) >> 1;
 
			break;
 

	
 
		case SLOPE_ENW:
 
			z = 8;
 
			y ^= 0xF;
 
			if (y - x < 0) {
 
				z += (y - x) >> 1;
 
			}
 
			break;
 
		/* Two corners at opposite sides are up. */
 
		case SLOPE_NS: return x + y < (int)TILE_SIZE ? (TILE_SIZE - x - y) >> 1 : (1 + x + y - TILE_SIZE) >> 1;
 
		case SLOPE_EW: return x >= y ? (x - y) >> 1 : (1 + y - x) >> 1;
 

	
 
		case SLOPE_SEN:
 
			z = 8;
 
			if (y - x < 0) {
 
				z += (y - x) >> 1;
 
			}
 
			break;
 

	
 
		case SLOPE_STEEP_S:
 
			z = 1 + ((x + y) >> 1);
 
			break;
 
		/* Very special cases. */
 
		case SLOPE_ELEVATED: return TILE_HEIGHT;
 

	
 
		case SLOPE_STEEP_W:
 
			z = 1 + ((x + (y ^ 0xF)) >> 1);
 
			break;
 

	
 
		case SLOPE_STEEP_N:
 
			z = 1 + (((x ^ 0xF) + (y ^ 0xF)) >> 1);
 
			break;
 
		/* Steep slopes. The top is at 2 * TILE_HEIGHT. */
 
		case SLOPE_STEEP_N: return (TILE_SIZE - x + TILE_SIZE - y) >> 1;
 
		case SLOPE_STEEP_E: return (TILE_SIZE + 1 + y - x) >> 1;
 
		case SLOPE_STEEP_S: return (1 + x + y) >> 1;
 
		case SLOPE_STEEP_W: return (TILE_SIZE + x - y) >> 1;
 

	
 
		case SLOPE_STEEP_E:
 
			z = 1 + (((x ^ 0xF) + y) >> 1);
 
			break;
 

	
 
		default: break;
 
		default: NOT_REACHED();
 
	}
 

	
 
	return z;
 
}
 

	
 
/**
src/saveload/afterload.cpp
Show inline comments
 
@@ -528,6 +528,25 @@ static uint FixVehicleInclination(Vehicl
 
}
 

	
 
/**
 
 * Check whether the ground vehicles are at the correct Z-coordinate. When they
 
 * are not, this will cause all kinds of problems later on as the vehicle might
 
 * not get onto bridges and so on.
 
 */
 
static void CheckGroundVehiclesAtCorrectZ()
 
{
 
	for (Vehicle *v : Vehicle::Iterate()) {
 
		if (v->IsGroundVehicle()) {
 
			/*
 
			 * Either the vehicle is not actually on the given tile, i.e. it is
 
			 * in the wormhole of a bridge or a tunnel, or the Z-coordinate must
 
			 * be the same as when it would be recalculated right now.
 
			 */
 
			assert(v->tile != TileVirtXY(v->x_pos, v->y_pos) || v->z_pos == GetSlopePixelZ(v->x_pos, v->y_pos, true));
 
		}
 
	}
 
}
 

	
 
/**
 
 * Checks for the possibility that a bridge may be on this tile
 
 * These are in fact all the tile types on which a bridge can be found
 
 * @param t The tile to analyze
 
@@ -1249,7 +1268,7 @@ bool AfterLoadGame()
 
					case DIAGDIR_SW: if ((v->x_pos & 0xF) != TILE_SIZE - 1) continue; break;
 
					case DIAGDIR_NW: if ((v->y_pos & 0xF) !=  0)            continue; break;
 
				}
 
			} else if (v->z_pos > GetSlopePixelZ(v->x_pos, v->y_pos, true)) {
 
			} else if (v->z_pos > GetTileMaxPixelZ(TileVirtXY(v->x_pos, v->y_pos))) {
 
				v->tile = GetNorthernBridgeEnd(v->tile);
 
				v->UpdatePosition();
 
			} else {
 
@@ -2594,6 +2613,24 @@ bool AfterLoadGame()
 
		}
 
	}
 

	
 

	
 
	if (IsSavegameVersionBefore(SLV_CONSISTENT_PARTIAL_Z)) {
 
		/*
 
		 * The logic of GetPartialPixelZ has been changed, so the resulting Zs on
 
		 * the map are consistent. This requires that the Z position of some
 
		 * vehicles is updated to reflect this new situation.
 
		 *
 
		 * This needs to be before SLV_158, because that performs asserts using
 
		 * GetSlopePixelZ which internally uses GetPartialPixelZ.
 
		 */
 
		for (Vehicle *v : Vehicle::Iterate()) {
 
			if (v->IsGroundVehicle() && TileVirtXY(v->x_pos, v->y_pos) == v->tile) {
 
				/* Vehicle is on the ground, and not in a wormhole. */
 
				v->z_pos = GetSlopePixelZ(v->x_pos, v->y_pos, true);
 
			}
 
		}
 
	}
 

	
 
	if (IsSavegameVersionBefore(SLV_158)) {
 
		for (Vehicle *v : Vehicle::Iterate()) {
 
			switch (v->type) {
 
@@ -3228,6 +3265,8 @@ bool AfterLoadGame()
 

	
 
	AfterLoadLinkGraphs();
 

	
 
	CheckGroundVehiclesAtCorrectZ();
 

	
 
	/* Start the scripts. This MUST happen after everything else except
 
	 * starting a new company. */
 
	StartScripts();
src/saveload/saveload.h
Show inline comments
 
@@ -348,6 +348,7 @@ enum SaveLoadVersion : uint16 {
 
	SLV_LINKGRAPH_EDGES,                    ///< 304  PR#10314 Explicitly store link graph edges destination, PR#10471 int64 instead of uint64 league rating
 

	
 
	SLV_VELOCITY_NAUTICAL,                  ///< 305  PR#10594 Separation of land and nautical velocity (knots!)
 
	SLV_CONSISTENT_PARTIAL_Z,               ///< 306  PR#10570 Conversion from an inconsistent partial Z calculation for slopes, to one that is (more) consistent.
 

	
 
	SL_MAX_VERSION,                         ///< Highest possible saveload version
 
};
src/tunnelbridge_cmd.cpp
Show inline comments
 
@@ -1680,18 +1680,15 @@ static int GetSlopePixelZ_TunnelBridge(T
 

	
 
		/* On the bridge ramp? */
 
		if (ground_vehicle) {
 
			int delta;
 

	
 
			if (tileh != SLOPE_FLAT) return z + TILE_HEIGHT;
 

	
 
			switch (dir) {
 
				default: NOT_REACHED();
 
				case DIAGDIR_NE: delta = (TILE_SIZE - 1 - x) / 2; break;
 
				case DIAGDIR_SE: delta = y / 2; break;
 
				case DIAGDIR_SW: delta = x / 2; break;
 
				case DIAGDIR_NW: delta = (TILE_SIZE - 1 - y) / 2; break;
 
				case DIAGDIR_NE: tileh = SLOPE_NE; break;
 
				case DIAGDIR_SE: tileh = SLOPE_SE; break;
 
				case DIAGDIR_SW: tileh = SLOPE_SW; break;
 
				case DIAGDIR_NW: tileh = SLOPE_NW; break;
 
			}
 
			return z + 1 + delta;
 
		}
 
	}
 

	
 
@@ -1859,6 +1856,29 @@ static void ChangeTileOwner_TunnelBridge
 
}
 

	
 
/**
 
 * Helper to prepare the ground vehicle when entering a bridge. This get called
 
 * when entering the bridge, at the last frame of travel on the bridge head.
 
 * Our calling function gets called before UpdateInclination/UpdateZPosition,
 
 * which normally controls the Z-coordinate. However, in the wormhole of the
 
 * bridge the vehicle is in a strange state so UpdateInclination does not get
 
 * called for the wormhole of the bridge and as such the going up/down bits
 
 * would remain set. As such, this function clears those. In doing so, the call
 
 * to UpdateInclination will not update the Z-coordinate, so that has to be
 
 * done here as well.
 
 * @param gv The ground vehicle entering the bridge.
 
 */
 
template <typename T>
 
static void PrepareToEnterBridge(T *gv)
 
{
 
	if (HasBit(gv->gv_flags, GVF_GOINGUP_BIT)) {
 
		gv->z_pos++;
 
		ClrBit(gv->gv_flags, GVF_GOINGUP_BIT);
 
	} else {
 
		ClrBit(gv->gv_flags, GVF_GOINGDOWN_BIT);
 
	}
 
}
 

	
 
/**
 
 * Frame when the 'enter tunnel' sound should be played. This is the second
 
 * frame on a tile, so the sound is played shortly after entering the tunnel
 
 * tile, while the vehicle is still visible.
 
@@ -1959,17 +1979,14 @@ static VehicleEnterTileStatus VehicleEnt
 
				case VEH_TRAIN: {
 
					Train *t = Train::From(v);
 
					t->track = TRACK_BIT_WORMHOLE;
 
					ClrBit(t->gv_flags, GVF_GOINGUP_BIT);
 
					ClrBit(t->gv_flags, GVF_GOINGDOWN_BIT);
 
					PrepareToEnterBridge(t);
 
					break;
 
				}
 

	
 
				case VEH_ROAD: {
 
					RoadVehicle *rv = RoadVehicle::From(v);
 
					rv->state = RVSB_WORMHOLE;
 
					/* There are no slopes inside bridges / tunnels. */
 
					ClrBit(rv->gv_flags, GVF_GOINGUP_BIT);
 
					ClrBit(rv->gv_flags, GVF_GOINGDOWN_BIT);
 
					PrepareToEnterBridge(rv);
 
					break;
 
				}
 

	
0 comments (0 inline, 0 general)