Changeset - r28563:a4308f732eb8
[Not reviewed]
master
0 5 0
Patric Stout - 3 months ago 2024-01-22 15:45:00
truebrain@openttd.org
Change: make for smooth-scrolling based on actual time

This means if rendering takes a bit longer, scrolling goes a bit
quicker, making travel time always about the same time for the
same distance.
5 files changed with 61 insertions and 29 deletions:
0 comments (0 inline, 0 general)
src/core/math_func.hpp
Show inline comments
 
@@ -350,23 +350,6 @@ constexpr int RoundDivSU(int a, uint b)
 
	}
 
}
 

	
 
/**
 
 * Computes (a / b) rounded away from zero.
 
 * @param a Numerator
 
 * @param b Denominator
 
 * @return Quotient, rounded away from zero
 
 */
 
constexpr int DivAwayFromZero(int a, uint b)
 
{
 
	const int _b = static_cast<int>(b);
 
	if (a > 0) {
 
		return (a + _b - 1) / _b;
 
	} else {
 
		/* Note: Behaviour of negative numerator division is truncation toward zero. */
 
		return (a - _b + 1) / _b;
 
	}
 
}
 

	
 
uint32_t IntSqrt(uint32_t num);
 

	
 
#endif /* MATH_FUNC_HPP */
src/intro_gui.cpp
Show inline comments
 
