mirror of
https://github.com/OpenTTD/OpenTTD.git
synced 2025-03-09 15:41:15 +00:00
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.
This commit is contained in:
parent
cbb24b5d71
commit
ea8c1d8597
@ -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);
|
uint32_t IntSqrt(uint32_t num);
|
||||||
|
|
||||||
#endif /* MATH_FUNC_HPP */
|
#endif /* MATH_FUNC_HPP */
|
||||||
|
@ -190,7 +190,7 @@ struct SelectGameWindow : public Window {
|
|||||||
this->mouse_idle_pos = _cursor.pos;
|
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. */
|
/* Move the main game viewport according to intro viewport commands. */
|
||||||
|
|
||||||
@ -252,7 +252,7 @@ struct SelectGameWindow : public Window {
|
|||||||
/* Update the viewport position. */
|
/* Update the viewport position. */
|
||||||
mw->viewport->dest_scrollpos_x = mw->viewport->scrollpos_x = pos.x;
|
mw->viewport->dest_scrollpos_x = mw->viewport->scrollpos_x = pos.x;
|
||||||
mw->viewport->dest_scrollpos_y = mw->viewport->scrollpos_y = pos.y;
|
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
|
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 */
|
/* If there is only one command, we just executed it and don't need to do any more */
|
||||||
|
@ -1868,11 +1868,65 @@ static inline void ClampViewportToMap(const Viewport *vp, int *scroll_x, int *sc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
* Update the viewport position being displayed.
|
||||||
* @param w %Window owning the viewport.
|
* @param w %Window owning the viewport.
|
||||||
*/
|
*/
|
||||||
void UpdateViewportPosition(Window *w)
|
void UpdateViewportPosition(Window *w, uint32_t delta_ms)
|
||||||
{
|
{
|
||||||
const Viewport *vp = w->viewport;
|
const Viewport *vp = w->viewport;
|
||||||
|
|
||||||
@ -1893,19 +1947,14 @@ void UpdateViewportPosition(Window *w)
|
|||||||
bool update_overlay = false;
|
bool update_overlay = false;
|
||||||
if (delta_x != 0 || delta_y != 0) {
|
if (delta_x != 0 || delta_y != 0) {
|
||||||
if (_settings_client.gui.smooth_scroll) {
|
if (_settings_client.gui.smooth_scroll) {
|
||||||
int max_scroll = Map::ScaleBySize1D(512 * ZOOM_LVL_BASE);
|
|
||||||
|
|
||||||
int delta_x_clamped;
|
int delta_x_clamped;
|
||||||
int delta_y_clamped;
|
int delta_y_clamped;
|
||||||
|
|
||||||
if (abs(delta_x) > abs(delta_y)) {
|
if (abs(delta_x) > abs(delta_y)) {
|
||||||
delta_x_clamped = Clamp(DivAwayFromZero(delta_x, 4), -max_scroll, max_scroll);
|
ClampSmoothScroll(delta_ms, delta_x, delta_y, delta_x_clamped, delta_y_clamped);
|
||||||
delta_y_clamped = delta_y * delta_x_clamped / delta_x;
|
|
||||||
} else {
|
} else {
|
||||||
delta_y_clamped = Clamp(DivAwayFromZero(delta_y, 4), -max_scroll, max_scroll);
|
ClampSmoothScroll(delta_ms, delta_y, delta_x, delta_y_clamped, delta_x_clamped);
|
||||||
delta_x_clamped = delta_x * delta_y_clamped / delta_y;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
w->viewport->scrollpos_x += delta_x_clamped;
|
w->viewport->scrollpos_x += delta_x_clamped;
|
||||||
w->viewport->scrollpos_y += delta_y_clamped;
|
w->viewport->scrollpos_y += delta_y_clamped;
|
||||||
} else {
|
} else {
|
||||||
|
@ -26,7 +26,7 @@ void InitializeWindowViewport(Window *w, int x, int y, int width, int height, st
|
|||||||
Viewport *IsPtInWindowViewport(const Window *w, int x, int y);
|
Viewport *IsPtInWindowViewport(const Window *w, int x, int y);
|
||||||
Point TranslateXYToTileCoord(const Viewport *vp, int x, int y, bool clamp_to_map = true);
|
Point TranslateXYToTileCoord(const Viewport *vp, int x, int y, bool clamp_to_map = true);
|
||||||
Point GetTileBelowCursor();
|
Point GetTileBelowCursor();
|
||||||
void UpdateViewportPosition(Window *w);
|
void UpdateViewportPosition(Window *w, uint32_t delta_ms);
|
||||||
|
|
||||||
bool MarkAllViewportsDirty(int left, int top, int right, int bottom);
|
bool MarkAllViewportsDirty(int left, int top, int right, int bottom);
|
||||||
|
|
||||||
|
@ -3066,7 +3066,7 @@ void UpdateWindows()
|
|||||||
|
|
||||||
for (Window *w : Window::Iterate()) {
|
for (Window *w : Window::Iterate()) {
|
||||||
/* Update viewport only if window is not shaded. */
|
/* 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();
|
NetworkDrawChatMessage();
|
||||||
/* Redraw mouse cursor in case it was hidden */
|
/* Redraw mouse cursor in case it was hidden */
|
||||||
|
Loading…
Reference in New Issue
Block a user