@@ -190,7 +190,7 @@ struct SelectGameWindow : public Window 
 
		this->mouse_idle_pos = _cursor.pos;
 
	}
 

	
 
	void OnRealtimeTick([[maybe_unused]] uint delta_ms) override
 
	void OnRealtimeTick(uint delta_ms) override
 
	{
 
		/* Move the main game viewport according to intro viewport commands. */
 

	
 
@@ -252,7 +252,7 @@ struct SelectGameWindow : public Window 
 
		/* Update the viewport position. */
 
		mw->viewport->dest_scrollpos_x = mw->viewport->scrollpos_x = pos.x;
 
		mw->viewport->dest_scrollpos_y = mw->viewport->scrollpos_y = pos.y;
 
		UpdateViewportPosition(mw);
 
		UpdateViewportPosition(mw, delta_ms);
 
		mw->SetDirty(); // Required during panning, otherwise logo graphics disappears
 

	
 
		/* If there is only one command, we just executed it and don't need to do any more */
src/viewport.cpp
Show inline comments
 
@@ -1869,10 +1869,64 @@ static inline void ClampViewportToMap(co
 
}
 

	
 
/**
 
 * Clamp the smooth scroll to a maxmimum speed and distance based on time elapsed.
 
 *
 
 * Every 30ms, we move 1/4th of the distance, to give a smooth movement experience.
 
 * But we never go over the max_scroll speed.
 
 *
 
 * @param delta_ms Time elapsed since last update.
 
 * @param delta_hi The distance to move in highest dimension (can't be zero).
 
 * @param delta_lo The distance to move in lowest dimension.
 
 * @param[out] delta_hi_clamped The clamped distance to move in highest dimension.
 
 * @param[out] delta_lo_clamped The clamped distance to move in lowest dimension.
 
 */
 
static void ClampSmoothScroll(uint32_t delta_ms, int64_t delta_hi, int64_t delta_lo, int &delta_hi_clamped, int &delta_lo_clamped)
 
{
 
	/** A tile is 64 pixels in width at 1x zoom; viewport coordinates are in 4x zoom. */
 
	constexpr int PIXELS_PER_TILE = TILE_PIXELS * 2 * ZOOM_LVL_BASE;
 
	constexpr int MS_PER_STEP = 30; ///< Time between each step in the smooth scroll.
 

	
 
	static uint32_t remainder_time = 0;
 

	
 
	assert(delta_hi != 0);
 

	
 
	int64_t delta_left = delta_hi;
 
	int max_scroll = 0;
 

	
 
	for (uint count = 0; count < (delta_ms + remainder_time) / MS_PER_STEP; count++) {
 
		/* We move 1/4th of the distance per 30ms, to give a smooth movement experience. */
 
		delta_left = delta_left * 3 / 4;
 
		/* But we don't allow more than 16 tiles movement per 30ms; longer distances take longer.
 
		 * This means that the full width of a map would take ~1 second, ignoring the slowdown:
 
		 * 256 / 16 * 30ms = 480ms. */
 
		max_scroll += Map::ScaleBySize1D(16 * PIXELS_PER_TILE);
 
	}
 
	remainder_time = (delta_ms + remainder_time) % MS_PER_STEP;
 

	
 
	/* We never go over the max_scroll speed. */
 
	delta_hi_clamped = Clamp(delta_hi - delta_left, -max_scroll, max_scroll);
 
	/* The lower delta is in ratio of the higher delta, so we keep going straight at the destination. */
 
	delta_lo_clamped = delta_lo * delta_hi_clamped / delta_hi;
 

	
 
	/* Ensure we always move (delta_hi can't be zero). */
 
	if (delta_hi_clamped == 0) {
 
		delta_hi_clamped = delta_hi > 0 ? 1 : -1;
 
	}
 

	
 
	/* Also ensure we always move on the lower delta. This is mostly to avoid a
 
	 * situation at borders, where you can't move from (x, y) to (x + 1, y),
 
	 * but you can move to (x + 1, y + 1). This due to how viewport clamping
 
	 * works and rounding issue. */
 
	if (delta_lo_clamped == 0 && delta_lo != 0) {
 
		delta_lo_clamped = delta_lo > 0 ? 1 : -1;
 
	}
 
}
 

	
 
/**
 
 * Update the viewport position being displayed.
 
 * @param w %Window owning the viewport.
 
 */
 
void UpdateViewportPosition(Window *w)
 
void UpdateViewportPosition(Window *w, uint32_t delta_ms)
 
{
 
	const Viewport *vp = w->viewport;
 

	
 
@@ -1893,19 +1947,14 @@ void UpdateViewportPosition(Window *w)
 
		bool update_overlay = false;
 
		if (delta_x != 0 || delta_y != 0) {
 
			if (_settings_client.gui.smooth_scroll) {
 
				int max_scroll = Map::ScaleBySize1D(512 * ZOOM_LVL_BASE);
 

	
 
				int delta_x_clamped;
 
				int delta_y_clamped;
 

	
 
				if (abs(delta_x) > abs(delta_y)) {
 
					delta_x_clamped = Clamp(DivAwayFromZero(delta_x, 4), -max_scroll, max_scroll);
 
					delta_y_clamped = delta_y * delta_x_clamped / delta_x;
 
					ClampSmoothScroll(delta_ms, delta_x, delta_y, delta_x_clamped, delta_y_clamped);
 
				} else {
 
					delta_y_clamped = Clamp(DivAwayFromZero(delta_y, 4), -max_scroll, max_scroll);
 
					delta_x_clamped = delta_x * delta_y_clamped / delta_y;
 
					ClampSmoothScroll(delta_ms, delta_y, delta_x, delta_y_clamped, delta_x_clamped);
 
				}
 

	
 
				w->viewport->scrollpos_x += delta_x_clamped;
 
				w->viewport->scrollpos_y += delta_y_clamped;
 
			} else {
src/viewport_func.h
Show inline comments
 
@@ -26,7 +26,7 @@ void InitializeWindowViewport(Window *w,
 
Viewport *IsPtInWindowViewport(const Window *w, int x, int y);
 
Point TranslateXYToTileCoord(const Viewport *vp, int x, int y, bool clamp_to_map = true);
 
Point GetTileBelowCursor();
 
void UpdateViewportPosition(Window *w);
 
void UpdateViewportPosition(Window *w, uint32_t delta_ms);
 

	
 
bool MarkAllViewportsDirty(int left, int top, int right, int bottom);
 

	
src/window.cpp
Show inline comments
 
@@ -3066,7 +3066,7 @@ void UpdateWindows()
 

	
 
	for (Window *w : Window::Iterate()) {
 
		/* Update viewport only if window is not shaded. */
 
		if (w->viewport != nullptr && !w->IsShaded()) UpdateViewportPosition(w);
 
		if (w->viewport != nullptr && !w->IsShaded()) UpdateViewportPosition(w, delta_ms.count());
 
	}
 
	NetworkDrawChatMessage();
 
	/* Redraw mouse cursor in case it was hidden */
0 comments (0 inline, 0 general)